) {
const tokensTexts = tokens.filter(token => token.selected).map(token => token.text).join("\r\n");
@@ -960,8 +1019,8 @@ class Tokenizer extends UI5Element {
* @protected
*/
scrollToStart() {
- if (this._scrollEnablement.scrollContainer) {
- this._scrollEnablement.scrollTo(0, 0);
+ if (this._scrollEnablement?.scrollContainer) {
+ this._scrollEnablement?.scrollTo(0, 0);
}
}
@@ -972,8 +1031,8 @@ class Tokenizer extends UI5Element {
*/
scrollToEnd() {
const expandedTokenizerScrollWidth = this.contentDom && (this.effectiveDir !== "rtl" ? this.contentDom.scrollWidth : -this.contentDom.scrollWidth);
- if (this._scrollEnablement.scrollContainer) {
- this._scrollEnablement.scrollTo(expandedTokenizerScrollWidth, 0, 5, 10);
+ if (this._scrollEnablement?.scrollContainer) {
+ this._scrollEnablement?.scrollTo(expandedTokenizerScrollWidth, 0, 5, 10);
}
}
@@ -991,9 +1050,9 @@ class Tokenizer extends UI5Element {
const tokenContainerRect = this.contentDom.getBoundingClientRect();
if (tokenRect.left < tokenContainerRect.left) {
- this._scrollEnablement.scrollTo(this.contentDom.scrollLeft - (tokenContainerRect.left - tokenRect.left + 5), 0);
+ this._scrollEnablement?.scrollTo(this.contentDom.scrollLeft - (tokenContainerRect.left - tokenRect.left + 5), 0);
} else if (tokenRect.right > tokenContainerRect.right) {
- this._scrollEnablement.scrollTo(this.contentDom.scrollLeft + (tokenRect.right - tokenContainerRect.right + 5), 0);
+ this._scrollEnablement?.scrollTo(this.contentDom.scrollLeft + (tokenRect.right - tokenContainerRect.right + 5), 0);
}
}
@@ -1025,6 +1084,10 @@ class Tokenizer extends UI5Element {
return Tokenizer.i18nBundle.getText(TOKENIZER_SHOW_ALL_ITEMS, this._nMoreCount);
}
+ get _clearAllText() {
+ return Tokenizer.i18nBundle.getText(TOKENIZER_CLEAR_ALL);
+ }
+
get showNMore() {
return !this.expanded && !!this.overflownTokens.length;
}
diff --git a/packages/main/src/i18n/messagebundle.properties b/packages/main/src/i18n/messagebundle.properties
index eadb967ade56..6035281c047b 100644
--- a/packages/main/src/i18n/messagebundle.properties
+++ b/packages/main/src/i18n/messagebundle.properties
@@ -444,6 +444,9 @@ TOKENIZER_POPOVER_REMOVE=All items
#XFLD: Token number indicator which is used to show all tokens in Tokenizer when all of the tokens are hidden
TOKENIZER_SHOW_ALL_ITEMS={0} Items
+#XFLD: Clear All link text in Tokenizer
+TOKENIZER_CLEAR_ALL=Clear All
+
#XACT: Label text for TreeListItem
TREE_ITEM_ARIA_LABEL=Tree Item
diff --git a/packages/main/src/themes/Tokenizer.css b/packages/main/src/themes/Tokenizer.css
index ba08b89f3fe6..2b0f74f06d37 100644
--- a/packages/main/src/themes/Tokenizer.css
+++ b/packages/main/src/themes/Tokenizer.css
@@ -7,6 +7,29 @@
height: 2.25rem;
}
+:host([multi-line]) {
+ height: auto;
+
+ .ui5-tokenizer--content {
+ display: flex;
+ align-content: baseline;
+ flex-wrap: wrap;
+ padding: .25rem;
+ box-sizing: border-box;
+ row-gap: .5rem;
+ column-gap: .25rem;
+ overflow-y: auto;
+ overflow-x: hidden;
+ }
+
+ ::slotted(ui5-token) {
+ margin: 0;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ max-width: 100%;
+ }
+}
+
:host([disabled]) {
opacity: 40%;
pointer-events: none;
@@ -52,6 +75,10 @@
box-sizing: border-box;
}
+.ui5-tokenizer--list{
+ display: contents;
+}
+
:host([_tokens-count="1"]) .ui5-tokenizer--content {
padding-inline-end: 4px;
box-sizing: border-box;
@@ -77,3 +104,21 @@
.ui5-tokenizer-more-text:active {
text-decoration: none;
}
+
+.ui5-tokenizer--clear-all {
+ color: var(--sapLinkColor);
+ font-family: var(--sapFontFamily);
+ font-size: var(--sapFontSize);
+ cursor: pointer;
+ outline: none;
+}
+
+.ui5-tokenizer--clear-all:hover {
+ color: var(--sapLink_Hover_Color);
+ text-decoration: var(--_ui5_link_hover_text_decoration);
+}
+
+.ui5-tokenizer--clear-all:active {
+ color: var(--sapLink_Active_Color);
+ text-decoration: var(--_ui5_link_active_text_decoration);
+}
\ No newline at end of file
diff --git a/packages/main/test/pages/Tokenizer-multi-line.html b/packages/main/test/pages/Tokenizer-multi-line.html
new file mode 100644
index 000000000000..064f91562bb9
--- /dev/null
+++ b/packages/main/test/pages/Tokenizer-multi-line.html
@@ -0,0 +1,161 @@
+
+
+
+
+
+
+ Tokenizer - multi-line
+
+
+
+
+
+
+
+
+
+
+
Multi Line
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Multi Line with long token
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Multi Line with long token and restricted height
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Multi Line with clear all
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Multi Line with restricted height and clear all
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Multi Line with clear all - single token
+
+
+
+
+ Multi Line in readonly mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Multi Line in disabled mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/main/test/specs/Tokenizer.spec.js b/packages/main/test/specs/Tokenizer.spec.js
index 341ce4c763a6..d406b994179e 100644
--- a/packages/main/test/specs/Tokenizer.spec.js
+++ b/packages/main/test/specs/Tokenizer.spec.js
@@ -233,44 +233,45 @@ describe("Accessibility", () => {
it("should test aria-readonly attribute", async () => {
const tokenizer = await browser.$("#nmore-tokenizer");
- const tokenizerContent = await tokenizer.shadow$(".ui5-tokenizer--content");
+ const tokenizerList = await tokenizer.shadow$(".ui5-tokenizer--list");
const readonlyTokenizer = await browser.$("#readonly-tokenizer");
- const readonlyTokenizerContent = await readonlyTokenizer.shadow$(".ui5-tokenizer--content");
+ const readonlyTokenizerList = await readonlyTokenizer.shadow$(".ui5-tokenizer--list");
assert.notOk(await tokenizer.getAttribute("readonly"), "tokenizer should not be readonly");
- assert.notOk(await tokenizerContent.getAttribute("aria-readonly"), "aria-readonly should not be set on tokenizer");
+ assert.notOk(await tokenizerList.getAttribute("aria-readonly"), "aria-readonly should not be set on tokenizer");
assert.ok(await readonlyTokenizer.getAttribute("readonly"), "tokenizer should be readonly");
- assert.ok(await readonlyTokenizerContent.getAttribute("aria-readonly"), "aria-readonly should be set on disabled tokenizer");
+ assert.ok(await readonlyTokenizerList.getAttribute("aria-readonly"), "aria-readonly should be set on disabled tokenizer");
});
it("should test aria-disabled attribute", async () => {
const tokenizer = await browser.$("#nmore-tokenizer");
- const tokenizerContent = await tokenizer.shadow$(".ui5-tokenizer--content");
+ const tokenizerList = await tokenizer.shadow$(".ui5-tokenizer--list");
+
const disabledTokenizer = await browser.$("#disabled-tokenizer");
- const disabledTokenizerContent = await disabledTokenizer.shadow$(".ui5-tokenizer--content");
+ const disabledTokenizerList = await disabledTokenizer.shadow$(".ui5-tokenizer--list");
assert.notOk(await tokenizer.getAttribute("disabled"), "tokenizer should not be disabled");
- assert.notOk(await tokenizerContent.getAttribute("aria-disabled"), "aria-disabled should not be set on tokenizer");
+ assert.notOk(await tokenizerList.getAttribute("aria-disabled"), "aria-disabled should not be set on tokenizer");
assert.ok(await disabledTokenizer.getAttribute("disabled"), "tokenizer should be disabled");
- assert.ok(await disabledTokenizerContent.getAttribute("aria-disabled"), "aria-disabled should be set on disabled tokenizer");
+ assert.ok(await disabledTokenizerList.getAttribute("aria-disabled"), "aria-disabled should be set on disabled tokenizer");
});
it("should test tokenizer content aria attributes", async () => {
const tokenizer = await browser.$("#nmore-tokenizer");
- const tokenizerContent = await tokenizer.shadow$(".ui5-tokenizer--content");
+ const tokenizerList = await tokenizer.shadow$(".ui5-tokenizer--list");
const expandedTokenizer = await browser.$("#expanded-tokenizer");
- const expandedTokenizerContent = await expandedTokenizer.shadow$(".ui5-tokenizer--content");
+ const expandedTokenizerList = await expandedTokenizer.shadow$(".ui5-tokenizer--list");
const keys = [
"TOKENIZER_ARIA_LABEL",
];
const texts = await getResourceBundleTexts(keys);
- assert.strictEqual(await tokenizerContent.getAttribute("role"), "listbox", "tokenizer content should have correct role=listbox");
- assert.strictEqual(await tokenizerContent.getAttribute("aria-label"), texts.TOKENIZER_ARIA_LABEL, "tokenizer content should have correct aria-label");
- assert.strictEqual(await expandedTokenizerContent.getAttribute("aria-label"), 'Test label', "tokenizer content should have correct aria-label when accesible name is set");
- assert.strictEqual(await expandedTokenizerContent.getAttribute("aria-description"), texts.TOKENIZER_ARIA_LABEL, "tokenizer content should have correct aria-description when accesible name is set");
+ assert.strictEqual(await tokenizerList.getAttribute("role"), "listbox", "tokenizer content should have correct role=listbox");
+ assert.strictEqual(await tokenizerList.getAttribute("aria-label"), texts.TOKENIZER_ARIA_LABEL, "tokenizer content should have correct aria-label");
+ assert.strictEqual(await expandedTokenizerList.getAttribute("aria-label"), 'Test label', "tokenizer content should have correct aria-label when accesible name is set");
+ assert.strictEqual(await expandedTokenizerList.getAttribute("aria-description"), texts.TOKENIZER_ARIA_LABEL, "tokenizer content should have correct aria-description when accesible name is set");
});
it("should test nMore aria attributes", async () => {
diff --git a/packages/website/docs/_components_pages/main/Tokenizer.mdx b/packages/website/docs/_components_pages/main/Tokenizer.mdx
index a39a0c15f524..82a521bcdc38 100644
--- a/packages/website/docs/_components_pages/main/Tokenizer.mdx
+++ b/packages/website/docs/_components_pages/main/Tokenizer.mdx
@@ -4,10 +4,19 @@ sidebar_class_name: newComponentBadge
---
import Basic from "../../_samples/main/Tokenizer/Basic/Basic.md";
+import MultiLine from "../../_samples/main/Tokenizer/MultiLine/Basic.md";
<%COMPONENT_OVERVIEW%>
## Basic Sample
+## More Samples
+
+### Multi-line and Clear All
+With multiLine enabled, tokens are displayed across multiple lines for improved readability.
+The showClearAll option adds a convenient 'Clear All' button, allowing users to remove all tokens at once.
+
+
+
<%COMPONENT_METADATA%>
diff --git a/packages/website/docs/_samples/main/Tokenizer/MultiLine/Basic.md b/packages/website/docs/_samples/main/Tokenizer/MultiLine/Basic.md
new file mode 100644
index 000000000000..ffccbf6dd13e
--- /dev/null
+++ b/packages/website/docs/_samples/main/Tokenizer/MultiLine/Basic.md
@@ -0,0 +1,4 @@
+import html from '!!raw-loader!./sample.html';
+import js from '!!raw-loader!./main.js';
+
+
\ No newline at end of file
diff --git a/packages/website/docs/_samples/main/Tokenizer/MultiLine/main.js b/packages/website/docs/_samples/main/Tokenizer/MultiLine/main.js
new file mode 100644
index 000000000000..e7554d001aac
--- /dev/null
+++ b/packages/website/docs/_samples/main/Tokenizer/MultiLine/main.js
@@ -0,0 +1,13 @@
+import "@ui5/webcomponents/dist/Token.js";
+import "@ui5/webcomponents/dist/Tokenizer.js";
+
+const clearAllTokenizer = document.getElementById('clear-all');
+
+
+clearAllTokenizer.addEventListener("ui5-token-delete", event => {
+ const tokens = event.detail?.tokens;
+
+ if (tokens) {
+ tokens.forEach(token => token.remove());
+ }
+});
\ No newline at end of file
diff --git a/packages/website/docs/_samples/main/Tokenizer/MultiLine/sample.html b/packages/website/docs/_samples/main/Tokenizer/MultiLine/sample.html
new file mode 100644
index 000000000000..3dd006c60105
--- /dev/null
+++ b/packages/website/docs/_samples/main/Tokenizer/MultiLine/sample.html
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+ Sample
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+