From 0bcb2ab92aee03fae160cd31b3579664a7a281e1 Mon Sep 17 00:00:00 2001 From: Andrei Vorobev Date: Tue, 19 May 2026 18:08:34 +0300 Subject: [PATCH 01/11] TagBox: Keeping click order when maxDisplayTag enabled and showMultiTagOnly disabled (T1328498) --- .../devextreme/js/__internal/ui/m_tag_box.ts | 37 ++++++++++++++- .../tagBox.tests.js | 46 +++++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/packages/devextreme/js/__internal/ui/m_tag_box.ts b/packages/devextreme/js/__internal/ui/m_tag_box.ts index f9a22288fbd1..206664c2cd97 100644 --- a/packages/devextreme/js/__internal/ui/m_tag_box.ts +++ b/packages/devextreme/js/__internal/ui/m_tag_box.ts @@ -1046,7 +1046,10 @@ class TagBox< this._selectedItems = this._getItemsFromPlain(this._valuesToUpdate); if (this._selectedItems.length === this._valuesToUpdate.length) { - this._tagsToRender = this._selectedItems; + this._tagsToRender = this._sortSelectedItemsByValues( + this._selectedItems, + this._valuesToUpdate, + ); this._renderTagsImpl(); isPlainDataUsed = true; d.resolve(); @@ -1113,6 +1116,38 @@ class TagBox< return selectedItems; } + _shouldUseClickOrderForTags(values): boolean { + return !this.option('showMultiTagOnly') + && this.option('maxDisplayedTags') + && values.length > this.option('maxDisplayedTags'); + } + + _sortSelectedItemsByValues(plainItems, values) { + if (!this._shouldUseClickOrderForTags(values)) { + return this._selectedItems; + } + + const selectedByOrderItems = values.reduce((selectedItems, currentValue) => { + const selectedItem = plainItems.find((dataItem) => { + if (isObject(currentValue)) { + // @ts-expect-error ts-error + return this._isValueEquals(dataItem, currentValue); + } + + // @ts-expect-error ts-error + return this._isValueEquals(this._valueGetter(dataItem), currentValue); + }); + + if (isDefined(selectedItem)) { + selectedItems.push(selectedItem); + } + + return selectedItems; + }, []); + + return selectedByOrderItems; + } + _filterSelectedItems(plainItems, values) { const selectedItems = plainItems.filter((dataItem) => { let currentValue; diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/tagBox.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/tagBox.tests.js index a235065b5fac..915deef5b812 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/tagBox.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/tagBox.tests.js @@ -1078,6 +1078,52 @@ QUnit.module('multi tag support', { assert.deepEqual(this.getTexts($tagBox.find('.' + TAGBOX_TAG_CLASS)), ['1', '2'], 'tags have correct text'); }); + QUnit.test('TagBox should preserve direct click order in leading tag when showMultiTagOnly is false', function(assert) { + const $tagBox = $('#tagBox').dxTagBox({ + items: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + showSelectionControls: true, + maxDisplayedTags: 2, + showMultiTagOnly: false, + opened: true + }); + + const tagBox = $tagBox.dxTagBox('instance'); + + this.clock.tick(TIME_TO_WAIT); + + const $listItems = getListItems(tagBox); + + $listItems.first().trigger('dxclick'); + $listItems.eq(7).trigger('dxclick'); + $listItems.eq(6).trigger('dxclick'); + + + assert.strictEqual($tagBox.find('.' + TAGBOX_TAG_CLASS).first().text(), '1', 'leading tag has correct text'); + }); + + QUnit.test('TagBox should preserve reverse click order in leading tag when showMultiTagOnly is false', function(assert) { + const $tagBox = $('#tagBox').dxTagBox({ + items: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + showSelectionControls: true, + maxDisplayedTags: 2, + showMultiTagOnly: false, + opened: true + }); + + const tagBox = $tagBox.dxTagBox('instance'); + + this.clock.tick(TIME_TO_WAIT); + + const $listItems = getListItems(tagBox); + + $listItems.last().trigger('dxclick'); + $listItems.eq(7).trigger('dxclick'); + $listItems.eq(6).trigger('dxclick'); + + + assert.strictEqual($tagBox.find('.' + TAGBOX_TAG_CLASS).first().text(), '10', 'leading tag has correct text'); + }); + QUnit.test('only one multi tag should be rendered when selectAll checked and value changind on runtime', function(assert) { let suppressSelectionChanged = false; From ca3a4390434de7bfc5eb6778662865624e166841 Mon Sep 17 00:00:00 2001 From: Andrei Vorobev Date: Wed, 20 May 2026 11:02:12 +0300 Subject: [PATCH 02/11] Tag Box: better solution for render tags by order --- .../devextreme/js/__internal/ui/m_tag_box.ts | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/packages/devextreme/js/__internal/ui/m_tag_box.ts b/packages/devextreme/js/__internal/ui/m_tag_box.ts index 206664c2cd97..89aa48c14040 100644 --- a/packages/devextreme/js/__internal/ui/m_tag_box.ts +++ b/packages/devextreme/js/__internal/ui/m_tag_box.ts @@ -1122,27 +1122,28 @@ class TagBox< && values.length > this.option('maxDisplayedTags'); } - _sortSelectedItemsByValues(plainItems, values) { + _sortSelectedItemsByValues(selectedItems, values) { if (!this._shouldUseClickOrderForTags(values)) { - return this._selectedItems; + return selectedItems; } + // @ts-expect-error ts-error + const isValueExprDefault = this._valueGetterExpr() === 'this'; - const selectedByOrderItems = values.reduce((selectedItems, currentValue) => { - const selectedItem = plainItems.find((dataItem) => { - if (isObject(currentValue)) { - // @ts-expect-error ts-error - return this._isValueEquals(dataItem, currentValue); - } + const mappedSelectedItems = selectedItems.reduce((result, item) => { + // @ts-expect-error ts-error + const itemValue = isValueExprDefault ? JSON.stringify(item) : this._valueGetter(item); + result[itemValue] = item; - // @ts-expect-error ts-error - return this._isValueEquals(this._valueGetter(dataItem), currentValue); - }); + return result; + }, {}); - if (isDefined(selectedItem)) { - selectedItems.push(selectedItem); + const selectedByOrderItems = values.reduce((result, currentValue) => { + const item = mappedSelectedItems[JSON.stringify(currentValue)]; + if (isDefined(item)) { + result.push(item); } - return selectedItems; + return result; }, []); return selectedByOrderItems; From b748f08fa05aff5de9c5df19d3abe18738b50032 Mon Sep 17 00:00:00 2001 From: Andrei Vorobev Date: Wed, 20 May 2026 15:27:15 +0300 Subject: [PATCH 03/11] TagBox: fixed critical comments on T1328498 --- .../devextreme/js/__internal/ui/m_tag_box.ts | 18 ++++++++++----- .../tagBox.tests.js | 22 ++++++++++++++----- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/packages/devextreme/js/__internal/ui/m_tag_box.ts b/packages/devextreme/js/__internal/ui/m_tag_box.ts index 89aa48c14040..5bfc61d91afa 100644 --- a/packages/devextreme/js/__internal/ui/m_tag_box.ts +++ b/packages/devextreme/js/__internal/ui/m_tag_box.ts @@ -1116,20 +1116,25 @@ class TagBox< return selectedItems; } - _shouldUseClickOrderForTags(values): boolean { + _shouldUseClickOrderForTags(values: TagBox['_valuesToUpdate']): boolean { + const maxDisplayedTags = this.option('maxDisplayedTags'); + return !this.option('showMultiTagOnly') - && this.option('maxDisplayedTags') - && values.length > this.option('maxDisplayedTags'); + && isDefined(maxDisplayedTags) + && values.length > maxDisplayedTags; } - _sortSelectedItemsByValues(selectedItems, values) { + _sortSelectedItemsByValues( + selectedItems: TagBox['_selectedItems'], + values: TagBox['_valuesToUpdate'], + ): TagBox['_selectedItems'] { if (!this._shouldUseClickOrderForTags(values)) { return selectedItems; } // @ts-expect-error ts-error const isValueExprDefault = this._valueGetterExpr() === 'this'; - const mappedSelectedItems = selectedItems.reduce((result, item) => { + const mappedSelectedItems = selectedItems?.reduce((result, item) => { // @ts-expect-error ts-error const itemValue = isValueExprDefault ? JSON.stringify(item) : this._valueGetter(item); result[itemValue] = item; @@ -1138,7 +1143,8 @@ class TagBox< }, {}); const selectedByOrderItems = values.reduce((result, currentValue) => { - const item = mappedSelectedItems[JSON.stringify(currentValue)]; + const normalizedValue = isValueExprDefault ? JSON.stringify(currentValue) : currentValue; + const item = mappedSelectedItems[normalizedValue]; if (isDefined(item)) { result.push(item); } diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/tagBox.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/tagBox.tests.js index 915deef5b812..aa0afe942b2f 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/tagBox.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/tagBox.tests.js @@ -1078,7 +1078,8 @@ QUnit.module('multi tag support', { assert.deepEqual(this.getTexts($tagBox.find('.' + TAGBOX_TAG_CLASS)), ['1', '2'], 'tags have correct text'); }); - QUnit.test('TagBox should preserve direct click order in leading tag when showMultiTagOnly is false', function(assert) { + + QUnit.test('TagBox should preserve reverse click order in leading tag when showMultiTagOnly is false', function(assert) { const $tagBox = $('#tagBox').dxTagBox({ items: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], showSelectionControls: true, @@ -1093,17 +1094,25 @@ QUnit.module('multi tag support', { const $listItems = getListItems(tagBox); - $listItems.first().trigger('dxclick'); + $listItems.last().trigger('dxclick'); $listItems.eq(7).trigger('dxclick'); $listItems.eq(6).trigger('dxclick'); - assert.strictEqual($tagBox.find('.' + TAGBOX_TAG_CLASS).first().text(), '1', 'leading tag has correct text'); + assert.strictEqual($tagBox.find('.' + TAGBOX_TAG_CLASS).first().text(), '10', 'leading tag has correct text'); }); - QUnit.test('TagBox should preserve reverse click order in leading tag when showMultiTagOnly is false', function(assert) { + QUnit.test('TagBox should work correct with string ID\'s in item when valueExpr is used', function(assert) { + const items = [ + { ID: 'a', Name: 'HD Video Player' }, + { ID: 'b', Name: 'SuperHD Video Player' }, + { ID: 'c', Name: 'SuperPlasma 50' }, + { ID: 'd', Name: 'SuperLED 50' } + ]; const $tagBox = $('#tagBox').dxTagBox({ - items: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + items: items, + valueExpr: 'ID', + displayExpr: 'Name', showSelectionControls: true, maxDisplayedTags: 2, showMultiTagOnly: false, @@ -1121,9 +1130,10 @@ QUnit.module('multi tag support', { $listItems.eq(6).trigger('dxclick'); - assert.strictEqual($tagBox.find('.' + TAGBOX_TAG_CLASS).first().text(), '10', 'leading tag has correct text'); + assert.strictEqual($tagBox.find('.' + TAGBOX_TAG_CLASS).first().text(), items[items.length - 1].Name, 'leading tag has correct text'); }); + QUnit.test('only one multi tag should be rendered when selectAll checked and value changind on runtime', function(assert) { let suppressSelectionChanged = false; From a3074a3dd39232894e9f3ffd8a64a14652961039 Mon Sep 17 00:00:00 2001 From: Andrei Vorobev Date: Wed, 20 May 2026 16:02:51 +0300 Subject: [PATCH 04/11] Tag Box: added declarations for methods from DataExpressionMixin to tagbox class --- .../devextreme/js/__internal/ui/m_tag_box.ts | 33 +++++++------------ 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/packages/devextreme/js/__internal/ui/m_tag_box.ts b/packages/devextreme/js/__internal/ui/m_tag_box.ts index 5bfc61d91afa..803d3ebd5624 100644 --- a/packages/devextreme/js/__internal/ui/m_tag_box.ts +++ b/packages/devextreme/js/__internal/ui/m_tag_box.ts @@ -51,13 +51,13 @@ const TEXTEDITOR_INPUT_CONTAINER_CLASS = 'dx-texteditor-input-container'; const TAGBOX_MOUSE_WHEEL_DELTA_MULTIPLIER = -0.3; -export interface TagBoxProperties extends Omit { - +export interface TagBoxProperties extends Omit< + Properties, + 'onCustomItemCreating' + | 'onItemClick' | 'onSelectionChanged' + | 'onOpened' | 'onClosed' + | 'onChange' | 'onCopy' | 'onCut' | 'onEnterKey' | 'onFocusIn' | 'onFocusOut' | 'onInput' | 'onKeyDown' | 'onKeyUp' | 'onPaste' + | 'onValueChanged' | 'validationMessagePosition' | 'onContentReady' | 'onDisposing' | 'onOptionChanged' | 'onInitialized'> { } class TagBox< @@ -95,6 +95,10 @@ class TagBox< _tagsToRender?: any[]; + declare _valueGetterExpr: () => string; + + declare _valueGetter: (item: any) => any; + _supportedKeys(): Record< // eslint-disable-next-line @typescript-eslint/no-invalid-void-type string, (e: KeyboardEvent, options?: Record) => boolean | void @@ -860,7 +864,6 @@ class TagBox< // @ts-expect-error ts-error const isListItemsLoaded = !!listSelectedItems && this._list._dataController.isLoaded(); const selectedItems = listSelectedItems || this.option('selectedItems'); - // @ts-expect-error ts-error const clientFilterFunction = creator.getLocalFilter(this._valueGetter); // @ts-expect-error ts-error const filteredItems = selectedItems.filter(clientFilterFunction); @@ -907,13 +910,11 @@ class TagBox< _createTagsData(values, filteredItems) { const items = []; const cache = {}; - // @ts-expect-error ts-error const isValueExprSpecified = this._valueGetterExpr() === 'this'; const { acceptCustomValue } = this.option(); const filteredValues = {}; filteredItems.forEach((filteredItem) => { - // @ts-expect-error const filteredItemValue = isValueExprSpecified ? JSON.stringify(filteredItem) : this._valueGetter(filteredItem); filteredValues[filteredItemValue] = filteredItem; @@ -967,7 +968,6 @@ class TagBox< return item; } const selectedItem = this.option('selectedItem'); - // @ts-expect-error const customItem = this._valueGetter(selectedItem) === value ? selectedItem : value; return customItem; @@ -1131,11 +1131,9 @@ class TagBox< if (!this._shouldUseClickOrderForTags(values)) { return selectedItems; } - // @ts-expect-error ts-error const isValueExprDefault = this._valueGetterExpr() === 'this'; const mappedSelectedItems = selectedItems?.reduce((result, item) => { - // @ts-expect-error ts-error const itemValue = isValueExprDefault ? JSON.stringify(item) : this._valueGetter(item); result[itemValue] = item; @@ -1165,7 +1163,6 @@ class TagBox< if (this._isValueEquals(dataItem, currentValue)) { return true; } - // @ts-expect-error ts-error } else if (this._isValueEquals(this._valueGetter(dataItem), currentValue)) { return true; } @@ -1294,7 +1291,6 @@ class TagBox< } _renderTag(item, $input): void { - // @ts-expect-error ts-error const value = this._valueGetter(item); if (!isDefined(value)) { @@ -1441,12 +1437,10 @@ class TagBox< const value = this._getValue().slice(); each(e.removedItems || [], (_, removedItem) => { - // @ts-expect-error ts-error this._removeTag(value, this._valueGetter(removedItem)); }); each(e.addedItems || [], (_, addedItem) => { - // @ts-expect-error ts-error this._addTag(value, this._valueGetter(addedItem)); }); @@ -1604,7 +1598,6 @@ class TagBox< } const dataController = this._dataController; - // @ts-expect-error ts-error const valueGetterExpr = this._valueGetterExpr(); if (isString(valueGetterExpr) && valueGetterExpr !== 'this') { @@ -1633,7 +1626,6 @@ class TagBox< } _dataSourceFilterFunction(itemData) { - // @ts-expect-error ts-error const itemValue = this._valueGetter(itemData); let result = true; @@ -1682,7 +1674,6 @@ class TagBox< return this ._getPlainItems(this._list.option('selectedItems')) - // @ts-expect-error .map((item) => this._valueGetter(item)); } @@ -1741,7 +1732,6 @@ class TagBox< } const previousItemsValuesMap = previousItems.reduce((map, item) => { - // @ts-expect-error ts-error const value = this._valueGetter(item); map[value] = item; return map; @@ -1749,7 +1739,6 @@ class TagBox< const addedItems = []; newItems.forEach((item) => { - // @ts-expect-error ts-error const value = this._valueGetter(item); if (!previousItemsValuesMap[value]) { addedItems.push(item as never); From 3e0c701ee8043293e65d2799cec6ffd7c7cf36ce Mon Sep 17 00:00:00 2001 From: Andrei Vorobev Date: Wed, 20 May 2026 16:07:23 +0300 Subject: [PATCH 05/11] TagBox: get option by destucturing due better types --- .../devextreme/js/__internal/ui/m_tag_box.ts | 5 +- packages/devextreme/playground/index.html | 8 + packages/devextreme/playground/index.ts | 222 ++++++++++++++++-- 3 files changed, 212 insertions(+), 23 deletions(-) diff --git a/packages/devextreme/js/__internal/ui/m_tag_box.ts b/packages/devextreme/js/__internal/ui/m_tag_box.ts index 803d3ebd5624..7d4c17f717e8 100644 --- a/packages/devextreme/js/__internal/ui/m_tag_box.ts +++ b/packages/devextreme/js/__internal/ui/m_tag_box.ts @@ -1117,7 +1117,7 @@ class TagBox< } _shouldUseClickOrderForTags(values: TagBox['_valuesToUpdate']): boolean { - const maxDisplayedTags = this.option('maxDisplayedTags'); + const { maxDisplayedTags } = this.option(); return !this.option('showMultiTagOnly') && isDefined(maxDisplayedTags) @@ -1219,8 +1219,7 @@ class TagBox< _renderTagsElements(items): void { const $multiTag = this._multiTagRequired() && this._renderMultiTag(this._input()); - const showMultiTagOnly = this.option('showMultiTagOnly'); - const maxDisplayedTags = this.option('maxDisplayedTags'); + const { showMultiTagOnly, maxDisplayedTags } = this.option(); items.forEach((item, index) => { // @ts-expect-error ts-error diff --git a/packages/devextreme/playground/index.html b/packages/devextreme/playground/index.html index ba4497ebcec5..6c37b20e7505 100644 --- a/packages/devextreme/playground/index.html +++ b/packages/devextreme/playground/index.html @@ -8,7 +8,15 @@
+
+
+
+
+
+
+
+
diff --git a/packages/devextreme/playground/index.ts b/packages/devextreme/playground/index.ts index 15f94bf2a99f..528917997119 100644 --- a/packages/devextreme/playground/index.ts +++ b/packages/devextreme/playground/index.ts @@ -1,32 +1,214 @@ import '../js/__internal/integration/jquery'; -import '../js/ui/card_view'; +import '../js/ui/tag_box'; import $ from 'jquery'; import { setupThemeSelector } from './themeSelector.ts'; -const customers = [ - { ID: 1, Company: 'Super Mart of the West', Address: '702 SW 8th Street', City: 'Bentonville', State: 'Arkansas', Zipcode: 72716, Phone: '(800) 555-2797' }, - { ID: 2, Company: 'Electronics Depot', Address: '2455 Paces Ferry Road NW', City: 'Atlanta', State: 'Georgia', Zipcode: 30339, Phone: '(800) 595-3232' }, - { ID: 3, Company: 'K&S Music', Address: '1000 Nicllet Mall', City: 'Minneapolis', State: 'Minnesota', Zipcode: 55403, Phone: '(612) 304-6073' }, - { ID: 4, Company: "Tom's Club", Address: '999 Lake Drive', City: 'Issaquah', State: 'Washington', Zipcode: 98027, Phone: '(800) 955-2292' }, - { ID: 5, Company: 'E-Mart', Address: '3333 Beverly Rd', City: 'Hoffman Estates', State: 'Illinois', Zipcode: 60179, Phone: '(847) 286-2500' }, - { ID: 6, Company: 'Walters', Address: '200 Wilmot Rd', City: 'Deerfield', State: 'Illinois', Zipcode: 60015, Phone: '(847) 940-2500' }, - { ID: 7, Company: 'StereoShack', Address: '400 Commerce S', City: 'Fort Worth', State: 'Texas', Zipcode: 76102, Phone: '(817) 820-0741' }, - { ID: 8, Company: 'Circuit Town', Address: '2200 Kensington Court', City: 'Oak Brook', State: 'Illinois', Zipcode: 60523, Phone: '(800) 955-2929' }, - { ID: 9, Company: 'Premier Buy', Address: '7601 Penn Avenue South', City: 'Richfield', State: 'Minnesota', Zipcode: 55423, Phone: '(612) 291-1000' }, - { ID: 10, Company: 'ElectrixMax', Address: '263 Shuman Blvd', City: 'Naperville', State: 'Illinois', Zipcode: 60563, Phone: '(630) 438-7800' }, - { ID: 11, Company: 'Video Emporium', Address: '1201 Elm Street', City: 'Dallas', State: 'Texas', Zipcode: 75270, Phone: '(214) 854-3000' }, - { ID: 12, Company: 'Screen Shop', Address: '1000 Lowes Blvd', City: 'Mooresville', State: 'North Carolina', Zipcode: 28117, Phone: '(800) 445-6937' }, +const products = [{ + ID: 1, + Name: 'HD Video Player', + Price: 330, + Current_Inventory: 225, + Backorder: 0, + Manufacturing: 10, + Category: 'Video Players', + ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/1.png', + }, { + ID: 2, + Name: 'SuperHD Video Player', + Price: 400, + Current_Inventory: 150, + Backorder: 0, + Manufacturing: 25, + Category: 'Video Players', + ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/2.png', + }, { + ID: 3, + Name: 'SuperPlasma 50', + Price: 2400, + Current_Inventory: 0, + Backorder: 0, + Manufacturing: 0, + Category: 'Televisions', + ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/3.png', + }, { + ID: 4, + Name: 'SuperLED 50', + Price: 1600, + Current_Inventory: 77, + Backorder: 0, + Manufacturing: 55, + Category: 'Televisions', + ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/4.png', + }, { + ID: 5, + Name: 'SuperLED 42', + Price: 1450, + Current_Inventory: 445, + Backorder: 0, + Manufacturing: 0, + Category: 'Televisions', + ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/5.png', + }, { + ID: 6, + Name: 'SuperLCD 55', + Price: 1350, + Current_Inventory: 345, + Backorder: 0, + Manufacturing: 5, + Category: 'Televisions', + ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/6.png', + }, { + ID: 7, + Name: 'SuperLCD 42', + Price: 1200, + Current_Inventory: 210, + Backorder: 0, + Manufacturing: 20, + Category: 'Televisions', + ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/7.png', + }, { + ID: 8, + Name: 'SuperPlasma 65', + Price: 3500, + Current_Inventory: 0, + Backorder: 0, + Manufacturing: 0, + Category: 'Televisions', + ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/8.png', + }, { + ID: 9, + Name: 'SuperLCD 70', + Price: 4000, + Current_Inventory: 95, + Backorder: 0, + Manufacturing: 5, + Category: 'Televisions', + ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/9.png', + }, { + ID: 10, + Name: 'DesktopLED 21', + Price: 175, + Current_Inventory: null, + Backorder: 425, + Manufacturing: 75, + Category: 'Monitors', + ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/10.png', + }, { + ID: 12, + Name: 'DesktopLCD 21', + Price: 170, + Current_Inventory: 210, + Backorder: 0, + Manufacturing: 60, + Category: 'Monitors', + ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/12.png', + }, { + ID: 13, + Name: 'DesktopLCD 19', + Price: 160, + Current_Inventory: 150, + Backorder: 0, + Manufacturing: 210, + Category: 'Monitors', + ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/13.png', + }, { + ID: 14, + Name: 'Projector Plus', + Price: 550, + Current_Inventory: null, + Backorder: 55, + Manufacturing: 10, + Category: 'Projectors', + ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/14.png', + }, { + ID: 15, + Name: 'Projector PlusHD', + Price: 750, + Current_Inventory: 110, + Backorder: 0, + Manufacturing: 90, + Category: 'Projectors', + ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/15.png', + }, { + ID: 16, + Name: 'Projector PlusHT', + Price: 1050, + Current_Inventory: 0, + Backorder: 75, + Manufacturing: 57, + Category: 'Projectors', + ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/16.png', + }, { + ID: 17, + Name: 'ExcelRemote IR', + Price: 150, + Current_Inventory: 650, + Backorder: 0, + Manufacturing: 190, + Category: 'Automation', + ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/17.png', + }, { + ID: 18, + Name: 'ExcelRemote Bluetooth', + Price: 180, + Current_Inventory: 310, + Backorder: 0, + Manufacturing: 0, + Category: 'Automation', + ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/18.png', + }, { + ID: 19, + Name: 'ExcelRemote IP', + Price: 200, + Current_Inventory: 0, + Backorder: 325, + Manufacturing: 225, + Category: 'Automation', + ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/19.png', + }]; + + const productLabel = { 'aria-label': 'Product' }; + +const items_simple = [1, 4, 3, 2, 5]; + +const products_with_strings = [ + { ID: 'a', Name: 'HD Video Player' }, + { ID: 'b', Name: 'SuperHD Video Player' }, + { ID: 'c', Name: 'SuperPlasma 50' }, + { ID: 'd', Name: 'SuperLED 50' }, ]; window.addEventListener('load', () => setupThemeSelector('theme-selector') .catch((err) => console.error('Theme loading failed:', err)) .then(() => { - $('#widget-container').dxCardView({ - dataSource: customers, - keyExpr: 'ID', - cardsPerRow: 'auto', - cardMinWidth: 320, - columns: ['Company', 'Address', 'City', 'State', 'Zipcode', 'Phone'], + $(() => { + $('#widget-container').dxTagBox({ + items: products, + valueExpr: 'ID', + displayExpr: 'Name', + showSelectionControls: true, + maxDisplayedTags: 2, + showMultiTagOnly: false, + inputAttr: productLabel, + }); + + $('#widget-container-second').dxTagBox({ + items: items_simple, + showSelectionControls: true, + maxDisplayedTags: 2, + showMultiTagOnly: false, + inputAttr: productLabel, + }); + + $('#widget-container-string-ids').dxTagBox({ + items: products_with_strings, + valueExpr: 'ID', + displayExpr: 'Name', + showSelectionControls: true, + maxDisplayedTags: 2, + showMultiTagOnly: false, + inputAttr: productLabel, + }); }); })); From 3927038b992926191eceac02ff088bf59c3cfe33 Mon Sep 17 00:00:00 2001 From: Andrei Vorobev Date: Wed, 20 May 2026 16:10:33 +0300 Subject: [PATCH 06/11] TagBox: got back content for playground --- packages/devextreme/playground/index.html | 10 +- packages/devextreme/playground/index.ts | 224 ++-------------------- 2 files changed, 22 insertions(+), 212 deletions(-) diff --git a/packages/devextreme/playground/index.html b/packages/devextreme/playground/index.html index 6c37b20e7505..020d1f74ba2e 100644 --- a/packages/devextreme/playground/index.html +++ b/packages/devextreme/playground/index.html @@ -8,16 +8,8 @@
-
-
-
-
-
-
-
-
- + \ No newline at end of file diff --git a/packages/devextreme/playground/index.ts b/packages/devextreme/playground/index.ts index 528917997119..68ff30f0d976 100644 --- a/packages/devextreme/playground/index.ts +++ b/packages/devextreme/playground/index.ts @@ -1,214 +1,32 @@ import '../js/__internal/integration/jquery'; -import '../js/ui/tag_box'; +import '../js/ui/card_view'; import $ from 'jquery'; import { setupThemeSelector } from './themeSelector.ts'; -const products = [{ - ID: 1, - Name: 'HD Video Player', - Price: 330, - Current_Inventory: 225, - Backorder: 0, - Manufacturing: 10, - Category: 'Video Players', - ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/1.png', - }, { - ID: 2, - Name: 'SuperHD Video Player', - Price: 400, - Current_Inventory: 150, - Backorder: 0, - Manufacturing: 25, - Category: 'Video Players', - ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/2.png', - }, { - ID: 3, - Name: 'SuperPlasma 50', - Price: 2400, - Current_Inventory: 0, - Backorder: 0, - Manufacturing: 0, - Category: 'Televisions', - ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/3.png', - }, { - ID: 4, - Name: 'SuperLED 50', - Price: 1600, - Current_Inventory: 77, - Backorder: 0, - Manufacturing: 55, - Category: 'Televisions', - ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/4.png', - }, { - ID: 5, - Name: 'SuperLED 42', - Price: 1450, - Current_Inventory: 445, - Backorder: 0, - Manufacturing: 0, - Category: 'Televisions', - ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/5.png', - }, { - ID: 6, - Name: 'SuperLCD 55', - Price: 1350, - Current_Inventory: 345, - Backorder: 0, - Manufacturing: 5, - Category: 'Televisions', - ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/6.png', - }, { - ID: 7, - Name: 'SuperLCD 42', - Price: 1200, - Current_Inventory: 210, - Backorder: 0, - Manufacturing: 20, - Category: 'Televisions', - ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/7.png', - }, { - ID: 8, - Name: 'SuperPlasma 65', - Price: 3500, - Current_Inventory: 0, - Backorder: 0, - Manufacturing: 0, - Category: 'Televisions', - ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/8.png', - }, { - ID: 9, - Name: 'SuperLCD 70', - Price: 4000, - Current_Inventory: 95, - Backorder: 0, - Manufacturing: 5, - Category: 'Televisions', - ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/9.png', - }, { - ID: 10, - Name: 'DesktopLED 21', - Price: 175, - Current_Inventory: null, - Backorder: 425, - Manufacturing: 75, - Category: 'Monitors', - ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/10.png', - }, { - ID: 12, - Name: 'DesktopLCD 21', - Price: 170, - Current_Inventory: 210, - Backorder: 0, - Manufacturing: 60, - Category: 'Monitors', - ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/12.png', - }, { - ID: 13, - Name: 'DesktopLCD 19', - Price: 160, - Current_Inventory: 150, - Backorder: 0, - Manufacturing: 210, - Category: 'Monitors', - ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/13.png', - }, { - ID: 14, - Name: 'Projector Plus', - Price: 550, - Current_Inventory: null, - Backorder: 55, - Manufacturing: 10, - Category: 'Projectors', - ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/14.png', - }, { - ID: 15, - Name: 'Projector PlusHD', - Price: 750, - Current_Inventory: 110, - Backorder: 0, - Manufacturing: 90, - Category: 'Projectors', - ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/15.png', - }, { - ID: 16, - Name: 'Projector PlusHT', - Price: 1050, - Current_Inventory: 0, - Backorder: 75, - Manufacturing: 57, - Category: 'Projectors', - ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/16.png', - }, { - ID: 17, - Name: 'ExcelRemote IR', - Price: 150, - Current_Inventory: 650, - Backorder: 0, - Manufacturing: 190, - Category: 'Automation', - ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/17.png', - }, { - ID: 18, - Name: 'ExcelRemote Bluetooth', - Price: 180, - Current_Inventory: 310, - Backorder: 0, - Manufacturing: 0, - Category: 'Automation', - ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/18.png', - }, { - ID: 19, - Name: 'ExcelRemote IP', - Price: 200, - Current_Inventory: 0, - Backorder: 325, - Manufacturing: 225, - Category: 'Automation', - ImageSrc: 'https://js.devexpress.com/jQuery/Demos/WidgetsGallery/JSDemos/images/products/19.png', - }]; - - const productLabel = { 'aria-label': 'Product' }; - -const items_simple = [1, 4, 3, 2, 5]; - -const products_with_strings = [ - { ID: 'a', Name: 'HD Video Player' }, - { ID: 'b', Name: 'SuperHD Video Player' }, - { ID: 'c', Name: 'SuperPlasma 50' }, - { ID: 'd', Name: 'SuperLED 50' }, +const customers = [ + { ID: 1, Company: 'Super Mart of the West', Address: '702 SW 8th Street', City: 'Bentonville', State: 'Arkansas', Zipcode: 72716, Phone: '(800) 555-2797' }, + { ID: 2, Company: 'Electronics Depot', Address: '2455 Paces Ferry Road NW', City: 'Atlanta', State: 'Georgia', Zipcode: 30339, Phone: '(800) 595-3232' }, + { ID: 3, Company: 'K&S Music', Address: '1000 Nicllet Mall', City: 'Minneapolis', State: 'Minnesota', Zipcode: 55403, Phone: '(612) 304-6073' }, + { ID: 4, Company: "Tom's Club", Address: '999 Lake Drive', City: 'Issaquah', State: 'Washington', Zipcode: 98027, Phone: '(800) 955-2292' }, + { ID: 5, Company: 'E-Mart', Address: '3333 Beverly Rd', City: 'Hoffman Estates', State: 'Illinois', Zipcode: 60179, Phone: '(847) 286-2500' }, + { ID: 6, Company: 'Walters', Address: '200 Wilmot Rd', City: 'Deerfield', State: 'Illinois', Zipcode: 60015, Phone: '(847) 940-2500' }, + { ID: 7, Company: 'StereoShack', Address: '400 Commerce S', City: 'Fort Worth', State: 'Texas', Zipcode: 76102, Phone: '(817) 820-0741' }, + { ID: 8, Company: 'Circuit Town', Address: '2200 Kensington Court', City: 'Oak Brook', State: 'Illinois', Zipcode: 60523, Phone: '(800) 955-2929' }, + { ID: 9, Company: 'Premier Buy', Address: '7601 Penn Avenue South', City: 'Richfield', State: 'Minnesota', Zipcode: 55423, Phone: '(612) 291-1000' }, + { ID: 10, Company: 'ElectrixMax', Address: '263 Shuman Blvd', City: 'Naperville', State: 'Illinois', Zipcode: 60563, Phone: '(630) 438-7800' }, + { ID: 11, Company: 'Video Emporium', Address: '1201 Elm Street', City: 'Dallas', State: 'Texas', Zipcode: 75270, Phone: '(214) 854-3000' }, + { ID: 12, Company: 'Screen Shop', Address: '1000 Lowes Blvd', City: 'Mooresville', State: 'North Carolina', Zipcode: 28117, Phone: '(800) 445-6937' }, ]; window.addEventListener('load', () => setupThemeSelector('theme-selector') .catch((err) => console.error('Theme loading failed:', err)) .then(() => { - $(() => { - $('#widget-container').dxTagBox({ - items: products, - valueExpr: 'ID', - displayExpr: 'Name', - showSelectionControls: true, - maxDisplayedTags: 2, - showMultiTagOnly: false, - inputAttr: productLabel, - }); - - $('#widget-container-second').dxTagBox({ - items: items_simple, - showSelectionControls: true, - maxDisplayedTags: 2, - showMultiTagOnly: false, - inputAttr: productLabel, - }); - - $('#widget-container-string-ids').dxTagBox({ - items: products_with_strings, - valueExpr: 'ID', - displayExpr: 'Name', - showSelectionControls: true, - maxDisplayedTags: 2, - showMultiTagOnly: false, - inputAttr: productLabel, - }); + $('#widget-container').dxCardView({ + dataSource: customers, + keyExpr: 'ID', + cardsPerRow: 'auto', + cardMinWidth: 320, + columns: ['Company', 'Address', 'City', 'State', 'Zipcode', 'Phone'], }); - })); + })); \ No newline at end of file From a1a0a567f83b1db33989c293200325e5dfe43f51 Mon Sep 17 00:00:00 2001 From: Andrei Vorobev Date: Wed, 20 May 2026 16:11:42 +0300 Subject: [PATCH 07/11] Tag Box: added empty lines in the end of files for playground files --- packages/devextreme/playground/index.html | 2 +- packages/devextreme/playground/index.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/devextreme/playground/index.html b/packages/devextreme/playground/index.html index 020d1f74ba2e..ba4497ebcec5 100644 --- a/packages/devextreme/playground/index.html +++ b/packages/devextreme/playground/index.html @@ -12,4 +12,4 @@ - \ No newline at end of file + diff --git a/packages/devextreme/playground/index.ts b/packages/devextreme/playground/index.ts index 68ff30f0d976..42d0773b31fa 100644 --- a/packages/devextreme/playground/index.ts +++ b/packages/devextreme/playground/index.ts @@ -29,4 +29,5 @@ window.addEventListener('load', () => cardMinWidth: 320, columns: ['Company', 'Address', 'City', 'State', 'Zipcode', 'Phone'], }); - })); \ No newline at end of file + })); + \ No newline at end of file From 5553ae1b1f16765003aa69d13bd8798388ac5c1f Mon Sep 17 00:00:00 2001 From: Andrei Vorobev Date: Wed, 20 May 2026 16:15:44 +0300 Subject: [PATCH 08/11] TagBox: fix unit test and typo --- packages/devextreme/js/__internal/ui/m_tag_box.ts | 4 ++-- .../tests/DevExpress.ui.widgets.editors/tagBox.tests.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/devextreme/js/__internal/ui/m_tag_box.ts b/packages/devextreme/js/__internal/ui/m_tag_box.ts index 7d4c17f717e8..5e1d85081c35 100644 --- a/packages/devextreme/js/__internal/ui/m_tag_box.ts +++ b/packages/devextreme/js/__internal/ui/m_tag_box.ts @@ -1117,9 +1117,9 @@ class TagBox< } _shouldUseClickOrderForTags(values: TagBox['_valuesToUpdate']): boolean { - const { maxDisplayedTags } = this.option(); + const { maxDisplayedTags, showMultiTagOnly } = this.option(); - return !this.option('showMultiTagOnly') + return !showMultiTagOnly && isDefined(maxDisplayedTags) && values.length > maxDisplayedTags; } diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/tagBox.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/tagBox.tests.js index aa0afe942b2f..195144002412 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/tagBox.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/tagBox.tests.js @@ -1102,7 +1102,7 @@ QUnit.module('multi tag support', { assert.strictEqual($tagBox.find('.' + TAGBOX_TAG_CLASS).first().text(), '10', 'leading tag has correct text'); }); - QUnit.test('TagBox should work correct with string ID\'s in item when valueExpr is used', function(assert) { + QUnit.test('TagBox should work correctly with string ID\'s in item when valueExpr is used', function(assert) { const items = [ { ID: 'a', Name: 'HD Video Player' }, { ID: 'b', Name: 'SuperHD Video Player' }, @@ -1126,8 +1126,8 @@ QUnit.module('multi tag support', { const $listItems = getListItems(tagBox); $listItems.last().trigger('dxclick'); - $listItems.eq(7).trigger('dxclick'); - $listItems.eq(6).trigger('dxclick'); + $listItems.eq(2).trigger('dxclick'); + $listItems.eq(1).trigger('dxclick'); assert.strictEqual($tagBox.find('.' + TAGBOX_TAG_CLASS).first().text(), items[items.length - 1].Name, 'leading tag has correct text'); From c2c4021ae60ec1b37b6d052ec0d462f923b69f4e Mon Sep 17 00:00:00 2001 From: Andrey Vorobev Date: Wed, 20 May 2026 16:21:30 +0300 Subject: [PATCH 09/11] _valueGetterExpr typing fix Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Andrey Vorobev --- packages/devextreme/js/__internal/ui/m_tag_box.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/devextreme/js/__internal/ui/m_tag_box.ts b/packages/devextreme/js/__internal/ui/m_tag_box.ts index 5e1d85081c35..7e34529cd746 100644 --- a/packages/devextreme/js/__internal/ui/m_tag_box.ts +++ b/packages/devextreme/js/__internal/ui/m_tag_box.ts @@ -95,7 +95,7 @@ class TagBox< _tagsToRender?: any[]; - declare _valueGetterExpr: () => string; + declare _valueGetterExpr: () => string | Function; declare _valueGetter: (item: any) => any; From 45d26f6c3d24188734736bb4b97e82002e3e6ff2 Mon Sep 17 00:00:00 2001 From: Andrei Vorobev Date: Wed, 20 May 2026 16:24:17 +0300 Subject: [PATCH 10/11] removed trailing whitespace from playground --- packages/devextreme/playground/index.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/devextreme/playground/index.ts b/packages/devextreme/playground/index.ts index 42d0773b31fa..68ff30f0d976 100644 --- a/packages/devextreme/playground/index.ts +++ b/packages/devextreme/playground/index.ts @@ -29,5 +29,4 @@ window.addEventListener('load', () => cardMinWidth: 320, columns: ['Company', 'Address', 'City', 'State', 'Zipcode', 'Phone'], }); - })); - \ No newline at end of file + })); \ No newline at end of file From c53a71ae1d4ee38b5a71f5248803e9ad9aac3549 Mon Sep 17 00:00:00 2001 From: Andrei Vorobev Date: Wed, 20 May 2026 17:16:30 +0300 Subject: [PATCH 11/11] TagBox: add some typings --- .../devextreme/js/__internal/ui/m_tag_box.ts | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/packages/devextreme/js/__internal/ui/m_tag_box.ts b/packages/devextreme/js/__internal/ui/m_tag_box.ts index 7e34529cd746..f3f6526c8c94 100644 --- a/packages/devextreme/js/__internal/ui/m_tag_box.ts +++ b/packages/devextreme/js/__internal/ui/m_tag_box.ts @@ -33,6 +33,9 @@ function xor(a: boolean, b: boolean): boolean { return (a || b) && !(a && b); } +type TagBoxItem = string | number | any; +type SelectedItemsMap = Record; + const TAGBOX_TAG_DATA_KEY = 'dxTagData'; const TAGBOX_TAG_DISPLAY_VALUE = 'dxTagDisplayValue'; @@ -95,10 +98,6 @@ class TagBox< _tagsToRender?: any[]; - declare _valueGetterExpr: () => string | Function; - - declare _valueGetter: (item: any) => any; - _supportedKeys(): Record< // eslint-disable-next-line @typescript-eslint/no-invalid-void-type string, (e: KeyboardEvent, options?: Record) => boolean | void @@ -864,6 +863,7 @@ class TagBox< // @ts-expect-error ts-error const isListItemsLoaded = !!listSelectedItems && this._list._dataController.isLoaded(); const selectedItems = listSelectedItems || this.option('selectedItems'); + // @ts-expect-error _valueGetter is injected by DataExpressionMixin const clientFilterFunction = creator.getLocalFilter(this._valueGetter); // @ts-expect-error ts-error const filteredItems = selectedItems.filter(clientFilterFunction); @@ -910,11 +910,13 @@ class TagBox< _createTagsData(values, filteredItems) { const items = []; const cache = {}; + // @ts-expect-error _valueGetterExpr is injected by DataExpressionMixin const isValueExprSpecified = this._valueGetterExpr() === 'this'; const { acceptCustomValue } = this.option(); const filteredValues = {}; filteredItems.forEach((filteredItem) => { + // @ts-expect-error _valueGetter is injected by DataExpressionMixin const filteredItemValue = isValueExprSpecified ? JSON.stringify(filteredItem) : this._valueGetter(filteredItem); filteredValues[filteredItemValue] = filteredItem; @@ -968,6 +970,7 @@ class TagBox< return item; } const selectedItem = this.option('selectedItem'); + // @ts-expect-error _valueGetter is injected by DataExpressionMixin const customItem = this._valueGetter(selectedItem) === value ? selectedItem : value; return customItem; @@ -1125,22 +1128,24 @@ class TagBox< } _sortSelectedItemsByValues( - selectedItems: TagBox['_selectedItems'], - values: TagBox['_valuesToUpdate'], - ): TagBox['_selectedItems'] { - if (!this._shouldUseClickOrderForTags(values)) { + selectedItems: TagBoxItem[], + values: TagBoxItem[], + ): TagBoxItem[] { + if (!this._shouldUseClickOrderForTags(values) || !selectedItems.length) { return selectedItems; } + // @ts-expect-error _valueGetterExpr is injected by DataExpressionMixin const isValueExprDefault = this._valueGetterExpr() === 'this'; - const mappedSelectedItems = selectedItems?.reduce((result, item) => { + const mappedSelectedItems = selectedItems.reduce((result, item) => { + // @ts-expect-error _valueGetter is injected by DataExpressionMixin const itemValue = isValueExprDefault ? JSON.stringify(item) : this._valueGetter(item); result[itemValue] = item; return result; }, {}); - const selectedByOrderItems = values.reduce((result, currentValue) => { + const selectedByOrderItems: TagBoxItem[] = values.reduce((result, currentValue) => { const normalizedValue = isValueExprDefault ? JSON.stringify(currentValue) : currentValue; const item = mappedSelectedItems[normalizedValue]; if (isDefined(item)) { @@ -1163,6 +1168,7 @@ class TagBox< if (this._isValueEquals(dataItem, currentValue)) { return true; } + // @ts-expect-error ts-error } else if (this._isValueEquals(this._valueGetter(dataItem), currentValue)) { return true; } @@ -1244,7 +1250,7 @@ class TagBox< const $tags = this._tagElements(); const selectedItems = this.option('selectedItems') ?? []; - // @ts-expect-error ts-error + // @ts-expect-error _valueGetter is injected by DataExpressionMixin const values = selectedItems.map((item) => this._valueGetter(item)); each($tags, (_, tag) => { @@ -1290,6 +1296,7 @@ class TagBox< } _renderTag(item, $input): void { + // @ts-expect-error _valueGetter is injected by DataExpressionMixin const value = this._valueGetter(item); if (!isDefined(value)) { @@ -1436,10 +1443,12 @@ class TagBox< const value = this._getValue().slice(); each(e.removedItems || [], (_, removedItem) => { + // @ts-expect-error _valueGetter is injected by DataExpressionMixin this._removeTag(value, this._valueGetter(removedItem)); }); each(e.addedItems || [], (_, addedItem) => { + // @ts-expect-error _valueGetter is injected by DataExpressionMixin this._addTag(value, this._valueGetter(addedItem)); }); @@ -1597,6 +1606,7 @@ class TagBox< } const dataController = this._dataController; + // @ts-expect-error _valueGetterExpr is injected by DataExpressionMixin const valueGetterExpr = this._valueGetterExpr(); if (isString(valueGetterExpr) && valueGetterExpr !== 'this') { @@ -1618,13 +1628,14 @@ class TagBox< _dataSourceFilterExpr() { const filter = []; - // @ts-expect-error + // @ts-expect-error _valueGetterExpr is injected by DataExpressionMixin this._getValue().forEach((value) => filter.push(['!', [this._valueGetterExpr(), value]])); return filter; } _dataSourceFilterFunction(itemData) { + // @ts-expect-error _valueGetter is injected by DataExpressionMixin const itemValue = this._valueGetter(itemData); let result = true; @@ -1673,6 +1684,7 @@ class TagBox< return this ._getPlainItems(this._list.option('selectedItems')) + // @ts-expect-error _valueGetter is injected by DataExpressionMixin .map((item) => this._valueGetter(item)); } @@ -1731,6 +1743,7 @@ class TagBox< } const previousItemsValuesMap = previousItems.reduce((map, item) => { + // @ts-expect-error _valueGetter is injected by DataExpressionMixin const value = this._valueGetter(item); map[value] = item; return map; @@ -1738,6 +1751,7 @@ class TagBox< const addedItems = []; newItems.forEach((item) => { + // @ts-expect-error _valueGetter is injected by DataExpressionMixin const value = this._valueGetter(item); if (!previousItemsValuesMap[value]) { addedItems.push(item as never);