diff --git a/src/plugins/settings/settings/setting/select-setting/select-setting.ts b/src/plugins/settings/settings/setting/select-setting/select-setting.ts index 395e2c33..19010d06 100644 --- a/src/plugins/settings/settings/setting/select-setting/select-setting.ts +++ b/src/plugins/settings/settings/setting/select-setting/select-setting.ts @@ -1,13 +1,13 @@ import {attr, boolAttr} from '@exadel/esl/modules/esl-base-element/core'; +import type {ESLSelect} from '@exadel/esl/modules/esl-forms/esl-select/core'; import {randUID} from '@exadel/esl/modules/esl-utils/misc/uid'; +import {memoize} from '@exadel/esl/modules/esl-utils/decorators/memoize'; import {UIPSetting} from '../setting'; -import {ChangeAttrConfig, UIPStateModel} from '../../../../../core/registration'; +import {ChangeAttrConfig, UIPStateModel} from '../../../../../core/base/model'; import TokenListUtils from '../../../../../utils/token-list-utils'; import {WARNING_MSG} from '../../../../../utils/warning-msg'; -import type {ESLSelect} from '@exadel/esl/modules/esl-forms/esl-select/core'; - /** * Custom setting for selecting attribute's value. * @extends UIPSetting @@ -30,6 +30,7 @@ export class UIPSelectSetting extends UIPSetting { /** Select field to change setting's value. */ protected $field: ESLSelect; + @memoize() protected get settingOptions(): string[] { return this.$field.options.map(opt => opt.value); } @@ -112,10 +113,18 @@ export class UIPSelectSetting extends UIPSetting { return this.multiple ? this.setValue('') : this.setInconsistency(WARNING_MSG.noMatch); } - /** Update setting's value for append {@link mode}. */ + /** Update setting's value for {@link mode} = "append". */ protected updateAppend(attrValues: (string | null)[]): void { - const commonOptions = TokenListUtils.intersection( - ...attrValues.map(val => TokenListUtils.split(val)), this.settingOptions); + // array of each attribute's value intersection with select options + const valuesOptions = attrValues.map(val => TokenListUtils.intersection(this.settingOptions, TokenListUtils.split(val))); + + // make empty option active if no options intersections among attribute values + if (this.settingOptions.includes('') && valuesOptions.every(inter => !inter.length)) { + return this.setValue(''); + } + + // common options among all attribute values + const commonOptions = TokenListUtils.intersection(...valuesOptions); if (this.multiple || commonOptions.length) return this.setValue(TokenListUtils.join(commonOptions)); diff --git a/src/utils/token-list-utils.ts b/src/utils/token-list-utils.ts index f4311577..8afd441f 100644 --- a/src/utils/token-list-utils.ts +++ b/src/utils/token-list-utils.ts @@ -25,11 +25,10 @@ export default class TokenListUtils { } /** Get array which contains only common elements from arrays. */ - static intersection(...arrays: T[][]): T[] { - return Array.from(arrays.reduce((intersect, arr) => { - arr.forEach(val => !intersect.has(val) && intersect.delete(val)); - return intersect; - }, new Set(arrays[0]))); + static intersection(...rest: T[][]): T[]; + static intersection(a: T[], b: T[], ...rest: T[][]): T[] { + if (rest.length) return TokenListUtils.intersection(a, TokenListUtils.intersection(b, ...rest)); + return a.filter(Set.prototype.has, new Set(b)); } /** Remove all element appearances from array. */