@fundamental-ngx/core 0.61.4 → 0.61.6
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/fesm2022/fundamental-ngx-core-breadcrumb.mjs +1 -1
- package/fesm2022/fundamental-ngx-core-breadcrumb.mjs.map +1 -1
- package/fesm2022/fundamental-ngx-core-carousel.mjs +7 -8
- package/fesm2022/fundamental-ngx-core-carousel.mjs.map +1 -1
- package/fesm2022/fundamental-ngx-core-form.mjs +12 -18
- package/fesm2022/fundamental-ngx-core-form.mjs.map +1 -1
- package/fesm2022/fundamental-ngx-core-icon.mjs +2 -2
- package/fesm2022/fundamental-ngx-core-icon.mjs.map +1 -1
- package/fesm2022/fundamental-ngx-core-inline-help.mjs +34 -15
- package/fesm2022/fundamental-ngx-core-inline-help.mjs.map +1 -1
- package/fesm2022/fundamental-ngx-core-menu.mjs +40 -28
- package/fesm2022/fundamental-ngx-core-menu.mjs.map +1 -1
- package/fesm2022/fundamental-ngx-core-multi-combobox.mjs +75 -31
- package/fesm2022/fundamental-ngx-core-multi-combobox.mjs.map +1 -1
- package/fesm2022/fundamental-ngx-core-popover.mjs +157 -18
- package/fesm2022/fundamental-ngx-core-popover.mjs.map +1 -1
- package/fesm2022/fundamental-ngx-core-product-switch.mjs +23 -27
- package/fesm2022/fundamental-ngx-core-product-switch.mjs.map +1 -1
- package/fesm2022/fundamental-ngx-core-shellbar.mjs +2 -2
- package/fesm2022/fundamental-ngx-core-shellbar.mjs.map +1 -1
- package/fesm2022/fundamental-ngx-core-tabs.mjs +19 -25
- package/fesm2022/fundamental-ngx-core-tabs.mjs.map +1 -1
- package/package.json +3 -3
- package/types/fundamental-ngx-core-carousel.d.ts +2 -3
- package/types/fundamental-ngx-core-form.d.ts +7 -10
- package/types/fundamental-ngx-core-inline-help.d.ts +20 -15
- package/types/fundamental-ngx-core-menu.d.ts +17 -9
- package/types/fundamental-ngx-core-multi-combobox.d.ts +18 -1
- package/types/fundamental-ngx-core-popover.d.ts +49 -4
- package/types/fundamental-ngx-core-product-switch.d.ts +20 -24
- package/types/fundamental-ngx-core-tabs.d.ts +5 -10
|
@@ -608,6 +608,10 @@ class PopoverService {
|
|
|
608
608
|
this._placementRefresh$ = new Subject();
|
|
609
609
|
/** @hidden */
|
|
610
610
|
this._ignoreTriggers = false;
|
|
611
|
+
/** @hidden Timer for delayed hover close, allows mouse to travel from trigger to overlay body. */
|
|
612
|
+
this._hoverCloseTimer = null;
|
|
613
|
+
/** @hidden Listeners for mouseenter/mouseleave on the overlay element. */
|
|
614
|
+
this._overlayHoverListeners = [];
|
|
611
615
|
/** @hidden */
|
|
612
616
|
this._modalBodyClass = 'fd-overlay-active';
|
|
613
617
|
/** @hidden */
|
|
@@ -676,6 +680,8 @@ class PopoverService {
|
|
|
676
680
|
}
|
|
677
681
|
});
|
|
678
682
|
this._destroyRef.onDestroy(() => {
|
|
683
|
+
this._clearHoverCloseTimer();
|
|
684
|
+
this._removeOverlayHoverListeners();
|
|
679
685
|
this._removeTriggerListeners();
|
|
680
686
|
if (this._overlayRef) {
|
|
681
687
|
this._overlayRef.detach();
|
|
@@ -717,12 +723,12 @@ class PopoverService {
|
|
|
717
723
|
}
|
|
718
724
|
/** Closes the popover. */
|
|
719
725
|
close(focusActiveElement = true) {
|
|
726
|
+
this._clearHoverCloseTimer();
|
|
727
|
+
this._removeOverlayHoverListeners();
|
|
720
728
|
if (this._overlayRef) {
|
|
721
729
|
this._overlayRef.dispose();
|
|
722
730
|
}
|
|
723
|
-
this.
|
|
724
|
-
this.isOpen.set(false);
|
|
725
|
-
this._programmaticChange = false;
|
|
731
|
+
this._safeSetIsOpen(false);
|
|
726
732
|
this.checkModalBackground();
|
|
727
733
|
this._focusLastActiveElementBeforeOpen(focusActiveElement);
|
|
728
734
|
}
|
|
@@ -748,9 +754,8 @@ class PopoverService {
|
|
|
748
754
|
if (this.fillControlMode()) {
|
|
749
755
|
this._listenOnResize();
|
|
750
756
|
}
|
|
751
|
-
this.
|
|
752
|
-
this.
|
|
753
|
-
this._programmaticChange = false;
|
|
757
|
+
this._safeSetIsOpen(true);
|
|
758
|
+
this._setupOverlayHoverListeners();
|
|
754
759
|
this._listenOnClose();
|
|
755
760
|
this._focusFirstTabbableElement();
|
|
756
761
|
this._onLoad.next(this._getPopoverBody()._elementRef);
|
|
@@ -784,9 +789,9 @@ class PopoverService {
|
|
|
784
789
|
}
|
|
785
790
|
}
|
|
786
791
|
/** Toggles the popover open state */
|
|
787
|
-
toggle(openAction = true, closeAction = true) {
|
|
792
|
+
toggle(openAction = true, closeAction = true, focusActiveElement) {
|
|
788
793
|
if (this.isOpen()) {
|
|
789
|
-
closeAction && this.close();
|
|
794
|
+
closeAction && this.close(focusActiveElement ?? true);
|
|
790
795
|
}
|
|
791
796
|
else {
|
|
792
797
|
openAction && this.open();
|
|
@@ -923,6 +928,7 @@ class PopoverService {
|
|
|
923
928
|
}
|
|
924
929
|
this._removeTriggerListeners();
|
|
925
930
|
if (this.triggers()?.length) {
|
|
931
|
+
const hasHoverTrigger = this._hasHoverTriggers();
|
|
926
932
|
this._normalizeTriggers().forEach((trigger) => {
|
|
927
933
|
this._eventRef.push(this._renderer.listen(this._triggerHtmlElement, trigger.trigger, (event) => {
|
|
928
934
|
if (this._ignoreTriggers || this.disabled()) {
|
|
@@ -930,7 +936,23 @@ class PopoverService {
|
|
|
930
936
|
}
|
|
931
937
|
const closeAction = !!trigger.closeAction;
|
|
932
938
|
const openAction = !!trigger.openAction;
|
|
933
|
-
|
|
939
|
+
// For hover triggers, use delayed close to prevent flickering
|
|
940
|
+
// when the mouse travels between the trigger and the popover body.
|
|
941
|
+
if (hasHoverTrigger && trigger.trigger === 'mouseenter' && openAction) {
|
|
942
|
+
this._clearHoverCloseTimer();
|
|
943
|
+
if (!this.isOpen()) {
|
|
944
|
+
this.toggle(openAction, closeAction);
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
else if (hasHoverTrigger && trigger.trigger === 'mouseleave' && closeAction) {
|
|
948
|
+
this._scheduleHoverClose();
|
|
949
|
+
}
|
|
950
|
+
else {
|
|
951
|
+
// When closing due to focusout, don't restore focus — the browser
|
|
952
|
+
// is already transferring focus to the next element.
|
|
953
|
+
const isFocusOut = trigger.trigger === 'focusout';
|
|
954
|
+
this.toggle(openAction, closeAction, isFocusOut ? false : undefined);
|
|
955
|
+
}
|
|
934
956
|
if (trigger.stopPropagation) {
|
|
935
957
|
event.stopImmediatePropagation();
|
|
936
958
|
}
|
|
@@ -946,6 +968,73 @@ class PopoverService {
|
|
|
946
968
|
this._triggerElement = trigger;
|
|
947
969
|
this._refreshTriggerListeners();
|
|
948
970
|
}
|
|
971
|
+
/**
|
|
972
|
+
* @hidden
|
|
973
|
+
* Safely sets isOpen signal, deferring if called during Angular's render cycle.
|
|
974
|
+
* This prevents NG0600 errors when focus events fire during template evaluation.
|
|
975
|
+
*/
|
|
976
|
+
_safeSetIsOpen(value) {
|
|
977
|
+
this._programmaticChange = true;
|
|
978
|
+
try {
|
|
979
|
+
this.isOpen.set(value);
|
|
980
|
+
this._programmaticChange = false;
|
|
981
|
+
}
|
|
982
|
+
catch (e) {
|
|
983
|
+
this._programmaticChange = false;
|
|
984
|
+
// NG0600: Writing to signals during render - defer to next macrotask
|
|
985
|
+
if (e instanceof Error && e.message?.includes('NG0600')) {
|
|
986
|
+
setTimeout(() => {
|
|
987
|
+
this._programmaticChange = true;
|
|
988
|
+
this.isOpen.set(value);
|
|
989
|
+
this._programmaticChange = false;
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
else {
|
|
993
|
+
throw e;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
/** @hidden Whether the current triggers include mouseenter or mouseleave. */
|
|
998
|
+
_hasHoverTriggers() {
|
|
999
|
+
return this.triggers().some((t) => {
|
|
1000
|
+
const name = typeof t === 'string' ? t : t.trigger;
|
|
1001
|
+
return name === 'mouseenter' || name === 'mouseleave';
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
/** @hidden Clears any pending delayed hover close. */
|
|
1005
|
+
_clearHoverCloseTimer() {
|
|
1006
|
+
if (this._hoverCloseTimer !== null) {
|
|
1007
|
+
clearTimeout(this._hoverCloseTimer);
|
|
1008
|
+
this._hoverCloseTimer = null;
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
/** @hidden Schedules a delayed close to allow mouse to travel from trigger to overlay body. */
|
|
1012
|
+
_scheduleHoverClose() {
|
|
1013
|
+
this._clearHoverCloseTimer();
|
|
1014
|
+
this._hoverCloseTimer = setTimeout(() => {
|
|
1015
|
+
this._hoverCloseTimer = null;
|
|
1016
|
+
this.close();
|
|
1017
|
+
}, 50);
|
|
1018
|
+
}
|
|
1019
|
+
/** @hidden Adds mouseenter/mouseleave listeners on the overlay element when hover triggers are active. */
|
|
1020
|
+
_setupOverlayHoverListeners() {
|
|
1021
|
+
this._removeOverlayHoverListeners();
|
|
1022
|
+
if (!this._hasHoverTriggers() || !this._overlayRef) {
|
|
1023
|
+
return;
|
|
1024
|
+
}
|
|
1025
|
+
const overlayElement = this._overlayRef.overlayElement;
|
|
1026
|
+
this._overlayHoverListeners.push(this._renderer.listen(overlayElement, 'mouseenter', () => {
|
|
1027
|
+
this._clearHoverCloseTimer();
|
|
1028
|
+
}));
|
|
1029
|
+
this._overlayHoverListeners.push(this._renderer.listen(overlayElement, 'mouseleave', () => {
|
|
1030
|
+
this._scheduleHoverClose();
|
|
1031
|
+
}));
|
|
1032
|
+
}
|
|
1033
|
+
/** @hidden Removes overlay hover listeners. */
|
|
1034
|
+
_removeOverlayHoverListeners() {
|
|
1035
|
+
this._overlayHoverListeners.forEach((fn) => fn());
|
|
1036
|
+
this._overlayHoverListeners = [];
|
|
1037
|
+
}
|
|
949
1038
|
/** @hidden */
|
|
950
1039
|
_normalizeTriggers() {
|
|
951
1040
|
return this.triggers().map((trigger, index) => {
|
|
@@ -1034,7 +1123,7 @@ class PopoverService {
|
|
|
1034
1123
|
/** Subscribe to close events from CDK Overlay, to throw proper events, change values */
|
|
1035
1124
|
_listenOnClose() {
|
|
1036
1125
|
const body = this._getPopoverBody();
|
|
1037
|
-
const closeEvents$ = merge(this._overlayRef.detachments(), outputToObservable(body.onClose), this._outsideClicks$());
|
|
1126
|
+
const closeEvents$ = merge(this._overlayRef.detachments(), outputToObservable(body.onClose), this._outsideClicks$(), this._escapeKeydowns$());
|
|
1038
1127
|
// Only use _stopCloseListening$ to stop listening, not _refresh$
|
|
1039
1128
|
// _refresh$ can emit due to signal effects and would complete the subscription prematurely
|
|
1040
1129
|
closeEvents$.pipe(takeUntil(this._stopCloseListening$), takeUntilDestroyed(this._destroyRef)).subscribe(() => {
|
|
@@ -1045,6 +1134,12 @@ class PopoverService {
|
|
|
1045
1134
|
_outsideClicks$() {
|
|
1046
1135
|
return merge(this._overlayRef.backdropClick(), this._overlayRef._outsidePointerEvents).pipe(filter((event) => this._shouldClose(event)));
|
|
1047
1136
|
}
|
|
1137
|
+
/** Listener for Escape keydown via CDK overlay's document-level keyboard dispatcher. */
|
|
1138
|
+
_escapeKeydowns$() {
|
|
1139
|
+
return this._overlayRef
|
|
1140
|
+
.keydownEvents()
|
|
1141
|
+
.pipe(filter((event) => this.closeOnEscapeKey() && event.key === 'Escape'));
|
|
1142
|
+
}
|
|
1048
1143
|
/** @hidden */
|
|
1049
1144
|
_shouldClose(event) {
|
|
1050
1145
|
return (this.isOpen() &&
|
|
@@ -1381,8 +1476,9 @@ class PopoverComponent {
|
|
|
1381
1476
|
if (currentIsOpen !== previousIsOpen) {
|
|
1382
1477
|
this.isOpenChange.emit(currentIsOpen);
|
|
1383
1478
|
}
|
|
1384
|
-
// Sync to service only if not already syncing (prevents loop)
|
|
1385
|
-
|
|
1479
|
+
// Sync to service only if not already syncing and not in mobile mode (prevents loop).
|
|
1480
|
+
// In mobile mode, the dialog handles open/close — the popover service should not be involved.
|
|
1481
|
+
if (!this._syncingIsOpen && !this.mobile()) {
|
|
1386
1482
|
this._syncingIsOpen = true;
|
|
1387
1483
|
this._popoverService.isOpen.set(currentIsOpen);
|
|
1388
1484
|
this._syncingIsOpen = false;
|
|
@@ -1396,6 +1492,10 @@ class PopoverComponent {
|
|
|
1396
1492
|
this._popoverService.isOpenChange
|
|
1397
1493
|
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
1398
1494
|
.subscribe((serviceIsOpen) => {
|
|
1495
|
+
// In mobile mode, the dialog handles open/close — ignore service changes.
|
|
1496
|
+
if (this.mobile()) {
|
|
1497
|
+
return;
|
|
1498
|
+
}
|
|
1399
1499
|
// Only update if different and not already syncing (prevents loop)
|
|
1400
1500
|
if (!this._syncingIsOpen && this.isOpen() !== serviceIsOpen) {
|
|
1401
1501
|
this._syncingIsOpen = true;
|
|
@@ -1411,8 +1511,9 @@ class PopoverComponent {
|
|
|
1411
1511
|
const triggerValue = this.trigger();
|
|
1412
1512
|
// Always sync disabled state to service (for both trigger directive and control usage)
|
|
1413
1513
|
this._popoverService.disabled.set(effectiveConfig.disabled);
|
|
1414
|
-
// Full sync only when trigger is set (for fdPopoverTrigger directive)
|
|
1415
|
-
|
|
1514
|
+
// Full sync only when trigger is set and not in mobile mode (for fdPopoverTrigger directive).
|
|
1515
|
+
// In mobile mode, the dialog handles open/close — the popover service should not be involved.
|
|
1516
|
+
if (triggerValue && !this.mobile()) {
|
|
1416
1517
|
this._syncToService(effectiveConfig, triggerValue);
|
|
1417
1518
|
}
|
|
1418
1519
|
});
|
|
@@ -1444,11 +1545,23 @@ class PopoverComponent {
|
|
|
1444
1545
|
}
|
|
1445
1546
|
/** Opens the popover. */
|
|
1446
1547
|
open() {
|
|
1447
|
-
|
|
1548
|
+
// In mobile mode, just update the signal — the mobile component's effect handles the dialog.
|
|
1549
|
+
if (this.mobile()) {
|
|
1550
|
+
this.isOpen.set(true);
|
|
1551
|
+
}
|
|
1552
|
+
else {
|
|
1553
|
+
this._popoverService.open();
|
|
1554
|
+
}
|
|
1448
1555
|
}
|
|
1449
1556
|
/** Closes the popover. */
|
|
1450
1557
|
close(focusActiveElement = true) {
|
|
1451
|
-
|
|
1558
|
+
// In mobile mode, just update the signal — the mobile component's effect handles the dialog.
|
|
1559
|
+
if (this.mobile()) {
|
|
1560
|
+
this.isOpen.set(false);
|
|
1561
|
+
}
|
|
1562
|
+
else {
|
|
1563
|
+
this._popoverService.close(focusActiveElement);
|
|
1564
|
+
}
|
|
1452
1565
|
}
|
|
1453
1566
|
/** Temporary sets the ignoring of the event triggers. */
|
|
1454
1567
|
setIgnoreTriggers(ignore) {
|
|
@@ -1496,7 +1609,7 @@ class PopoverComponent {
|
|
|
1496
1609
|
!activeElement?.classList.contains(SELECT_CLASS_NAMES.selectControl)) {
|
|
1497
1610
|
// prevent page scrolling on Space keydown
|
|
1498
1611
|
event.preventDefault();
|
|
1499
|
-
this.
|
|
1612
|
+
this.toggle();
|
|
1500
1613
|
}
|
|
1501
1614
|
}
|
|
1502
1615
|
/** @hidden - Sync all input signals to the service */
|
|
@@ -1689,9 +1802,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
1689
1802
|
}]
|
|
1690
1803
|
}] });
|
|
1691
1804
|
|
|
1805
|
+
/**
|
|
1806
|
+
* Builds a computed PopoverConfig from individual signal accessors.
|
|
1807
|
+
* Reads all provided accessors reactively and assembles a config object.
|
|
1808
|
+
*
|
|
1809
|
+
* @example
|
|
1810
|
+
* ```typescript
|
|
1811
|
+
* readonly popoverConfig = buildPopoverConfig({
|
|
1812
|
+
* placement: () => this.placement() ?? 'bottom',
|
|
1813
|
+
* triggers: this.triggers,
|
|
1814
|
+
* disabled: this.disabled,
|
|
1815
|
+
* });
|
|
1816
|
+
* ```
|
|
1817
|
+
*/
|
|
1818
|
+
function buildPopoverConfig(signals) {
|
|
1819
|
+
return computed(() => {
|
|
1820
|
+
const config = {};
|
|
1821
|
+
for (const key of Object.keys(signals)) {
|
|
1822
|
+
const accessor = signals[key];
|
|
1823
|
+
if (accessor) {
|
|
1824
|
+
config[key] = accessor();
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
return config;
|
|
1828
|
+
});
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1692
1831
|
/**
|
|
1693
1832
|
* Generated bundle index. Do not edit.
|
|
1694
1833
|
*/
|
|
1695
1834
|
|
|
1696
|
-
export { BasePopoverClass, FD_POPOVER_COMPONENT, POPOVER_COMPONENT, PopoverBodyComponent, PopoverBodyDirective, PopoverBodyFooterDirective, PopoverBodyHeaderDirective, PopoverComponent, PopoverContainerDirective, PopoverControlComponent, PopoverMobileComponent, PopoverMobileModule, PopoverModule, PopoverService, PopoverTriggerDirective, SELECT_CLASS_NAMES };
|
|
1835
|
+
export { BasePopoverClass, FD_POPOVER_COMPONENT, POPOVER_COMPONENT, PopoverBodyComponent, PopoverBodyDirective, PopoverBodyFooterDirective, PopoverBodyHeaderDirective, PopoverComponent, PopoverContainerDirective, PopoverControlComponent, PopoverMobileComponent, PopoverMobileModule, PopoverModule, PopoverService, PopoverTriggerDirective, SELECT_CLASS_NAMES, buildPopoverConfig };
|
|
1697
1836
|
//# sourceMappingURL=fundamental-ngx-core-popover.mjs.map
|