2019-10-25 16:43:28 +02:00
|
|
|
/**
|
|
|
|
* Helpers to create HTML elements used by Choices
|
|
|
|
* Can be overridden by providing `callbackOnCreateTemplates` option
|
|
|
|
*/
|
2017-10-10 17:59:49 +02:00
|
|
|
|
2021-12-17 22:26:52 +01:00
|
|
|
import { Choice } from './interfaces/choice';
|
|
|
|
import { ClassNames } from './interfaces/class-names';
|
|
|
|
import { Group } from './interfaces/group';
|
|
|
|
import { Item } from './interfaces/item';
|
|
|
|
import { PassedElementType } from './interfaces/passed-element-type';
|
|
|
|
|
2019-12-23 19:22:54 +01:00
|
|
|
const templates = {
|
2017-10-18 10:08:27 +02:00
|
|
|
containerOuter(
|
2019-12-23 19:22:54 +01:00
|
|
|
{ containerOuter }: Pick<ClassNames, 'containerOuter'>,
|
|
|
|
dir: HTMLElement['dir'],
|
|
|
|
isSelectElement: boolean,
|
|
|
|
isSelectOneElement: boolean,
|
|
|
|
searchEnabled: boolean,
|
2021-12-17 22:26:52 +01:00
|
|
|
passedElementType: PassedElementType,
|
2019-12-23 19:22:54 +01:00
|
|
|
): HTMLDivElement {
|
2019-10-25 16:43:28 +02:00
|
|
|
const div = Object.assign(document.createElement('div'), {
|
|
|
|
className: containerOuter,
|
|
|
|
});
|
2019-10-29 19:26:11 +01:00
|
|
|
|
2019-10-25 16:43:28 +02:00
|
|
|
div.dataset.type = passedElementType;
|
2019-10-29 19:26:11 +01:00
|
|
|
|
|
|
|
if (dir) {
|
|
|
|
div.dir = dir;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isSelectOneElement) {
|
|
|
|
div.tabIndex = 0;
|
|
|
|
}
|
|
|
|
|
2019-10-25 16:43:28 +02:00
|
|
|
if (isSelectElement) {
|
|
|
|
div.setAttribute('role', searchEnabled ? 'combobox' : 'listbox');
|
2019-10-29 19:26:11 +01:00
|
|
|
if (searchEnabled) {
|
|
|
|
div.setAttribute('aria-autocomplete', 'list');
|
|
|
|
}
|
2017-10-10 17:59:49 +02:00
|
|
|
}
|
2019-10-29 19:26:11 +01:00
|
|
|
|
2019-10-25 16:43:28 +02:00
|
|
|
div.setAttribute('aria-haspopup', 'true');
|
|
|
|
div.setAttribute('aria-expanded', 'false');
|
2017-10-10 17:59:49 +02:00
|
|
|
|
2019-10-25 16:43:28 +02:00
|
|
|
return div;
|
2017-10-10 17:59:49 +02:00
|
|
|
},
|
2019-11-02 14:49:33 +01:00
|
|
|
|
2019-12-23 19:22:54 +01:00
|
|
|
containerInner({
|
|
|
|
containerInner,
|
|
|
|
}: Pick<ClassNames, 'containerInner'>): HTMLDivElement {
|
2019-10-25 16:43:28 +02:00
|
|
|
return Object.assign(document.createElement('div'), {
|
|
|
|
className: containerInner,
|
|
|
|
});
|
2017-10-10 17:59:49 +02:00
|
|
|
},
|
2019-11-02 14:49:33 +01:00
|
|
|
|
2019-12-23 19:22:54 +01:00
|
|
|
itemList(
|
|
|
|
{
|
|
|
|
list,
|
|
|
|
listSingle,
|
|
|
|
listItems,
|
|
|
|
}: Pick<ClassNames, 'list' | 'listSingle' | 'listItems'>,
|
|
|
|
isSelectOneElement: boolean,
|
|
|
|
): HTMLDivElement {
|
2019-10-25 16:43:28 +02:00
|
|
|
return Object.assign(document.createElement('div'), {
|
|
|
|
className: `${list} ${isSelectOneElement ? listSingle : listItems}`,
|
2018-05-28 14:55:44 +02:00
|
|
|
});
|
2017-10-10 17:59:49 +02:00
|
|
|
},
|
2019-11-02 14:49:33 +01:00
|
|
|
|
2019-12-23 19:22:54 +01:00
|
|
|
placeholder(
|
|
|
|
{ placeholder }: Pick<ClassNames, 'placeholder'>,
|
|
|
|
value: string,
|
|
|
|
): HTMLDivElement {
|
2019-10-25 16:43:28 +02:00
|
|
|
return Object.assign(document.createElement('div'), {
|
|
|
|
className: placeholder,
|
|
|
|
innerHTML: value,
|
|
|
|
});
|
2017-10-10 17:59:49 +02:00
|
|
|
},
|
|
|
|
|
2019-10-25 16:43:28 +02:00
|
|
|
item(
|
2019-12-23 19:22:54 +01:00
|
|
|
{
|
|
|
|
item,
|
|
|
|
button,
|
|
|
|
highlightedState,
|
|
|
|
itemSelectable,
|
|
|
|
placeholder,
|
|
|
|
}: Pick<
|
|
|
|
ClassNames,
|
|
|
|
'item' | 'button' | 'highlightedState' | 'itemSelectable' | 'placeholder'
|
|
|
|
>,
|
2019-10-25 16:43:28 +02:00
|
|
|
{
|
|
|
|
id,
|
|
|
|
value,
|
|
|
|
label,
|
|
|
|
customProperties,
|
|
|
|
active,
|
|
|
|
disabled,
|
|
|
|
highlighted,
|
|
|
|
placeholder: isPlaceholder,
|
2019-12-23 19:22:54 +01:00
|
|
|
}: Item,
|
|
|
|
removeItemButton: boolean,
|
|
|
|
): HTMLDivElement {
|
2019-10-25 16:43:28 +02:00
|
|
|
const div = Object.assign(document.createElement('div'), {
|
|
|
|
className: item,
|
|
|
|
innerHTML: label,
|
|
|
|
});
|
2019-10-29 19:26:11 +01:00
|
|
|
|
2019-10-25 16:43:28 +02:00
|
|
|
Object.assign(div.dataset, {
|
|
|
|
item: '',
|
|
|
|
id,
|
|
|
|
value,
|
|
|
|
customProperties,
|
2018-05-28 14:55:44 +02:00
|
|
|
});
|
2019-10-25 16:43:28 +02:00
|
|
|
|
2019-10-29 19:26:11 +01:00
|
|
|
if (active) {
|
|
|
|
div.setAttribute('aria-selected', 'true');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (disabled) {
|
|
|
|
div.setAttribute('aria-disabled', 'true');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isPlaceholder) {
|
|
|
|
div.classList.add(placeholder);
|
|
|
|
}
|
2019-11-02 14:49:33 +01:00
|
|
|
|
2019-10-25 16:43:28 +02:00
|
|
|
div.classList.add(highlighted ? highlightedState : itemSelectable);
|
2017-10-10 17:59:49 +02:00
|
|
|
|
2017-10-18 09:54:23 +02:00
|
|
|
if (removeItemButton) {
|
2019-10-29 19:26:11 +01:00
|
|
|
if (disabled) {
|
|
|
|
div.classList.remove(itemSelectable);
|
|
|
|
}
|
2019-10-25 16:43:28 +02:00
|
|
|
div.dataset.deletable = '';
|
|
|
|
/** @todo This MUST be localizable, not hardcoded! */
|
|
|
|
const REMOVE_ITEM_TEXT = 'Remove item';
|
|
|
|
const removeButton = Object.assign(document.createElement('button'), {
|
|
|
|
type: 'button',
|
|
|
|
className: button,
|
|
|
|
innerHTML: REMOVE_ITEM_TEXT,
|
2018-05-28 14:55:44 +02:00
|
|
|
});
|
2019-10-25 16:43:28 +02:00
|
|
|
removeButton.setAttribute(
|
|
|
|
'aria-label',
|
|
|
|
`${REMOVE_ITEM_TEXT}: '${value}'`,
|
|
|
|
);
|
|
|
|
removeButton.dataset.button = '';
|
|
|
|
div.appendChild(removeButton);
|
2017-10-10 17:59:49 +02:00
|
|
|
}
|
|
|
|
|
2019-10-25 16:43:28 +02:00
|
|
|
return div;
|
2017-10-10 17:59:49 +02:00
|
|
|
},
|
2019-11-02 14:49:33 +01:00
|
|
|
|
2019-12-23 19:22:54 +01:00
|
|
|
choiceList(
|
|
|
|
{ list }: Pick<ClassNames, 'list'>,
|
|
|
|
isSelectOneElement: boolean,
|
|
|
|
): HTMLDivElement {
|
2019-10-25 16:43:28 +02:00
|
|
|
const div = Object.assign(document.createElement('div'), {
|
|
|
|
className: list,
|
2018-05-28 14:55:44 +02:00
|
|
|
});
|
2019-10-29 19:26:11 +01:00
|
|
|
|
|
|
|
if (!isSelectOneElement) {
|
|
|
|
div.setAttribute('aria-multiselectable', 'true');
|
|
|
|
}
|
2019-10-25 16:43:28 +02:00
|
|
|
div.setAttribute('role', 'listbox');
|
2019-10-29 19:26:11 +01:00
|
|
|
|
2019-10-25 16:43:28 +02:00
|
|
|
return div;
|
2017-10-10 17:59:49 +02:00
|
|
|
},
|
|
|
|
|
2019-12-23 19:22:54 +01:00
|
|
|
choiceGroup(
|
|
|
|
{
|
|
|
|
group,
|
|
|
|
groupHeading,
|
|
|
|
itemDisabled,
|
|
|
|
}: Pick<ClassNames, 'group' | 'groupHeading' | 'itemDisabled'>,
|
|
|
|
{ id, value, disabled }: Group,
|
|
|
|
): HTMLDivElement {
|
2019-10-25 16:43:28 +02:00
|
|
|
const div = Object.assign(document.createElement('div'), {
|
|
|
|
className: `${group} ${disabled ? itemDisabled : ''}`,
|
|
|
|
});
|
2019-10-29 19:26:11 +01:00
|
|
|
|
2019-10-25 16:43:28 +02:00
|
|
|
div.setAttribute('role', 'group');
|
2019-10-29 19:26:11 +01:00
|
|
|
|
|
|
|
Object.assign(div.dataset, {
|
|
|
|
group: '',
|
|
|
|
id,
|
|
|
|
value,
|
|
|
|
});
|
|
|
|
|
|
|
|
if (disabled) {
|
|
|
|
div.setAttribute('aria-disabled', 'true');
|
|
|
|
}
|
|
|
|
|
2019-10-25 16:43:28 +02:00
|
|
|
div.appendChild(
|
|
|
|
Object.assign(document.createElement('div'), {
|
|
|
|
className: groupHeading,
|
|
|
|
innerHTML: value,
|
|
|
|
}),
|
2017-10-10 17:59:49 +02:00
|
|
|
);
|
2019-10-29 19:26:11 +01:00
|
|
|
|
2019-10-25 16:43:28 +02:00
|
|
|
return div;
|
2017-10-10 17:59:49 +02:00
|
|
|
},
|
|
|
|
|
2019-10-25 16:43:28 +02:00
|
|
|
choice(
|
2019-11-13 15:49:23 +01:00
|
|
|
{
|
|
|
|
item,
|
|
|
|
itemChoice,
|
|
|
|
itemSelectable,
|
|
|
|
selectedState,
|
|
|
|
itemDisabled,
|
|
|
|
placeholder,
|
2019-12-23 19:22:54 +01:00
|
|
|
}: Pick<
|
|
|
|
ClassNames,
|
|
|
|
| 'item'
|
|
|
|
| 'itemChoice'
|
|
|
|
| 'itemSelectable'
|
|
|
|
| 'selectedState'
|
|
|
|
| 'itemDisabled'
|
|
|
|
| 'placeholder'
|
|
|
|
>,
|
2019-10-25 16:43:28 +02:00
|
|
|
{
|
|
|
|
id,
|
|
|
|
value,
|
|
|
|
label,
|
|
|
|
groupId,
|
|
|
|
elementId,
|
2019-11-13 15:49:23 +01:00
|
|
|
disabled: isDisabled,
|
|
|
|
selected: isSelected,
|
2019-10-25 16:43:28 +02:00
|
|
|
placeholder: isPlaceholder,
|
2019-12-23 19:22:54 +01:00
|
|
|
}: Choice,
|
|
|
|
selectText: string,
|
|
|
|
): HTMLDivElement {
|
2019-10-25 16:43:28 +02:00
|
|
|
const div = Object.assign(document.createElement('div'), {
|
|
|
|
id: elementId,
|
|
|
|
innerHTML: label,
|
2019-11-13 15:49:23 +01:00
|
|
|
className: `${item} ${itemChoice}`,
|
2019-10-25 16:43:28 +02:00
|
|
|
});
|
2019-10-29 19:26:11 +01:00
|
|
|
|
2019-11-13 15:49:23 +01:00
|
|
|
if (isSelected) {
|
|
|
|
div.classList.add(selectedState);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isPlaceholder) {
|
|
|
|
div.classList.add(placeholder);
|
|
|
|
}
|
|
|
|
|
2019-12-23 19:22:54 +01:00
|
|
|
div.setAttribute('role', groupId && groupId > 0 ? 'treeitem' : 'option');
|
2019-10-29 19:26:11 +01:00
|
|
|
|
2019-10-25 16:43:28 +02:00
|
|
|
Object.assign(div.dataset, {
|
|
|
|
choice: '',
|
|
|
|
id,
|
|
|
|
value,
|
|
|
|
selectText,
|
|
|
|
});
|
2019-10-29 19:26:11 +01:00
|
|
|
|
2019-11-13 15:49:23 +01:00
|
|
|
if (isDisabled) {
|
|
|
|
div.classList.add(itemDisabled);
|
2019-10-25 16:43:28 +02:00
|
|
|
div.dataset.choiceDisabled = '';
|
|
|
|
div.setAttribute('aria-disabled', 'true');
|
2019-10-29 19:26:11 +01:00
|
|
|
} else {
|
2019-11-13 15:49:23 +01:00
|
|
|
div.classList.add(itemSelectable);
|
2019-10-29 19:26:11 +01:00
|
|
|
div.dataset.choiceSelectable = '';
|
|
|
|
}
|
2017-10-10 17:59:49 +02:00
|
|
|
|
2019-10-25 16:43:28 +02:00
|
|
|
return div;
|
|
|
|
},
|
2019-11-02 14:49:33 +01:00
|
|
|
|
2019-12-23 19:22:54 +01:00
|
|
|
input(
|
|
|
|
{ input, inputCloned }: Pick<ClassNames, 'input' | 'inputCloned'>,
|
|
|
|
placeholderValue: string,
|
|
|
|
): HTMLInputElement {
|
2019-10-25 16:43:28 +02:00
|
|
|
const inp = Object.assign(document.createElement('input'), {
|
|
|
|
type: 'text',
|
|
|
|
className: `${input} ${inputCloned}`,
|
|
|
|
autocomplete: 'off',
|
|
|
|
autocapitalize: 'off',
|
|
|
|
spellcheck: false,
|
|
|
|
});
|
2019-10-29 19:26:11 +01:00
|
|
|
|
2019-10-25 16:43:28 +02:00
|
|
|
inp.setAttribute('role', 'textbox');
|
|
|
|
inp.setAttribute('aria-autocomplete', 'list');
|
|
|
|
inp.setAttribute('aria-label', placeholderValue);
|
2019-10-29 19:26:11 +01:00
|
|
|
|
2019-10-25 16:43:28 +02:00
|
|
|
return inp;
|
|
|
|
},
|
2019-11-02 14:49:33 +01:00
|
|
|
|
2019-12-23 19:22:54 +01:00
|
|
|
dropdown({
|
|
|
|
list,
|
|
|
|
listDropdown,
|
|
|
|
}: Pick<ClassNames, 'list' | 'listDropdown'>): HTMLDivElement {
|
2019-10-25 16:43:28 +02:00
|
|
|
const div = document.createElement('div');
|
2019-10-29 19:26:11 +01:00
|
|
|
|
2019-10-25 16:43:28 +02:00
|
|
|
div.classList.add(list, listDropdown);
|
|
|
|
div.setAttribute('aria-expanded', 'false');
|
2019-10-29 19:26:11 +01:00
|
|
|
|
2019-10-25 16:43:28 +02:00
|
|
|
return div;
|
|
|
|
},
|
2019-11-02 14:49:33 +01:00
|
|
|
|
2019-12-23 19:22:54 +01:00
|
|
|
notice(
|
|
|
|
{
|
|
|
|
item,
|
|
|
|
itemChoice,
|
|
|
|
noResults,
|
|
|
|
noChoices,
|
|
|
|
}: Pick<ClassNames, 'item' | 'itemChoice' | 'noResults' | 'noChoices'>,
|
|
|
|
innerHTML: string,
|
|
|
|
type: 'no-choices' | 'no-results' | '' = '',
|
|
|
|
): HTMLDivElement {
|
2019-10-25 16:43:28 +02:00
|
|
|
const classes = [item, itemChoice];
|
2019-10-29 19:26:11 +01:00
|
|
|
|
|
|
|
if (type === 'no-choices') {
|
|
|
|
classes.push(noChoices);
|
|
|
|
} else if (type === 'no-results') {
|
|
|
|
classes.push(noResults);
|
|
|
|
}
|
|
|
|
|
2019-10-25 16:43:28 +02:00
|
|
|
return Object.assign(document.createElement('div'), {
|
|
|
|
innerHTML,
|
|
|
|
className: classes.join(' '),
|
|
|
|
});
|
2017-10-10 17:59:49 +02:00
|
|
|
},
|
2019-11-02 14:49:33 +01:00
|
|
|
|
2019-12-23 19:22:54 +01:00
|
|
|
option({
|
|
|
|
label,
|
|
|
|
value,
|
|
|
|
customProperties,
|
|
|
|
active,
|
|
|
|
disabled,
|
|
|
|
}: Item): HTMLOptionElement {
|
2019-10-25 16:43:28 +02:00
|
|
|
const opt = new Option(label, value, false, active);
|
2019-10-29 19:26:11 +01:00
|
|
|
|
|
|
|
if (customProperties) {
|
2019-12-23 19:22:54 +01:00
|
|
|
opt.dataset.customProperties = `${customProperties}`;
|
2019-10-29 19:26:11 +01:00
|
|
|
}
|
2019-12-23 19:22:54 +01:00
|
|
|
|
|
|
|
opt.disabled = !!disabled;
|
2019-10-29 19:26:11 +01:00
|
|
|
|
2019-10-25 16:43:28 +02:00
|
|
|
return opt;
|
2017-10-10 17:59:49 +02:00
|
|
|
},
|
2019-12-23 19:22:54 +01:00
|
|
|
};
|
2017-10-10 17:59:49 +02:00
|
|
|
|
2019-12-23 19:22:54 +01:00
|
|
|
export default templates;
|