diff --git a/packages/fiori/src/ShellBarSearch.ts b/packages/fiori/src/ShellBarSearch.ts index 743a1e2ab678..a3abb0fd3d9f 100644 --- a/packages/fiori/src/ShellBarSearch.ts +++ b/packages/fiori/src/ShellBarSearch.ts @@ -1,4 +1,5 @@ import property from "@ui5/webcomponents-base/dist/decorators/property.js"; +import slot from "@ui5/webcomponents-base/dist/decorators/slot.js"; import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; import Search from "./Search.js"; import { isPhone } from "@ui5/webcomponents-base/dist/Device.js"; @@ -10,6 +11,7 @@ import { SHELLBAR_SEARCH_EXPANDED, SHELLBAR_SEARCH_COLLAPSED, } from "./generated/i18n/i18n-defaults.js"; +import type BusyIndicator from "@ui5/webcomponents/dist/BusyIndicator.js"; /** * @class @@ -19,6 +21,7 @@ import { * @public * @since 2.10.0 * @experimental + * @slot {Array} busyIndicator - Defines the busy indicator to be displayed as an overlay. */ @customElement({ tag: "ui5-shellbar-search", @@ -38,6 +41,24 @@ class ShellBarSearch extends Search { @property({ type: Boolean }) autoOpen = false; + /** + * Defines the busy indicator to be displayed as an overlay. + * @public + */ + @slot({ + type: HTMLElement, + invalidateOnChildChange: true, + }) + busyIndicator!: Array; + + /** + * Tracks if the slotted BusyIndicator is active. + * Used to apply reduced opacity to the search field content when busy. + * @private + */ + @property({ type: Boolean }) + _isBusy = false; + _handleSearchIconPress() { super._handleSearchIconPress(); @@ -94,6 +115,11 @@ class ShellBarSearch extends Search { onBeforeRendering(): void { super.onBeforeRendering(); + // Track if busy indicator is active to apply opacity to search content. + // The busy indicator renders as an overlay in the shadow DOM, so we reduce + // opacity of the underlying search field to show the busy state visually. + this._isBusy = this.busyIndicator.length > 0 && this.busyIndicator[0].hasAttribute("active"); + if (isPhone()) { this.collapsed = true; } diff --git a/packages/fiori/src/ShellBarSearchTemplate.tsx b/packages/fiori/src/ShellBarSearchTemplate.tsx index ef5cef85316b..0b5df160d9bf 100644 --- a/packages/fiori/src/ShellBarSearchTemplate.tsx +++ b/packages/fiori/src/ShellBarSearchTemplate.tsx @@ -5,7 +5,10 @@ import ShellBarSearchPopoverTemplate from "./ShellBarSearchPopoverTemplate.js"; export default function ShellBarSearchTemplate(this: ShellBarSearch) { return ( <> - { SearchFieldTemplate.call(this) } +
+ { SearchFieldTemplate.call(this) } + +
{ ShellBarSearchPopoverTemplate.call(this) } ); diff --git a/packages/fiori/src/themes/ShellBarSearch.css b/packages/fiori/src/themes/ShellBarSearch.css index 7183bf831143..e81e21b3ff92 100644 --- a/packages/fiori/src/themes/ShellBarSearch.css +++ b/packages/fiori/src/themes/ShellBarSearch.css @@ -1,3 +1,15 @@ :host(:not([collapsed])) { min-width: 13rem; +} + +.ui5-shellbar-search-busy-wrapper { + position: relative; + display: contents; +} + +/* Apply reduced opacity when busy indicator is active. + The busy indicator is slotted and renders as an overlay on top, + so we reduce opacity of the search field underneath to indicate busy state. */ +:host([_is-busy]) .ui5-search-field-root { + opacity: var(--sapContent_DisabledOpacity); } \ No newline at end of file diff --git a/packages/fiori/test/pages/ShellBar_busy_search.html b/packages/fiori/test/pages/ShellBar_busy_search.html new file mode 100644 index 000000000000..71c6e6a7d4fa --- /dev/null +++ b/packages/fiori/test/pages/ShellBar_busy_search.html @@ -0,0 +1,131 @@ + + + + + ShellBar - Busy Search Demo + + + + + + + + + + +
+

ShellBar with Busy Search - POC

+

This demo shows a BusyIndicator slotted inside ShellBarSearch, rendering as an overlay.

+ +
+ Toggle Busy State +
+ + + + + + + + + + +
+ +
+

Standalone ShellBarSearch with Busy Indicator

+

ShellBarSearch outside of ShellBar for easier testing.

+ +
+ Toggle Busy State +
+ + + + + +
+ +
+

Comparison: Wrapped in BusyIndicator (Old Approach)

+

For comparison: ShellBarSearch wrapped in BusyIndicator instead of slotted.

+ +
+ Toggle Busy State +
+ + + + + +
+ + + + + diff --git a/packages/main/src/BusyIndicatorTemplate.tsx b/packages/main/src/BusyIndicatorTemplate.tsx index 504c588eb6b3..7a3e7ffb4f99 100644 --- a/packages/main/src/BusyIndicatorTemplate.tsx +++ b/packages/main/src/BusyIndicatorTemplate.tsx @@ -2,6 +2,36 @@ import type BusyIndicator from "./BusyIndicator.js"; import Label from "./Label.js"; export default function BusyIndicatorTemplate(this: BusyIndicator) { + // Overlay mode: When BusyIndicator has no slotted content, render only the busy area + // as an absolute positioned overlay. This allows it to be slotted inside other components + // without wrapping them, avoiding slot contract violations. + if (!this.hasContent) { + return this._isBusy ? ( +
+ {this.textPosition.top && BusyIndicatorBusyText.call(this)} +
+
+
+
+
+ {this.textPosition.bottom && BusyIndicatorBusyText.call(this)} +
+ ) : <>; + } + return (
{this._isBusy && ( diff --git a/packages/main/src/themes/BusyIndicator.css b/packages/main/src/themes/BusyIndicator.css index fa18a8312941..18c96c577138 100644 --- a/packages/main/src/themes/BusyIndicator.css +++ b/packages/main/src/themes/BusyIndicator.css @@ -85,6 +85,12 @@ z-index: 99; } +.ui5-busy-indicator-busy-area.ui5-busy-indicator-busy-area-overlay { + position: absolute; + inset: 0; + z-index: 99; +} + .ui5-busy-indicator-busy-area { display: flex; justify-content: center;