diff --git a/dist/OutSystemsUI.css b/dist/OutSystemsUI.css index d3486cb760..c349a917bc 100644 --- a/dist/OutSystemsUI.css +++ b/dist/OutSystemsUI.css @@ -12699,7 +12699,7 @@ html[data-uieditorversion^="1"] .wizard-wrapper-item{ font:normal normal normal 20px/1 FontAwesome; height:100%; pointer-events:none; - position:absolute; + position:fixed; right:16px; top:0; -webkit-transition:-webkit-transform 200ms ease-in-out; @@ -12940,6 +12940,11 @@ html[data-uieditorversion^="1"] .wizard-wrapper-item{ color:var(--color-neutral-6); pointer-events:none; } +.osui-dropdown-serverside--is-inside-popup .osui-dropdown-serverside__balloon-wrapper{ + top:calc(var(--osui-dropdown-ss-top) + var(--osui-dropdown-ss-input-height) + 4px); + position:fixed; + overflow:visible; +} .osui-dropdown-serverside--not-valid .osui-dropdown-serverside__selected-values-wrapper{ border-color:var(--color-error); } diff --git a/dist/OutSystemsUI.d.ts b/dist/OutSystemsUI.d.ts index 145793edc8..14857de45c 100644 --- a/dist/OutSystemsUI.d.ts +++ b/dist/OutSystemsUI.d.ts @@ -193,6 +193,7 @@ declare namespace OSFramework.OSUI.GlobalEnum { MainContent = "main-content", MenuLinks = "app-menu-links", Placeholder = "ph", + Popup = "popup-dialog", SkipContent = "skip-nav" } enum CSSSelectors { @@ -319,6 +320,7 @@ declare namespace OSFramework.OSUI.GlobalEnum { Prefix = "on", Resize = "resize", Scroll = "scroll", + ScrollEnd = "scrollend", TouchEnd = "touchend", TouchMove = "touchmove", TouchStart = "touchstart", @@ -1035,6 +1037,7 @@ declare namespace OSFramework.OSUI.Helper { static GetElementById(id: string): HTMLElement; static GetElementByUniqueId(uniqueId: string): HTMLElement; static GetFocusableElements(element: HTMLElement): HTMLElement[]; + static IsInsidePopupWidget(element: HTMLElement): boolean; static Move(element: HTMLElement, target: HTMLElement): void; static SetInputValue(inputElem: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement, value: string): void; static TagSelector(element: HTMLElement, htmlTag: string): HTMLElement | undefined; @@ -1934,6 +1937,7 @@ declare namespace OSFramework.OSUI.Patterns.Dropdown.ServerSide { private _hasA11yEnabled; private _intersectionObserver; private _isBlocked; + private _isInsidePopup; private _isOpen; private _platformEventOnToggleCallback; private _selectValuesWrapper; @@ -2033,6 +2037,7 @@ declare namespace OSFramework.OSUI.Patterns.Dropdown.ServerSide.Enum { BalloonWrapper = "osui-dropdown-serverside__balloon-wrapper", ErrorMessage = "osui-dropdown-serverside-error-message", IsDisabled = "osui-dropdown-serverside--is-disabled", + IsInsidePopup = "osui-dropdown-serverside--is-inside-popup", IsOpened = "osui-dropdown-serverside--is-opened", IsVisible = "osui-dropdown-serverside-visible", NotValid = "osui-dropdown-serverside--not-valid", diff --git a/dist/OutSystemsUI.js b/dist/OutSystemsUI.js index ae76fd5776..c10f3d308b 100644 --- a/dist/OutSystemsUI.js +++ b/dist/OutSystemsUI.js @@ -256,6 +256,7 @@ var OSFramework; CssClassElements["MainContent"] = "main-content"; CssClassElements["MenuLinks"] = "app-menu-links"; CssClassElements["Placeholder"] = "ph"; + CssClassElements["Popup"] = "popup-dialog"; CssClassElements["SkipContent"] = "skip-nav"; })(CssClassElements = GlobalEnum.CssClassElements || (GlobalEnum.CssClassElements = {})); let CSSSelectors; @@ -395,6 +396,7 @@ var OSFramework; HTMLEvent["Prefix"] = "on"; HTMLEvent["Resize"] = "resize"; HTMLEvent["Scroll"] = "scroll"; + HTMLEvent["ScrollEnd"] = "scrollend"; HTMLEvent["TouchEnd"] = "touchend"; HTMLEvent["TouchMove"] = "touchmove"; HTMLEvent["TouchStart"] = "touchstart"; @@ -2789,6 +2791,12 @@ var OSFramework; const _filteredElements = Array.from(_focusableElems).filter((element) => element.getAttribute(OSUI.Constants.FocusTrapIgnoreAttr) !== 'true'); return [..._filteredElements]; } + static IsInsidePopupWidget(element) { + const _popup = document.querySelector(OSUI.Constants.Dot + OSUI.GlobalEnum.CssClassElements.Popup); + if (_popup && element) { + return _popup.contains(element); + } + } static Move(element, target) { if (element && target) { target.appendChild(element); @@ -2860,11 +2868,26 @@ var OSFramework; } } static _scrollToInvalidInput(element, isSmooth, elementParentClass) { + const browser = OSFramework.OSUI.Helper.DeviceInfo.GetBrowser(); OutSystems.OSUI.Utils.ScrollToElement(element.id, isSmooth, 0, elementParentClass); + if (browser === OSUI.GlobalEnum.Browser.safari || OSFramework.OSUI.Helper.DeviceInfo.IsIos) { + if (isSmooth) { + console.warn('Due to the unsupported scrollend event on Safari/iOS, the smooth transition is disabled and the invalid input will be focused directly.'); + } + element.focus(); + } + else { + const activeScreenElement = Helper.Dom.ClassSelector(document.body, OSUI.GlobalEnum.CssClassElements.ActiveScreen); + const focusOnScrollEnd = () => { + element.focus(); + activeScreenElement.removeEventListener(OSUI.GlobalEnum.HTMLEvent.ScrollEnd, focusOnScrollEnd); + }; + activeScreenElement.addEventListener(OSUI.GlobalEnum.HTMLEvent.ScrollEnd, focusOnScrollEnd); + } } static _searchElementId(element, isSmooth, elementParentClass) { const elementToSearch = element.parentElement; - if (elementToSearch.id !== '') { + if (elementToSearch.id !== OSUI.Constants.EmptyString) { this._scrollToInvalidInput(elementToSearch, isSmooth, elementParentClass); } else { @@ -2876,10 +2899,12 @@ var OSFramework; errorCode: OutSystems.OSUI.ErrorCodes.Utilities.FailGetInvalidInput, callback: () => { let element = document.body; - if (elementId !== '') { + if (elementId !== OSUI.Constants.EmptyString) { element = Helper.Dom.GetElementById(elementId); } - this._checkInvalidInputs(element, isSmooth, elementParentClass); + Helper.AsyncInvocation(() => { + this._checkInvalidInputs(element, isSmooth, elementParentClass); + }); }, }); return result; @@ -4462,12 +4487,6 @@ var OSFramework; var BottomSheet; (function (BottomSheet_1) { class BottomSheet extends Patterns.AbstractPattern { - get gestureEventInstance() { - return this._gestureEventInstance; - } - get hasGestureEvents() { - return this._hasGestureEvents; - } constructor(uniqueId, configs) { super(uniqueId, new BottomSheet_1.BottomSheetConfig(configs)); this._isOpen = false; @@ -4480,6 +4499,12 @@ var OSFramework; }, }; } + get gestureEventInstance() { + return this._gestureEventInstance; + } + get hasGestureEvents() { + return this._hasGestureEvents; + } _handleFocusBehavior() { const opts = { focusTargetElement: this._parentSelf, @@ -5404,7 +5429,8 @@ var OSFramework; throw new Error(`${OSUI.ErrorCodes.Dropdown.HasNoImplementation.code}: ${OSUI.ErrorCodes.Dropdown.HasNoImplementation.message}`); } _moveBallonElement() { - OSUI.Helper.Dom.Move(this._balloonWrapperElement, this._activeScreenElement); + const balloon = document.adoptNode(this._balloonWrapperElement); + this._activeScreenElement.appendChild(balloon); } _onBodyClick(_eventType, event) { if (this._isOpen === false) { @@ -5802,7 +5828,13 @@ var OSFramework; this.setA11YProperties(); this._setUpEvents(); this._setCssClasses(); - this._moveBallonElement(); + this._isInsidePopup = OSUI.Helper.Dom.IsInsidePopupWidget(this.selfElement); + if (this._isInsidePopup) { + OSUI.Helper.Dom.Styles.AddClass(this.selfElement, ServerSide.Enum.CssClass.IsInsidePopup); + } + else { + this._moveBallonElement(); + } this._setBalloonCoordinates(); } unsetCallbacks() { @@ -6036,6 +6068,7 @@ var OSFramework; CssClass["BalloonWrapper"] = "osui-dropdown-serverside__balloon-wrapper"; CssClass["ErrorMessage"] = "osui-dropdown-serverside-error-message"; CssClass["IsDisabled"] = "osui-dropdown-serverside--is-disabled"; + CssClass["IsInsidePopup"] = "osui-dropdown-serverside--is-inside-popup"; CssClass["IsOpened"] = "osui-dropdown-serverside--is-opened"; CssClass["IsVisible"] = "osui-dropdown-serverside-visible"; CssClass["NotValid"] = "osui-dropdown-serverside--not-valid"; diff --git a/src/scripts/OSFramework/OSUI/GlobalEnum.ts b/src/scripts/OSFramework/OSUI/GlobalEnum.ts index 03687916b9..54eb6f91d7 100644 --- a/src/scripts/OSFramework/OSUI/GlobalEnum.ts +++ b/src/scripts/OSFramework/OSUI/GlobalEnum.ts @@ -34,6 +34,7 @@ namespace OSFramework.OSUI.GlobalEnum { MainContent = 'main-content', MenuLinks = 'app-menu-links', Placeholder = 'ph', + Popup = 'popup-dialog', SkipContent = 'skip-nav', } diff --git a/src/scripts/OSFramework/OSUI/Helper/Dom.ts b/src/scripts/OSFramework/OSUI/Helper/Dom.ts index cef5682dc9..3453d43b31 100644 --- a/src/scripts/OSFramework/OSUI/Helper/Dom.ts +++ b/src/scripts/OSFramework/OSUI/Helper/Dom.ts @@ -437,6 +437,29 @@ namespace OSFramework.OSUI.Helper { return [..._filteredElements] as HTMLElement[]; } + /** + * Method to check if element is inside a Popup widget + * + * @static + * @param {HTMLElement} element + * @return {*} {boolean} + * @memberof Dom + */ + public static IsInsidePopupWidget(element: HTMLElement): boolean { + const _popup = document.querySelectorAll(Constants.Dot + GlobalEnum.CssClassElements.Popup); + let _isInsidePopup = false; + + if (_popup.length > 0 && element) { + _popup.forEach((popup) => { + if (popup.contains(element)) { + _isInsidePopup = true; + } + }); + } + + return _isInsidePopup; + } + /** * Moves a given HTML element to target position. * diff --git a/src/scripts/OSFramework/OSUI/Pattern/Dropdown/ServerSide/DropdownServerSide.ts b/src/scripts/OSFramework/OSUI/Pattern/Dropdown/ServerSide/DropdownServerSide.ts index cc89c90194..af608ce60a 100644 --- a/src/scripts/OSFramework/OSUI/Pattern/Dropdown/ServerSide/DropdownServerSide.ts +++ b/src/scripts/OSFramework/OSUI/Pattern/Dropdown/ServerSide/DropdownServerSide.ts @@ -63,6 +63,8 @@ namespace OSFramework.OSUI.Patterns.Dropdown.ServerSide { private _intersectionObserver: IntersectionObserver; // Store a Flag property that will control if the dropdown is blocked (like it's under closing animation) private _isBlocked = false; + // Store if Dropdown is being used inside a popup widget + private _isInsidePopup: boolean; // Store the Element State, by default is closed! private _isOpen = false; // Platform OnClose Callback @@ -507,9 +509,8 @@ namespace OSFramework.OSUI.Patterns.Dropdown.ServerSide { this._selfElementBoundingClientRect.x + this._selfElementBoundingClientRect.width && selfElement.y === this._selfElementBoundingClientRect.y) ) - - // Store the new selElement coordinates - this._selfElementBoundingClientRect.x = selfElement.x; + // Store the new selElement coordinates + this._selfElementBoundingClientRect.x = selfElement.x; this._selfElementBoundingClientRect.y = selfElement.y; // Set Css inline variables @@ -649,8 +650,8 @@ namespace OSFramework.OSUI.Patterns.Dropdown.ServerSide { Event.DOMEvents.Listeners.Type.BodyOnClick, this._eventOnBodyClick ); - - if(Helper.DeviceInfo.IsPhone === false) { + + if (Helper.DeviceInfo.IsPhone === false) { // Add the ScreenScroll callback that will be used to update the balloon coodinates Event.DOMEvents.Listeners.GlobalListenerManager.Instance.addHandler( Event.DOMEvents.Listeners.Type.ScreenOnScroll, @@ -730,7 +731,7 @@ namespace OSFramework.OSUI.Patterns.Dropdown.ServerSide { this._eventOnBodyClick ); - if(Helper.DeviceInfo.IsPhone === false) { + if (Helper.DeviceInfo.IsPhone === false) { Event.DOMEvents.Listeners.GlobalListenerManager.Instance.removeHandler( Event.DOMEvents.Listeners.Type.ScreenOnScroll, this._eventOnScreenScroll @@ -971,8 +972,20 @@ namespace OSFramework.OSUI.Patterns.Dropdown.ServerSide { this._setUpEvents(); // Add CSS classes this._setCssClasses(); - // Ensure that the Move only happens after HTML elements has been set! - this._moveBallonElement(); + + // Check if the dropdown is placed inside a Popup Widget + this._isInsidePopup = Helper.Dom.IsInsidePopupWidget(this.selfElement); + + if (this._isInsidePopup) { + /* If it is inside, then do not perform the MoveElement and instead add a class to change some CSS properties. + This is done due to the changes in recat-dom recent version, where listeners are all placed on the reactContainer, making any widget with events to loose + its context when its moved inside a Popup, that is placed outside the reactContainer*/ + Helper.Dom.Styles.AddClass(this.selfElement, Enum.CssClass.IsInsidePopup); + } else { + // Ensure that the Move only happens after HTML elements has been set! + this._moveBallonElement(); + } + // Set the balloon coordinates this._setBalloonCoordinates(); } @@ -1275,4 +1288,4 @@ namespace OSFramework.OSUI.Patterns.Dropdown.ServerSide { } } } -} \ No newline at end of file +} diff --git a/src/scripts/OSFramework/OSUI/Pattern/Dropdown/ServerSide/DropdownServerSideEnum.ts b/src/scripts/OSFramework/OSUI/Pattern/Dropdown/ServerSide/DropdownServerSideEnum.ts index 5582304cf0..4c3dca4781 100644 --- a/src/scripts/OSFramework/OSUI/Pattern/Dropdown/ServerSide/DropdownServerSideEnum.ts +++ b/src/scripts/OSFramework/OSUI/Pattern/Dropdown/ServerSide/DropdownServerSideEnum.ts @@ -24,6 +24,7 @@ namespace OSFramework.OSUI.Patterns.Dropdown.ServerSide.Enum { BalloonWrapper = 'osui-dropdown-serverside__balloon-wrapper', ErrorMessage = 'osui-dropdown-serverside-error-message', IsDisabled = 'osui-dropdown-serverside--is-disabled', + IsInsidePopup = 'osui-dropdown-serverside--is-inside-popup', IsOpened = 'osui-dropdown-serverside--is-opened', IsVisible = 'osui-dropdown-serverside-visible', NotValid = 'osui-dropdown-serverside--not-valid', diff --git a/src/scripts/OSFramework/OSUI/Pattern/Dropdown/ServerSide/scss/_dropdown-serverside.scss b/src/scripts/OSFramework/OSUI/Pattern/Dropdown/ServerSide/scss/_dropdown-serverside.scss index 64bcd2216d..2e1ab95b27 100644 --- a/src/scripts/OSFramework/OSUI/Pattern/Dropdown/ServerSide/scss/_dropdown-serverside.scss +++ b/src/scripts/OSFramework/OSUI/Pattern/Dropdown/ServerSide/scss/_dropdown-serverside.scss @@ -112,9 +112,7 @@ $balloonMobileTopMargin: 5vh; overflow: hidden; // --osui-dropdown-ss-thresholdanimateval is the value used as a threshold to animate down the balloon transform: translateY(calc(-1 * var(--osui-dropdown-ss-thresholdanimateval))); - transition: - opacity 250ms ease, - transform 300ms ease-in-out; + transition: opacity 250ms ease, transform 300ms ease-in-out; // Service Studio Preview & { @@ -297,6 +295,19 @@ $balloonMobileTopMargin: 5vh; } } + // When inside Popup widget + &--is-inside-popup { + .osui-dropdown-serverside__balloon-wrapper { + top: calc(var(--osui-dropdown-ss-top) + var(--osui-dropdown-ss-input-height) + 4px); + position: fixed; + overflow: visible; + + &.osui-dropdown-serverside--is-opened { + z-index: calc(var(--osui-popup-layer) + var(--layer-local-tier-1)); + } + } + } + // When it's not valid &--not-valid { .osui-dropdown-serverside__selected-values-wrapper { @@ -344,10 +355,10 @@ $balloonMobileTopMargin: 5vh; } } -// Inside Popup -body:has(.popup-dialog):has(.osui-dropdown-serverside--is-opened) { - .osui-dropdown-serverside__balloon-wrapper.osui-dropdown-serverside--is-opened { - z-index: calc(var(--osui-popup-layer) + var(--layer-local-tier-1)); +// Inside Form & Popup +.form { + .osui-dropdown-serverside--is-inside-popup input[data-input] { + margin-bottom: 0; } }