diff --git a/packages/base/package.json b/packages/base/package.json index d90b1a1e1c26..4811b4b0cde9 100644 --- a/packages/base/package.json +++ b/packages/base/package.json @@ -44,8 +44,9 @@ "lit-html": "^2.0.1" }, "devDependencies": { - "@openui5/sap.ui.core": "1.120.3", - "@ui5/webcomponents-tools": "1.22.0", + "@buxlabs/amd-to-es6": "0.16.1", + "@openui5/sap.ui.core": "1.116.0", + "@ui5/webcomponents-tools": "1.22.0-rc.1", "chromedriver": "120.0.0", "clean-css": "^5.2.2", "copy-and-watch": "^0.1.5", diff --git a/packages/fiori/package.json b/packages/fiori/package.json index f5d2eb864aee..6dfaec46ac09 100644 --- a/packages/fiori/package.json +++ b/packages/fiori/package.json @@ -53,7 +53,7 @@ "@zxing/library": "^0.17.1" }, "devDependencies": { - "@ui5/webcomponents-tools": "1.22.0", + "@ui5/webcomponents-tools": "1.22.0-rc.1", "chromedriver": "120.0.0" } } diff --git a/packages/fiori/src/DynamicPage.hbs b/packages/fiori/src/DynamicPage.hbs new file mode 100644 index 000000000000..2a7008023b0d --- /dev/null +++ b/packages/fiori/src/DynamicPage.hbs @@ -0,0 +1,51 @@ +
DynamicPageTitle
consists of a heading
+ * on the left side, content in the middle, and actions on the right. The displayed
+ * content changes based on the current mode of the DynamicPageHeader.DynamicPageHeader
is a generic container, which
+ * can contain a single layout component. The header works in two modes - expanded and snapped and its
+ * behavior can be adjusted with the help of different properties.Content area
is a generic container, which can have a single UI5 layout.Footer
is positioned at the bottom with a small offset and used for additional
+ * actions, the footer floats above the content.DynamicPage
if you need to have a title, that is always visible
+ * and a header, that has configurable Expanding/Snapping functionality.
+ * If you don't need the Expanding/Snapping functionality it is better to use the
+ * ui5-page
as a lighter component.
+ *
+ * The app can add to the default
slot of the ui5-dynamic-page either content that is designed to fit its container (e.g. has 100% height),
+ * or content with own height that may overflow its container. In the second case the DynamicPage
will show a scrollbar that allows the user
+ * scroll through the content.
+ *
+ * DynamicPageTitle
is not supported in the following case:
+ * When the DynamicPage
has a scroll bar, the component usually scrolls to the snapping point - the point,
+ * where the DynamicPageHeader
is scrolled out completely.
+ * However, when there is a scroll bar, but not enough content to reach the snapping point,
+ * the snapping is not possible using scrolling.F6 / Shift + F6
or Ctrl + Alt(Option) + Down / Ctrl + Alt(Option) + Up
.
+ * In order to use this functionality, you need to import the following module:
+ * import "@ui5/webcomponents-base/dist/features/F6Navigation.js"
import "@ui5/webcomponents-fiori/dist/DynamicPage.js";
+ *
+ * @constructor
+ * @extends sap.ui.webc.base.UI5Element
+ * @since 1.122
+ * @public
+ */
+@customElement({
+ tag: "ui5-dynamic-page",
+ renderer: litRender,
+ styles: DynamicPageCss,
+ template: DynamicPageTemplate,
+ dependencies: [DynamicPageHeader, DynamicPageTitle, DynamicPageHeaderActions],
+})
+
+class DynamicPage extends UI5Element {
+ static i18nBundle: I18nBundle;
+
+ constructor() {
+ super();
+
+ this._updateMediaRange = this.updateMediaRange.bind(this);
+ }
+
+ static async onDefine() {
+ DynamicPage.i18nBundle = await getI18nBundle("@ui5/webcomponents-fiori");
+ }
+
+ /**
+ * Defines if the header is snapped.
+ *
+ * @default false
+ * @public
+ */
+ @property({ type: Boolean })
+ headerSnapped!: boolean;
+
+ /**
+ * Defines if the header is pinned.
+ *
+ * @default false
+ * @public
+ */
+ @property({ type: Boolean })
+ headerPinned!: boolean;
+
+ /**
+ * Defines if the footer is shown.
+ *
+ * @default false
+ * @public
+ */
+ @property({ type: Boolean })
+ showFooter!: boolean;
+
+ /**
+ * Defines the current media query size.
+ *
+ * @private
+ */
+ @property()
+ mediaRange!: string;
+
+ /**
+ * Defines the content of the Dynamic Page.
+ *
+ * @public
+ */
+ @slot({ "default": true, type: HTMLElement })
+ content!: HTMLElement[];
+
+ /**
+ * Defines the title HTML Element.
+ *
+ * @public
+ */
+ @slot({ type: DynamicPageTitle })
+ titleArea!: HTMLElement[];
+
+ /**
+ * Defines the title HTML Element.
+ *
+ * @public
+ */
+ @slot({ type: DynamicPageHeader })
+ headerArea!: HTMLElement[];
+
+ /**
+ * Defines the title HTML Element.
+ *
+ * @public
+ */
+ @slot({ type: HTMLElement })
+ footer!: HTMLElement[];
+
+ skipSnapOnScroll = false;
+ showHeaderInStickArea = false;
+ _updateMediaRange: ResizeObserverCallback;
+
+ onEnterDOM() {
+ ResizeHandler.register(this, this._updateMediaRange);
+ }
+
+ onExitDOM() {
+ ResizeHandler.deregister(this, this._updateMediaRange);
+ }
+
+ onBeforeRendering(): void {
+ if (this.dynamicPageTitle) {
+ this.dynamicPageTitle.snapped = this.headerSnapped;
+ }
+ }
+
+ get classes() {
+ return {
+ root: {
+ "ui5-dynamic-page-root": true,
+ },
+ scrollContainer: {
+ "ui5-dynamic-page-scroll-container": true,
+ },
+ headerWrapper: {
+ "ui5-dynamic-page-title-header-wrapper": true,
+ },
+ content: {
+ "ui5-dynamic-page-content": true,
+ },
+ fitContent: {
+ "ui5-dynamic-page-fit-content": true,
+ },
+ footer: {
+ "ui5-dynamic-page-footer": true,
+ },
+ spacer: {
+ "ui5-dynamic-page-spacer": true,
+ },
+ };
+ }
+
+ get dynamicPageTitle(): DynamicPageTitle | null {
+ return this.querySelectorui5-dynamic-page-header
is part of the DynamicPage family
+ * and is used to serve as header of the DynamicPage
.
+ *
+ * DynamicPageHeader
can hold any layout control and has two states - expanded
+ * and collapsed (snapped). The switching between these states happens when:
+ *
+ * DynamicPageTitle
DynamicPage
property headerSnapped
DynamicPageHeader
depends on the behavior of the
+ * content that is displayed.
+ *
+ *
+ * @constructor
+ * @extends sap.ui.webc.base.UI5Element
+ * @public
+ * @since 1.122
+ */
+@customElement({
+ tag: "ui5-dynamic-page-header",
+ renderer: litRender,
+ styles: DynamicPageHeaderCss,
+ template: DynamicPageHeaderTemplate,
+})
+class DynamicPageHeader extends UI5Element {
+ static i18nBundle: I18nBundle;
+
+ static async onDefine() {
+ DynamicPageHeader.i18nBundle = await getI18nBundle("@ui5/webcomponents-fiori");
+ }
+ /**
+ * Defines the content of the Dynamic Page Header.
+ *
+ * @public
+ */
+ @slot({ "default": true, type: HTMLElement })
+ content!: HTMLElement[];
+
+ get classes() {
+ return {
+ root: {
+ "ui5-dynamic-page-header-root": true,
+ },
+ };
+ }
+}
+
+DynamicPageHeader.define();
+
+export default DynamicPageHeader;
diff --git a/packages/fiori/src/DynamicPageHeaderActions.hbs b/packages/fiori/src/DynamicPageHeaderActions.hbs
new file mode 100644
index 000000000000..cab5e8a62644
--- /dev/null
+++ b/packages/fiori/src/DynamicPageHeaderActions.hbs
@@ -0,0 +1,21 @@
+DynamicPageHeaderActions
component is part of the DynamicPage
+ * family and is holding the action buttons behind the DynamicPageTitle
and the DynamicPageHeader
.
+ *
+ * The "pin" action is used to attach the header to a certain state (expanded/collapsed).
+ * The expand/collapse action is used to switch between the two states of DynamicPageHeader
.
+ *
+ *
+ * @constructor
+ * @extends sap.ui.webc.base.UI5Element
+ * @private
+ * @since 1.122
+ */
+@customElement({
+ tag: "ui5-dynamic-page-header-actions",
+ renderer: litRender,
+ styles: DynamicPageHeaderActionsCss,
+ template: DynamicPageHeaderActionsTemplate,
+})
+
+/**
+ * Event that is being fired by clicking on the expand button.
+ *
+ * @public
+ */
+@event("expand-button-click")
+
+/**
+ * Event that is being fired by clicking on the pin button.
+ *
+ * @public
+ */
+@event("pin-button-click")
+
+class DynamicPageHeaderActions extends UI5Element {
+ static i18nBundle: I18nBundle;
+
+ static async onDefine() {
+ DynamicPageHeaderActions.i18nBundle = await getI18nBundle("@ui5/webcomponents-fiori");
+ }
+
+ /**
+ * Defines whether the header is pinned.
+ *
+ * @public
+ * @default false
+ */
+ @property({ type: Boolean })
+ pinned!: boolean;
+
+ /**
+ * Defines whether the header is snapped.
+ *
+ * @public
+ * @default false
+ */
+ @property({ type: Boolean })
+ snapped!: boolean;
+
+ /**
+ * Contains attributes to be added to HTML to gain accessibility.
+ *
+ * @public
+ * @default {}
+ */
+ @property({ type: Object })
+ accessibilityAttributes!: { controls: string };
+
+ get classes() {
+ return {
+ root: {
+ "ui5-dynamic-page-header-actions-root": true,
+ },
+ wrapper: {
+ "ui5-dynamic-page-header-actions--wrapper": true,
+ },
+ };
+ }
+
+ get arrowButtonIcon() {
+ return this.snapped ? "slim-arrow-down" : "slim-arrow-up";
+ }
+
+ get pinButtonIcon() {
+ return this.pinned ? "pushpin-on" : "pushpin-off";
+ }
+
+ get expandButton(): HTMLElement | null | undefined {
+ return this.getDomRef()?.querySelector(".ui5-dynamic-page-header-action-expand");
+ }
+
+ get pinButton(): HTMLElement | null | undefined {
+ return this.getDomRef()?.querySelector(".ui5-dynamic-page-header-action-pin");
+ }
+
+ get pinLabel() {
+ return DynamicPageHeaderActions.i18nBundle.getText(DYNAMIC_PAGE_ARIA_LABEL_PIN_HEADER);
+ }
+
+ get expandLabel() {
+ return this.snapped
+ ? DynamicPageHeaderActions.i18nBundle.getText(DYNAMIC_PAGE_ARIA_LABEL_EXPAND_HEADER)
+ : DynamicPageHeaderActions.i18nBundle.getText(DYNAMIC_PAGE_ARIA_LABEL_SNAP_HEADER);
+ }
+
+ focusExpandButton() {
+ this.expandButton?.focus();
+ }
+
+ focusPinButton() {
+ this.pinButton?.focus();
+ }
+
+ onExpandClick() {
+ this.fireEvent("expand-button-click");
+ }
+
+ onPinClick() {
+ this.fireEvent("pin-button-click");
+ }
+}
+
+DynamicPageHeaderActions.define();
+
+export default DynamicPageHeaderActions;
diff --git a/packages/fiori/src/DynamicPageTitle.hbs b/packages/fiori/src/DynamicPageTitle.hbs
new file mode 100644
index 000000000000..acf3d0999141
--- /dev/null
+++ b/packages/fiori/src/DynamicPageTitle.hbs
@@ -0,0 +1,47 @@
+DynamicPage
.
+ *
+ * The DynamicPageTitle
component is part of the DynamicPage
+ * family and is used to serve as title of the DynamicPage
.
+ *
+ * DynamicPageTitle
can hold any component and displays the most important
+ * information regarding the object that will always remain visible while scrolling.
+ *
+ * Note: The actions
slot accepts any UI5 web component, but it`s
+ * recommended to use ui5-toolbar
.
+ *
+ * The user can switch between the expanded/collapsed states of the
+ * DynamicPage
by clicking on the DynamicPageTitle
+ * or by using the expand/collapse visual indicators, positioned at the bottom of the
+ * DynamicPageTitle
and the DynamicPageHeader
inside ui5-dynamic-page-header-actions
.
+ *
+ * DynamicPageTitle
depends on the behavior of the
+ * content that is displayed.
+ *
+ *
+ * @constructor
+ * @extends sap.ui.webc.base.UI5Element
+ * @public
+ */
+@customElement({
+ tag: "ui5-dynamic-page-title",
+ fastNavigation: true,
+ renderer: litRender,
+ styles: DynamicPageTitleCss,
+ template: DynamicPageTitleTemplate,
+})
+class DynamicPageTitle extends UI5Element {
+ static i18nBundle: I18nBundle;
+
+ static async onDefine() {
+ DynamicPageTitle.i18nBundle = await getI18nBundle("@ui5/webcomponents-fiori");
+ }
+
+ /**
+ * Defines the content of the Heading of the Dynamic Page.
+ *
+ * @public
+ */
+ @slot({ type: HTMLElement })
+ heading!: HTMLElement[];
+
+ /**
+ * Defines the heading that is shown only when the header is snapped.
+ *
+ * @public
+ */
+ @slot({ type: HTMLElement })
+ snappedHeading!: HTMLElement[];
+
+ /**
+ * Defines the heading that is shown only when the header is expanded.
+ *
+ * @public
+ */
+ @slot({ type: HTMLElement })
+ expandedHeading!: HTMLElement[];
+
+ /**
+ * Defines the actions in the Dynamic page title.
+ *
+ * @public
+ */
+ @slot({ type: HTMLElement })
+ actions!: HTMLElement[];
+
+ /**
+ * Defines the navigation actions in the Dynamic page title.
+ *
+ * @public
+ */
+ @slot({ type: HTMLElement })
+ navigationActions!: HTMLElement[];
+
+ /**
+ * Defines the content of the Dynamic page title.
+ *
+ * @public
+ */
+ @slot({ "default": true, type: HTMLElement })
+ content!: HTMLElement[];
+
+ /**
+ * Defines the content of the title that is shown only when the header is snapped.
+ *
+ * @public
+ */
+ @slot({ type: HTMLElement })
+ snappedContent!: HTMLElement[];
+
+ /**
+ * Defines the content of the title that is shown only when the header is expanded.
+ *
+ * @public
+ */
+ @slot({ type: HTMLElement })
+ expandedContent!: HTMLElement[];
+
+ /**
+ * Defines the content of the breadcrumbs inside Dynamic Page Title.
+ *
+ * @public
+ */
+ @slot({ type: HTMLElement })
+ breadcrumbs!: HTMLElement[];
+
+ /**
+ * Defines if the title is snapped.
+ *
+ * @public
+ * @default false
+ */
+ @property({ type: Boolean })
+ snapped!: boolean;
+
+ /**
+ * Defines if the mobileNavigationActions are shown.
+ *
+ * @private
+ */
+ @property({ type: Boolean })
+ mobileNavigationActions!: boolean;
+
+ /**
+ * Indicates if the elements is on focus
+ * @private
+ */
+ @property({ type: Boolean })
+ focused!: boolean;
+
+ _handleResize: ResizeObserverCallback;
+ minContentWidth?: number;
+ minActionsWidth?: number;
+
+ constructor() {
+ super();
+ this._handleResize = this.handleResize.bind(this);
+ }
+
+ get hasContent() {
+ return !!this.content.length;
+ }
+
+ get hasHeading() {
+ return !!this.heading.length;
+ }
+
+ get headingSlotName() {
+ if (this.hasHeading) {
+ return "heading";
+ }
+ if (!this.snapped) {
+ return "expandedHeading";
+ }
+ return "snappedHeading";
+ }
+
+ get contentSlotName() {
+ return !this.snapped ? "expandedContent" : "snappedContent";
+ }
+
+ get _headerExpanded() {
+ return !this.snapped;
+ }
+
+ get _ariaDescribedbyText() {
+ return DynamicPageTitle.i18nBundle.getText(DYNAMIC_PAGE_ARIA_DESCR_TOGGLE_HEADER);
+ }
+
+ get _ariaLabelledBy() {
+ const hasAnyHeading = this[this.headingSlotName].length;
+ if (hasAnyHeading) {
+ return `${this._id}-heading`;
+ }
+ }
+
+ get classes() {
+ return {
+ root: {
+ "ui5-dynamic-page-title-root": true,
+ },
+ focusArea: {
+ "ui5-dynamic-page-title-focus-area": true,
+ },
+ topArea: {
+ "ui5-dynamic-page-title--top-area": true,
+ },
+ wrapper: {
+ "ui5-dynamic-page-title--wrapper": true,
+ },
+ heading: {
+ "ui5-dynamic-page-title--heading": true,
+ },
+ content: {
+ "ui5-dynamic-page-title--content": true,
+ },
+ actions: {
+ "ui5-dynamic-page-title--actions": true,
+ },
+ actionsSeparator: {
+ "ui5-dynamic-page-title--actions-separator": true,
+ },
+ };
+ }
+
+ get styles() {
+ return {
+ content: {
+ "min-width": `${this.minContentWidth || 0}px`,
+ },
+ actions: {
+ "min-width": `${this.minActionsWidth || 0}px`,
+ },
+ };
+ }
+
+ onEnterDOM() {
+ ResizeHandler.register(this, this._handleResize);
+ }
+
+ onExitDOM() {
+ ResizeHandler.deregister(this, this._handleResize);
+ }
+
+ onBeforeRendering(): void {
+ this.prepareLayoutActions();
+ }
+
+ prepareLayoutActions() {
+ // all navigation/layout actions should have the NeverOverflow behavior
+ const navigationActions = this.querySelectorLorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc ex mi, elementum et ante commodo, semper sollicitudin magna. Sed dapibus ut tortor quis varius. Sed luctus sem at nunc porta vulputate. Suspendisse lobortis arcu est, quis ultrices ipsum fermentum a. Vestibulum a ipsum placerat ligula gravida fringilla at id ex. Etiam pellentesque lorem sed sagittis aliquam. Quisque semper orci risus, vel efficitur dui euismod aliquet. Morbi sapien sapien, rhoncus et rutrum nec, rhoncus id nisl. Cras non tincidunt enim, id eleifend neque.
+Sed mauris nunc, sollicitudin eget mi nec, tincidunt molestie diam. Aliquam orci dui, vestibulum quis blandit et, interdum vel arcu. Vestibulum sit amet quam ut sapien pulvinar consequat ac sed turpis. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Phasellus quam diam, mattis vitae condimentum ac, ornare vel dui. Pellentesque orci tellus, tempor vel ex eget, luctus vehicula massa. Nullam laoreet, lacus ut vulputate maximus, quam sem molestie nunc, vitae convallis sem nisi nec magna.
+Nullam a felis odio. Curabitur quis tellus nec risus suscipit dictum vitae eget augue. Maecenas faucibus felis a arcu ultricies, sed tempor ante pellentesque. Ut hendrerit ipsum sit amet ligula bibendum, sit amet vehicula lacus condimentum. Morbi lacinia nibh eu placerat sollicitudin. Praesent accumsan pulvinar massa, eget finibus odio lacinia ullamcorper. Integer neque quam, pretium sed accumsan id, euismod non nibh.
+Quisque luctus non mi in mollis. Suspendisse id lectus felis. Aliquam elementum nibh nibh. Nullam eget condimentum dolor. Suspendisse id dui non lorem imperdiet iaculis. Pellentesque ex turpis, vehicula a arcu quis, mattis auctor tellus. In ornare, libero ac rhoncus eleifend, felis orci sagittis felis, ac molestie est lacus fermentum odio. Mauris et porta nibh. Integer tellus leo, laoreet nec odio non, rutrum bibendum magna.
+Morbi lorem libero, imperdiet id condimentum ac, tempor ut velit. Integer a laoreet sem. Nunc at sagittis nisi. Sed placerat diam eu porttitor dignissim. Maecenas nec fringilla tortor. Pellentesque ut elit est. Curabitur quis elit at mauris ullamcorper fringilla. Nullam diam mi, porttitor dictum orci nec, molestie luctus metus. Nunc ut ex blandit, elementum erat eget, pulvinar sapien. Donec nec lorem eu nunc eleifend tempor at non tortor. In quam velit, ornare at rutrum ac, porta ac augue. Suspendisse venenatis semper lacus at venenatis. Praesent vestibulum ligula nulla, at tempus lorem consequat suscipit. Aenean consequat dapibus dui, at bibendum mauris porta a.
+Vivamus ut efficitur massa, ac porttitor dolor. Nullam nec elit quis urna convallis tincidunt. Suspendisse potenti. Phasellus dignissim eget mauris eu egestas. Phasellus accumsan pulvinar pharetra. Pellentesque egestas viverra ipsum. Maecenas a dapibus neque. Morbi tincidunt ultricies enim, ac vehicula elit dapibus sit amet. Donec vel felis at felis elementum sodales id id erat. Pellentesque rutrum placerat mollis. In hac habitasse platea dictumst. Sed eget quam aliquet, pretium ligula vitae, lobortis mauris. Aenean eget ipsum auctor, dapibus quam at, pharetra nunc.
+