diff --git a/CHANGELOG.md b/CHANGELOG.md index e8a7ce35ccac..b99e7b2a519c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,44 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.7.0-rc.0](https://github.com/SAP/ui5-webcomponents/compare/v2.6.2...v2.7.0-rc.0) (2025-01-16) + + +### Bug Fixes + +* ai button and color palette bring their own items ([#10517](https://github.com/SAP/ui5-webcomponents/issues/10517)) ([11b9356](https://github.com/SAP/ui5-webcomponents/commit/11b9356a13d01ece1ebcf4185b61259b9ad6d364)) +* i18n decorator property definition ([#10521](https://github.com/SAP/ui5-webcomponents/issues/10521)) ([d19f4e9](https://github.com/SAP/ui5-webcomponents/commit/d19f4e99bad74432bff21587da43a5844f17f8cf)) +* **ui5-form:** prevent content overflow ([#10552](https://github.com/SAP/ui5-webcomponents/issues/10552)) ([819a453](https://github.com/SAP/ui5-webcomponents/commit/819a453c1a9dd8c55a89c6c32c34e73f9e127e86)) +* **ui5-label:** adjust asterisk size ([#10533](https://github.com/SAP/ui5-webcomponents/issues/10533)) ([44a245d](https://github.com/SAP/ui5-webcomponents/commit/44a245d685c0ace10f93af21652c6c89eb421498)), closes [#10494](https://github.com/SAP/ui5-webcomponents/issues/10494) +* **ui5-popover:** fix opener reference ([#10507](https://github.com/SAP/ui5-webcomponents/issues/10507)) ([a38a28e](https://github.com/SAP/ui5-webcomponents/commit/a38a28e11245e0121bd0a2d6b9d1f5b294952a42)) +* **ui5-toast:** fix template and remove additional text ([#10559](https://github.com/SAP/ui5-webcomponents/issues/10559)) ([ad8f08f](https://github.com/SAP/ui5-webcomponents/commit/ad8f08f1a1b8d2e02b3a929afa46a38ae708feb3)) +* **ui5-toolbar:** correct display of overflow button in hidden container ([#10529](https://github.com/SAP/ui5-webcomponents/issues/10529)) ([3a2f8e3](https://github.com/SAP/ui5-webcomponents/commit/3a2f8e365a2319176e70e4e8cc8e575f58511c6f)) +* **ui5-toolbar:** overflow behaviour ([#10547](https://github.com/SAP/ui5-webcomponents/issues/10547)) ([8df4eb2](https://github.com/SAP/ui5-webcomponents/commit/8df4eb215a15e37df225bbd98836719b584f3065)) +* **ui5-view-settings-dialog:** adjust reset button announcement ([#10320](https://github.com/SAP/ui5-webcomponents/issues/10320)) ([42b29f3](https://github.com/SAP/ui5-webcomponents/commit/42b29f3eb6c435959ee67551d5f9f450995370ce)), closes [#10191](https://github.com/SAP/ui5-webcomponents/issues/10191) + + +### Features + +* **create-package:** generate jsx component template ([#10531](https://github.com/SAP/ui5-webcomponents/issues/10531)) ([dd2d45d](https://github.com/SAP/ui5-webcomponents/commit/dd2d45d4c3947f52773272f6a77be294f240d28f)) +* **ui5-side-navigation:** update existing design for Horizon theme ([#10337](https://github.com/SAP/ui5-webcomponents/issues/10337)) ([d5eff51](https://github.com/SAP/ui5-webcomponents/commit/d5eff51f56042d5fbf94e87d6bd94cdbe2d55982)) +* **ui5-table:** popin-text property added ([#10514](https://github.com/SAP/ui5-webcomponents/issues/10514)) ([a01a303](https://github.com/SAP/ui5-webcomponents/commit/a01a3036526cd02eaac1a973ac5054a7c9c8a194)) +* **ui5-table:** table row actions feature added ([#10460](https://github.com/SAP/ui5-webcomponents/issues/10460)) ([dfe09ae](https://github.com/SAP/ui5-webcomponents/commit/dfe09ae51987ef6237fccfbf0bcb76abb8251dee)) + + + + + +## [2.6.2](https://github.com/SAP/ui5-webcomponents/compare/v2.6.2-rc.0...v2.6.2) (2025-01-09) + + +### Bug Fixes + +* **build:** fix incorrect module ref in custom-elements-manifest ([#10497](https://github.com/SAP/ui5-webcomponents/issues/10497)) ([ea8cdea](https://github.com/SAP/ui5-webcomponents/commit/ea8cdea0b43f31c11479aa2997387be0ef71c6cb)) + + + + + ## [2.6.2-rc.0](https://github.com/SAP/ui5-webcomponents/compare/v2.6.1...v2.6.2-rc.0) (2025-01-09) diff --git a/docs/internal/Toolbar.md b/docs/internal/Toolbar.md index e7165a30cc78..9f4a69215b91 100644 --- a/docs/internal/Toolbar.md +++ b/docs/internal/Toolbar.md @@ -105,42 +105,4 @@ import ToolbarButton from "./dist/ToolbarButton.js";     -``` - -## Events -  -Abstract items can provide a map of events through the `subscribedEvents` getter. The toolbar will actively monitor these events on the physical items, and when triggered, it will also fire the information to the corresponding abstract item. This mechanism proves useful when the abstract item requires synchronization of changes or interactions with the physical items. Importantly, events described as public offer benefits to consumers of the abstract items informing them about interactions with the physical elements. Additionally, the map contains information about the popover, such as `preventClosing: true`, which ensures that the popover remains open when this event is triggered by the physical item. -  -A good example is the Map of the `ui5-toolbar-select`: -  -```javascript -get subscribedEvents() { -  const map = new Map(); -  -  map.set("click", { preventClosing: true }); -  map.set("change", { preventClosing: false }); -  map.set("open", { preventClosing: true }); -  map.set("close", { preventClosing: true }); -  -  return map; -} -``` -  -The `ui5-toolbar-select` then waits for the toolbar to fire the `change` event, in order to notify (synchronize) its `options` slots: -  -```ts -_onEventHandler(e: Event): void { -  if (e.type === "change") { -    // update options -    const selectedOption = (e as CustomEvent).detail.selectedOption; -    const selectedOptionIndex = Number(selectedOption?.getAttribute("data-ui5-external-action-item-index")); -    this.options.forEach((option: Option, index: number) => { -      if (index === selectedOptionIndex) { -        option.setAttribute("selected", ""); -      } else { -        option.removeAttribute("selected"); -      } -    }); -  } -} ``` \ No newline at end of file diff --git a/lerna.json b/lerna.json index cb1a84d13fb8..2559776872d3 100644 --- a/lerna.json +++ b/lerna.json @@ -14,7 +14,7 @@ "packages/create-package", "packages/compat" ], - "version": "2.6.2-rc.0", + "version": "2.7.0-rc.0", "command": { "publish": { "allowBranch": "*", diff --git a/packages/ai/CHANGELOG.md b/packages/ai/CHANGELOG.md index b5ace112987d..d1e47ad6db3f 100644 --- a/packages/ai/CHANGELOG.md +++ b/packages/ai/CHANGELOG.md @@ -3,6 +3,25 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.7.0-rc.0](https://github.com/SAP/ui5-webcomponents/compare/v2.6.2...v2.7.0-rc.0) (2025-01-16) + + +### Bug Fixes + +* ai button and color palette bring their own items ([#10517](https://github.com/SAP/ui5-webcomponents/issues/10517)) ([11b9356](https://github.com/SAP/ui5-webcomponents/commit/11b9356a13d01ece1ebcf4185b61259b9ad6d364)) + + + + + +## [2.6.2](https://github.com/SAP/ui5-webcomponents/compare/v2.6.2-rc.0...v2.6.2) (2025-01-09) + +**Note:** Version bump only for package @ui5/webcomponents-ai + + + + + ## [2.6.2-rc.0](https://github.com/SAP/ui5-webcomponents/compare/v2.6.1...v2.6.2-rc.0) (2025-01-09) **Note:** Version bump only for package @ui5/webcomponents-ai diff --git a/packages/ai/README.md b/packages/ai/README.md index 2459ecdbf61a..d40a1ee5a82b 100644 --- a/packages/ai/README.md +++ b/packages/ai/README.md @@ -13,6 +13,7 @@ Provides web components implementing AI-related visual and interaction. | Web Component | Tag name | Module import | |--------------------------|--------------------------------|---------------------------------------------------------| | Button | `ui5-ai-button` | `import "@ui5/webcomponents-ai/dist/Button.js";` | +| Button State | `ui5-ai-button-state` | comes with `ui5-ai-button` | | PromptInput | `ui5-ai-prompt-input` | `import "@ui5/webcomponents-ai/dist/PromptInput.js";` | ## Provided assets diff --git a/packages/ai/package.json b/packages/ai/package.json index 50451454d355..1cc5d564f9b2 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -1,6 +1,6 @@ { "name": "@ui5/webcomponents-ai", - "version": "2.6.2-rc.0", + "version": "2.7.0-rc.0", "description": "UI5 Web Components: webcomponents.ai", "ui5": { "webComponentsPackage": true @@ -45,13 +45,13 @@ "directory": "packages/ai" }, "dependencies": { - "@ui5/webcomponents": "2.6.2-rc.0", - "@ui5/webcomponents-base": "2.6.2-rc.0", - "@ui5/webcomponents-icons": "2.6.2-rc.0", - "@ui5/webcomponents-theming": "2.6.2-rc.0" + "@ui5/webcomponents": "2.7.0-rc.0", + "@ui5/webcomponents-base": "2.7.0-rc.0", + "@ui5/webcomponents-icons": "2.7.0-rc.0", + "@ui5/webcomponents-theming": "2.7.0-rc.0" }, "devDependencies": { - "@ui5/webcomponents-tools": "2.6.2-rc.0", + "@ui5/webcomponents-tools": "2.7.0-rc.0", "chromedriver": "^131.0.0" } } diff --git a/packages/ai/src/Button.ts b/packages/ai/src/Button.ts index b9e90523c2ff..dcd3b919f13a 100644 --- a/packages/ai/src/Button.ts +++ b/packages/ai/src/Button.ts @@ -9,6 +9,7 @@ import jsxRenderer from "@ui5/webcomponents-base/dist/renderer/JsxRenderer.js"; import type SplitButton from "@ui5/webcomponents/dist/SplitButton.js"; import type ButtonDesign from "@ui5/webcomponents/dist/types/ButtonDesign.js"; import type ButtonState from "./ButtonState.js"; +import "./ButtonState.js"; import ButtonTemplate from "./ButtonTemplate.js"; diff --git a/packages/base/CHANGELOG.md b/packages/base/CHANGELOG.md index 82cf87510846..e675b2c2a74e 100644 --- a/packages/base/CHANGELOG.md +++ b/packages/base/CHANGELOG.md @@ -3,6 +3,26 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.7.0-rc.0](https://github.com/SAP/ui5-webcomponents/compare/v2.6.2...v2.7.0-rc.0) (2025-01-16) + + +### Bug Fixes + +* i18n decorator property definition ([#10521](https://github.com/SAP/ui5-webcomponents/issues/10521)) ([d19f4e9](https://github.com/SAP/ui5-webcomponents/commit/d19f4e99bad74432bff21587da43a5844f17f8cf)) +* **ui5-popover:** fix opener reference ([#10507](https://github.com/SAP/ui5-webcomponents/issues/10507)) ([a38a28e](https://github.com/SAP/ui5-webcomponents/commit/a38a28e11245e0121bd0a2d6b9d1f5b294952a42)) + + + + + +## [2.6.2](https://github.com/SAP/ui5-webcomponents/compare/v2.6.2-rc.0...v2.6.2) (2025-01-09) + +**Note:** Version bump only for package @ui5/webcomponents-base + + + + + ## [2.6.2-rc.0](https://github.com/SAP/ui5-webcomponents/compare/v2.6.1...v2.6.2-rc.0) (2025-01-09) **Note:** Version bump only for package @ui5/webcomponents-base diff --git a/packages/base/package.json b/packages/base/package.json index 789171083f1f..bc6001ec97f4 100644 --- a/packages/base/package.json +++ b/packages/base/package.json @@ -1,6 +1,6 @@ { "name": "@ui5/webcomponents-base", - "version": "2.6.2-rc.0", + "version": "2.7.0-rc.0", "description": "UI5 Web Components: webcomponents.base", "author": "SAP SE (https://www.sap.com)", "license": "Apache-2.0", @@ -57,7 +57,7 @@ }, "devDependencies": { "@openui5/sap.ui.core": "1.120.17", - "@ui5/webcomponents-tools": "2.6.2-rc.0", + "@ui5/webcomponents-tools": "2.7.0-rc.0", "chromedriver": "^131.0.0", "clean-css": "^5.2.2", "copy-and-watch": "^0.1.5", diff --git a/packages/base/src/UI5Element.ts b/packages/base/src/UI5Element.ts index 283cf8b2d042..ee85df302a5f 100644 --- a/packages/base/src/UI5Element.ts +++ b/packages/base/src/UI5Element.ts @@ -41,6 +41,7 @@ import { updateFormValue, setFormValue } from "./features/InputElementsFormSuppo import type { IFormInputElement } from "./features/InputElementsFormSupport.js"; import { getComponentFeature, subscribeForFeatureLoad } from "./FeaturesRegistry.js"; import { getI18nBundle } from "./i18nBundle.js"; +import type I18nBundle from "./i18nBundle.js"; import { fetchCldr } from "./asset-registries/LocaleData.js"; import getLocale from "./locale/getLocale.js"; @@ -1080,6 +1081,10 @@ abstract class UI5Element extends HTMLElement { return true; } + get isUI5AbstractElement(): boolean { + return !(this.constructor as typeof UI5Element)._needsShadowDOM(); + } + get classes(): ClassMap { return {}; } @@ -1288,6 +1293,11 @@ abstract class UI5Element extends HTMLElement { static asyncFinished: boolean; static definePromise: Promise | undefined; + static i18nBundleStorage: Record = {}; + + static get i18nBundles(): Record { + return this.i18nBundleStorage; + } /** * Registers a UI5 Web Component in the browser window object @@ -1304,8 +1314,7 @@ abstract class UI5Element extends HTMLElement { const [i18nBundles] = result; Object.entries(this.getMetadata().getI18n()).forEach((pair, index) => { const propertyName = pair[0]; - const targetClass = pair[1].target; - (targetClass as Record)[propertyName] = i18nBundles[index]; + this.i18nBundleStorage[propertyName] = i18nBundles[index]; }); this.asyncFinished = true; }; diff --git a/packages/base/src/config/Theme.ts b/packages/base/src/config/Theme.ts index 6cb3f9e68927..4f5791796a05 100644 --- a/packages/base/src/config/Theme.ts +++ b/packages/base/src/config/Theme.ts @@ -64,8 +64,7 @@ const getDefaultTheme = (): string => { * @returns {boolean} */ const isTheme = (theme: string) => { - const currentTheme = getTheme(); - return currentTheme === theme || currentTheme === `${theme}_exp`; + return getTheme() === theme; }; /** diff --git a/packages/base/src/decorators/i18n.ts b/packages/base/src/decorators/i18n.ts index 7b2858ba9d46..a30016a34f61 100644 --- a/packages/base/src/decorators/i18n.ts +++ b/packages/base/src/decorators/i18n.ts @@ -20,6 +20,14 @@ const i18n = (bundleName: string): i18nDecorator => { if (!target.metadata.i18n) { target.metadata.i18n = {}; } + + Object.defineProperty(target, propertyName, { + get() { + return target.i18nBundles[propertyName]; + }, + set() {}, + }); + target.metadata.i18n[propertyName] = { bundleName, target, diff --git a/packages/base/src/thirdparty/preact/jsxRuntime.module.js b/packages/base/src/thirdparty/preact/jsxRuntime.module.js index a4dcda26aaf0..c690262a08fa 100644 --- a/packages/base/src/thirdparty/preact/jsxRuntime.module.js +++ b/packages/base/src/thirdparty/preact/jsxRuntime.module.js @@ -1,2 +1 @@ import{options as r,Fragment as e}from"./preact.module.js";export{Fragment}from"./preact.module.js";var t=/["&<]/;function n(r){if(0===r.length||!1===t.test(r))return r;for(var e=0,n=0,o="",f="";n2&&(e.children=arguments.length>3?n.call(arguments,2):t),"function"==typeof l&&null!=l.defaultProps)for(r in l.defaultProps)void 0===e[r]&&(e[r]=l.defaultProps[r]);return m(l,e,i,o,null)}function m(n,t,i,o,r){var e={type:n,props:t,key:i,ref:o,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:null==r?++u:r,__i:-1,__u:0};return null==r&&null!=l.vnode&&l.vnode(e),e}function b(){return{current:null}}function k(n){return n.children}function x(n,l){this.props=n,this.context=l}function C(n,l){if(null==l)return n.__?C(n.__,n.__i+1):null;for(var u;lu&&i.sort(e));P.__r=0}function $(n,l,u,t,i,o,r,e,f,c,s){var a,h,y,d,w,_,g=t&&t.__k||v,m=l.length;for(f=I(u,l,g,f),a=0;a0?m(o.type,o.props,o.key,o.ref?o.ref:null,o.__v):o).__=n,o.__b=n.__b+1,r=null,-1!==(f=o.__i=O(o,u,e,a))&&(a--,(r=u[f])&&(r.__u|=2)),null==r||null===r.__v?(-1==f&&h--,"function"!=typeof o.type&&(o.__u|=4)):f!==e&&(f==e-1?h--:f==e+1?h++:(f>e?h--:h++,o.__u|=4))):o=n.__k[i]=null;if(a)for(i=0;i(null!=f&&0==(2&f.__u)?1:0))for(;r>=0||e=0){if((f=l[r])&&0==(2&f.__u)&&i==f.key&&o===f.type)return r;r--}if(e2&&(f.children=arguments.length>3?n.call(arguments,2):t),m(l.type,f,i||l.key,o||l.ref,null)}function J(n,l){var u={__c:l="__cC"+h++,__:n,Consumer:function(n,l){return n.children(l)},Provider:function(n){var u,t;return this.getChildContext||(u=new Set,(t={})[l]=this,this.getChildContext=function(){return t},this.componentWillUnmount=function(){u=null},this.shouldComponentUpdate=function(n){this.props.value!==n.value&&u.forEach(function(n){n.__e=!0,M(n)})},this.sub=function(n){u.add(n);var l=n.componentWillUnmount;n.componentWillUnmount=function(){u&&u.delete(n),l&&l.call(n)}}),n.children}};return u.Provider.__=u.Consumer.contextType=u}n=v.slice,l={__e:function(n,l,u,t){for(var i,o,r;l=l.__;)if((i=l.__c)&&!i.__)try{if((o=i.constructor)&&null!=o.getDerivedStateFromError&&(i.setState(o.getDerivedStateFromError(n)),r=i.__d),null!=i.componentDidCatch&&(i.componentDidCatch(n,t||{}),r=i.__d),r)return i.__E=i}catch(l){n=l}throw n}},u=0,t=function(n){return null!=n&&null==n.constructor},x.prototype.setState=function(n,l){var u;u=null!=this.__s&&this.__s!==this.state?this.__s:this.__s=w({},this.state),"function"==typeof n&&(n=n(w({},u),this.props)),n&&w(u,n),null!=n&&this.__v&&(l&&this._sb.push(l),M(this))},x.prototype.forceUpdate=function(n){this.__v&&(this.__e=!0,n&&this.__h.push(n),M(this))},x.prototype.render=k,i=[],r="function"==typeof Promise?Promise.prototype.then.bind(Promise.resolve()):setTimeout,e=function(n,l){return n.__v.__b-l.__v.__b},P.__r=0,f=/(PointerCapture)$|Capture$/i,c=0,s=A(!1),a=A(!0),h=0;export{x as Component,k as Fragment,G as cloneElement,J as createContext,g as createElement,b as createRef,g as h,E as hydrate,t as isValidElement,l as options,D as render,L as toChildArray}; -//# sourceMappingURL=preact.module.js.map diff --git a/packages/base/src/util/ColorConversion.ts b/packages/base/src/util/ColorConversion.ts index 1dd1ebe6cd1a..8564c4d0db90 100644 --- a/packages/base/src/util/ColorConversion.ts +++ b/packages/base/src/util/ColorConversion.ts @@ -254,8 +254,8 @@ const RGBStringToRGBObject = (color: string): ColorRGB => { const HSLToRGB = (color: ColorHSL): ColorRGB => { // Formula taken from https://www.rapidtables.com/convert/color/hsl-to-rgb.html - let saturation = color.s * 100, - lightness = color.l * 100, + let saturation = color.s, + lightness = color.l, red, green, blue; @@ -276,7 +276,7 @@ const HSLToRGB = (color: ColorHSL): ColorRGB => { lightness /= 100; } - const hue = color.h, + const hue = ((color.h % 360) + 360) % 360, d = saturation * (1 - Math.abs(2 * lightness - 1)), m = 255 * (lightness - 0.5 * d), x = d * (1 - Math.abs(((hue / 60) % 2) - 1)), @@ -345,8 +345,8 @@ const HEXToRGB = (hex: string): ColorRGB => { * @param {Object} color Receives an object with the properties for each of the main colors(r, g, b) */ const RGBtoHEX = (color: ColorRGB): string => { - const hexMap = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"]; - let hexValue = "#"; + const hexMap = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"]; + let hexValue = ""; let divisionNumber = color.r / 16; let remainder = color.r % 16; @@ -374,30 +374,35 @@ const RGBToHSL = (color: ColorRGB): ColorHSL => { min = Math.min(R, G, B), delta = max - min; - let h = 0, - s; + let h = (max + min) / 2; + let s = (max + min) / 2; + let l = (max + min) / 2; - // Hue calculation - if (delta === 0) { + if (max === min) { h = 0; - } else if (max === R) { - h = 60 * (((G - B) / delta) % 6); - } else if (max === G) { - h = 60 * (((B - R) / delta) + 2); - } else if (max === B) { - h = 60 * (((R - G) / delta) + 4); - } - - // Lightness calculation - const l = (max + min) / 2; - - // Saturation calculation - if (delta === 0) { s = 0; } else { - s = delta / (1 - Math.abs(2 * l - 1)); + s = l > 0.5 ? delta / (2 - max - min) : delta / (max + min); + + switch (max) { + case R: + h = (G - B) / delta + (G < B ? 6 : 0); + break; + case G: + h = (B - R) / delta + 2; + break; + case B: + h = (R - G) / delta + 4; + break; + } + + h /= 6; } + h = Math.round(h * 360); + s = Math.round(s * 100); + l = Math.round(l * 100); + return { h, s, diff --git a/packages/base/src/util/throttle.ts b/packages/base/src/util/throttle.ts new file mode 100644 index 000000000000..22686b928c20 --- /dev/null +++ b/packages/base/src/util/throttle.ts @@ -0,0 +1,16 @@ +function throttle(func: () => void, delay: number): () => void { + let wait = false; + + return (...args) => { + if (wait) { + return; + } + + func(...args); + wait = true; + setTimeout(() => { + wait = false; + }, delay); + }; +} +export default throttle; diff --git a/packages/compat/CHANGELOG.md b/packages/compat/CHANGELOG.md index 95b61b6144a7..eba1add037f1 100644 --- a/packages/compat/CHANGELOG.md +++ b/packages/compat/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.7.0-rc.0](https://github.com/SAP/ui5-webcomponents/compare/v2.6.2...v2.7.0-rc.0) (2025-01-16) + +**Note:** Version bump only for package @ui5/webcomponents-compat + + + + + +## [2.6.2](https://github.com/SAP/ui5-webcomponents/compare/v2.6.2-rc.0...v2.6.2) (2025-01-09) + +**Note:** Version bump only for package @ui5/webcomponents-compat + + + + + ## [2.6.2-rc.0](https://github.com/SAP/ui5-webcomponents/compare/v2.6.1...v2.6.2-rc.0) (2025-01-09) **Note:** Version bump only for package @ui5/webcomponents-compat diff --git a/packages/compat/package.json b/packages/compat/package.json index 9fb320bbba15..33672f526452 100644 --- a/packages/compat/package.json +++ b/packages/compat/package.json @@ -1,6 +1,6 @@ { "name": "@ui5/webcomponents-compat", - "version": "2.6.2-rc.0", + "version": "2.7.0-rc.0", "description": "UI5 Web Components: webcomponents.compat", "ui5": { "webComponentsPackage": true @@ -45,13 +45,13 @@ "directory": "packages/compat" }, "dependencies": { - "@ui5/webcomponents": "2.6.2-rc.0", - "@ui5/webcomponents-base": "2.6.2-rc.0", - "@ui5/webcomponents-icons": "2.6.2-rc.0", - "@ui5/webcomponents-theming": "2.6.2-rc.0" + "@ui5/webcomponents": "2.7.0-rc.0", + "@ui5/webcomponents-base": "2.7.0-rc.0", + "@ui5/webcomponents-icons": "2.7.0-rc.0", + "@ui5/webcomponents-theming": "2.7.0-rc.0" }, "devDependencies": { - "@ui5/webcomponents-tools": "2.6.2-rc.0", + "@ui5/webcomponents-tools": "2.7.0-rc.0", "chromedriver": "^131.0.0" } } diff --git a/packages/create-package/CHANGELOG.md b/packages/create-package/CHANGELOG.md index e4a2a2854094..e8af4fdd946f 100644 --- a/packages/create-package/CHANGELOG.md +++ b/packages/create-package/CHANGELOG.md @@ -3,6 +3,25 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.7.0-rc.0](https://github.com/SAP/ui5-webcomponents/compare/v2.6.2...v2.7.0-rc.0) (2025-01-16) + + +### Features + +* **create-package:** generate jsx component template ([#10531](https://github.com/SAP/ui5-webcomponents/issues/10531)) ([dd2d45d](https://github.com/SAP/ui5-webcomponents/commit/dd2d45d4c3947f52773272f6a77be294f240d28f)) + + + + + +## [2.6.2](https://github.com/SAP/ui5-webcomponents/compare/v2.6.2-rc.0...v2.6.2) (2025-01-09) + +**Note:** Version bump only for package @ui5/create-webcomponents-package + + + + + ## [2.6.2-rc.0](https://github.com/SAP/ui5-webcomponents/compare/v2.6.1...v2.6.2-rc.0) (2025-01-09) **Note:** Version bump only for package @ui5/create-webcomponents-package diff --git a/packages/create-package/package.json b/packages/create-package/package.json index f534dc2eaf42..4980df5d74c6 100644 --- a/packages/create-package/package.json +++ b/packages/create-package/package.json @@ -1,6 +1,6 @@ { "name": "@ui5/create-webcomponents-package", - "version": "2.6.2-rc.0", + "version": "2.7.0-rc.0", "description": "UI5 Web Components: create package", "author": "SAP SE (https://www.sap.com)", "license": "Apache-2.0", diff --git a/packages/create-package/template/src/MyFirstComponent.hbs b/packages/create-package/template/src/MyFirstComponent.hbs deleted file mode 100644 index bbc12813845e..000000000000 --- a/packages/create-package/template/src/MyFirstComponent.hbs +++ /dev/null @@ -1,6 +0,0 @@ -
- {{counterText}} :: {{count}} -
diff --git a/packages/create-package/template/src/MyFirstComponent.ts b/packages/create-package/template/src/MyFirstComponent.ts index 2a1894d7ef9b..3471b7ba9652 100644 --- a/packages/create-package/template/src/MyFirstComponent.ts +++ b/packages/create-package/template/src/MyFirstComponent.ts @@ -1,12 +1,12 @@ import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js"; import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; import property from "@ui5/webcomponents-base/dist/decorators/property.js"; -import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js"; -import { getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js"; +import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js"; +import jsxRenderer from "@ui5/webcomponents-base/dist/renderer/JsxRenderer.js"; import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js"; // Template -import INIT_PACKAGE_VAR_CLASS_NAMETemplate from "./generated/templates/INIT_PACKAGE_VAR_CLASS_NAMETemplate.lit.js"; +import INIT_PACKAGE_VAR_CLASS_NAMETemplate from "./INIT_PACKAGE_VAR_CLASS_NAMETemplate.js"; // Styles import INIT_PACKAGE_VAR_CLASS_NAMECss from "./generated/themes/INIT_PACKAGE_VAR_CLASS_NAME.css.js"; @@ -26,17 +26,14 @@ import { COUNT } from "./generated/i18n/i18n-defaults.js"; */ @customElement({ tag: "INIT_PACKAGE_VAR_TAG", - renderer: litRender, + renderer: jsxRenderer, styles: INIT_PACKAGE_VAR_CLASS_NAMECss, template: INIT_PACKAGE_VAR_CLASS_NAMETemplate, }) class INIT_PACKAGE_VAR_CLASS_NAME extends UI5Element { + @i18n("INIT_PACKAGE_VAR_NAME") static i18nBundle: I18nBundle; - static async onDefine() { - INIT_PACKAGE_VAR_CLASS_NAME.i18nBundle = await getI18nBundle("INIT_PACKAGE_VAR_NAME"); - } - /** * Defines the component count. * @default 0 diff --git a/packages/create-package/template/src/MyFirstComponentTemplate.tsx b/packages/create-package/template/src/MyFirstComponentTemplate.tsx new file mode 100644 index 000000000000..031584d6dd37 --- /dev/null +++ b/packages/create-package/template/src/MyFirstComponentTemplate.tsx @@ -0,0 +1,9 @@ +import type INIT_PACKAGE_VAR_CLASS_NAME from "./INIT_PACKAGE_VAR_CLASS_NAME.js"; + +export default function INIT_PACKAGE_VAR_CLASS_NAMETemplate(this: INIT_PACKAGE_VAR_CLASS_NAME) { + return ( +
+ {this.counterText} :: {this.count} +
+ ); +} diff --git a/packages/fiori/CHANGELOG.md b/packages/fiori/CHANGELOG.md index 45065c9e2a87..f26aeed5f307 100644 --- a/packages/fiori/CHANGELOG.md +++ b/packages/fiori/CHANGELOG.md @@ -3,6 +3,31 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.7.0-rc.0](https://github.com/SAP/ui5-webcomponents/compare/v2.6.2...v2.7.0-rc.0) (2025-01-16) + + +### Bug Fixes + +* i18n decorator property definition ([#10521](https://github.com/SAP/ui5-webcomponents/issues/10521)) ([d19f4e9](https://github.com/SAP/ui5-webcomponents/commit/d19f4e99bad74432bff21587da43a5844f17f8cf)) +* **ui5-view-settings-dialog:** adjust reset button announcement ([#10320](https://github.com/SAP/ui5-webcomponents/issues/10320)) ([42b29f3](https://github.com/SAP/ui5-webcomponents/commit/42b29f3eb6c435959ee67551d5f9f450995370ce)), closes [#10191](https://github.com/SAP/ui5-webcomponents/issues/10191) + + +### Features + +* **ui5-side-navigation:** update existing design for Horizon theme ([#10337](https://github.com/SAP/ui5-webcomponents/issues/10337)) ([d5eff51](https://github.com/SAP/ui5-webcomponents/commit/d5eff51f56042d5fbf94e87d6bd94cdbe2d55982)) + + + + + +## [2.6.2](https://github.com/SAP/ui5-webcomponents/compare/v2.6.2-rc.0...v2.6.2) (2025-01-09) + +**Note:** Version bump only for package @ui5/webcomponents-fiori + + + + + ## [2.6.2-rc.0](https://github.com/SAP/ui5-webcomponents/compare/v2.6.1...v2.6.2-rc.0) (2025-01-09) **Note:** Version bump only for package @ui5/webcomponents-fiori diff --git a/packages/fiori/cypress/specs/ShellBar.cy.ts b/packages/fiori/cypress/specs/ShellBar.cy.ts new file mode 100644 index 000000000000..455b3c18c9f1 --- /dev/null +++ b/packages/fiori/cypress/specs/ShellBar.cy.ts @@ -0,0 +1,241 @@ +import { html } from "lit"; +import "@ui5/webcomponents-icons/dist/activities.js"; +import "@ui5/webcomponents-icons/dist/sys-help.js"; +import "@ui5/webcomponents-icons/dist/da.js"; +import "@ui5/webcomponents/dist/ToggleButton.js"; +import "@ui5/webcomponents/dist/Button.js"; +import "@ui5/webcomponents/dist/Switch.js"; +import "@ui5/webcomponents/dist/Tag.js"; +import "@ui5/webcomponents/dist/Avatar.js"; +import "@ui5/webcomponents/dist/Input.js"; +import "@ui5/webcomponents/dist/features/InputSuggestions.js"; +import "@ui5/webcomponents/dist/SuggestionItem.js"; +import "@ui5/webcomponents/dist/SuggestionItemCustom.js"; +import "@ui5/webcomponents/dist/SuggestionItemGroup.js"; +import "@ui5/webcomponents/dist/List.js"; +import "../../src/ShellBar.js"; +import "../../src/ShellBarItem.js"; + +describe("Responsiveness", () => { + const basicTemplate = html` + + + + + + + + + + + + + + + + +
Instructions
+
+ + + PR2 +
`; + + const templateWithMenuItems = html` + + + + Application 1 + Application 2 + Application 3 + Application 4 + Application 5 + + + + + + + + + + + + +
Instructions
+
+
`; + + const templateWithOnlyOneAction = html` + + `; + beforeEach(() => { + cy.mount(basicTemplate).as("html"); + + cy.get("#shellbar") + .as("shellbar"); + }); + + afterEach(() => { + cy.viewport(1920, 1080); + }); + + it("tests XXL Breakpoint 1920px", () => { + cy.viewport(1920, 1080); + cy.get("@shellbar") + .find("ui5-button[slot='startButton']") + .as("backButton"); + + cy.get("@shellbar") + .shadow() + .find(".ui5-shellbar-title") + .as("primaryTitle"); + + cy.get("@shellbar") + .shadow() + .find(".ui5-shellbar-secondary-title") + .as("secondaryTitle"); + + cy.get("@shellbar") + .shadow() + .find(".ui5-shellbar-search-button") + .as("searchButton"); + + cy.get("@shellbar") + .shadow() + .find(".ui5-shellbar-custom-item") + .as("customActionIcon1"); + + cy.get("@shellbar") + .shadow() + .find(".ui5-shellbar-bell-button") + .as("notificationsIcon"); + + cy.get("@shellbar") + .shadow() + .find(".ui5-shellbar-image-button") + .as("profileIcon"); + + cy.get("@shellbar") + .shadow() + .find(".ui5-shellbar-button-product-switch") + .as("productSwitchIcon"); + + cy.get("@shellbar") + .find("ui5-toggle-button[slot='assistant']") + .as("assistant"); + + cy.get("@backButton").should("be.visible"); + cy.get("@primaryTitle").should("be.visible"); + cy.get("@secondaryTitle").should("be.visible"); + cy.get("@customActionIcon1").should("be.visible"); + cy.get("@notificationsIcon").should("be.visible"); + cy.get("@profileIcon").should("be.visible"); + cy.get("@productSwitchIcon").should("be.visible"); + cy.get("@assistant").should("be.visible"); + + cy.get("@searchButton").click(); + + cy.get("@shellbar") + .find("ui5-input[slot='searchField']") + .as("searchField").should("be.visible"); + + cy.get("@searchButton").click(); + }); + + it("tests S Breakpoint 320px", () => { + cy.get("html").viewport("iphone-x"); + cy.get("@shellbar") + .shadow() + .find(".ui5-shellbar-overflow-button") + .should("exist"); + cy.get("@shellbar") + .shadow() + .get("ui5-switch") + .should("be.hidden"); + }); + + it("tests items visibility in Lean mode", () => { + cy.get("@shellbar") + .find("ui5-button[slot='startButton']") + .as("backButton"); + + cy.get("@shellbar") + .shadow() + .find(".ui5-shellbar-search-button") + .as("searchButton"); + + cy.get("@shellbar") + .shadow() + .find(".ui5-shellbar-bell-button") + .as("notificationsIcon"); + + cy.get("@shellbar") + .shadow() + .find(".ui5-shellbar-image-button") + .as("profileIcon"); + + cy.get("@shellbar") + .shadow() + .find(".ui5-shellbar-button-product-switch") + .as("productSwitchIcon"); + }); + + it("tests logo and Primary title when no menuItems are presented", () => { + cy.get("@shellbar") + .shadow() + .find(".ui5-shellbar-logo-area") + .as("logoLink"); + + cy.get("@logoLink").should("exist"); + }); + + it("tests Primary title when menuItems are presented", () => { + cy.mount(templateWithMenuItems).as("html1"); + + cy.get("@shellbar") + .shadow() + .find(".ui5-shellbar-menu-button") + .as("menuButton"); + + cy.get("@menuButton").should("be.visible"); + }); + + it("tests XXL Breakpoint Search bar", () => { + cy.get("@shellbar").invoke("attr", "show-open-search-field", "true"); + cy.viewport(2560, 1080); + cy.get("[slot='searchField']") + .should("exist"); + }); + + it("Test overflow button not showing, when only one action is presented", () => { + cy.mount(templateWithOnlyOneAction).as("html1"); + + cy.get("html").viewport("iphone-6"); + cy.get("@shellbar") + .shadow() + .find(".ui5-shellbar-overflow-button") + .should("be.hidden"); + }); +}); diff --git a/packages/fiori/cypress/specs/Timeline.cy.ts b/packages/fiori/cypress/specs/Timeline.cy.ts new file mode 100644 index 000000000000..b2c84ed3efa8 --- /dev/null +++ b/packages/fiori/cypress/specs/Timeline.cy.ts @@ -0,0 +1,55 @@ +import { html } from "lit"; +import "../../src/Timeline.js"; +import "../../src/TimelineItem.js"; +import "@ui5/webcomponents-icons/dist/accept.js"; +import "@ui5/webcomponents-icons/dist/message-information.js"; +import "@ui5/webcomponents-icons/dist/decline.js"; +import "@ui5/webcomponents-icons/dist/message-warning.js"; + +describe("Accessibility", () => { + beforeEach(() => { + cy.mount(html` + + + + Compilation succeeded. + + + Lint completed with minor issues. + + + + `); + + cy.get("#test-timeline").as("timeline"); + }); + + it("item with state attribute has aria-description, item without state does not", () => { + cy.get(`ui5-timeline-item[state="Positive"]`).each($itemWithState => { + cy.wrap($itemWithState) + .shadow() + .find(".ui5-tli-bubble") + .should("have.attr", "aria-description"); + }); + + cy.get(`ui5-timeline-item:not([state="Positive"])`).each($itemWithoutState => { + cy.wrap($itemWithoutState) + .shadow() + .find(".ui5-tli-bubble") + .should("not.have.attr", "aria-description"); + }); + }); +}); diff --git a/packages/fiori/package.json b/packages/fiori/package.json index 709a5dbe2d31..0745c7e68a0f 100644 --- a/packages/fiori/package.json +++ b/packages/fiori/package.json @@ -1,6 +1,6 @@ { "name": "@ui5/webcomponents-fiori", - "version": "2.6.2-rc.0", + "version": "2.7.0-rc.0", "description": "UI5 Web Components: webcomponents.fiori", "ui5": { "webComponentsPackage": true @@ -53,14 +53,14 @@ "directory": "packages/fiori" }, "dependencies": { - "@ui5/webcomponents": "2.6.2-rc.0", - "@ui5/webcomponents-base": "2.6.2-rc.0", - "@ui5/webcomponents-icons": "2.6.2-rc.0", - "@ui5/webcomponents-theming": "2.6.2-rc.0", + "@ui5/webcomponents": "2.7.0-rc.0", + "@ui5/webcomponents-base": "2.7.0-rc.0", + "@ui5/webcomponents-icons": "2.7.0-rc.0", + "@ui5/webcomponents-theming": "2.7.0-rc.0", "@zxing/library": "^0.21.3" }, "devDependencies": { - "@ui5/webcomponents-tools": "2.6.2-rc.0", + "@ui5/webcomponents-tools": "2.7.0-rc.0", "chromedriver": "^131.0.0", "lit": "^2.0.0" } diff --git a/packages/fiori/src/BarcodeScannerDialog.ts b/packages/fiori/src/BarcodeScannerDialog.ts index 7f2db9b07473..56bb3220e64f 100644 --- a/packages/fiori/src/BarcodeScannerDialog.ts +++ b/packages/fiori/src/BarcodeScannerDialog.ts @@ -7,7 +7,6 @@ import customElement from "@ui5/webcomponents-base/dist/decorators/customElement import property from "@ui5/webcomponents-base/dist/decorators/property.js"; import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js"; import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js"; -import { getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js"; import type { Result, Exception } from "@zxing/library"; import type { Interval } from "@ui5/webcomponents-base/dist/types.js"; // eslint-disable-next-line import/no-extraneous-dependencies @@ -185,10 +184,6 @@ class BarcodeScannerDialog extends UI5Element { this._handleCaptureRegionBound = this._handleDrawCaptureRegion.bind(this); } - static async onDefine() { - BarcodeScannerDialog.i18nBundle = await getI18nBundle("@ui5/webcomponents-fiori"); - } - async onAfterRendering() { if (!this._hasGetUserMedia()) { this.fireDecoratorEvent("scan-error", { message: "getUserMedia() is not supported by your browser" }); diff --git a/packages/fiori/src/DynamicPage.ts b/packages/fiori/src/DynamicPage.ts index a9bd4eac6e13..3756e044f0d9 100644 --- a/packages/fiori/src/DynamicPage.ts +++ b/packages/fiori/src/DynamicPage.ts @@ -7,9 +7,6 @@ import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js"; import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js"; import jsxRenderer from "@ui5/webcomponents-base/dist/renderer/JsxRenderer.js"; import { renderFinished } from "@ui5/webcomponents-base/dist/Render.js"; -import ResizeHandler from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js"; -import type { ResizeObserverCallback } from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js"; -import MediaRange from "@ui5/webcomponents-base/dist/MediaRange.js"; import announce from "@ui5/webcomponents-base/dist/util/InvisibleMessage.js"; import InvisibleMessageMode from "@ui5/webcomponents-base/dist/types/InvisibleMessageMode.js"; import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js"; @@ -153,14 +150,6 @@ class DynamicPage extends UI5Element { @property({ type: Boolean }) showFooter = false; - /** - * Defines the current media query size. - * - * @private - */ - @property() - mediaRange?: string; - /** * Defines the content of the Dynamic Page. * @@ -203,8 +192,6 @@ class DynamicPage extends UI5Element { @property({ type: Boolean }) _headerSnapped = false; - _updateMediaRange: ResizeObserverCallback; - @query(".ui5-dynamic-page-scroll-container") scrollContainer?: HTMLElement; @@ -213,16 +200,6 @@ class DynamicPage extends UI5Element { constructor() { super(); - - this._updateMediaRange = this.updateMediaRange.bind(this); - } - - onEnterDOM() { - ResizeHandler.register(this, this._updateMediaRange); - } - - onExitDOM() { - ResizeHandler.deregister(this, this._updateMediaRange); } onBeforeRendering() { @@ -431,10 +408,6 @@ class DynamicPage extends UI5Element { this.dynamicPageTitle?.removeAttribute("hovered"); await renderFinished(); } - - updateMediaRange() { - this.mediaRange = MediaRange.getCurrentRange(MediaRange.RANGESETS.RANGE_4STEPS, this.getDomRef()!.offsetWidth); - } } DynamicPage.define(); diff --git a/packages/fiori/src/DynamicPageTitle.ts b/packages/fiori/src/DynamicPageTitle.ts index 2952905d3860..400d738a13ef 100644 --- a/packages/fiori/src/DynamicPageTitle.ts +++ b/packages/fiori/src/DynamicPageTitle.ts @@ -274,8 +274,8 @@ class DynamicPageTitle extends UI5Element { } } - get _needsSeparator() { - return (this.navigationBar.length && this.actionsBar.length); + get _needsSeparator(): boolean { + return (this.navigationBar.length > 0 && this.actionsBar.length > 0); } prepareLayoutActions() { diff --git a/packages/fiori/src/ShellBar.hbs b/packages/fiori/src/ShellBar.hbs index 183a3a091ff0..b409b5917713 100644 --- a/packages/fiori/src/ShellBar.hbs +++ b/packages/fiori/src/ShellBar.hbs @@ -1,214 +1,209 @@
+ @keydown="{{_onKeyDown}}" + part="root">
- {{#if startButton.length}} - + {{/if}} {{#if hasMenuItems}} - {{#unless showLogoInMenuButton}} -
- {{#if hasMidContent}}
- {{else}} -
- {{#if _isXXLBreakpoint}} - {{#if hasSearchField}} - - {{/if}} - {{/if}} -
{{/if}} +
+ {{#if showAdditionalContext}} +
+
+
+ {{#each startContent}} +
+ +
+ {{/each}} +
+ {{#each endContent}} +
+ +
+ {{/each}} +
+
+
+ {{else}} +
+ {{/if}} +
+ {{#if hasSearchField}} + {{#if _showFullWidthSearch}} +
+
+ +
+ + {{_cancelBtnText}} + +
+ {{/if}} -
-
- - {{#unless hasMidContent }} - {{#unless _isXXLBreakpoint }} - {{#if hasSearchField}} - {{#if _fullWidthSearch}} -
-
+
+ {{#unless _showFullWidthSearch}} + {{/unless}}
- - {{_cancelBtnText}} - -
- {{/if}} -
- {{#unless _fullWidthSearch}} - - {{/unless}} -
+ + {{/if}} + {{#if hasAssistant}} +
+ +
+ {{/if}} - - {{/if}} - {{/unless}} - {{/unless}} + {{#if showNotifications}} + + {{/if}} - {{#if hasAssistant}} - - {{/if}} + {{#each customItemsInfo}} + + {{/each}} - {{#each customItemsInfo}} - {{/each}} - - {{#if showNotifications}} - - {{/if}} - - - {{#if hasProfile}} - {{> profileButton}} - {{/if}} + {{#if hasProfile}} + {{> profileButton}} + {{/if}} - {{#if showProductSwitch}} - - {{/if}} + {{#if showProductSwitch}} + + {{/if}} +
-
{{#*inline "profileButton"}} @@ -216,9 +211,10 @@ profile-btn id="{{this._id}}-item-3" @click={{_handleProfilePress}} - style="{{styles.items.profile}}" tooltip="{{_profileText}}" - class="ui5-shellbar-button ui5-shellbar-image-button" + class="ui5-shellbar-button ui5-shellbar-image-button ui5-shellbar-no-overflow-button ui5-shellbar-items-for-arrow-nav" + aria-label="{{imageBtnText}}" + aria-haspopup="dialog" .accessibilityAttributes={{accInfo.profile.accessibilityAttributes}} data-ui5-stable="profile" > @@ -226,4 +222,42 @@ {{/inline}} + +{{#*inline "singleLogo"}} + +{{/inline}} + +{{#*inline "combinedLogo"}} +
+ {{#if hasLogo}} + + {{/if}} +
+ {{#if primaryTitle}} +

+ {{primaryTitle}} +

+ {{/if}} +
+
+{{/inline}} + {{>include "./ShellBarPopover.hbs"}} \ No newline at end of file diff --git a/packages/fiori/src/ShellBar.ts b/packages/fiori/src/ShellBar.ts index 87810bfaea29..f3bb40a281ea 100644 --- a/packages/fiori/src/ShellBar.ts +++ b/packages/fiori/src/ShellBar.ts @@ -7,13 +7,20 @@ import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js"; import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js"; import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js"; import ResizeHandler from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js"; -import { isSpace, isEnter } from "@ui5/webcomponents-base/dist/Keys.js"; +import { + isSpace, + isEnter, + isLeft, + isRight, +} from "@ui5/webcomponents-base/dist/Keys.js"; +import { getEffectiveAriaLabelText } from "@ui5/webcomponents-base/dist/util/AccessibilityTextsHelper.js"; import ListItemStandard from "@ui5/webcomponents/dist/ListItemStandard.js"; import List from "@ui5/webcomponents/dist/List.js"; import type { ListSelectionChangeEventDetail } from "@ui5/webcomponents/dist/List.js"; import type { ResizeObserverCallback } from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js"; import Popover from "@ui5/webcomponents/dist/Popover.js"; import Button from "@ui5/webcomponents/dist/Button.js"; +import Menu from "@ui5/webcomponents/dist/Menu.js"; import Icon from "@ui5/webcomponents/dist/Icon.js"; import type Input from "@ui5/webcomponents/dist/Input.js"; import type { IButton } from "@ui5/webcomponents/dist/Button.js"; @@ -25,13 +32,16 @@ import "@ui5/webcomponents-icons/dist/overflow.js"; import "@ui5/webcomponents-icons/dist/grid.js"; import "@ui5/webcomponents-icons/dist/slim-arrow-down.js"; import type { - Timeout, ClassMap, AccessibilityAttributes, AriaRole, } from "@ui5/webcomponents-base"; import type ListItemBase from "@ui5/webcomponents/dist/ListItemBase.js"; import type PopoverHorizontalAlign from "@ui5/webcomponents/dist/types/PopoverHorizontalAlign.js"; +import throttle from "@ui5/webcomponents-base/dist/util/throttle.js"; +import { getScopedVarName } from "@ui5/webcomponents-base/dist/CustomElementsScope.js"; +import getActiveElement from "@ui5/webcomponents-base/dist/util/getActiveElement.js"; + import type ShellBarItem from "./ShellBarItem.js"; // Templates @@ -45,11 +55,18 @@ import { SHELLBAR_LABEL, SHELLBAR_LOGO, SHELLBAR_NOTIFICATIONS, + SHELLBAR_NOTIFICATIONS_NO_COUNT, SHELLBAR_CANCEL, SHELLBAR_PROFILE, SHELLBAR_PRODUCTS, SHELLBAR_SEARCH, + SHELLBAR_SEARCH_FIELD, SHELLBAR_OVERFLOW, + SHELLBAR_LOGO_AREA, + SHELLBAR_ADDITIONAL_CONTEXT, + SHELLBAR_SEARCHFIELD_DESCRIPTION, + SHELLBAR_SEARCH_BTN_OPEN, + SHELLBAR_PRODUCT_SWITCH_BTN, } from "./generated/i18n/i18n-defaults.js"; type ShellBarLogoAccessibilityAttributes = { @@ -57,7 +74,7 @@ type ShellBarLogoAccessibilityAttributes = { name?: string, } type ShellBarProfileAccessibilityAttributes = Pick; -type ShellBarAreaAccessibilityAttributes = Pick; +type ShellBarAreaAccessibilityAttributes = Pick; type ShellBarAccessibilityAttributes = { logo?: ShellBarLogoAccessibilityAttributes notifications?: ShellBarAreaAccessibilityAttributes @@ -87,6 +104,10 @@ type ShellBarMenuItemClickEventDetail = { item: HTMLElement; }; +type ShellBarContentItemVisibilityChangeEventDetail = { + items: Array +}; + type ShellBarSearchButtonEventDetail = { targetRef: HTMLElement; searchFieldVisible: boolean; @@ -96,7 +117,6 @@ interface IShelBarItemInfo { id: string, icon?: string, text?: string, - priority: number, show: boolean, count?: string, custom?: boolean, @@ -104,14 +124,17 @@ interface IShelBarItemInfo { stableDomRef?: string, refItemid?: string, press: (e: MouseEvent) => void, - styles: object, domOrder: number, classes: string, order?: number, profile?: boolean, + tooltip?: string, } -const HANDLE_RESIZE_DEBOUNCE_RATE = 200; // ms +const RESIZE_THROTTLE_RATE = 40; // ms + +// actions always visible in lean mode, order is important +const PREDEFINED_PLACE_ACTIONS = ["feedback", "sys-help"]; /** * @class @@ -159,6 +182,7 @@ const HANDLE_RESIZE_DEBOUNCE_RATE = 200; // ms List, Popover, ListItemStandard, + Menu, ], }) /** @@ -230,6 +254,18 @@ const HANDLE_RESIZE_DEBOUNCE_RATE = 200; // ms bubbles: true, }) +/** + * Fired, when an item from the startContent or endContent slots is hidden or shown. + * **Note:** The `content-item-visibility-change` event is in an experimental state and is a subject to change. + * + * @param {Array} array of all the items that are hidden + * @public + * @since 2.7.0 + */ +@event("content-item-visibility-change", { + bubbles: true, +}) + class ShellBar extends UI5Element { eventDetails!: { "notifications-click": ShellBarNotificationsClickEventDetail, @@ -238,6 +274,7 @@ class ShellBar extends UI5Element { "logo-click": ShellBarLogoClickEventDetail, "menu-item-click": ShellBarMenuItemClickEventDetail, "search-button-click": ShellBarSearchButtonEventDetail, + "content-item-visibility-change": ShellBarContentItemVisibilityChangeEventDetail } /** * Defines the `primaryTitle`. @@ -327,14 +364,14 @@ class ShellBar extends UI5Element { * @public * @since 1.10.0 */ - @property({ type: Object }) - accessibilityAttributes: ShellBarAccessibilityAttributes = {}; + @property({ type: Object }) + accessibilityAttributes: ShellBarAccessibilityAttributes = {}; /** * @private */ @property() - breakpointSize?: string; + breakpointSize = "S"; /** * @private @@ -355,10 +392,12 @@ class ShellBar extends UI5Element { _overflowPopoverExpanded = false; @property({ type: Boolean, noAttribute: true }) - _fullWidthSearch = false; + hasVisibleStartContent = false; @property({ type: Boolean, noAttribute: true }) - _isXXLBreakpoint = false; + hasVisibleEndContent = false; + + _cachedHiddenContent: Array = []; /** * Defines the assistant slot. @@ -401,7 +440,7 @@ class ShellBar extends UI5Element { logo!: Array; /** - * Defines the items displayed in menu after a click on the primary title. + * Defines the items displayed in menu after a click on a start button. * * **Note:** You can use the `` and its ancestors. * @since 0.10 @@ -419,7 +458,7 @@ class ShellBar extends UI5Element { /** * Defines a `ui5-button` in the bar that will be placed in the beginning. - * We encourage this slot to be used for a back or home button. + * We encourage this slot to be used for a menu button. * It gets overstyled to match ShellBar's styling. * @public */ @@ -435,6 +474,26 @@ class ShellBar extends UI5Element { @slot() midContent!: Array; + /** + * Define the items displayed in the start of the additional content area. + * **Note:** The `startContent` slot is in an experimental state and is a subject to change. + * + * @public + * @since 2.7.0 + */ + @slot({ type: HTMLElement, individualSlots: true }) + startContent!: Array; + + /** + * Define the items displayed in the end of the additional content area. + * **Note:** The `endContent` slot is in an experimental state and is a subject to change. + * + * @public + * @since 2.7.0 + */ + @slot({ type: HTMLElement, individualSlots: true }) + endContent!: Array; + @i18n("@ui5/webcomponents-fiori") static i18nBundle: I18nBundle; overflowPopover?: Popover | null; @@ -442,9 +501,16 @@ class ShellBar extends UI5Element { _isInitialRendering: boolean; _defaultItemPressPrevented: boolean; menuItemsObserver: MutationObserver; - _debounceInterval?: Timeout | null; + additionalContextObserver: MutationObserver; _hiddenIcons: Array; _handleResize: ResizeObserverCallback; + _overflowNotifications: string | null; + _lastOffsetWidth = 0; + _observableContent: Array = []; + _searchBarAutoOpen: boolean = false; + _searchBarAutoClosed: boolean = false; + _searchIconPressed: boolean = false; + _headerPress: () => void; static get FIORI_3_BREAKPOINTS() { @@ -474,6 +540,7 @@ class ShellBar extends UI5Element { this._hiddenIcons = []; this._itemsInfo = []; this._isInitialRendering = true; + this._overflowNotifications = null; // marks if preventDefault() is called in item's press handler this._defaultItemPressPrevented = false; @@ -482,6 +549,10 @@ class ShellBar extends UI5Element { this._updateClonedMenuItems(); }); + this.additionalContextObserver = new MutationObserver(() => { + this._updateAdditionalContextItems(); + }); + this._headerPress = () => { this._updateClonedMenuItems(); @@ -492,22 +563,113 @@ class ShellBar extends UI5Element { } }; - this._handleResize = () => { - this._debounce(() => { - this.menuPopover = this._getMenuPopover(); - this.overflowPopover = this._getOverflowPopover(); - this.overflowPopover.open = false; + this._handleResize = throttle(() => { + this.menuPopover = this._getMenuPopover(); + this.overflowPopover = this._getOverflowPopover(); + this.overflowPopover.open = false; + if (this._lastOffsetWidth !== this.offsetWidth) { this._overflowActions(); - }, HANDLE_RESIZE_DEBOUNCE_RATE); - }; + if (this._searchBarAutoOpen) { + this._searchBarInitialState(); + } + } + }, RESIZE_THROTTLE_RATE); + } + + _searchBarInitialState() { + const spacerWidth = this.shadowRoot!.querySelector(".ui5-shellbar-spacer") ? this.shadowRoot!.querySelector(".ui5-shellbar-spacer")!.getBoundingClientRect().width : 0; + const searchFieldWidth = this.domCalculatedValues("--_ui5_shellbar_search_field_width"); + if (this._searchIconPressed || document.activeElement === this.searchField[0]) { + return; + } + if (this._showFullWidthSearch) { + this.showSearchField = false; + this._searchBarAutoClosed = true; + return; + } + if ((spacerWidth <= 0 || this.additionalContextHidden.length !== 0) && this.showSearchField === true) { + this.showSearchField = false; + this._searchBarAutoClosed = true; + } + if (spacerWidth > searchFieldWidth && this.additionalContextHidden.length === 0 && this.showSearchField === false) { + this.showSearchField = true; + this._searchBarAutoClosed = false; + } + } + + _onKeyDown(e: KeyboardEvent) { + const items = this._getVisibleAndInteractiveItems(); + const activeElement = getActiveElement(); + const currentIndex = items.findIndex(el => el === activeElement); + + if (isLeft(e) || isRight(e)) { + e.preventDefault();// Prevent the default behavior to avoid any further automatic focus movemen + + // Focus navigation based on the key pressed + if (isLeft(e)) { + this._focusPreviousItem(items, currentIndex); + } else if (isRight(e)) { + this._focusNextItem(items, currentIndex); + } + } + } + + _focusNextItem(items: HTMLElement[], currentIndex: number) { + if (currentIndex < items.length - 1) { + (items[currentIndex + 1]).focus(); // Focus the next element + } + } + + _focusPreviousItem(items: HTMLElement[], currentIndex: number) { + if (currentIndex > 0) { + (items[currentIndex - 1]).focus(); // Focus the previous element + } + } + + _isVisible(element: HTMLElement): boolean { + const style = getComputedStyle(element); + + return style.display !== "none" && style.visibility !== "hidden" && element.offsetWidth > 0 && element.offsetHeight > 0; + } + + _isInteractive(element: HTMLElement | UI5Element): boolean { + const component = element as UI5Element; + if (component.isUI5Element) { + const dom = component.getFocusDomRef(); + return dom?.tabIndex === 0; + } + return element.tabIndex === 0; + } + + _getNavigableContent() { + return [ + ...this.startButton, + ...this.logo, + ...this.shadowRoot!.querySelectorAll(".ui5-shellbar-logo"), + ...this.shadowRoot!.querySelectorAll(".ui5-shellbar-logo-area"), + ...this.shadowRoot!.querySelectorAll(".ui5-shellbar-menu-button"), + ...this.startContent, + ...this.endContent, + ...this._getRightChildItems(), + ] as HTMLElement[]; + } + + _getRightChildItems() { + return [ + ...this.searchField, + ...this.shadowRoot!.querySelectorAll(".ui5-shellbar-search-item-for-arrow-nav"), + ...this.assistant, + ...this.shadowRoot!.querySelectorAll(".ui5-shellbar-items-for-arrow-nav"), + ] as HTMLElement[]; } - _debounce(fn: () => void, delay: number) { - clearTimeout(this._debounceInterval!); - this._debounceInterval = setTimeout(() => { - this._debounceInterval = null; - fn(); - }, delay); + _getVisibleAndInteractiveItems() { + const items = this._getNavigableContent(); + const visibleAndInteractiveItems = items.filter(item => { + return this._isVisible(item) && this._isInteractive(item); + }); + + return visibleAndInteractiveItems; } _menuItemPress(e: CustomEvent) { @@ -564,6 +726,19 @@ class ShellBar extends UI5Element { } } + _calculateCSSREMValue(styleSet: CSSStyleDeclaration, propertyName: string): number { + return Number(styleSet.getPropertyValue(propertyName).replace("rem", "")) * parseInt(getComputedStyle(document.body).getPropertyValue("font-size")); + } + + _parsePxValue(styleSet: CSSStyleDeclaration, propertyName: string): number { + return Number(styleSet.getPropertyValue(propertyName).replace("px", "")); + } + + domCalculatedValues(cssVar: string): number { + const shellbarComputerStyle = getComputedStyle(this.getDomRef()!); + return this._calculateCSSREMValue(shellbarComputerStyle, getScopedVarName(cssVar)); // px + } + onBeforeRendering() { this.withLogo = this.hasLogo; @@ -578,12 +753,26 @@ class ShellBar extends UI5Element { }); this._observeMenuItems(); + this._observeAdditionalContextItems(); + this._updateSeparatorsVisibility(); } - onAfterRendering() { - this._overflowActions(); + get additionalContextSorted() { + return this.additionalContext.sort((a, b) => { + return parseInt(a.getAttribute("data-hide-order") || "0") - parseInt(b.getAttribute("data-hide-order") || "0"); + }).map(item => this.shadowRoot!.querySelector(`#${item.slot}`)).filter(item => item !== null); + } - this._fullWidthSearch = this._showFullWidthSearch; + get additionalContextContainer() { + return this.shadowRoot!.querySelector(".ui5-shellbar-overflow-container-additional-content"); + } + + onAfterRendering() { + requestAnimationFrame(() => { + this._lastOffsetWidth = this.offsetWidth; + this._overflowActions(); + }); + this._searchBarAutoOpen = this._searchBarAutoClosed || (this.showSearchField && !this._searchIconPressed); } /** @@ -607,88 +796,132 @@ class ShellBar extends UI5Element { if (this.breakpointSize !== mappedSize) { this.breakpointSize = mappedSize; } - - this._isXXLBreakpoint = this.breakpointSize === "XXL"; - return mappedSize; } - _handleSizeS() { - const hasIcons = this.showNotifications || this.showProductSwitch || !!this.searchField.length || !!this.items.length; - - const newItems = this._getAllItems(hasIcons).map((info): IShelBarItemInfo => { - const isOverflowIcon = info.classes.indexOf("ui5-shellbar-overflow-button") !== -1; - const isImageIcon = info.classes.indexOf("ui5-shellbar-image-button") !== -1; - const shouldStayOnScreen = isOverflowIcon || (isImageIcon && this.hasProfile); + _hideOverflowItems(hiddenItems: number, items: IShelBarItemInfo[]) { + for (let i = 0; hiddenItems > 0 && i < items.length; i++) { + // start from last item + const item = items[items.length - 1 - i]; + if (item.classes.indexOf("ui5-shellbar-no-overflow-button") === -1) { + item.classes = `${item.classes} ui5-shellbar-hidden-button`; + hiddenItems--; + } + } - return { - ...info, - classes: `${info.classes} ${shouldStayOnScreen ? "" : "ui5-shellbar-hidden-button"} ui5-shellbar-button`, - styles: { - order: shouldStayOnScreen ? 1 : -1, - }, - }; - }); + // assistant is a slot, still described in the itemsInfo for the purpose of the overflow + // so if marked as hidden, it should be hidden separately + this._updateAssistantIconVisibility(items); - this._updateItemsInfo(newItems); + return hiddenItems; } - _handleActionsOverflow() { - const rightContainerRect = this.shadowRoot!.querySelector(".ui5-shellbar-overflow-container-right")!.getBoundingClientRect(); - let overflowSelector = ".ui5-shellbar-button:not(.ui5-shellbar-overflow-button):not(.ui5-shellbar-invisible-button)"; + _hideAdditionalContext() { + const container = this.additionalContextContainer; + const totalWidth = container?.offsetWidth || 0; - if (this.showSearchField) { - overflowSelector += ",.ui5-shellbar-search-field"; - } + const additionalContextSorted = this.additionalContextSorted.toReversed(); - const elementsToOverflow = this.shadowRoot!.querySelectorAll