mirror of
https://github.com/Choices-js/Choices.git
synced 2026-03-14 22:55:46 +01:00
When resolving the remove item/label/icon, add a 3rd argument item argument. Update default remove item label to use (Fixes #1296)
This commit is contained in:
parent
20ee8d13ff
commit
5ca0350e8e
9 changed files with 65 additions and 41 deletions
|
|
@ -703,21 +703,23 @@ Return type must be safe to insert into HTML (ie use the 1st argument which is s
|
|||
|
||||
### removeItemIconText
|
||||
|
||||
**Type:** `String/Function` **Default:** `Remove item"` **Arguments:** `value`, `valueRaw`
|
||||
**Type:** `String/Function` **Default:** `Remove item"` **Arguments:** `value`, `valueRaw`, `item`
|
||||
|
||||
**Input types affected:** `text`, `select-one`, `select-multiple`
|
||||
|
||||
**Usage:** The text/icon for the remove button. To access the item's value, pass a function with a `value` argument (see the **default config** [https://github.com/jshjohnson/Choices#setup] for an example), otherwise pass a string.
|
||||
To access the item's label, use the 3rd argument. *Note*; this label is not escaped.
|
||||
|
||||
Return type must be safe to insert into HTML (ie use the 1st argument which is sanitised)
|
||||
|
||||
### removeItemLabelText
|
||||
|
||||
**Type:** `String/Function` **Default:** `Remove item: ${value}"` **Arguments:** `value`, `valueRaw`
|
||||
**Type:** `String/Function` **Default:** `Remove item: ${value}"` **Arguments:** `value`, `valueRaw`, `item`
|
||||
|
||||
**Input types affected:** `text`, `select-one`, `select-multiple`
|
||||
|
||||
**Usage:** The text for the remove button's aria label. To access the item's value, pass a function with a `value` argument (see the **default config** [https://github.com/jshjohnson/Choices#setup] for an example), otherwise pass a string.
|
||||
To access the item's label, use the 3rd argument. *Note*; this label is not escaped.
|
||||
|
||||
Return type must be safe to insert into HTML (ie use the 1st argument which is sanitised)
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import {
|
|||
escapeForTemplate,
|
||||
generateId,
|
||||
getAdjacentEl,
|
||||
getChoiceForOutput,
|
||||
getClassNames,
|
||||
getClassNamesSelector,
|
||||
isScrolledIntoView,
|
||||
|
|
@ -415,7 +416,7 @@ class Choices {
|
|||
this._store.dispatch(highlightItem(choice, true));
|
||||
|
||||
if (runEvent) {
|
||||
this.passedElement.triggerEvent(EventType.highlightItem, this._getChoiceForOutput(choice));
|
||||
this.passedElement.triggerEvent(EventType.highlightItem, getChoiceForOutput(choice));
|
||||
}
|
||||
|
||||
return this;
|
||||
|
|
@ -433,7 +434,7 @@ class Choices {
|
|||
this._store.dispatch(highlightItem(choice, false));
|
||||
|
||||
if (runEvent) {
|
||||
this.passedElement.triggerEvent(EventType.unhighlightItem, this._getChoiceForOutput(choice));
|
||||
this.passedElement.triggerEvent(EventType.unhighlightItem, getChoiceForOutput(choice));
|
||||
}
|
||||
|
||||
return this;
|
||||
|
|
@ -445,7 +446,7 @@ class Choices {
|
|||
if (!item.highlighted) {
|
||||
this._store.dispatch(highlightItem(item, true));
|
||||
|
||||
this.passedElement.triggerEvent(EventType.highlightItem, this._getChoiceForOutput(item));
|
||||
this.passedElement.triggerEvent(EventType.highlightItem, getChoiceForOutput(item));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -459,7 +460,7 @@ class Choices {
|
|||
if (item.highlighted) {
|
||||
this._store.dispatch(highlightItem(item, false));
|
||||
|
||||
this.passedElement.triggerEvent(EventType.highlightItem, this._getChoiceForOutput(item));
|
||||
this.passedElement.triggerEvent(EventType.highlightItem, getChoiceForOutput(item));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -545,7 +546,7 @@ class Choices {
|
|||
|
||||
getValue<B extends boolean = false>(valueOnly?: B): EventChoiceValueType<B> | EventChoiceValueType<B>[] {
|
||||
const values = this._store.items.map((item) => {
|
||||
return (valueOnly ? item.value : this._getChoiceForOutput(item)) as EventChoiceValueType<B>;
|
||||
return (valueOnly ? item.value : getChoiceForOutput(item)) as EventChoiceValueType<B>;
|
||||
});
|
||||
|
||||
return this._isSelectOneElement || this.config.singleModeForMultiSelect ? values[0] : values;
|
||||
|
|
@ -848,7 +849,7 @@ class Choices {
|
|||
this._searcher.reset();
|
||||
|
||||
if (choice.selected) {
|
||||
this.passedElement.triggerEvent(EventType.removeItem, this._getChoiceForOutput(choice));
|
||||
this.passedElement.triggerEvent(EventType.removeItem, getChoiceForOutput(choice));
|
||||
}
|
||||
|
||||
return this;
|
||||
|
|
@ -1178,23 +1179,12 @@ class Choices {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use utils.getChoiceForOutput
|
||||
*/
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
_getChoiceForOutput(choice: ChoiceFull, keyCode?: number): EventChoice {
|
||||
return {
|
||||
id: choice.id,
|
||||
highlighted: choice.highlighted,
|
||||
labelClass: choice.labelClass,
|
||||
labelDescription: choice.labelDescription,
|
||||
customProperties: choice.customProperties,
|
||||
disabled: choice.disabled,
|
||||
active: choice.active,
|
||||
label: choice.label,
|
||||
placeholder: choice.placeholder,
|
||||
value: choice.value,
|
||||
groupValue: choice.group ? choice.group.label : undefined,
|
||||
element: choice.element,
|
||||
keyCode,
|
||||
};
|
||||
return getChoiceForOutput(choice, keyCode);
|
||||
}
|
||||
|
||||
_triggerChange(value): void {
|
||||
|
|
@ -1423,7 +1413,7 @@ class Choices {
|
|||
|
||||
if (canAddItem && typeof config.addItemFilter === 'function' && !config.addItemFilter(value)) {
|
||||
canAddItem = false;
|
||||
notice = resolveNoticeFunction(config.customAddItemText, value);
|
||||
notice = resolveNoticeFunction(config.customAddItemText, value, undefined);
|
||||
}
|
||||
|
||||
if (canAddItem) {
|
||||
|
|
@ -1437,13 +1427,13 @@ class Choices {
|
|||
}
|
||||
if (!config.duplicateItemsAllowed) {
|
||||
canAddItem = false;
|
||||
notice = resolveNoticeFunction(config.uniqueItemText, value);
|
||||
notice = resolveNoticeFunction(config.uniqueItemText, value, undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (canAddItem) {
|
||||
notice = resolveNoticeFunction(config.addItemText, value);
|
||||
notice = resolveNoticeFunction(config.addItemText, value, undefined);
|
||||
}
|
||||
|
||||
if (notice) {
|
||||
|
|
@ -2075,10 +2065,11 @@ class Choices {
|
|||
this._store.dispatch(addItem(item));
|
||||
|
||||
if (withEvents) {
|
||||
this.passedElement.triggerEvent(EventType.addItem, this._getChoiceForOutput(item));
|
||||
const eventChoice = getChoiceForOutput(item);
|
||||
this.passedElement.triggerEvent(EventType.addItem, eventChoice);
|
||||
|
||||
if (userTriggered) {
|
||||
this.passedElement.triggerEvent(EventType.choice, this._getChoiceForOutput(item));
|
||||
this.passedElement.triggerEvent(EventType.choice, eventChoice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2094,7 +2085,7 @@ class Choices {
|
|||
this._clearNotice();
|
||||
}
|
||||
|
||||
this.passedElement.triggerEvent(EventType.removeItem, this._getChoiceForOutput(item));
|
||||
this.passedElement.triggerEvent(EventType.removeItem, getChoiceForOutput(item));
|
||||
}
|
||||
|
||||
_addChoice(choice: ChoiceFull, withEvents: boolean = true, userTriggered = false): void {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { ClassNames } from './interfaces/class-names';
|
||||
import { Options } from './interfaces/options';
|
||||
import { sortByAlpha } from './lib/utils';
|
||||
import { sanitise, sortByAlpha } from './lib/utils';
|
||||
import { EventChoice } from './interfaces';
|
||||
|
||||
export const DEFAULT_CLASSNAMES: ClassNames = {
|
||||
containerOuter: ['choices'],
|
||||
|
|
@ -79,7 +80,8 @@ export const DEFAULT_CONFIG: Options = {
|
|||
customAddItemText: 'Only values matching specific conditions can be added',
|
||||
addItemText: (value: string) => `Press Enter to add <b>"${value}"</b>`,
|
||||
removeItemIconText: (): string => `Remove item`,
|
||||
removeItemLabelText: (value: string): string => `Remove item: ${value}`,
|
||||
removeItemLabelText: (value: string, _valueRaw: string, i?: EventChoice): string =>
|
||||
`Remove item: ${i ? sanitise<string>(i.label) : value}`,
|
||||
maxItemText: (maxItemCount: number): string => `Only ${maxItemCount} values can be added`,
|
||||
valueComparer: (value1: string, value2: string): boolean => value1 === value2,
|
||||
fuseOptions: {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
// eslint-disable-next-line import/no-cycle
|
||||
import { InputChoice } from './input-choice';
|
||||
|
||||
export type EventChoiceValueType<B extends boolean> = B extends true ? string : EventChoice;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { StringUntrusted } from './string-untrusted';
|
||||
// eslint-disable-next-line
|
||||
import { Types } from './types';
|
||||
|
||||
export interface InputChoice {
|
||||
|
|
@ -13,5 +14,5 @@ export interface InputChoice {
|
|||
placeholder?: boolean;
|
||||
selected?: boolean;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
value: any;
|
||||
value: any; // string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ export interface Options {
|
|||
*
|
||||
* @default
|
||||
* ```
|
||||
* (value, valueRaw) => `Remove item`;
|
||||
* (value, valueRaw, item) => `Remove item`;
|
||||
* ```
|
||||
*/
|
||||
removeItemIconText: string | Types.NoticeStringFunction;
|
||||
|
|
@ -215,7 +215,7 @@ export interface Options {
|
|||
*
|
||||
* @default
|
||||
* ```
|
||||
* (value, valueRaw) => `Remove item: ${value}`;
|
||||
* (value, valueRaw, item) => `Remove item: ${value}`;
|
||||
* ```
|
||||
*/
|
||||
removeItemLabelText: string | Types.NoticeStringFunction;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
import { StringUntrusted } from './string-untrusted';
|
||||
import { StringPreEscaped } from './string-pre-escaped';
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import { EventChoice } from './event-choice';
|
||||
|
||||
export namespace Types {
|
||||
export type StrToEl = (str: string) => HTMLElement | HTMLInputElement | HTMLOptionElement;
|
||||
export type EscapeForTemplateFn = (allowHTML: boolean, s: StringUntrusted | StringPreEscaped | string) => string;
|
||||
export type GetClassNamesFn = (s: string | Array<string>) => string;
|
||||
export type StringFunction = () => string;
|
||||
export type NoticeStringFunction = (value: string, valueRaw: string) => string;
|
||||
export type NoticeStringFunction = (value: string, valueRaw: string, item?: EventChoice) => string;
|
||||
export type NoticeLimitFunction = (maxItemCount: number) => string;
|
||||
export type FilterFunction = (value: string) => boolean;
|
||||
export type ValueCompareFunction = (value1: string, value2: string) => boolean;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { StringPreEscaped } from '../interfaces/string-pre-escaped';
|
|||
import { ChoiceFull } from '../interfaces/choice-full';
|
||||
import { Types } from '../interfaces/types';
|
||||
import { canUseDom } from '../interfaces/build-flags';
|
||||
import { EventChoice } from '../interfaces';
|
||||
|
||||
const getRandomNumber = (min: number, max: number): number => Math.floor(Math.random() * (max - min) + min);
|
||||
|
||||
|
|
@ -91,10 +92,6 @@ export const strToEl = ((): ((str: string) => Element) => {
|
|||
};
|
||||
})();
|
||||
|
||||
export const resolveNoticeFunction = (fn: Types.NoticeStringFunction | string, value: string): string => {
|
||||
return typeof fn === 'function' ? fn(sanitise(value), value) : fn;
|
||||
};
|
||||
|
||||
export const resolveStringFunction = (fn: Types.StringFunction | string): string => {
|
||||
return typeof fn === 'function' ? fn() : fn;
|
||||
};
|
||||
|
|
@ -133,6 +130,32 @@ export const unwrapStringForEscaped = (s?: StringUntrusted | StringPreEscaped |
|
|||
return '';
|
||||
};
|
||||
|
||||
export const getChoiceForOutput = (choice: ChoiceFull, keyCode?: number): EventChoice => {
|
||||
return {
|
||||
id: choice.id,
|
||||
highlighted: choice.highlighted,
|
||||
labelClass: choice.labelClass,
|
||||
labelDescription: choice.labelDescription,
|
||||
customProperties: choice.customProperties,
|
||||
disabled: choice.disabled,
|
||||
active: choice.active,
|
||||
label: choice.label,
|
||||
placeholder: choice.placeholder,
|
||||
value: choice.value,
|
||||
groupValue: choice.group ? choice.group.label : undefined,
|
||||
element: choice.element,
|
||||
keyCode,
|
||||
};
|
||||
};
|
||||
|
||||
export const resolveNoticeFunction = (
|
||||
fn: Types.NoticeStringFunction | string,
|
||||
value: StringUntrusted | StringPreEscaped | string,
|
||||
item?: EventChoice,
|
||||
): string => {
|
||||
return typeof fn === 'function' ? fn(sanitise<string>(value), unwrapStringForRaw(value), item) : fn;
|
||||
};
|
||||
|
||||
export const escapeForTemplate = (allowHTML: boolean, s: StringUntrusted | StringPreEscaped | string): string =>
|
||||
allowHTML ? unwrapStringForEscaped(s) : (sanitise(s) as string);
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import {
|
|||
escapeForTemplate,
|
||||
addClassesToElement,
|
||||
removeClassesFromElement,
|
||||
getChoiceForOutput,
|
||||
} from './lib/utils';
|
||||
import { NoticeType, NoticeTypes, TemplateOptions, Templates as TemplatesInterface } from './interfaces/templates';
|
||||
import { StringUntrusted } from './interfaces/string-untrusted';
|
||||
|
|
@ -189,9 +190,10 @@ const templates: TemplatesInterface = {
|
|||
const removeButton = document.createElement('button');
|
||||
removeButton.type = 'button';
|
||||
addClassesToElement(removeButton, button);
|
||||
setElementHtml(removeButton, true, resolveNoticeFunction(removeItemIconText, choice.value));
|
||||
const eventChoice = getChoiceForOutput(choice);
|
||||
setElementHtml(removeButton, true, resolveNoticeFunction(removeItemIconText, choice.value, eventChoice));
|
||||
|
||||
const REMOVE_ITEM_LABEL = resolveNoticeFunction(removeItemLabelText, choice.value);
|
||||
const REMOVE_ITEM_LABEL = resolveNoticeFunction(removeItemLabelText, choice.value, eventChoice);
|
||||
if (REMOVE_ITEM_LABEL) {
|
||||
removeButton.setAttribute('aria-label', REMOVE_ITEM_LABEL);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue