Skip to content

Commit

Permalink
feat(ui5-dynamic-page-title): implement snapped title on mobile (#9898)
Browse files Browse the repository at this point in the history
* feat(ui5-dynamic-page-title): add snapped title for mobile devices

- Introduced `snappedTitleOnMobile` property to display a simplified
  title on mobile when the header is snapped.
- Added `snappedTitleOnMobileText` to define the text for the
  mobile-specific snapped title.
- This feature optimizes title display on small screens, improving
  mobile usability.
- Clicking the snapped title on mobile expands the header.
- On non-mobile devices, the standard title is displayed, ignoring the
  mobile-specific title.

* feat(ui5-dynamic-page-title): refactor snapped title implementation

- Addressed review comments by changing the implementation of the snapped title on mobile.
- Replaced properties with a slot for better flexibility and maintainability.

* feat(ui5-dynamic-page-title): refactor snapped title documentation

* feat(ui5-dynamic-page-title): streamline snapped title handling on mobile

Refactored the implementation to pass the "snappedTitleOnMobile" state
from parent to child. This change ensures proper state management,
addressing previous review comments.
  • Loading branch information
kgogov authored Sep 26, 2024
1 parent 2d19119 commit b8b9a98
Show file tree
Hide file tree
Showing 9 changed files with 427 additions and 40 deletions.
30 changes: 16 additions & 14 deletions packages/fiori/src/DynamicPage.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
{{> header-actions}}
{{/if}}
</header>

{{#if headerInContent}}
<slot tabindex="{{headerTabIndex}}" ?aria-hidden="{{headerAriaHidden}}" name="headerArea"></slot>
{{/if}}
Expand All @@ -42,17 +42,19 @@
</div>

{{#*inline "header-actions"}}
{{#if hasHeading}}
<ui5-dynamic-page-header-actions
?snapped="{{headerSnapped}}"
?pinned="{{headerPinned}}"
?hide-pin-button="{{hidePinButton}}"
.accessibilityAttributes="{{_accAttributesForHeaderActions}}"
@ui5-expand-button-click={{onExpandClick}}
@ui5-pin-button-click={{onPinClick}}
@ui5-expand-button-hover-in={{onExpandHoverIn}}
@ui5-expand-button-hover-out={{onExpandHoverOut}}
>
</ui5-dynamic-page-header-actions>
{{/if}}
{{#unless hasSnappedTitleOnMobile}}
{{#if hasHeading}}
<ui5-dynamic-page-header-actions
?snapped="{{headerSnapped}}"
?pinned="{{headerPinned}}"
?hide-pin-button="{{hidePinButton}}"
.accessibilityAttributes="{{_accAttributesForHeaderActions}}"
@ui5-expand-button-click={{onExpandClick}}
@ui5-pin-button-click={{onPinClick}}
@ui5-expand-button-hover-in={{onExpandHoverIn}}
@ui5-expand-button-hover-out={{onExpandHoverOut}}
>
</ui5-dynamic-page-header-actions>
{{/if}}
{{/unless}}
{{/inline}}
11 changes: 11 additions & 0 deletions packages/fiori/src/DynamicPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import announce from "@ui5/webcomponents-base/dist/util/InvisibleMessage.js";
import InvisibleMessageMode from "@ui5/webcomponents-base/dist/types/InvisibleMessageMode.js";
import { getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js";
import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
import { isPhone } from "@ui5/webcomponents-base/dist/Device.js";

import debounce from "@ui5/webcomponents-base/dist/util/debounce.js";

Expand Down Expand Up @@ -223,6 +224,7 @@ class DynamicPage extends UI5Element {
if (this.dynamicPageTitle) {
this.dynamicPageTitle.snapped = this._headerSnapped;
this.dynamicPageTitle.interactive = this.hasHeading;
this.dynamicPageTitle.hasSnappedTitleOnMobile = !!this.hasSnappedTitleOnMobile;
}
}

Expand Down Expand Up @@ -278,6 +280,10 @@ class DynamicPage extends UI5Element {
return this._headerSnapped;
}

get hasSnappedTitleOnMobile() {
return isPhone() && this.headerSnapped && this.dynamicPageTitle?.snappedTitleOnMobile.length;
}

/**
* Defines if the header is snapped.
*
Expand Down Expand Up @@ -327,6 +333,11 @@ class DynamicPage extends UI5Element {
this.fireEvent("title-toggle");
await renderFinished();
this.headerActions?.focusExpandButton();

if (this.hasSnappedTitleOnMobile) {
this.dynamicPageTitle?.focus();
}

announce(this._headerLabel, InvisibleMessageMode.Polite);
}

Expand Down
61 changes: 35 additions & 26 deletions packages/fiori/src/DynamicPageTitle.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -9,39 +9,48 @@
aria-labelledby="{{_ariaLabelledBy}}"
aria-describedby="{{_id}}-toggle-description">
</span>
<div class="ui5-dynamic-page-title--top-area">
<slot name="breadcrumbs"></slot>

{{#if mobileNavigationActions}}
<slot name="navigationBar"></slot>
{{/if}}
</div>
{{#if hasSnappedTitleOnMobile}}
<div id="{{_id}}-heading" class="ui5-dynamic-page--snapped-title-on-mobile">
<slot name="snappedTitleOnMobile"></slot>
<ui5-icon name="slim-arrow-down" mode="Decorative"></ui5-icon>
</div>
{{else}}
<div class="ui5-dynamic-page-title--top-area">
<slot name="breadcrumbs"></slot>

<div class="ui5-dynamic-page-title--wrapper"
@ui5-_min-content-width-change={{onMinContentWidthChange}}>
<div id="{{_id}}-heading" class="ui5-dynamic-page-title--heading">
<slot name="{{headingSlotName}}"></slot>
{{#if mobileNavigationActions}}
<slot name="navigationBar"></slot>
{{/if}}
</div>

{{#if hasContent}}
<div class="ui5-dynamic-page-title--content"
style="{{styles.content}}">
<slot></slot>
<div class="ui5-dynamic-page-title--wrapper"
@ui5-_min-content-width-change={{onMinContentWidthChange}}>
<div id="{{_id}}-heading" class="ui5-dynamic-page-title--heading">
<slot name="{{headingSlotName}}"></slot>
</div>
{{/if}}

<div class="ui5-dynamic-page-title--actions"
style="{{styles.actions}}">
<slot name="actionsBar"></slot>
{{#unless mobileNavigationActions}}
{{#if _needsSeparator}}
<div class="ui5-dynamic-page-title--actions-separator"></div>
{{/if}}
<slot name="navigationBar"></slot>
{{/unless}}
{{#if hasContent}}
<div class="ui5-dynamic-page-title--content"
style="{{styles.content}}">
<slot></slot>
</div>
{{/if}}

<div class="ui5-dynamic-page-title--actions"
style="{{styles.actions}}">
<slot name="actionsBar"></slot>
{{#unless mobileNavigationActions}}
{{#if _needsSeparator}}
<div class="ui5-dynamic-page-title--actions-separator"></div>
{{/if}}
<slot name="navigationBar"></slot>
{{/unless}}
</div>
</div>
</div>

<slot name="{{subheadingSlotName}}"></slot>
<slot name="{{subheadingSlotName}}"></slot>
{{/if}}

<span id="{{_id}}-toggle-description" class="ui5-hidden-text">{{_ariaDescribedbyText}}</span>
</div>
26 changes: 26 additions & 0 deletions packages/fiori/src/DynamicPageTitle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import type Toolbar from "@ui5/webcomponents/dist/Toolbar.js";
import type { ToolbarMinWidthChangeEventDetail } from "@ui5/webcomponents/dist/Toolbar.js";
import ToolbarItemOverflowBehavior from "@ui5/webcomponents/dist/types/ToolbarItemOverflowBehavior.js";
import { isDesktop } from "@ui5/webcomponents-base/dist/Device.js";
import Icon from "@ui5/webcomponents/dist/Icon.js";
import Title from "@ui5/webcomponents/dist/Title.js";

// Template
import DynamicPageTitleTemplate from "./generated/templates/DynamicPageTitleTemplate.lit.js";
Expand Down Expand Up @@ -63,6 +65,7 @@ import {
renderer: litRender,
styles: DynamicPageTitleCss,
template: DynamicPageTitleTemplate,
dependencies: [Title, Icon],
})

/**
Expand Down Expand Up @@ -110,6 +113,13 @@ class DynamicPageTitle extends UI5Element {
@property({ type: Number })
minActionsWidth?: number;

/**
* Indicates whether the title has snapped on mobile devices.
* @private
*/
@property({ type: Boolean })
hasSnappedTitleOnMobile = false;

/**
* Defines the content of the Heading of the Dynamic Page.
*
Expand All @@ -132,6 +142,22 @@ class DynamicPageTitle extends UI5Element {
@slot({ type: HTMLElement })
snappedHeading!: HTMLElement[];

/**
* Defines the content of the snapped title on mobile devices.
*
* This slot is displayed only when the `DynamicPageTitle` is in the snapped state on mobile devices.
* It should be used to provide a simplified, single-line title that takes up less space on smaller screens.
*
* **Note:**
* - The content set in this slot **overrides** all other content set in the `DynamicPageTitle` slots when displayed.
* - The slot is intended for a single `ui5-title` component.
*
* @public
* @since 2.3.0
*/
@slot({ type: HTMLElement })
snappedTitleOnMobile!: Array<Title>;

/**
* Defines the bar with actions in the Dynamic page title.
*
Expand Down
7 changes: 7 additions & 0 deletions packages/fiori/src/themes/DynamicPage.css
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,11 @@

:host([media-range="XL"]) ::slotted([slot="headerArea"]) {
padding: var(--_ui5_dynamic_page_header_padding_XL);
}

/* snappedTitleOnMobile */
:host([_header-snapped]) ::slotted([slot="headerArea"]) {
height: 0;
padding: 0;
visibility: hidden;
}
12 changes: 12 additions & 0 deletions packages/fiori/src/themes/DynamicPageTitle.css
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@
box-shadow: var(--sapContent_HeaderShadow);
}

:host([has-snapped-title-on-mobile]) {
min-height: var(--_ui5_dynamic_page_snapped_title_on_mobile_min_height);
line-height: var(--_ui5_dynamic_page_snapped_title_on_mobile_line_height);
}

/* breadcrumbs */
::slotted([ui5-breadcrumbs][slot="breadcrumbs"]) {
padding: var(--_ui5_dynamic_page_title_breadcrumbs_padding_top) 0
Expand Down Expand Up @@ -88,6 +93,13 @@
min-width: 1px;
}

.ui5-dynamic-page--snapped-title-on-mobile {
display: flex;
justify-content: space-between;
align-items: center;
pointer-events: none;
}

.ui5-dynamic-page-title--content {
padding: 0 0 0 1rem;
flex-shrink: 1.6;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@

--_ui5_dynamic_page_title_hover_background: var(--sapObjectHeader_Hover_Background);

--_ui5_dynamic_page_snapped_title_on_mobile_line_height: 2rem;
--_ui5_dynamic_page_snapped_title_on_mobile_min_height: 2rem;
}
Loading

0 comments on commit b8b9a98

Please sign in to comment.