@aquera/ngx-smart-table 0.0.17-patch-0.6 → 0.0.17-patch-0.8
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-chip-editor.mjs +5 -2
- package/esm2020/lib/editors/nile-select-editor.mjs +119 -6
- package/esm2020/lib/renderer/components/st-cell/st-cell.component.mjs +9 -3
- package/esm2020/lib/renderer/components/st-column-menu/st-column-menu.component.mjs +36 -17
- package/esm2020/lib/renderer/components/st-row-actions-dropdown/st-row-actions-dropdown.component.mjs +34 -27
- package/esm2020/lib/renderer/components/st-table/st-table.component.mjs +5 -5
- package/fesm2015/aquera-ngx-smart-table.mjs +212 -58
- package/fesm2015/aquera-ngx-smart-table.mjs.map +1 -1
- package/fesm2020/aquera-ngx-smart-table.mjs +201 -53
- package/fesm2020/aquera-ngx-smart-table.mjs.map +1 -1
- package/lib/editors/nile-chip-editor.d.ts +1 -0
- package/lib/editors/nile-select-editor.d.ts +11 -0
- package/lib/renderer/components/st-column-menu/st-column-menu.component.d.ts +4 -2
- package/lib/renderer/components/st-row-actions-dropdown/st-row-actions-dropdown.component.d.ts +4 -2
- package/lib/renderer/components/st-table/st-table.component.d.ts +2 -0
- package/package.json +1 -1
|
@@ -3889,15 +3889,128 @@ class NileSelectEditor {
|
|
|
3889
3889
|
// Clear container and append select
|
|
3890
3890
|
context.container.innerHTML = '';
|
|
3891
3891
|
context.container.appendChild(this.select);
|
|
3892
|
-
// Focus
|
|
3892
|
+
// Focus after mount; open dropdown only once nile-popup exists (avoids null this.popup in nile-select)
|
|
3893
|
+
const openOnEdit = this.options.openDropdownOnEdit !== false;
|
|
3893
3894
|
setTimeout(() => {
|
|
3895
|
+
void this.activateSelectAfterMount(openOnEdit);
|
|
3896
|
+
}, 50);
|
|
3897
|
+
}
|
|
3898
|
+
async activateSelectAfterMount(openOnEdit) {
|
|
3899
|
+
try {
|
|
3900
|
+
this.select?.focus();
|
|
3901
|
+
}
|
|
3902
|
+
catch {
|
|
3903
|
+
// ignore
|
|
3904
|
+
}
|
|
3905
|
+
if (!openOnEdit || !this.select || this.options.disabled) {
|
|
3906
|
+
return;
|
|
3907
|
+
}
|
|
3908
|
+
await this.openNileSelectDropdownSafely();
|
|
3909
|
+
}
|
|
3910
|
+
/** Lit @query('nile-popup') / .select — must exist before handleOpenChange touches this.popup.popup */
|
|
3911
|
+
isNileSelectPopupMounted(select) {
|
|
3912
|
+
return !!select.shadowRoot?.querySelector('nile-popup');
|
|
3913
|
+
}
|
|
3914
|
+
async invokeShowHandlingPromise(host) {
|
|
3915
|
+
if (typeof host.show !== 'function') {
|
|
3916
|
+
return;
|
|
3917
|
+
}
|
|
3918
|
+
try {
|
|
3919
|
+
const out = host.show();
|
|
3920
|
+
if (out != null && typeof out.then === 'function') {
|
|
3921
|
+
await out.catch(() => undefined);
|
|
3922
|
+
}
|
|
3923
|
+
}
|
|
3924
|
+
catch {
|
|
3925
|
+
// sync throw from show()
|
|
3926
|
+
}
|
|
3927
|
+
}
|
|
3928
|
+
async openNileSelectDropdownSafely() {
|
|
3929
|
+
const select = this.select;
|
|
3930
|
+
if (!select) {
|
|
3931
|
+
return;
|
|
3932
|
+
}
|
|
3933
|
+
try {
|
|
3934
|
+
await customElements.whenDefined('nile-select');
|
|
3935
|
+
}
|
|
3936
|
+
catch {
|
|
3937
|
+
// ignore
|
|
3938
|
+
}
|
|
3939
|
+
if (this.options.enableVirtualScroll) {
|
|
3894
3940
|
try {
|
|
3895
|
-
|
|
3941
|
+
await customElements.whenDefined('nile-virtual-select');
|
|
3896
3942
|
}
|
|
3897
|
-
catch
|
|
3898
|
-
//
|
|
3943
|
+
catch {
|
|
3944
|
+
// ignore — not registered in all environments
|
|
3945
|
+
}
|
|
3946
|
+
// Same idea as nile-popup: inner host may not exist until Lit finishes a pass (or several).
|
|
3947
|
+
const maxVirtualWaitPasses = 50;
|
|
3948
|
+
for (let i = 0; i < maxVirtualWaitPasses; i++) {
|
|
3949
|
+
const vs = select.shadowRoot?.querySelector('nile-virtual-select');
|
|
3950
|
+
if (vs) {
|
|
3951
|
+
await this.invokeShowHandlingPromise(vs);
|
|
3952
|
+
return;
|
|
3953
|
+
}
|
|
3954
|
+
const lit = select;
|
|
3955
|
+
if (lit.updateComplete?.then) {
|
|
3956
|
+
try {
|
|
3957
|
+
await lit.updateComplete;
|
|
3958
|
+
}
|
|
3959
|
+
catch {
|
|
3960
|
+
// ignore
|
|
3961
|
+
}
|
|
3962
|
+
}
|
|
3963
|
+
await new Promise(resolve => requestAnimationFrame(() => resolve()));
|
|
3899
3964
|
}
|
|
3900
|
-
|
|
3965
|
+
return;
|
|
3966
|
+
}
|
|
3967
|
+
const maxWaitPasses = 50;
|
|
3968
|
+
for (let i = 0; i < maxWaitPasses; i++) {
|
|
3969
|
+
if (this.isNileSelectPopupMounted(select)) {
|
|
3970
|
+
break;
|
|
3971
|
+
}
|
|
3972
|
+
const lit = select;
|
|
3973
|
+
if (lit.updateComplete?.then) {
|
|
3974
|
+
try {
|
|
3975
|
+
await lit.updateComplete;
|
|
3976
|
+
}
|
|
3977
|
+
catch {
|
|
3978
|
+
// ignore
|
|
3979
|
+
}
|
|
3980
|
+
}
|
|
3981
|
+
await new Promise(resolve => requestAnimationFrame(() => resolve()));
|
|
3982
|
+
}
|
|
3983
|
+
if (!this.isNileSelectPopupMounted(select)) {
|
|
3984
|
+
return;
|
|
3985
|
+
}
|
|
3986
|
+
const host = select;
|
|
3987
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
3988
|
+
if (!this.isNileSelectPopupMounted(select)) {
|
|
3989
|
+
return;
|
|
3990
|
+
}
|
|
3991
|
+
try {
|
|
3992
|
+
if (typeof host.show === 'function') {
|
|
3993
|
+
await this.invokeShowHandlingPromise(host);
|
|
3994
|
+
}
|
|
3995
|
+
else {
|
|
3996
|
+
host.open = true;
|
|
3997
|
+
}
|
|
3998
|
+
return;
|
|
3999
|
+
}
|
|
4000
|
+
catch {
|
|
4001
|
+
// ignore
|
|
4002
|
+
}
|
|
4003
|
+
await new Promise(resolve => setTimeout(resolve, 40));
|
|
4004
|
+
const lit = select;
|
|
4005
|
+
if (lit.updateComplete?.then) {
|
|
4006
|
+
try {
|
|
4007
|
+
await lit.updateComplete;
|
|
4008
|
+
}
|
|
4009
|
+
catch {
|
|
4010
|
+
// ignore
|
|
4011
|
+
}
|
|
4012
|
+
}
|
|
4013
|
+
}
|
|
3901
4014
|
}
|
|
3902
4015
|
/**
|
|
3903
4016
|
* Set the initial value for the select
|
|
@@ -4945,6 +5058,7 @@ class NileChipEditor {
|
|
|
4945
5058
|
this.acceptsInitialKeypress = false;
|
|
4946
5059
|
this.eventListeners = [];
|
|
4947
5060
|
this.trackedValues = [];
|
|
5061
|
+
this.hasChangeOccurred = false;
|
|
4948
5062
|
}
|
|
4949
5063
|
edit(context) {
|
|
4950
5064
|
if (!context.container) {
|
|
@@ -5097,6 +5211,7 @@ class NileChipEditor {
|
|
|
5097
5211
|
const detail = e.detail;
|
|
5098
5212
|
const newValue = Array.isArray(detail?.value) ? [...detail.value] : (this.chip?.value ? [...this.chip.value] : []);
|
|
5099
5213
|
this.trackedValues = newValue;
|
|
5214
|
+
this.hasChangeOccurred = true;
|
|
5100
5215
|
context.onChange(newValue);
|
|
5101
5216
|
});
|
|
5102
5217
|
// Keyboard handling on the chip element
|
|
@@ -5165,6 +5280,7 @@ class NileChipEditor {
|
|
|
5165
5280
|
this.chip = undefined;
|
|
5166
5281
|
this.cellContainer = undefined;
|
|
5167
5282
|
this.trackedValues = [];
|
|
5283
|
+
this.hasChangeOccurred = false;
|
|
5168
5284
|
}
|
|
5169
5285
|
focus() {
|
|
5170
5286
|
try {
|
|
@@ -5173,7 +5289,7 @@ class NileChipEditor {
|
|
|
5173
5289
|
catch { /* ignore */ }
|
|
5174
5290
|
}
|
|
5175
5291
|
getCurrentValue() {
|
|
5176
|
-
if (this.
|
|
5292
|
+
if (this.hasChangeOccurred) {
|
|
5177
5293
|
return [...this.trackedValues];
|
|
5178
5294
|
}
|
|
5179
5295
|
if (!this.chip)
|
|
@@ -6381,8 +6497,14 @@ class StCellComponent {
|
|
|
6381
6497
|
}
|
|
6382
6498
|
}
|
|
6383
6499
|
onCellClick() {
|
|
6384
|
-
//
|
|
6385
|
-
//
|
|
6500
|
+
// First click: parent <td> focuses this cell (bubbles after this handler).
|
|
6501
|
+
// Second click while already focused: enter edit (spreadsheet-style).
|
|
6502
|
+
if (this.isEditable &&
|
|
6503
|
+
!this.cellLoading &&
|
|
6504
|
+
!this.cell.isEditing() &&
|
|
6505
|
+
this.cell.isFocused()) {
|
|
6506
|
+
this.startEdit();
|
|
6507
|
+
}
|
|
6386
6508
|
}
|
|
6387
6509
|
onCellDoubleClick() {
|
|
6388
6510
|
// Double-click starts editing regardless of EditMode (Excel-like behavior)
|
|
@@ -8096,7 +8218,7 @@ class StColumnMenuDropdownComponent {
|
|
|
8096
8218
|
*/
|
|
8097
8219
|
this.isOpen = false;
|
|
8098
8220
|
/**
|
|
8099
|
-
* Position of the dropdown (x, y coordinates)
|
|
8221
|
+
* Position of the dropdown (x, y coordinates, triggerTop for flip positioning)
|
|
8100
8222
|
*/
|
|
8101
8223
|
this.position = { x: 0, y: 0 };
|
|
8102
8224
|
/**
|
|
@@ -8209,25 +8331,41 @@ class StColumnMenuDropdownComponent {
|
|
|
8209
8331
|
this.dropdownStyle = {};
|
|
8210
8332
|
return;
|
|
8211
8333
|
}
|
|
8212
|
-
const viewportWidth = window.innerWidth;
|
|
8213
|
-
const viewportHeight = window.innerHeight;
|
|
8214
|
-
const dropdownWidth = 280; // Approximate dropdown width
|
|
8215
|
-
const dropdownHeight = 300; // Approximate max dropdown height
|
|
8216
8334
|
let { x, y } = this.position;
|
|
8217
|
-
//
|
|
8218
|
-
if (x + dropdownWidth > viewportWidth) {
|
|
8219
|
-
x = Math.max(10, viewportWidth - dropdownWidth - 10);
|
|
8220
|
-
}
|
|
8221
|
-
// Adjust vertical position if dropdown would overflow
|
|
8222
|
-
if (y + dropdownHeight > viewportHeight) {
|
|
8223
|
-
y = Math.max(10, viewportHeight - dropdownHeight - 10);
|
|
8224
|
-
}
|
|
8335
|
+
// Render at initial position first
|
|
8225
8336
|
this.dropdownStyle = {
|
|
8226
8337
|
position: 'fixed',
|
|
8227
8338
|
left: `${x}px`,
|
|
8228
8339
|
top: `${y}px`,
|
|
8229
|
-
'z-index': 9999
|
|
8340
|
+
'z-index': 9999,
|
|
8341
|
+
visibility: 'hidden'
|
|
8230
8342
|
};
|
|
8343
|
+
// After rendering, measure actual size and adjust if needed
|
|
8344
|
+
requestAnimationFrame(() => {
|
|
8345
|
+
const viewportWidth = window.innerWidth;
|
|
8346
|
+
const viewportHeight = window.innerHeight;
|
|
8347
|
+
const el = this.dropdownPanel?.nativeElement;
|
|
8348
|
+
const dropdownWidth = el?.offsetWidth || 280;
|
|
8349
|
+
const dropdownHeight = el?.offsetHeight || 200;
|
|
8350
|
+
// Adjust horizontal position if dropdown would overflow
|
|
8351
|
+
if (x + dropdownWidth > viewportWidth) {
|
|
8352
|
+
x = Math.max(10, viewportWidth - dropdownWidth - 10);
|
|
8353
|
+
}
|
|
8354
|
+
// Adjust vertical position — flip above the trigger if it overflows bottom
|
|
8355
|
+
if (y + dropdownHeight > viewportHeight) {
|
|
8356
|
+
const triggerTop = this.position.triggerTop ?? this.position.y;
|
|
8357
|
+
y = triggerTop - dropdownHeight - 4;
|
|
8358
|
+
}
|
|
8359
|
+
// Clamp to viewport edges
|
|
8360
|
+
x = Math.max(10, x);
|
|
8361
|
+
y = Math.max(10, y);
|
|
8362
|
+
this.dropdownStyle = {
|
|
8363
|
+
position: 'fixed',
|
|
8364
|
+
left: `${x}px`,
|
|
8365
|
+
top: `${y}px`,
|
|
8366
|
+
'z-index': 9999
|
|
8367
|
+
};
|
|
8368
|
+
});
|
|
8231
8369
|
}
|
|
8232
8370
|
/**
|
|
8233
8371
|
* Check if an action is disabled
|
|
@@ -8294,10 +8432,10 @@ class StColumnMenuDropdownComponent {
|
|
|
8294
8432
|
}
|
|
8295
8433
|
}
|
|
8296
8434
|
StColumnMenuDropdownComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StColumnMenuDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
8297
|
-
StColumnMenuDropdownComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: StColumnMenuDropdownComponent, selector: "st-column-menu-dropdown", inputs: { isOpen: "isOpen", position: "position", context: "context" }, outputs: { actionClicked: "actionClicked", closed: "closed" }, host: { listeners: { "click": "onBackdropClick($event)" } }, viewQueries: [{ propertyName: "filterPopup", first: true, predicate: ["filterPopup"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<!-- Dropdown container with backdrop -->\n<div class=\"dropdown-container\" *ngIf=\"isOpen && context\">\n <!-- Backdrop -->\n <div class=\"dropdown-backdrop\" (click)=\"closed.emit()\"></div>\n \n <!-- Dropdown menu -->\n <div class=\"column-menu-dropdown\" [ngStyle]=\"dropdownStyle\">\n <!-- Main menu with actions -->\n <nile-menu *ngIf=\"!isFilterOpen\">\n <!-- Dynamically render all visible actions -->\n <ng-container *ngFor=\"let action of visibleActions; let i = index; let last = last\">\n <nile-menu-item \n (click)=\"onActionClick(action)\"\n [class.disabled]=\"isActionDisabled(action)\"\n [class.active]=\"isActionActive(action)\">\n <span class=\"checkmark\" *ngIf=\"isActionActive(action)\">\u2713</span>\n <nile-icon slot=\"prefix\" *ngIf=\"action.icon && !isActionActive(action)\" [name]=\"action.icon\"></nile-icon>\n <span class=\"action-label\">{{ action.label }}</span>\n </nile-menu-item>\n \n <!-- Add divider after action groups -->\n <nile-divider *ngIf=\"shouldShowDividerAfter(action, i, last)\"></nile-divider>\n </ng-container>\n \n <!-- Fallback if no actions -->\n <nile-menu-item *ngIf=\"visibleActions.length === 0\">\n No actions available\n </nile-menu-item>\n </nile-menu>\n \n <!-- Filter popup (conditionally rendered) -->\n <st-column-filter\n #filterPopup\n *ngIf=\"isFilterOpen && context\"\n [column]=\"context.column\"\n [tableState]=\"context.tableState\"\n [columnIndex]=\"context.columnIndex\"\n [isFirstColumn]=\"context.isFirstColumn\"\n [isLastColumn]=\"context.isLastColumn\"\n [isOpen]=\"isFilterOpen\"\n (filterApplied)=\"onFilterApplied($event)\"\n (filterCleared)=\"onFilterCleared()\"\n (closed)=\"onFilterClosed()\">\n </st-column-filter>\n </div>\n</div>\n", styles: [".dropdown-container{position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9998}.dropdown-backdrop{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:auto;z-index:9998}.column-menu-dropdown{min-width:200px;max-width:300px;background-color:#fff;border-radius:8px;box-shadow:0 10px 25px #00000026;overflow:hidden;pointer-events:auto;z-index:9999}nile-menu nile-divider::part(divider){margin:0}nile-menu nile-menu-item::part(base){height:2.5rem;min-height:auto}nile-menu nile-menu-item .checkmark{margin-right:8px;color:#4299e1;font-weight:700}\n"], components: [{ type: StColumnFilterComponent, selector: "st-column-filter", inputs: ["column", "tableState", "columnIndex", "isFirstColumn", "isLastColumn", "isOpen", "filterContext"], outputs: ["closed", "filterApplied", "filterCleared"] }], directives: [{ type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
|
|
8435
|
+
StColumnMenuDropdownComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: StColumnMenuDropdownComponent, selector: "st-column-menu-dropdown", inputs: { isOpen: "isOpen", position: "position", context: "context" }, outputs: { actionClicked: "actionClicked", closed: "closed" }, host: { listeners: { "click": "onBackdropClick($event)" } }, viewQueries: [{ propertyName: "filterPopup", first: true, predicate: ["filterPopup"], descendants: true }, { propertyName: "dropdownPanel", first: true, predicate: ["dropdownPanel"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<!-- Dropdown container with backdrop -->\n<div class=\"dropdown-container\" *ngIf=\"isOpen && context\">\n <!-- Backdrop -->\n <div class=\"dropdown-backdrop\" (click)=\"closed.emit()\"></div>\n \n <!-- Dropdown menu -->\n <div class=\"column-menu-dropdown\" #dropdownPanel [ngStyle]=\"dropdownStyle\">\n <!-- Main menu with actions -->\n <nile-menu *ngIf=\"!isFilterOpen\">\n <!-- Dynamically render all visible actions -->\n <ng-container *ngFor=\"let action of visibleActions; let i = index; let last = last\">\n <nile-menu-item \n (click)=\"onActionClick(action)\"\n [class.disabled]=\"isActionDisabled(action)\"\n [class.active]=\"isActionActive(action)\">\n <span class=\"checkmark\" *ngIf=\"isActionActive(action)\">\u2713</span>\n <nile-icon slot=\"prefix\" *ngIf=\"action.icon && !isActionActive(action)\" [name]=\"action.icon\"></nile-icon>\n <span class=\"action-label\">{{ action.label }}</span>\n </nile-menu-item>\n \n <!-- Add divider after action groups -->\n <nile-divider *ngIf=\"shouldShowDividerAfter(action, i, last)\"></nile-divider>\n </ng-container>\n \n <!-- Fallback if no actions -->\n <nile-menu-item *ngIf=\"visibleActions.length === 0\">\n No actions available\n </nile-menu-item>\n </nile-menu>\n \n <!-- Filter popup (conditionally rendered) -->\n <st-column-filter\n #filterPopup\n *ngIf=\"isFilterOpen && context\"\n [column]=\"context.column\"\n [tableState]=\"context.tableState\"\n [columnIndex]=\"context.columnIndex\"\n [isFirstColumn]=\"context.isFirstColumn\"\n [isLastColumn]=\"context.isLastColumn\"\n [isOpen]=\"isFilterOpen\"\n (filterApplied)=\"onFilterApplied($event)\"\n (filterCleared)=\"onFilterCleared()\"\n (closed)=\"onFilterClosed()\">\n </st-column-filter>\n </div>\n</div>\n", styles: [".dropdown-container{position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9998}.dropdown-backdrop{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:auto;z-index:9998}.column-menu-dropdown{min-width:200px;max-width:300px;background-color:#fff;border-radius:8px;box-shadow:0 10px 25px #00000026;overflow:hidden;pointer-events:auto;z-index:9999}nile-menu nile-divider::part(divider){margin:0}nile-menu nile-menu-item::part(base){height:2.5rem;min-height:auto}nile-menu nile-menu-item .checkmark{margin-right:8px;color:#4299e1;font-weight:700}\n"], components: [{ type: StColumnFilterComponent, selector: "st-column-filter", inputs: ["column", "tableState", "columnIndex", "isFirstColumn", "isLastColumn", "isOpen", "filterContext"], outputs: ["closed", "filterApplied", "filterCleared"] }], directives: [{ type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
|
|
8298
8436
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StColumnMenuDropdownComponent, decorators: [{
|
|
8299
8437
|
type: Component,
|
|
8300
|
-
args: [{ selector: 'st-column-menu-dropdown', template: "<!-- Dropdown container with backdrop -->\n<div class=\"dropdown-container\" *ngIf=\"isOpen && context\">\n <!-- Backdrop -->\n <div class=\"dropdown-backdrop\" (click)=\"closed.emit()\"></div>\n \n <!-- Dropdown menu -->\n <div class=\"column-menu-dropdown\" [ngStyle]=\"dropdownStyle\">\n <!-- Main menu with actions -->\n <nile-menu *ngIf=\"!isFilterOpen\">\n <!-- Dynamically render all visible actions -->\n <ng-container *ngFor=\"let action of visibleActions; let i = index; let last = last\">\n <nile-menu-item \n (click)=\"onActionClick(action)\"\n [class.disabled]=\"isActionDisabled(action)\"\n [class.active]=\"isActionActive(action)\">\n <span class=\"checkmark\" *ngIf=\"isActionActive(action)\">\u2713</span>\n <nile-icon slot=\"prefix\" *ngIf=\"action.icon && !isActionActive(action)\" [name]=\"action.icon\"></nile-icon>\n <span class=\"action-label\">{{ action.label }}</span>\n </nile-menu-item>\n \n <!-- Add divider after action groups -->\n <nile-divider *ngIf=\"shouldShowDividerAfter(action, i, last)\"></nile-divider>\n </ng-container>\n \n <!-- Fallback if no actions -->\n <nile-menu-item *ngIf=\"visibleActions.length === 0\">\n No actions available\n </nile-menu-item>\n </nile-menu>\n \n <!-- Filter popup (conditionally rendered) -->\n <st-column-filter\n #filterPopup\n *ngIf=\"isFilterOpen && context\"\n [column]=\"context.column\"\n [tableState]=\"context.tableState\"\n [columnIndex]=\"context.columnIndex\"\n [isFirstColumn]=\"context.isFirstColumn\"\n [isLastColumn]=\"context.isLastColumn\"\n [isOpen]=\"isFilterOpen\"\n (filterApplied)=\"onFilterApplied($event)\"\n (filterCleared)=\"onFilterCleared()\"\n (closed)=\"onFilterClosed()\">\n </st-column-filter>\n </div>\n</div>\n", styles: [".dropdown-container{position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9998}.dropdown-backdrop{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:auto;z-index:9998}.column-menu-dropdown{min-width:200px;max-width:300px;background-color:#fff;border-radius:8px;box-shadow:0 10px 25px #00000026;overflow:hidden;pointer-events:auto;z-index:9999}nile-menu nile-divider::part(divider){margin:0}nile-menu nile-menu-item::part(base){height:2.5rem;min-height:auto}nile-menu nile-menu-item .checkmark{margin-right:8px;color:#4299e1;font-weight:700}\n"] }]
|
|
8438
|
+
args: [{ selector: 'st-column-menu-dropdown', template: "<!-- Dropdown container with backdrop -->\n<div class=\"dropdown-container\" *ngIf=\"isOpen && context\">\n <!-- Backdrop -->\n <div class=\"dropdown-backdrop\" (click)=\"closed.emit()\"></div>\n \n <!-- Dropdown menu -->\n <div class=\"column-menu-dropdown\" #dropdownPanel [ngStyle]=\"dropdownStyle\">\n <!-- Main menu with actions -->\n <nile-menu *ngIf=\"!isFilterOpen\">\n <!-- Dynamically render all visible actions -->\n <ng-container *ngFor=\"let action of visibleActions; let i = index; let last = last\">\n <nile-menu-item \n (click)=\"onActionClick(action)\"\n [class.disabled]=\"isActionDisabled(action)\"\n [class.active]=\"isActionActive(action)\">\n <span class=\"checkmark\" *ngIf=\"isActionActive(action)\">\u2713</span>\n <nile-icon slot=\"prefix\" *ngIf=\"action.icon && !isActionActive(action)\" [name]=\"action.icon\"></nile-icon>\n <span class=\"action-label\">{{ action.label }}</span>\n </nile-menu-item>\n \n <!-- Add divider after action groups -->\n <nile-divider *ngIf=\"shouldShowDividerAfter(action, i, last)\"></nile-divider>\n </ng-container>\n \n <!-- Fallback if no actions -->\n <nile-menu-item *ngIf=\"visibleActions.length === 0\">\n No actions available\n </nile-menu-item>\n </nile-menu>\n \n <!-- Filter popup (conditionally rendered) -->\n <st-column-filter\n #filterPopup\n *ngIf=\"isFilterOpen && context\"\n [column]=\"context.column\"\n [tableState]=\"context.tableState\"\n [columnIndex]=\"context.columnIndex\"\n [isFirstColumn]=\"context.isFirstColumn\"\n [isLastColumn]=\"context.isLastColumn\"\n [isOpen]=\"isFilterOpen\"\n (filterApplied)=\"onFilterApplied($event)\"\n (filterCleared)=\"onFilterCleared()\"\n (closed)=\"onFilterClosed()\">\n </st-column-filter>\n </div>\n</div>\n", styles: [".dropdown-container{position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9998}.dropdown-backdrop{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:auto;z-index:9998}.column-menu-dropdown{min-width:200px;max-width:300px;background-color:#fff;border-radius:8px;box-shadow:0 10px 25px #00000026;overflow:hidden;pointer-events:auto;z-index:9999}nile-menu nile-divider::part(divider){margin:0}nile-menu nile-menu-item::part(base){height:2.5rem;min-height:auto}nile-menu nile-menu-item .checkmark{margin-right:8px;color:#4299e1;font-weight:700}\n"] }]
|
|
8301
8439
|
}], propDecorators: { isOpen: [{
|
|
8302
8440
|
type: Input
|
|
8303
8441
|
}], position: [{
|
|
@@ -8311,6 +8449,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImpor
|
|
|
8311
8449
|
}], filterPopup: [{
|
|
8312
8450
|
type: ViewChild,
|
|
8313
8451
|
args: ['filterPopup']
|
|
8452
|
+
}], dropdownPanel: [{
|
|
8453
|
+
type: ViewChild,
|
|
8454
|
+
args: ['dropdownPanel']
|
|
8314
8455
|
}], onBackdropClick: [{
|
|
8315
8456
|
type: HostListener,
|
|
8316
8457
|
args: ['click', ['$event']]
|
|
@@ -8323,7 +8464,7 @@ class StRowActionsDropdownComponent {
|
|
|
8323
8464
|
*/
|
|
8324
8465
|
this.isOpen = false;
|
|
8325
8466
|
/**
|
|
8326
|
-
* Position of the dropdown (x, y coordinates)
|
|
8467
|
+
* Position of the dropdown (x, y coordinates, triggerTop for flip positioning)
|
|
8327
8468
|
*/
|
|
8328
8469
|
this.position = { x: 0, y: 0 };
|
|
8329
8470
|
/**
|
|
@@ -8378,35 +8519,39 @@ class StRowActionsDropdownComponent {
|
|
|
8378
8519
|
this.dropdownStyle = {};
|
|
8379
8520
|
return;
|
|
8380
8521
|
}
|
|
8381
|
-
const DROPDOWN_WIDTH = 200; // Approximate width
|
|
8382
|
-
const DROPDOWN_HEIGHT = this.visibleActions.length * 40 + 16; // Approximate height
|
|
8383
|
-
const viewportWidth = window.innerWidth;
|
|
8384
|
-
const viewportHeight = window.innerHeight;
|
|
8385
8522
|
let left = this.position.x;
|
|
8386
8523
|
let top = this.position.y;
|
|
8387
|
-
//
|
|
8388
|
-
if (left + DROPDOWN_WIDTH > viewportWidth) {
|
|
8389
|
-
left = viewportWidth - DROPDOWN_WIDTH - 10;
|
|
8390
|
-
}
|
|
8391
|
-
// Check if dropdown would overflow bottom edge
|
|
8392
|
-
if (top + DROPDOWN_HEIGHT > viewportHeight) {
|
|
8393
|
-
// Position above the trigger
|
|
8394
|
-
top = this.position.y - DROPDOWN_HEIGHT;
|
|
8395
|
-
}
|
|
8396
|
-
// Ensure dropdown doesn't go off-screen on the left
|
|
8397
|
-
if (left < 10) {
|
|
8398
|
-
left = 10;
|
|
8399
|
-
}
|
|
8400
|
-
// Ensure dropdown doesn't go off-screen on the top
|
|
8401
|
-
if (top < 10) {
|
|
8402
|
-
top = 10;
|
|
8403
|
-
}
|
|
8524
|
+
// Render at initial position first (hidden until measured)
|
|
8404
8525
|
this.dropdownStyle = {
|
|
8405
8526
|
position: 'fixed',
|
|
8406
8527
|
left: `${left}px`,
|
|
8407
8528
|
top: `${top}px`,
|
|
8408
|
-
zIndex: TableZIndex.ROW_ACTIONS_DROPDOWN
|
|
8529
|
+
zIndex: TableZIndex.ROW_ACTIONS_DROPDOWN,
|
|
8530
|
+
visibility: 'hidden'
|
|
8409
8531
|
};
|
|
8532
|
+
// After rendering, measure actual size and adjust position directly on the DOM
|
|
8533
|
+
// (OnPush change detection won't pick up property changes inside requestAnimationFrame)
|
|
8534
|
+
requestAnimationFrame(() => {
|
|
8535
|
+
const el = this.dropdownPanel?.nativeElement;
|
|
8536
|
+
if (!el)
|
|
8537
|
+
return;
|
|
8538
|
+
const viewportWidth = window.innerWidth;
|
|
8539
|
+
const viewportHeight = window.innerHeight;
|
|
8540
|
+
const dropdownWidth = el.offsetWidth || 200;
|
|
8541
|
+
const dropdownHeight = el.offsetHeight || (this.visibleActions.length * 40 + 16);
|
|
8542
|
+
if (left + dropdownWidth > viewportWidth) {
|
|
8543
|
+
left = viewportWidth - dropdownWidth - 10;
|
|
8544
|
+
}
|
|
8545
|
+
if (top + dropdownHeight > viewportHeight) {
|
|
8546
|
+
const triggerTop = this.position.triggerTop ?? this.position.y;
|
|
8547
|
+
top = triggerTop - dropdownHeight - 4;
|
|
8548
|
+
}
|
|
8549
|
+
left = Math.max(10, left);
|
|
8550
|
+
top = Math.max(10, top);
|
|
8551
|
+
el.style.left = `${left}px`;
|
|
8552
|
+
el.style.top = `${top}px`;
|
|
8553
|
+
el.style.visibility = 'visible';
|
|
8554
|
+
});
|
|
8410
8555
|
}
|
|
8411
8556
|
/**
|
|
8412
8557
|
* Handle action click
|
|
@@ -8460,10 +8605,10 @@ class StRowActionsDropdownComponent {
|
|
|
8460
8605
|
}
|
|
8461
8606
|
}
|
|
8462
8607
|
StRowActionsDropdownComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StRowActionsDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
8463
|
-
StRowActionsDropdownComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: StRowActionsDropdownComponent, selector: "st-row-actions-dropdown", inputs: { isOpen: "isOpen", position: "position", context: "context" }, outputs: { actionClicked: "actionClicked", closed: "closed" }, host: { listeners: { "document:keydown.escape": "onEscapeKey($event)" } }, usesOnChanges: true, ngImport: i0, template: "<div class=\"dropdown-container\" *ngIf=\"isOpen && context\">\n <!-- Backdrop -->\n <div class=\"dropdown-overlay\" (click)=\"closed.emit()\"></div>\n \n <!-- Dropdown menu -->\n <div class=\"dropdown-menu\" [ngStyle]=\"dropdownStyle\">\n <nile-menu *ngIf=\"isOpen\"
|
|
8608
|
+
StRowActionsDropdownComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: StRowActionsDropdownComponent, selector: "st-row-actions-dropdown", inputs: { isOpen: "isOpen", position: "position", context: "context" }, outputs: { actionClicked: "actionClicked", closed: "closed" }, host: { listeners: { "document:keydown.escape": "onEscapeKey($event)" } }, viewQueries: [{ propertyName: "dropdownPanel", first: true, predicate: ["dropdownPanel"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"dropdown-container\" *ngIf=\"isOpen && context\">\n <!-- Backdrop -->\n <div class=\"dropdown-overlay\" (click)=\"closed.emit()\"></div>\n \n <!-- Dropdown menu -->\n <div class=\"dropdown-menu\" #dropdownPanel [ngStyle]=\"dropdownStyle\">\n <nile-menu *ngIf=\"isOpen\">\n <ng-container *ngFor=\"let action of visibleActions\">\n <nile-menu-item [class.disabled]=\"isActionDisabled(action)\" (click)=\"onActionClick(action)\" class=\"action-label\">\n <nile-icon *ngIf=\"action.icon\" size=\"14\" slot=\"prefix\" [name]=\"action.icon\"></nile-icon>\n {{ action.label }}\n </nile-menu-item>\n </ng-container>\n \n <nile-menu-item *ngIf=\"visibleActions.length === 0\">No actions available</nile-menu-item>\n </nile-menu>\n </div>\n</div>\n", styles: [".dropdown-container{position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9998}.dropdown-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background-color:transparent;pointer-events:auto;z-index:9998}.dropdown-menu{position:fixed;background-color:#fff;box-shadow:0 5px 15px #0000004d,0 0 0 1px #0000001a;overflow:hidden;pointer-events:auto;z-index:9999}.action-icon{display:flex;align-items:center;justify-content:center;font-size:16px;width:20px;flex-shrink:0}.action-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dropdown-empty{padding:16px;text-align:center;color:#a0aec0;font-size:14px;font-style:italic}nile-menu{height:fit-content}\n"], directives: [{ type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
8464
8609
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StRowActionsDropdownComponent, decorators: [{
|
|
8465
8610
|
type: Component,
|
|
8466
|
-
args: [{ selector: 'st-row-actions-dropdown', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"dropdown-container\" *ngIf=\"isOpen && context\">\n <!-- Backdrop -->\n <div class=\"dropdown-overlay\" (click)=\"closed.emit()\"></div>\n \n <!-- Dropdown menu -->\n <div class=\"dropdown-menu\" [ngStyle]=\"dropdownStyle\">\n <nile-menu *ngIf=\"isOpen\"
|
|
8611
|
+
args: [{ selector: 'st-row-actions-dropdown', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"dropdown-container\" *ngIf=\"isOpen && context\">\n <!-- Backdrop -->\n <div class=\"dropdown-overlay\" (click)=\"closed.emit()\"></div>\n \n <!-- Dropdown menu -->\n <div class=\"dropdown-menu\" #dropdownPanel [ngStyle]=\"dropdownStyle\">\n <nile-menu *ngIf=\"isOpen\">\n <ng-container *ngFor=\"let action of visibleActions\">\n <nile-menu-item [class.disabled]=\"isActionDisabled(action)\" (click)=\"onActionClick(action)\" class=\"action-label\">\n <nile-icon *ngIf=\"action.icon\" size=\"14\" slot=\"prefix\" [name]=\"action.icon\"></nile-icon>\n {{ action.label }}\n </nile-menu-item>\n </ng-container>\n \n <nile-menu-item *ngIf=\"visibleActions.length === 0\">No actions available</nile-menu-item>\n </nile-menu>\n </div>\n</div>\n", styles: [".dropdown-container{position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9998}.dropdown-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background-color:transparent;pointer-events:auto;z-index:9998}.dropdown-menu{position:fixed;background-color:#fff;box-shadow:0 5px 15px #0000004d,0 0 0 1px #0000001a;overflow:hidden;pointer-events:auto;z-index:9999}.action-icon{display:flex;align-items:center;justify-content:center;font-size:16px;width:20px;flex-shrink:0}.action-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dropdown-empty{padding:16px;text-align:center;color:#a0aec0;font-size:14px;font-style:italic}nile-menu{height:fit-content}\n"] }]
|
|
8467
8612
|
}], propDecorators: { isOpen: [{
|
|
8468
8613
|
type: Input
|
|
8469
8614
|
}], position: [{
|
|
@@ -8474,6 +8619,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImpor
|
|
|
8474
8619
|
type: Output
|
|
8475
8620
|
}], closed: [{
|
|
8476
8621
|
type: Output
|
|
8622
|
+
}], dropdownPanel: [{
|
|
8623
|
+
type: ViewChild,
|
|
8624
|
+
args: ['dropdownPanel']
|
|
8477
8625
|
}], onEscapeKey: [{
|
|
8478
8626
|
type: HostListener,
|
|
8479
8627
|
args: ['document:keydown.escape', ['$event']]
|
|
@@ -9751,10 +9899,10 @@ class StTableComponent {
|
|
|
9751
9899
|
event.stopPropagation();
|
|
9752
9900
|
const target = event.currentTarget;
|
|
9753
9901
|
const rect = target.getBoundingClientRect();
|
|
9754
|
-
// Calculate position (below the button by default)
|
|
9755
9902
|
const position = {
|
|
9756
9903
|
x: rect.left,
|
|
9757
|
-
y: rect.bottom + 4
|
|
9904
|
+
y: rect.bottom + 4,
|
|
9905
|
+
triggerTop: rect.top
|
|
9758
9906
|
};
|
|
9759
9907
|
// Create context
|
|
9760
9908
|
const context = {
|
|
@@ -9815,10 +9963,10 @@ class StTableComponent {
|
|
|
9815
9963
|
event.stopPropagation();
|
|
9816
9964
|
const target = event.currentTarget;
|
|
9817
9965
|
const rect = target.getBoundingClientRect();
|
|
9818
|
-
// Calculate position (below the button by default)
|
|
9819
9966
|
const position = {
|
|
9820
9967
|
x: rect.left,
|
|
9821
|
-
y: rect.bottom + 4
|
|
9968
|
+
y: rect.bottom + 4,
|
|
9969
|
+
triggerTop: rect.top
|
|
9822
9970
|
};
|
|
9823
9971
|
// Create context
|
|
9824
9972
|
const context = {
|