diff --git a/packages/main/src/Button.ts b/packages/main/src/Button.ts index 0af5c780cf14..5c863a0b918a 100644 --- a/packages/main/src/Button.ts +++ b/packages/main/src/Button.ts @@ -101,6 +101,11 @@ let activeButton: Button | null = null; * @native */ @event("click") +/** + * Fired whenever the active state of the component changes. + * @private + */ +@event("_active-state-change") class Button extends UI5Element implements IFormElement { /** * Defines the component design. @@ -337,7 +342,7 @@ class Button extends UI5Element implements IFormElement { this._deactivate = () => { if (activeButton) { - activeButton.active = false; + activeButton._setActiveState(false); } }; @@ -354,7 +359,7 @@ class Button extends UI5Element implements IFormElement { return; } - this.active = true; + this._setActiveState(true); }; this._ontouchstart = { @@ -407,7 +412,7 @@ class Button extends UI5Element implements IFormElement { } markEvent(e, "button"); - this.active = true; + this._setActiveState(true); activeButton = this; // eslint-disable-line } @@ -417,10 +422,12 @@ class Button extends UI5Element implements IFormElement { e.stopPropagation(); } - this.active = false; + if (this.active) { + this._setActiveState(false); + } if (activeButton) { - activeButton.active = false; + activeButton._setActiveState(false); } } @@ -432,13 +439,15 @@ class Button extends UI5Element implements IFormElement { markEvent(e, "button"); if (isSpace(e) || isEnter(e)) { - this.active = true; + this._setActiveState(true); } } _onkeyup(e: KeyboardEvent) { if (isSpace(e) || isEnter(e)) { - this.active = false; + if (this.active) { + this._setActiveState(false); + } } } @@ -446,7 +455,11 @@ class Button extends UI5Element implements IFormElement { if (this.nonInteractive) { return; } - this.active = false; + + if (this.active) { + this._setActiveState(false); + } + if (isDesktop()) { this.focused = false; } @@ -463,6 +476,16 @@ class Button extends UI5Element implements IFormElement { } } + _setActiveState(active: boolean) { + const eventPrevented = !this.fireEvent("_active-state-change", null, true); + + if (eventPrevented) { + return; + } + + this.active = active; + } + get hasButtonType() { return this.design !== ButtonDesign.Default && this.design !== ButtonDesign.Transparent; } diff --git a/packages/main/src/SplitButton.hbs b/packages/main/src/SplitButton.hbs index bd8504e7ffa1..fdda448627c9 100644 --- a/packages/main/src/SplitButton.hbs +++ b/packages/main/src/SplitButton.hbs @@ -17,11 +17,11 @@ tabindex="-1" ?disabled="{{disabled}}" ?active="{{_textButtonActive}}" - @click="{{_fireClick}}" + @click="{{_handleMouseClick}}" @touchstart={{_textButtonPress}} @mousedown={{_textButtonPress}} @mouseup={{_textButtonRelease}} - @focusin={{_setTabIndexValue}} + @focusin={{_textButtonFocusIn}} @focusout={{_onFocusOut}} > {{#if isTextButton}} @@ -39,12 +39,14 @@ icon="slim-arrow-down" tabindex="-1" ?disabled="{{disabled}}" - ?active="{{_arrowButtonActive}}" + ?active="{{effectiveActiveArrowButton}}" aria-expanded="{{accessibilityInfo.ariaExpanded}}" aria-haspopup="{{accessibilityInfo.ariaHaspopup}}" - @click="{{_fireArrowClick}}" + @click="{{_handleArrowButtonAction}}" + @mousedown={{_arrowButtonPress}} + @mouseup={{_arrowButtonRelease}} @focusin={{_setTabIndexValue}} - @focusout={{_onFocusOut}} + @ui5-_active-state-change={{_onArrowButtonActiveStateChange}} > diff --git a/packages/main/src/SplitButton.ts b/packages/main/src/SplitButton.ts index 4d2e89622258..d7f6594b7748 100644 --- a/packages/main/src/SplitButton.ts +++ b/packages/main/src/SplitButton.ts @@ -13,6 +13,8 @@ import { isUpAlt, isF4, isShift, + isTabNext, + isTabPrevious, } from "@ui5/webcomponents-base/dist/Keys.js"; import { getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js"; import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js"; @@ -132,6 +134,18 @@ class SplitButton extends UI5Element { @property() activeIcon!: string; + /** + * Defines whether the arrow button should have the active state styles or not. + * + * @type {boolean} + * @name sap.ui.webc.main.SplitButton.prototype.activeArrowButton + * @defaultvalue false + * @public + * @since 1.20.0 + */ + @property({ type: Boolean }) + activeArrowButton!: boolean; + /** * Defines the component design. * @@ -231,13 +245,14 @@ class SplitButton extends UI5Element { _textButtonIcon!: string; /** - * Defines the active state of the arrow button + * Defines the state of the internal Button used for the Arrow button of the SplitButton. + * * @type {boolean} * @defaultvalue false * @private */ @property({ type: Boolean, noAttribute: true }) - _arrowButtonActive!: boolean; + _activeArrowButton!: boolean; /** * Defines the text of the component. @@ -253,6 +268,8 @@ class SplitButton extends UI5Element { text!: Array; _textButtonPress: { handleEvent: () => void, passive: boolean }; + _isDefaultActionPressed = false; + _isKeyDownOperation = false; static i18nBundle: I18nBundle; @@ -266,7 +283,7 @@ class SplitButton extends UI5Element { const handleTouchStartEvent = () => { this._textButtonActive = true; this.focused = false; - this._setTabIndexValue(); + this._tabIndex = "-1"; }; this._textButtonPress = { @@ -275,6 +292,18 @@ class SplitButton extends UI5Element { }; } + /** + * Function that makes sure the focus is properly managed. + * @private + */ + _manageFocus(button?: Button | SplitButton) { + const buttons: Array