<div class="textfield select ">
<label class="textfield__label select__label " for="select1">
Select label
</label>
<div class="textfield__inner">
<select name="select" id="select1" class="textfield__input select__input">
<option value="1">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Veritatis error velit quidem alias nihil mollitia temporibus, maxime dicta repellat. Perspiciatis veniam doloribus, quia corporis commodi sed omnis adipisci facilis nisi.
</option>
<option value="2">
Option 2
</option>
<option value="3">
Option 3
</option>
</select>
<svg class="icon select__icon" focusable="false">
<use href="../../inc/svg/global.4609ec92109fc41e7ad4764ef897ea8e.svg#"></use>
</svg>
</div>
</div>
{% set input %}
<select
name="{{ data.name }}"
id="{{ data.id }}"
{% if 'select--multiple' in modifier %} multiple{% endif %}
{% if 'select--search' in modifier %} data-search="true"{% endif %}
class="textfield__input select__input"
{% if data.isDisabled %} disabled{% endif %}
{% if 'select--search' in modifier %} data-no-results-text="{{ data.noResultsText }}"{% endif %}
{% if 'select--multiple' in modifier %} data-remove-item-text="{{ data.removeItemText }}"{% endif %}
{{ data.attributes }}
>
{% if data.placeholder or data.hasPlaceholder %}
<option value="placeholder" disabled selected>
{% if data.placeholder %}
{{ data.placeholder }}
{% endif %}
</option>
{% endif %}
{% for option in data.options %}
<option
value="{{ option.value }}"
{% if option.isSelected %} selected {% endif %}
>
{{ option.name }}
</option>
{% endfor %}
</select>
{% include '@icon' with { class: 'select__icon', modifier: '', name: data.iconName } %}
{% endset %}
{% include '@textfield' with {
data: data,
input: input,
class: 'select ' ~ class,
modifier: modifier,
labelClass: 'select__label' ~ ' ' ~ labelClass
} %}
{
"language": "en-US",
"data": {
"label": "Select label",
"id": "select1",
"name": "select",
"options": [
{
"name": "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Veritatis error velit quidem alias nihil mollitia temporibus, maxime dicta repellat. Perspiciatis veniam doloribus, quia corporis commodi sed omnis adipisci facilis nisi.",
"value": "1"
},
{
"name": "Option 2",
"value": "2"
},
{
"name": "Option 3",
"value": "3"
}
]
}
}
.select {
position: relative;
margin: 0;
cursor: pointer;
&.is-disabled {
cursor: default;
}
}
.select__icon {
position: absolute;
top: 50%;
right: 16px;
transform: translateY(-50%);
font-size: 24px;
pointer-events: none;
.select__container.is-open + & {
transform: translateY(-50%) rotate(180deg);
}
}
.select__inner {
padding-right: calc(24px + 4px);
min-height: 32px;
height: auto;
box-shadow: $elevation-01;
display: flex;
align-items: center;
.select__container.is-focused & {
border-color: $L-border-focus;
}
}
.select__dropdown {
visibility: hidden;
z-index: 2;
position: absolute;
top: 100%;
width: 100%;
background-color: $L-background-weak;
border: none;
overflow: hidden;
word-break: break-all; /* stylelint-disable-line plugin/no-unsupported-browser-features */
will-change: visibility;
border-radius: $border-radius-base;
box-shadow: $elevation-02;
.select--search & {
padding-top: 48px;
}
.select__container.is-flipped & {
top: auto;
bottom: 100%;
.select--search & {
padding-top: 0;
padding-bottom: 48px;
}
}
.select__container.is-open & {
visibility: visible;
}
}
.select__choices-input {
background: transparent;
border: none;
appearance: none;
height: 48px;
display: none;
width: 100% !important; /* overwrite plugin inline styles */
padding: 10px 16px;
z-index: -1;
pointer-events: none;
position: absolute;
top: 100%;
left: 0;
.select__container.is-flipped & {
top: auto;
bottom: 100%;
}
.select--multiple:not(.select--search) & {
color: transparent;
outline: none;
}
.select--search .select__container.is-open & {
pointer-events: auto;
}
&:not(select) {
display: block;
z-index: 3;
}
&::-ms-expand {
display: none;
}
}
.select__list {
.select__dropdown & {
padding: 8px 0;
max-height: 220px;
overflow-y: auto;
will-change: scroll-position;
.select--search & {
border-top: 1px solid $L-border;
}
.select__container.is-flipped & {
.select--search & {
border-top: 0;
border-bottom: 1px solid $L-border;
}
}
}
}
.select__list--single,
.select__list--multiple {
overflow: hidden;
white-space: nowrap;
padding: 4px 0;
width: 100%;
height: 100%;
}
.select__list--multiple {
overflow: visible;
white-space: normal;
padding: 10px 0 4px;
line-height: 0;
}
.select__item {
padding: 8px 16px;
color: $L-text-black;
font-size: $font-size-small;
line-height: $line-height-base;
.select__list--single & {
padding: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: $L-text;
}
.select__list--multiple & {
display: inline-block;
background-color: $L-background-medium;
font-size: 12px;
padding: 2px 24px 2px 8px;
margin-bottom: 6px;
max-width: 190px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
border-radius: $border-radius-base;
position: relative;
cursor: pointer;
}
&:not(:last-child) {
.select__list--multiple & {
margin-right: 8px;
}
}
&.select__item--highlighted {
background-color: $L-background-hover;
}
&.has-no-results {
cursor: default;
&:hover {
.select__list:not(.select__list--single) & {
color: $L-text;
background-color: transparent;
}
}
}
&[data-value='placeholder'] {
color: $L-text-medium;
font-size: $font-size-small;
letter-spacing: $letter-spacing-50;
.select__dropdown & {
display: none;
}
}
}
.select__remove {
appearance: none;
background: transparent;
border: 0;
padding: 0;
margin: 0;
display: inline-flex;
align-items: center;
justify-content: center;
position: absolute;
top: 0;
right: 0;
width: 24px;
height: 100%;
opacity: .8;
cursor: pointer;
.select__list--single & {
display: none;
}
}
.select__search-icon {
display: none;
font-size: 24px;
position: absolute;
top: 12px;
right: 16px;
z-index: map-get($zindex, 'default');
.select__container.is-flipped & {
top: auto;
bottom: 12px;
}
.select--search & {
display: inline-block;
}
}
.select__close-icon {
font-size: 15px;
pointer-events: none;
}
.select__option-span {
font-size: $font-size-tiny;
}
import Choices, { Choices as ChoicesLib } from 'choices.js';
import Component from '../../component/component';
import Icon from '../../icon/icon';
import '../textfield/textfield';
import './select.scss';
export interface ISelectSettings {
noResultsText: string;
search: boolean;
removeItemText: string;
}
export default class Select extends Component {
static initSelector: string = '.select';
public settings: ISelectSettings;
public input: JQuery;
public choices: Choices;
public multiple: boolean;
constructor(target: HTMLElement) {
super(target);
this.input = this.element.find('.select__input');
if (this.input.length) {
this.settings = $.extend({
multiselect: false,
noResultsText: 'No results found',
removeItemText: 'Remove item',
search: false,
}, this.input.data());
this.multiple = !!this.input.attr('multiple');
this.init();
}
}
setValue(value: string | string[]): void {
this.choices.setChoiceByValue(value);
}
init(): void {
this.choices = new Choices(this.input[0] as HTMLInputElement, {
callbackOnCreateTemplates: (template: ChoicesLib.Types.strToEl): Partial<ChoicesLib.Templates> => ({
dropdown: (classNames: ChoicesLib.ClassNames): HTMLElement => template(`
<div class="${classNames.list} ${classNames.listDropdown}" aria-expanded="false">
${Icon.render('search', '', 'select__search-icon')}
</div>
`) as HTMLElement,
item: (classNames: ChoicesLib.ClassNames, data: ChoicesLib.Choice): HTMLElement => template(`
<div class="${classNames.item}" data-item data-id="${data.id}" data-value="${data.value}" ${data.active ? 'aria-selected="true"' : ''} ${data.disabled ? 'aria-disabled="true"' : ''} data-deletable>
${data.label}
<button type="button" class="${classNames.button}" aria-label="${this.settings.removeItemText}" data-button>${Icon.render('close', '', 'select__close-icon')}</button>
</div>
`) as HTMLElement,
}),
classNames: {
activeState: 'is-active',
button: 'select__remove',
containerInner: 'textfield__input select__inner',
containerOuter: 'select__container',
disabledState: 'is-disabled',
flippedState: 'is-flipped',
focusState: 'is-focused',
group: 'select__group',
groupHeading: 'select__group-heading',
highlightedState: 'select__item--highlighted',
input: 'select__choices-input',
inputCloned: 'select__choices-input--cloned',
item: 'select__item',
itemChoice: 'select__item--choice',
itemDisabled: 'select__item--disabled',
itemSelectable: 'select__item--selectable',
list: 'select__list',
listDropdown: 'select__dropdown',
listItems: 'select__list--multiple',
listSingle: 'select__list--single',
loadingState: 'is-loading',
noChoices: 'has-no-choices',
noResults: 'has-no-results',
openState: 'is-open',
placeholder: 'select__placeholder',
selectedState: 'is-selected',
},
editItems: false,
itemSelectText: '',
noResultsText: this.settings.noResultsText,
removeItemButton: true,
removeItems: true,
renderSelectedChoices: 'always',
searchChoices: this.settings.search,
searchEnabled: this.settings.search,
});
this.addEventListeners();
}
addEventListeners(): void {
// Multiselect context
if (this.multiple) {
// Deselect a selected item when clicking on it.
this.input[0].addEventListener('choice', (event: CustomEvent) => {
const values: string[] | string = this.choices.getValue(true);
const value: string = event.detail.choice.value;
if (values?.includes(value)) {
setTimeout((): Choices => this.choices.removeActiveItemsByValue(value));
}
});
// Close dropdown when clicking on empty space between currently selected items in select list.
this.element.find('.select__inner > .select__list').on('click', (): void => {
this.choices.hideDropdown();
});
}
this.input[0].addEventListener('setValue', (event: CustomEvent): void => {
const values: string | string[] = event.detail;
if (typeof values !== 'undefined') {
this.setValue(event.detail);
}
});
this.input[0].addEventListener('hideDropdown', (): void => {
this.choices.hideDropdown();
// Clear search input when closing dropdown
if (this.settings.search) {
this.choices.clearInput();
}
});
this.input[0].addEventListener('showDropdown', (): void => {
// Focus search input when opening dropdown if search enabled.
if (this.settings.search) {
this.element.find('.select__dropdown .select__choices-input--cloned').trigger('focus');
}
this.choices.showDropdown();
});
}
}
Used to display a placeholder text and force the user to make a selection
<div class="textfield select ">
<label class="textfield__label select__label " for="select1">
Select label
</label>
<div class="textfield__inner">
<select name="select" id="select1" class="textfield__input select__input">
<option value="placeholder" disabled selected>
Placeholder
</option>
<option value="1">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Veritatis error velit quidem alias nihil mollitia temporibus, maxime dicta repellat. Perspiciatis veniam doloribus, quia corporis commodi sed omnis adipisci facilis nisi.
</option>
<option value="2">
Option 2
</option>
<option value="3">
Option 3
</option>
</select>
<svg class="icon select__icon" focusable="false">
<use href="../../inc/svg/global.4609ec92109fc41e7ad4764ef897ea8e.svg#"></use>
</svg>
</div>
</div>
{% set input %}
<select
name="{{ data.name }}"
id="{{ data.id }}"
{% if 'select--multiple' in modifier %} multiple{% endif %}
{% if 'select--search' in modifier %} data-search="true"{% endif %}
class="textfield__input select__input"
{% if data.isDisabled %} disabled{% endif %}
{% if 'select--search' in modifier %} data-no-results-text="{{ data.noResultsText }}"{% endif %}
{% if 'select--multiple' in modifier %} data-remove-item-text="{{ data.removeItemText }}"{% endif %}
{{ data.attributes }}
>
{% if data.placeholder or data.hasPlaceholder %}
<option value="placeholder" disabled selected>
{% if data.placeholder %}
{{ data.placeholder }}
{% endif %}
</option>
{% endif %}
{% for option in data.options %}
<option
value="{{ option.value }}"
{% if option.isSelected %} selected {% endif %}
>
{{ option.name }}
</option>
{% endfor %}
</select>
{% include '@icon' with { class: 'select__icon', modifier: '', name: data.iconName } %}
{% endset %}
{% include '@textfield' with {
data: data,
input: input,
class: 'select ' ~ class,
modifier: modifier,
labelClass: 'select__label' ~ ' ' ~ labelClass
} %}
{
"language": "en-US",
"data": {
"label": "Select label",
"id": "select1",
"name": "select",
"options": [
{
"name": "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Veritatis error velit quidem alias nihil mollitia temporibus, maxime dicta repellat. Perspiciatis veniam doloribus, quia corporis commodi sed omnis adipisci facilis nisi.",
"value": "1"
},
{
"name": "Option 2",
"value": "2"
},
{
"name": "Option 3",
"value": "3"
}
],
"placeholder": "Placeholder"
}
}
.select {
position: relative;
margin: 0;
cursor: pointer;
&.is-disabled {
cursor: default;
}
}
.select__icon {
position: absolute;
top: 50%;
right: 16px;
transform: translateY(-50%);
font-size: 24px;
pointer-events: none;
.select__container.is-open + & {
transform: translateY(-50%) rotate(180deg);
}
}
.select__inner {
padding-right: calc(24px + 4px);
min-height: 32px;
height: auto;
box-shadow: $elevation-01;
display: flex;
align-items: center;
.select__container.is-focused & {
border-color: $L-border-focus;
}
}
.select__dropdown {
visibility: hidden;
z-index: 2;
position: absolute;
top: 100%;
width: 100%;
background-color: $L-background-weak;
border: none;
overflow: hidden;
word-break: break-all; /* stylelint-disable-line plugin/no-unsupported-browser-features */
will-change: visibility;
border-radius: $border-radius-base;
box-shadow: $elevation-02;
.select--search & {
padding-top: 48px;
}
.select__container.is-flipped & {
top: auto;
bottom: 100%;
.select--search & {
padding-top: 0;
padding-bottom: 48px;
}
}
.select__container.is-open & {
visibility: visible;
}
}
.select__choices-input {
background: transparent;
border: none;
appearance: none;
height: 48px;
display: none;
width: 100% !important; /* overwrite plugin inline styles */
padding: 10px 16px;
z-index: -1;
pointer-events: none;
position: absolute;
top: 100%;
left: 0;
.select__container.is-flipped & {
top: auto;
bottom: 100%;
}
.select--multiple:not(.select--search) & {
color: transparent;
outline: none;
}
.select--search .select__container.is-open & {
pointer-events: auto;
}
&:not(select) {
display: block;
z-index: 3;
}
&::-ms-expand {
display: none;
}
}
.select__list {
.select__dropdown & {
padding: 8px 0;
max-height: 220px;
overflow-y: auto;
will-change: scroll-position;
.select--search & {
border-top: 1px solid $L-border;
}
.select__container.is-flipped & {
.select--search & {
border-top: 0;
border-bottom: 1px solid $L-border;
}
}
}
}
.select__list--single,
.select__list--multiple {
overflow: hidden;
white-space: nowrap;
padding: 4px 0;
width: 100%;
height: 100%;
}
.select__list--multiple {
overflow: visible;
white-space: normal;
padding: 10px 0 4px;
line-height: 0;
}
.select__item {
padding: 8px 16px;
color: $L-text-black;
font-size: $font-size-small;
line-height: $line-height-base;
.select__list--single & {
padding: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: $L-text;
}
.select__list--multiple & {
display: inline-block;
background-color: $L-background-medium;
font-size: 12px;
padding: 2px 24px 2px 8px;
margin-bottom: 6px;
max-width: 190px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
border-radius: $border-radius-base;
position: relative;
cursor: pointer;
}
&:not(:last-child) {
.select__list--multiple & {
margin-right: 8px;
}
}
&.select__item--highlighted {
background-color: $L-background-hover;
}
&.has-no-results {
cursor: default;
&:hover {
.select__list:not(.select__list--single) & {
color: $L-text;
background-color: transparent;
}
}
}
&[data-value='placeholder'] {
color: $L-text-medium;
font-size: $font-size-small;
letter-spacing: $letter-spacing-50;
.select__dropdown & {
display: none;
}
}
}
.select__remove {
appearance: none;
background: transparent;
border: 0;
padding: 0;
margin: 0;
display: inline-flex;
align-items: center;
justify-content: center;
position: absolute;
top: 0;
right: 0;
width: 24px;
height: 100%;
opacity: .8;
cursor: pointer;
.select__list--single & {
display: none;
}
}
.select__search-icon {
display: none;
font-size: 24px;
position: absolute;
top: 12px;
right: 16px;
z-index: map-get($zindex, 'default');
.select__container.is-flipped & {
top: auto;
bottom: 12px;
}
.select--search & {
display: inline-block;
}
}
.select__close-icon {
font-size: 15px;
pointer-events: none;
}
.select__option-span {
font-size: $font-size-tiny;
}
import Choices, { Choices as ChoicesLib } from 'choices.js';
import Component from '../../component/component';
import Icon from '../../icon/icon';
import '../textfield/textfield';
import './select.scss';
export interface ISelectSettings {
noResultsText: string;
search: boolean;
removeItemText: string;
}
export default class Select extends Component {
static initSelector: string = '.select';
public settings: ISelectSettings;
public input: JQuery;
public choices: Choices;
public multiple: boolean;
constructor(target: HTMLElement) {
super(target);
this.input = this.element.find('.select__input');
if (this.input.length) {
this.settings = $.extend({
multiselect: false,
noResultsText: 'No results found',
removeItemText: 'Remove item',
search: false,
}, this.input.data());
this.multiple = !!this.input.attr('multiple');
this.init();
}
}
setValue(value: string | string[]): void {
this.choices.setChoiceByValue(value);
}
init(): void {
this.choices = new Choices(this.input[0] as HTMLInputElement, {
callbackOnCreateTemplates: (template: ChoicesLib.Types.strToEl): Partial<ChoicesLib.Templates> => ({
dropdown: (classNames: ChoicesLib.ClassNames): HTMLElement => template(`
<div class="${classNames.list} ${classNames.listDropdown}" aria-expanded="false">
${Icon.render('search', '', 'select__search-icon')}
</div>
`) as HTMLElement,
item: (classNames: ChoicesLib.ClassNames, data: ChoicesLib.Choice): HTMLElement => template(`
<div class="${classNames.item}" data-item data-id="${data.id}" data-value="${data.value}" ${data.active ? 'aria-selected="true"' : ''} ${data.disabled ? 'aria-disabled="true"' : ''} data-deletable>
${data.label}
<button type="button" class="${classNames.button}" aria-label="${this.settings.removeItemText}" data-button>${Icon.render('close', '', 'select__close-icon')}</button>
</div>
`) as HTMLElement,
}),
classNames: {
activeState: 'is-active',
button: 'select__remove',
containerInner: 'textfield__input select__inner',
containerOuter: 'select__container',
disabledState: 'is-disabled',
flippedState: 'is-flipped',
focusState: 'is-focused',
group: 'select__group',
groupHeading: 'select__group-heading',
highlightedState: 'select__item--highlighted',
input: 'select__choices-input',
inputCloned: 'select__choices-input--cloned',
item: 'select__item',
itemChoice: 'select__item--choice',
itemDisabled: 'select__item--disabled',
itemSelectable: 'select__item--selectable',
list: 'select__list',
listDropdown: 'select__dropdown',
listItems: 'select__list--multiple',
listSingle: 'select__list--single',
loadingState: 'is-loading',
noChoices: 'has-no-choices',
noResults: 'has-no-results',
openState: 'is-open',
placeholder: 'select__placeholder',
selectedState: 'is-selected',
},
editItems: false,
itemSelectText: '',
noResultsText: this.settings.noResultsText,
removeItemButton: true,
removeItems: true,
renderSelectedChoices: 'always',
searchChoices: this.settings.search,
searchEnabled: this.settings.search,
});
this.addEventListeners();
}
addEventListeners(): void {
// Multiselect context
if (this.multiple) {
// Deselect a selected item when clicking on it.
this.input[0].addEventListener('choice', (event: CustomEvent) => {
const values: string[] | string = this.choices.getValue(true);
const value: string = event.detail.choice.value;
if (values?.includes(value)) {
setTimeout((): Choices => this.choices.removeActiveItemsByValue(value));
}
});
// Close dropdown when clicking on empty space between currently selected items in select list.
this.element.find('.select__inner > .select__list').on('click', (): void => {
this.choices.hideDropdown();
});
}
this.input[0].addEventListener('setValue', (event: CustomEvent): void => {
const values: string | string[] = event.detail;
if (typeof values !== 'undefined') {
this.setValue(event.detail);
}
});
this.input[0].addEventListener('hideDropdown', (): void => {
this.choices.hideDropdown();
// Clear search input when closing dropdown
if (this.settings.search) {
this.choices.clearInput();
}
});
this.input[0].addEventListener('showDropdown', (): void => {
// Focus search input when opening dropdown if search enabled.
if (this.settings.search) {
this.element.find('.select__dropdown .select__choices-input--cloned').trigger('focus');
}
this.choices.showDropdown();
});
}
}
Used to display an empty select and force the user to make a selection
<div class="textfield select ">
<label class="textfield__label select__label " for="select1">
Select label
</label>
<div class="textfield__inner">
<select name="select" id="select1" class="textfield__input select__input">
<option value="placeholder" disabled selected>
</option>
<option value="1">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Veritatis error velit quidem alias nihil mollitia temporibus, maxime dicta repellat. Perspiciatis veniam doloribus, quia corporis commodi sed omnis adipisci facilis nisi.
</option>
<option value="2">
Option 2
</option>
<option value="3">
Option 3
</option>
</select>
<svg class="icon select__icon" focusable="false">
<use href="../../inc/svg/global.4609ec92109fc41e7ad4764ef897ea8e.svg#"></use>
</svg>
</div>
</div>
{% set input %}
<select
name="{{ data.name }}"
id="{{ data.id }}"
{% if 'select--multiple' in modifier %} multiple{% endif %}
{% if 'select--search' in modifier %} data-search="true"{% endif %}
class="textfield__input select__input"
{% if data.isDisabled %} disabled{% endif %}
{% if 'select--search' in modifier %} data-no-results-text="{{ data.noResultsText }}"{% endif %}
{% if 'select--multiple' in modifier %} data-remove-item-text="{{ data.removeItemText }}"{% endif %}
{{ data.attributes }}
>
{% if data.placeholder or data.hasPlaceholder %}
<option value="placeholder" disabled selected>
{% if data.placeholder %}
{{ data.placeholder }}
{% endif %}
</option>
{% endif %}
{% for option in data.options %}
<option
value="{{ option.value }}"
{% if option.isSelected %} selected {% endif %}
>
{{ option.name }}
</option>
{% endfor %}
</select>
{% include '@icon' with { class: 'select__icon', modifier: '', name: data.iconName } %}
{% endset %}
{% include '@textfield' with {
data: data,
input: input,
class: 'select ' ~ class,
modifier: modifier,
labelClass: 'select__label' ~ ' ' ~ labelClass
} %}
{
"language": "en-US",
"data": {
"label": "Select label",
"id": "select1",
"name": "select",
"options": [
{
"name": "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Veritatis error velit quidem alias nihil mollitia temporibus, maxime dicta repellat. Perspiciatis veniam doloribus, quia corporis commodi sed omnis adipisci facilis nisi.",
"value": "1"
},
{
"name": "Option 2",
"value": "2"
},
{
"name": "Option 3",
"value": "3"
}
],
"hasPlaceholder": true
}
}
.select {
position: relative;
margin: 0;
cursor: pointer;
&.is-disabled {
cursor: default;
}
}
.select__icon {
position: absolute;
top: 50%;
right: 16px;
transform: translateY(-50%);
font-size: 24px;
pointer-events: none;
.select__container.is-open + & {
transform: translateY(-50%) rotate(180deg);
}
}
.select__inner {
padding-right: calc(24px + 4px);
min-height: 32px;
height: auto;
box-shadow: $elevation-01;
display: flex;
align-items: center;
.select__container.is-focused & {
border-color: $L-border-focus;
}
}
.select__dropdown {
visibility: hidden;
z-index: 2;
position: absolute;
top: 100%;
width: 100%;
background-color: $L-background-weak;
border: none;
overflow: hidden;
word-break: break-all; /* stylelint-disable-line plugin/no-unsupported-browser-features */
will-change: visibility;
border-radius: $border-radius-base;
box-shadow: $elevation-02;
.select--search & {
padding-top: 48px;
}
.select__container.is-flipped & {
top: auto;
bottom: 100%;
.select--search & {
padding-top: 0;
padding-bottom: 48px;
}
}
.select__container.is-open & {
visibility: visible;
}
}
.select__choices-input {
background: transparent;
border: none;
appearance: none;
height: 48px;
display: none;
width: 100% !important; /* overwrite plugin inline styles */
padding: 10px 16px;
z-index: -1;
pointer-events: none;
position: absolute;
top: 100%;
left: 0;
.select__container.is-flipped & {
top: auto;
bottom: 100%;
}
.select--multiple:not(.select--search) & {
color: transparent;
outline: none;
}
.select--search .select__container.is-open & {
pointer-events: auto;
}
&:not(select) {
display: block;
z-index: 3;
}
&::-ms-expand {
display: none;
}
}
.select__list {
.select__dropdown & {
padding: 8px 0;
max-height: 220px;
overflow-y: auto;
will-change: scroll-position;
.select--search & {
border-top: 1px solid $L-border;
}
.select__container.is-flipped & {
.select--search & {
border-top: 0;
border-bottom: 1px solid $L-border;
}
}
}
}
.select__list--single,
.select__list--multiple {
overflow: hidden;
white-space: nowrap;
padding: 4px 0;
width: 100%;
height: 100%;
}
.select__list--multiple {
overflow: visible;
white-space: normal;
padding: 10px 0 4px;
line-height: 0;
}
.select__item {
padding: 8px 16px;
color: $L-text-black;
font-size: $font-size-small;
line-height: $line-height-base;
.select__list--single & {
padding: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: $L-text;
}
.select__list--multiple & {
display: inline-block;
background-color: $L-background-medium;
font-size: 12px;
padding: 2px 24px 2px 8px;
margin-bottom: 6px;
max-width: 190px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
border-radius: $border-radius-base;
position: relative;
cursor: pointer;
}
&:not(:last-child) {
.select__list--multiple & {
margin-right: 8px;
}
}
&.select__item--highlighted {
background-color: $L-background-hover;
}
&.has-no-results {
cursor: default;
&:hover {
.select__list:not(.select__list--single) & {
color: $L-text;
background-color: transparent;
}
}
}
&[data-value='placeholder'] {
color: $L-text-medium;
font-size: $font-size-small;
letter-spacing: $letter-spacing-50;
.select__dropdown & {
display: none;
}
}
}
.select__remove {
appearance: none;
background: transparent;
border: 0;
padding: 0;
margin: 0;
display: inline-flex;
align-items: center;
justify-content: center;
position: absolute;
top: 0;
right: 0;
width: 24px;
height: 100%;
opacity: .8;
cursor: pointer;
.select__list--single & {
display: none;
}
}
.select__search-icon {
display: none;
font-size: 24px;
position: absolute;
top: 12px;
right: 16px;
z-index: map-get($zindex, 'default');
.select__container.is-flipped & {
top: auto;
bottom: 12px;
}
.select--search & {
display: inline-block;
}
}
.select__close-icon {
font-size: 15px;
pointer-events: none;
}
.select__option-span {
font-size: $font-size-tiny;
}
import Choices, { Choices as ChoicesLib } from 'choices.js';
import Component from '../../component/component';
import Icon from '../../icon/icon';
import '../textfield/textfield';
import './select.scss';
export interface ISelectSettings {
noResultsText: string;
search: boolean;
removeItemText: string;
}
export default class Select extends Component {
static initSelector: string = '.select';
public settings: ISelectSettings;
public input: JQuery;
public choices: Choices;
public multiple: boolean;
constructor(target: HTMLElement) {
super(target);
this.input = this.element.find('.select__input');
if (this.input.length) {
this.settings = $.extend({
multiselect: false,
noResultsText: 'No results found',
removeItemText: 'Remove item',
search: false,
}, this.input.data());
this.multiple = !!this.input.attr('multiple');
this.init();
}
}
setValue(value: string | string[]): void {
this.choices.setChoiceByValue(value);
}
init(): void {
this.choices = new Choices(this.input[0] as HTMLInputElement, {
callbackOnCreateTemplates: (template: ChoicesLib.Types.strToEl): Partial<ChoicesLib.Templates> => ({
dropdown: (classNames: ChoicesLib.ClassNames): HTMLElement => template(`
<div class="${classNames.list} ${classNames.listDropdown}" aria-expanded="false">
${Icon.render('search', '', 'select__search-icon')}
</div>
`) as HTMLElement,
item: (classNames: ChoicesLib.ClassNames, data: ChoicesLib.Choice): HTMLElement => template(`
<div class="${classNames.item}" data-item data-id="${data.id}" data-value="${data.value}" ${data.active ? 'aria-selected="true"' : ''} ${data.disabled ? 'aria-disabled="true"' : ''} data-deletable>
${data.label}
<button type="button" class="${classNames.button}" aria-label="${this.settings.removeItemText}" data-button>${Icon.render('close', '', 'select__close-icon')}</button>
</div>
`) as HTMLElement,
}),
classNames: {
activeState: 'is-active',
button: 'select__remove',
containerInner: 'textfield__input select__inner',
containerOuter: 'select__container',
disabledState: 'is-disabled',
flippedState: 'is-flipped',
focusState: 'is-focused',
group: 'select__group',
groupHeading: 'select__group-heading',
highlightedState: 'select__item--highlighted',
input: 'select__choices-input',
inputCloned: 'select__choices-input--cloned',
item: 'select__item',
itemChoice: 'select__item--choice',
itemDisabled: 'select__item--disabled',
itemSelectable: 'select__item--selectable',
list: 'select__list',
listDropdown: 'select__dropdown',
listItems: 'select__list--multiple',
listSingle: 'select__list--single',
loadingState: 'is-loading',
noChoices: 'has-no-choices',
noResults: 'has-no-results',
openState: 'is-open',
placeholder: 'select__placeholder',
selectedState: 'is-selected',
},
editItems: false,
itemSelectText: '',
noResultsText: this.settings.noResultsText,
removeItemButton: true,
removeItems: true,
renderSelectedChoices: 'always',
searchChoices: this.settings.search,
searchEnabled: this.settings.search,
});
this.addEventListeners();
}
addEventListeners(): void {
// Multiselect context
if (this.multiple) {
// Deselect a selected item when clicking on it.
this.input[0].addEventListener('choice', (event: CustomEvent) => {
const values: string[] | string = this.choices.getValue(true);
const value: string = event.detail.choice.value;
if (values?.includes(value)) {
setTimeout((): Choices => this.choices.removeActiveItemsByValue(value));
}
});
// Close dropdown when clicking on empty space between currently selected items in select list.
this.element.find('.select__inner > .select__list').on('click', (): void => {
this.choices.hideDropdown();
});
}
this.input[0].addEventListener('setValue', (event: CustomEvent): void => {
const values: string | string[] = event.detail;
if (typeof values !== 'undefined') {
this.setValue(event.detail);
}
});
this.input[0].addEventListener('hideDropdown', (): void => {
this.choices.hideDropdown();
// Clear search input when closing dropdown
if (this.settings.search) {
this.choices.clearInput();
}
});
this.input[0].addEventListener('showDropdown', (): void => {
// Focus search input when opening dropdown if search enabled.
if (this.settings.search) {
this.element.find('.select__dropdown .select__choices-input--cloned').trigger('focus');
}
this.choices.showDropdown();
});
}
}
<div class="textfield select--multiple select ">
<label class="textfield__label select__label " for="select1">
Select label
</label>
<div class="textfield__inner">
<select name="select" id="select1" multiple class="textfield__input select__input" data-remove-item-text="Remove item">
<option value="1">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Veritatis error velit quidem alias nihil mollitia temporibus, maxime dicta repellat. Perspiciatis veniam doloribus, quia corporis commodi sed omnis adipisci facilis nisi.
</option>
<option value="2">
Option 2
</option>
<option value="3">
Option 3
</option>
<option value="4">
Option 4
</option>
<option value="5">
Option 5
</option>
</select>
<svg class="icon select__icon" focusable="false">
<use href="../../inc/svg/global.4609ec92109fc41e7ad4764ef897ea8e.svg#"></use>
</svg>
</div>
</div>
{% set input %}
<select
name="{{ data.name }}"
id="{{ data.id }}"
{% if 'select--multiple' in modifier %} multiple{% endif %}
{% if 'select--search' in modifier %} data-search="true"{% endif %}
class="textfield__input select__input"
{% if data.isDisabled %} disabled{% endif %}
{% if 'select--search' in modifier %} data-no-results-text="{{ data.noResultsText }}"{% endif %}
{% if 'select--multiple' in modifier %} data-remove-item-text="{{ data.removeItemText }}"{% endif %}
{{ data.attributes }}
>
{% if data.placeholder or data.hasPlaceholder %}
<option value="placeholder" disabled selected>
{% if data.placeholder %}
{{ data.placeholder }}
{% endif %}
</option>
{% endif %}
{% for option in data.options %}
<option
value="{{ option.value }}"
{% if option.isSelected %} selected {% endif %}
>
{{ option.name }}
</option>
{% endfor %}
</select>
{% include '@icon' with { class: 'select__icon', modifier: '', name: data.iconName } %}
{% endset %}
{% include '@textfield' with {
data: data,
input: input,
class: 'select ' ~ class,
modifier: modifier,
labelClass: 'select__label' ~ ' ' ~ labelClass
} %}
{
"language": "en-US",
"data": {
"label": "Select label",
"id": "select1",
"name": "select",
"options": [
{
"name": "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Veritatis error velit quidem alias nihil mollitia temporibus, maxime dicta repellat. Perspiciatis veniam doloribus, quia corporis commodi sed omnis adipisci facilis nisi.",
"value": "1"
},
{
"name": "Option 2",
"value": "2"
},
{
"name": "Option 3",
"value": "3"
},
{
"name": "Option 4",
"value": "4"
},
{
"name": "Option 5",
"value": "5"
}
],
"removeItemText": "Remove item"
},
"modifier": "select--multiple"
}
.select {
position: relative;
margin: 0;
cursor: pointer;
&.is-disabled {
cursor: default;
}
}
.select__icon {
position: absolute;
top: 50%;
right: 16px;
transform: translateY(-50%);
font-size: 24px;
pointer-events: none;
.select__container.is-open + & {
transform: translateY(-50%) rotate(180deg);
}
}
.select__inner {
padding-right: calc(24px + 4px);
min-height: 32px;
height: auto;
box-shadow: $elevation-01;
display: flex;
align-items: center;
.select__container.is-focused & {
border-color: $L-border-focus;
}
}
.select__dropdown {
visibility: hidden;
z-index: 2;
position: absolute;
top: 100%;
width: 100%;
background-color: $L-background-weak;
border: none;
overflow: hidden;
word-break: break-all; /* stylelint-disable-line plugin/no-unsupported-browser-features */
will-change: visibility;
border-radius: $border-radius-base;
box-shadow: $elevation-02;
.select--search & {
padding-top: 48px;
}
.select__container.is-flipped & {
top: auto;
bottom: 100%;
.select--search & {
padding-top: 0;
padding-bottom: 48px;
}
}
.select__container.is-open & {
visibility: visible;
}
}
.select__choices-input {
background: transparent;
border: none;
appearance: none;
height: 48px;
display: none;
width: 100% !important; /* overwrite plugin inline styles */
padding: 10px 16px;
z-index: -1;
pointer-events: none;
position: absolute;
top: 100%;
left: 0;
.select__container.is-flipped & {
top: auto;
bottom: 100%;
}
.select--multiple:not(.select--search) & {
color: transparent;
outline: none;
}
.select--search .select__container.is-open & {
pointer-events: auto;
}
&:not(select) {
display: block;
z-index: 3;
}
&::-ms-expand {
display: none;
}
}
.select__list {
.select__dropdown & {
padding: 8px 0;
max-height: 220px;
overflow-y: auto;
will-change: scroll-position;
.select--search & {
border-top: 1px solid $L-border;
}
.select__container.is-flipped & {
.select--search & {
border-top: 0;
border-bottom: 1px solid $L-border;
}
}
}
}
.select__list--single,
.select__list--multiple {
overflow: hidden;
white-space: nowrap;
padding: 4px 0;
width: 100%;
height: 100%;
}
.select__list--multiple {
overflow: visible;
white-space: normal;
padding: 10px 0 4px;
line-height: 0;
}
.select__item {
padding: 8px 16px;
color: $L-text-black;
font-size: $font-size-small;
line-height: $line-height-base;
.select__list--single & {
padding: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: $L-text;
}
.select__list--multiple & {
display: inline-block;
background-color: $L-background-medium;
font-size: 12px;
padding: 2px 24px 2px 8px;
margin-bottom: 6px;
max-width: 190px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
border-radius: $border-radius-base;
position: relative;
cursor: pointer;
}
&:not(:last-child) {
.select__list--multiple & {
margin-right: 8px;
}
}
&.select__item--highlighted {
background-color: $L-background-hover;
}
&.has-no-results {
cursor: default;
&:hover {
.select__list:not(.select__list--single) & {
color: $L-text;
background-color: transparent;
}
}
}
&[data-value='placeholder'] {
color: $L-text-medium;
font-size: $font-size-small;
letter-spacing: $letter-spacing-50;
.select__dropdown & {
display: none;
}
}
}
.select__remove {
appearance: none;
background: transparent;
border: 0;
padding: 0;
margin: 0;
display: inline-flex;
align-items: center;
justify-content: center;
position: absolute;
top: 0;
right: 0;
width: 24px;
height: 100%;
opacity: .8;
cursor: pointer;
.select__list--single & {
display: none;
}
}
.select__search-icon {
display: none;
font-size: 24px;
position: absolute;
top: 12px;
right: 16px;
z-index: map-get($zindex, 'default');
.select__container.is-flipped & {
top: auto;
bottom: 12px;
}
.select--search & {
display: inline-block;
}
}
.select__close-icon {
font-size: 15px;
pointer-events: none;
}
.select__option-span {
font-size: $font-size-tiny;
}
import Choices, { Choices as ChoicesLib } from 'choices.js';
import Component from '../../component/component';
import Icon from '../../icon/icon';
import '../textfield/textfield';
import './select.scss';
export interface ISelectSettings {
noResultsText: string;
search: boolean;
removeItemText: string;
}
export default class Select extends Component {
static initSelector: string = '.select';
public settings: ISelectSettings;
public input: JQuery;
public choices: Choices;
public multiple: boolean;
constructor(target: HTMLElement) {
super(target);
this.input = this.element.find('.select__input');
if (this.input.length) {
this.settings = $.extend({
multiselect: false,
noResultsText: 'No results found',
removeItemText: 'Remove item',
search: false,
}, this.input.data());
this.multiple = !!this.input.attr('multiple');
this.init();
}
}
setValue(value: string | string[]): void {
this.choices.setChoiceByValue(value);
}
init(): void {
this.choices = new Choices(this.input[0] as HTMLInputElement, {
callbackOnCreateTemplates: (template: ChoicesLib.Types.strToEl): Partial<ChoicesLib.Templates> => ({
dropdown: (classNames: ChoicesLib.ClassNames): HTMLElement => template(`
<div class="${classNames.list} ${classNames.listDropdown}" aria-expanded="false">
${Icon.render('search', '', 'select__search-icon')}
</div>
`) as HTMLElement,
item: (classNames: ChoicesLib.ClassNames, data: ChoicesLib.Choice): HTMLElement => template(`
<div class="${classNames.item}" data-item data-id="${data.id}" data-value="${data.value}" ${data.active ? 'aria-selected="true"' : ''} ${data.disabled ? 'aria-disabled="true"' : ''} data-deletable>
${data.label}
<button type="button" class="${classNames.button}" aria-label="${this.settings.removeItemText}" data-button>${Icon.render('close', '', 'select__close-icon')}</button>
</div>
`) as HTMLElement,
}),
classNames: {
activeState: 'is-active',
button: 'select__remove',
containerInner: 'textfield__input select__inner',
containerOuter: 'select__container',
disabledState: 'is-disabled',
flippedState: 'is-flipped',
focusState: 'is-focused',
group: 'select__group',
groupHeading: 'select__group-heading',
highlightedState: 'select__item--highlighted',
input: 'select__choices-input',
inputCloned: 'select__choices-input--cloned',
item: 'select__item',
itemChoice: 'select__item--choice',
itemDisabled: 'select__item--disabled',
itemSelectable: 'select__item--selectable',
list: 'select__list',
listDropdown: 'select__dropdown',
listItems: 'select__list--multiple',
listSingle: 'select__list--single',
loadingState: 'is-loading',
noChoices: 'has-no-choices',
noResults: 'has-no-results',
openState: 'is-open',
placeholder: 'select__placeholder',
selectedState: 'is-selected',
},
editItems: false,
itemSelectText: '',
noResultsText: this.settings.noResultsText,
removeItemButton: true,
removeItems: true,
renderSelectedChoices: 'always',
searchChoices: this.settings.search,
searchEnabled: this.settings.search,
});
this.addEventListeners();
}
addEventListeners(): void {
// Multiselect context
if (this.multiple) {
// Deselect a selected item when clicking on it.
this.input[0].addEventListener('choice', (event: CustomEvent) => {
const values: string[] | string = this.choices.getValue(true);
const value: string = event.detail.choice.value;
if (values?.includes(value)) {
setTimeout((): Choices => this.choices.removeActiveItemsByValue(value));
}
});
// Close dropdown when clicking on empty space between currently selected items in select list.
this.element.find('.select__inner > .select__list').on('click', (): void => {
this.choices.hideDropdown();
});
}
this.input[0].addEventListener('setValue', (event: CustomEvent): void => {
const values: string | string[] = event.detail;
if (typeof values !== 'undefined') {
this.setValue(event.detail);
}
});
this.input[0].addEventListener('hideDropdown', (): void => {
this.choices.hideDropdown();
// Clear search input when closing dropdown
if (this.settings.search) {
this.choices.clearInput();
}
});
this.input[0].addEventListener('showDropdown', (): void => {
// Focus search input when opening dropdown if search enabled.
if (this.settings.search) {
this.element.find('.select__dropdown .select__choices-input--cloned').trigger('focus');
}
this.choices.showDropdown();
});
}
}
<div class="textfield select--multiple select--search select ">
<label class="textfield__label select__label " for="select1">
Select label
</label>
<div class="textfield__inner">
<select name="select" id="select1" multiple data-search="true" class="textfield__input select__input" data-no-results-text="No results found" data-remove-item-text="Remove item">
<option value="1">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Veritatis error velit quidem alias nihil mollitia temporibus, maxime dicta repellat. Perspiciatis veniam doloribus, quia corporis commodi sed omnis adipisci facilis nisi.
</option>
<option value="2">
Option 2
</option>
<option value="3">
Option 3
</option>
</select>
<svg class="icon select__icon" focusable="false">
<use href="../../inc/svg/global.4609ec92109fc41e7ad4764ef897ea8e.svg#"></use>
</svg>
</div>
</div>
{% set input %}
<select
name="{{ data.name }}"
id="{{ data.id }}"
{% if 'select--multiple' in modifier %} multiple{% endif %}
{% if 'select--search' in modifier %} data-search="true"{% endif %}
class="textfield__input select__input"
{% if data.isDisabled %} disabled{% endif %}
{% if 'select--search' in modifier %} data-no-results-text="{{ data.noResultsText }}"{% endif %}
{% if 'select--multiple' in modifier %} data-remove-item-text="{{ data.removeItemText }}"{% endif %}
{{ data.attributes }}
>
{% if data.placeholder or data.hasPlaceholder %}
<option value="placeholder" disabled selected>
{% if data.placeholder %}
{{ data.placeholder }}
{% endif %}
</option>
{% endif %}
{% for option in data.options %}
<option
value="{{ option.value }}"
{% if option.isSelected %} selected {% endif %}
>
{{ option.name }}
</option>
{% endfor %}
</select>
{% include '@icon' with { class: 'select__icon', modifier: '', name: data.iconName } %}
{% endset %}
{% include '@textfield' with {
data: data,
input: input,
class: 'select ' ~ class,
modifier: modifier,
labelClass: 'select__label' ~ ' ' ~ labelClass
} %}
{
"language": "en-US",
"data": {
"label": "Select label",
"id": "select1",
"name": "select",
"options": [
{
"name": "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Veritatis error velit quidem alias nihil mollitia temporibus, maxime dicta repellat. Perspiciatis veniam doloribus, quia corporis commodi sed omnis adipisci facilis nisi.",
"value": "1"
},
{
"name": "Option 2",
"value": "2"
},
{
"name": "Option 3",
"value": "3"
}
],
"noResultsText": "No results found",
"removeItemText": "Remove item"
},
"modifier": "select--multiple select--search"
}
.select {
position: relative;
margin: 0;
cursor: pointer;
&.is-disabled {
cursor: default;
}
}
.select__icon {
position: absolute;
top: 50%;
right: 16px;
transform: translateY(-50%);
font-size: 24px;
pointer-events: none;
.select__container.is-open + & {
transform: translateY(-50%) rotate(180deg);
}
}
.select__inner {
padding-right: calc(24px + 4px);
min-height: 32px;
height: auto;
box-shadow: $elevation-01;
display: flex;
align-items: center;
.select__container.is-focused & {
border-color: $L-border-focus;
}
}
.select__dropdown {
visibility: hidden;
z-index: 2;
position: absolute;
top: 100%;
width: 100%;
background-color: $L-background-weak;
border: none;
overflow: hidden;
word-break: break-all; /* stylelint-disable-line plugin/no-unsupported-browser-features */
will-change: visibility;
border-radius: $border-radius-base;
box-shadow: $elevation-02;
.select--search & {
padding-top: 48px;
}
.select__container.is-flipped & {
top: auto;
bottom: 100%;
.select--search & {
padding-top: 0;
padding-bottom: 48px;
}
}
.select__container.is-open & {
visibility: visible;
}
}
.select__choices-input {
background: transparent;
border: none;
appearance: none;
height: 48px;
display: none;
width: 100% !important; /* overwrite plugin inline styles */
padding: 10px 16px;
z-index: -1;
pointer-events: none;
position: absolute;
top: 100%;
left: 0;
.select__container.is-flipped & {
top: auto;
bottom: 100%;
}
.select--multiple:not(.select--search) & {
color: transparent;
outline: none;
}
.select--search .select__container.is-open & {
pointer-events: auto;
}
&:not(select) {
display: block;
z-index: 3;
}
&::-ms-expand {
display: none;
}
}
.select__list {
.select__dropdown & {
padding: 8px 0;
max-height: 220px;
overflow-y: auto;
will-change: scroll-position;
.select--search & {
border-top: 1px solid $L-border;
}
.select__container.is-flipped & {
.select--search & {
border-top: 0;
border-bottom: 1px solid $L-border;
}
}
}
}
.select__list--single,
.select__list--multiple {
overflow: hidden;
white-space: nowrap;
padding: 4px 0;
width: 100%;
height: 100%;
}
.select__list--multiple {
overflow: visible;
white-space: normal;
padding: 10px 0 4px;
line-height: 0;
}
.select__item {
padding: 8px 16px;
color: $L-text-black;
font-size: $font-size-small;
line-height: $line-height-base;
.select__list--single & {
padding: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: $L-text;
}
.select__list--multiple & {
display: inline-block;
background-color: $L-background-medium;
font-size: 12px;
padding: 2px 24px 2px 8px;
margin-bottom: 6px;
max-width: 190px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
border-radius: $border-radius-base;
position: relative;
cursor: pointer;
}
&:not(:last-child) {
.select__list--multiple & {
margin-right: 8px;
}
}
&.select__item--highlighted {
background-color: $L-background-hover;
}
&.has-no-results {
cursor: default;
&:hover {
.select__list:not(.select__list--single) & {
color: $L-text;
background-color: transparent;
}
}
}
&[data-value='placeholder'] {
color: $L-text-medium;
font-size: $font-size-small;
letter-spacing: $letter-spacing-50;
.select__dropdown & {
display: none;
}
}
}
.select__remove {
appearance: none;
background: transparent;
border: 0;
padding: 0;
margin: 0;
display: inline-flex;
align-items: center;
justify-content: center;
position: absolute;
top: 0;
right: 0;
width: 24px;
height: 100%;
opacity: .8;
cursor: pointer;
.select__list--single & {
display: none;
}
}
.select__search-icon {
display: none;
font-size: 24px;
position: absolute;
top: 12px;
right: 16px;
z-index: map-get($zindex, 'default');
.select__container.is-flipped & {
top: auto;
bottom: 12px;
}
.select--search & {
display: inline-block;
}
}
.select__close-icon {
font-size: 15px;
pointer-events: none;
}
.select__option-span {
font-size: $font-size-tiny;
}
import Choices, { Choices as ChoicesLib } from 'choices.js';
import Component from '../../component/component';
import Icon from '../../icon/icon';
import '../textfield/textfield';
import './select.scss';
export interface ISelectSettings {
noResultsText: string;
search: boolean;
removeItemText: string;
}
export default class Select extends Component {
static initSelector: string = '.select';
public settings: ISelectSettings;
public input: JQuery;
public choices: Choices;
public multiple: boolean;
constructor(target: HTMLElement) {
super(target);
this.input = this.element.find('.select__input');
if (this.input.length) {
this.settings = $.extend({
multiselect: false,
noResultsText: 'No results found',
removeItemText: 'Remove item',
search: false,
}, this.input.data());
this.multiple = !!this.input.attr('multiple');
this.init();
}
}
setValue(value: string | string[]): void {
this.choices.setChoiceByValue(value);
}
init(): void {
this.choices = new Choices(this.input[0] as HTMLInputElement, {
callbackOnCreateTemplates: (template: ChoicesLib.Types.strToEl): Partial<ChoicesLib.Templates> => ({
dropdown: (classNames: ChoicesLib.ClassNames): HTMLElement => template(`
<div class="${classNames.list} ${classNames.listDropdown}" aria-expanded="false">
${Icon.render('search', '', 'select__search-icon')}
</div>
`) as HTMLElement,
item: (classNames: ChoicesLib.ClassNames, data: ChoicesLib.Choice): HTMLElement => template(`
<div class="${classNames.item}" data-item data-id="${data.id}" data-value="${data.value}" ${data.active ? 'aria-selected="true"' : ''} ${data.disabled ? 'aria-disabled="true"' : ''} data-deletable>
${data.label}
<button type="button" class="${classNames.button}" aria-label="${this.settings.removeItemText}" data-button>${Icon.render('close', '', 'select__close-icon')}</button>
</div>
`) as HTMLElement,
}),
classNames: {
activeState: 'is-active',
button: 'select__remove',
containerInner: 'textfield__input select__inner',
containerOuter: 'select__container',
disabledState: 'is-disabled',
flippedState: 'is-flipped',
focusState: 'is-focused',
group: 'select__group',
groupHeading: 'select__group-heading',
highlightedState: 'select__item--highlighted',
input: 'select__choices-input',
inputCloned: 'select__choices-input--cloned',
item: 'select__item',
itemChoice: 'select__item--choice',
itemDisabled: 'select__item--disabled',
itemSelectable: 'select__item--selectable',
list: 'select__list',
listDropdown: 'select__dropdown',
listItems: 'select__list--multiple',
listSingle: 'select__list--single',
loadingState: 'is-loading',
noChoices: 'has-no-choices',
noResults: 'has-no-results',
openState: 'is-open',
placeholder: 'select__placeholder',
selectedState: 'is-selected',
},
editItems: false,
itemSelectText: '',
noResultsText: this.settings.noResultsText,
removeItemButton: true,
removeItems: true,
renderSelectedChoices: 'always',
searchChoices: this.settings.search,
searchEnabled: this.settings.search,
});
this.addEventListeners();
}
addEventListeners(): void {
// Multiselect context
if (this.multiple) {
// Deselect a selected item when clicking on it.
this.input[0].addEventListener('choice', (event: CustomEvent) => {
const values: string[] | string = this.choices.getValue(true);
const value: string = event.detail.choice.value;
if (values?.includes(value)) {
setTimeout((): Choices => this.choices.removeActiveItemsByValue(value));
}
});
// Close dropdown when clicking on empty space between currently selected items in select list.
this.element.find('.select__inner > .select__list').on('click', (): void => {
this.choices.hideDropdown();
});
}
this.input[0].addEventListener('setValue', (event: CustomEvent): void => {
const values: string | string[] = event.detail;
if (typeof values !== 'undefined') {
this.setValue(event.detail);
}
});
this.input[0].addEventListener('hideDropdown', (): void => {
this.choices.hideDropdown();
// Clear search input when closing dropdown
if (this.settings.search) {
this.choices.clearInput();
}
});
this.input[0].addEventListener('showDropdown', (): void => {
// Focus search input when opening dropdown if search enabled.
if (this.settings.search) {
this.element.find('.select__dropdown .select__choices-input--cloned').trigger('focus');
}
this.choices.showDropdown();
});
}
}
<div class="textfield select is-invalid">
<label class="textfield__label select__label " for="select1">
Select label
</label>
<div class="textfield__inner">
<select name="select" id="select1" class="textfield__input select__input">
<option value="1">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Veritatis error velit quidem alias nihil mollitia temporibus, maxime dicta repellat. Perspiciatis veniam doloribus, quia corporis commodi sed omnis adipisci facilis nisi.
</option>
<option value="2">
Option 2
</option>
<option value="3">
Option 3
</option>
</select>
<svg class="icon select__icon" focusable="false">
<use href="../../inc/svg/global.4609ec92109fc41e7ad4764ef897ea8e.svg#"></use>
</svg>
</div>
<div class="textfield__error">
Please check your input
</div>
</div>
{% set input %}
<select
name="{{ data.name }}"
id="{{ data.id }}"
{% if 'select--multiple' in modifier %} multiple{% endif %}
{% if 'select--search' in modifier %} data-search="true"{% endif %}
class="textfield__input select__input"
{% if data.isDisabled %} disabled{% endif %}
{% if 'select--search' in modifier %} data-no-results-text="{{ data.noResultsText }}"{% endif %}
{% if 'select--multiple' in modifier %} data-remove-item-text="{{ data.removeItemText }}"{% endif %}
{{ data.attributes }}
>
{% if data.placeholder or data.hasPlaceholder %}
<option value="placeholder" disabled selected>
{% if data.placeholder %}
{{ data.placeholder }}
{% endif %}
</option>
{% endif %}
{% for option in data.options %}
<option
value="{{ option.value }}"
{% if option.isSelected %} selected {% endif %}
>
{{ option.name }}
</option>
{% endfor %}
</select>
{% include '@icon' with { class: 'select__icon', modifier: '', name: data.iconName } %}
{% endset %}
{% include '@textfield' with {
data: data,
input: input,
class: 'select ' ~ class,
modifier: modifier,
labelClass: 'select__label' ~ ' ' ~ labelClass
} %}
{
"language": "en-US",
"data": {
"label": "Select label",
"id": "select1",
"name": "select",
"options": [
{
"name": "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Veritatis error velit quidem alias nihil mollitia temporibus, maxime dicta repellat. Perspiciatis veniam doloribus, quia corporis commodi sed omnis adipisci facilis nisi.",
"value": "1"
},
{
"name": "Option 2",
"value": "2"
},
{
"name": "Option 3",
"value": "3"
}
],
"isInvalid": true,
"error": "Please check your input"
}
}
.select {
position: relative;
margin: 0;
cursor: pointer;
&.is-disabled {
cursor: default;
}
}
.select__icon {
position: absolute;
top: 50%;
right: 16px;
transform: translateY(-50%);
font-size: 24px;
pointer-events: none;
.select__container.is-open + & {
transform: translateY(-50%) rotate(180deg);
}
}
.select__inner {
padding-right: calc(24px + 4px);
min-height: 32px;
height: auto;
box-shadow: $elevation-01;
display: flex;
align-items: center;
.select__container.is-focused & {
border-color: $L-border-focus;
}
}
.select__dropdown {
visibility: hidden;
z-index: 2;
position: absolute;
top: 100%;
width: 100%;
background-color: $L-background-weak;
border: none;
overflow: hidden;
word-break: break-all; /* stylelint-disable-line plugin/no-unsupported-browser-features */
will-change: visibility;
border-radius: $border-radius-base;
box-shadow: $elevation-02;
.select--search & {
padding-top: 48px;
}
.select__container.is-flipped & {
top: auto;
bottom: 100%;
.select--search & {
padding-top: 0;
padding-bottom: 48px;
}
}
.select__container.is-open & {
visibility: visible;
}
}
.select__choices-input {
background: transparent;
border: none;
appearance: none;
height: 48px;
display: none;
width: 100% !important; /* overwrite plugin inline styles */
padding: 10px 16px;
z-index: -1;
pointer-events: none;
position: absolute;
top: 100%;
left: 0;
.select__container.is-flipped & {
top: auto;
bottom: 100%;
}
.select--multiple:not(.select--search) & {
color: transparent;
outline: none;
}
.select--search .select__container.is-open & {
pointer-events: auto;
}
&:not(select) {
display: block;
z-index: 3;
}
&::-ms-expand {
display: none;
}
}
.select__list {
.select__dropdown & {
padding: 8px 0;
max-height: 220px;
overflow-y: auto;
will-change: scroll-position;
.select--search & {
border-top: 1px solid $L-border;
}
.select__container.is-flipped & {
.select--search & {
border-top: 0;
border-bottom: 1px solid $L-border;
}
}
}
}
.select__list--single,
.select__list--multiple {
overflow: hidden;
white-space: nowrap;
padding: 4px 0;
width: 100%;
height: 100%;
}
.select__list--multiple {
overflow: visible;
white-space: normal;
padding: 10px 0 4px;
line-height: 0;
}
.select__item {
padding: 8px 16px;
color: $L-text-black;
font-size: $font-size-small;
line-height: $line-height-base;
.select__list--single & {
padding: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: $L-text;
}
.select__list--multiple & {
display: inline-block;
background-color: $L-background-medium;
font-size: 12px;
padding: 2px 24px 2px 8px;
margin-bottom: 6px;
max-width: 190px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
border-radius: $border-radius-base;
position: relative;
cursor: pointer;
}
&:not(:last-child) {
.select__list--multiple & {
margin-right: 8px;
}
}
&.select__item--highlighted {
background-color: $L-background-hover;
}
&.has-no-results {
cursor: default;
&:hover {
.select__list:not(.select__list--single) & {
color: $L-text;
background-color: transparent;
}
}
}
&[data-value='placeholder'] {
color: $L-text-medium;
font-size: $font-size-small;
letter-spacing: $letter-spacing-50;
.select__dropdown & {
display: none;
}
}
}
.select__remove {
appearance: none;
background: transparent;
border: 0;
padding: 0;
margin: 0;
display: inline-flex;
align-items: center;
justify-content: center;
position: absolute;
top: 0;
right: 0;
width: 24px;
height: 100%;
opacity: .8;
cursor: pointer;
.select__list--single & {
display: none;
}
}
.select__search-icon {
display: none;
font-size: 24px;
position: absolute;
top: 12px;
right: 16px;
z-index: map-get($zindex, 'default');
.select__container.is-flipped & {
top: auto;
bottom: 12px;
}
.select--search & {
display: inline-block;
}
}
.select__close-icon {
font-size: 15px;
pointer-events: none;
}
.select__option-span {
font-size: $font-size-tiny;
}
import Choices, { Choices as ChoicesLib } from 'choices.js';
import Component from '../../component/component';
import Icon from '../../icon/icon';
import '../textfield/textfield';
import './select.scss';
export interface ISelectSettings {
noResultsText: string;
search: boolean;
removeItemText: string;
}
export default class Select extends Component {
static initSelector: string = '.select';
public settings: ISelectSettings;
public input: JQuery;
public choices: Choices;
public multiple: boolean;
constructor(target: HTMLElement) {
super(target);
this.input = this.element.find('.select__input');
if (this.input.length) {
this.settings = $.extend({
multiselect: false,
noResultsText: 'No results found',
removeItemText: 'Remove item',
search: false,
}, this.input.data());
this.multiple = !!this.input.attr('multiple');
this.init();
}
}
setValue(value: string | string[]): void {
this.choices.setChoiceByValue(value);
}
init(): void {
this.choices = new Choices(this.input[0] as HTMLInputElement, {
callbackOnCreateTemplates: (template: ChoicesLib.Types.strToEl): Partial<ChoicesLib.Templates> => ({
dropdown: (classNames: ChoicesLib.ClassNames): HTMLElement => template(`
<div class="${classNames.list} ${classNames.listDropdown}" aria-expanded="false">
${Icon.render('search', '', 'select__search-icon')}
</div>
`) as HTMLElement,
item: (classNames: ChoicesLib.ClassNames, data: ChoicesLib.Choice): HTMLElement => template(`
<div class="${classNames.item}" data-item data-id="${data.id}" data-value="${data.value}" ${data.active ? 'aria-selected="true"' : ''} ${data.disabled ? 'aria-disabled="true"' : ''} data-deletable>
${data.label}
<button type="button" class="${classNames.button}" aria-label="${this.settings.removeItemText}" data-button>${Icon.render('close', '', 'select__close-icon')}</button>
</div>
`) as HTMLElement,
}),
classNames: {
activeState: 'is-active',
button: 'select__remove',
containerInner: 'textfield__input select__inner',
containerOuter: 'select__container',
disabledState: 'is-disabled',
flippedState: 'is-flipped',
focusState: 'is-focused',
group: 'select__group',
groupHeading: 'select__group-heading',
highlightedState: 'select__item--highlighted',
input: 'select__choices-input',
inputCloned: 'select__choices-input--cloned',
item: 'select__item',
itemChoice: 'select__item--choice',
itemDisabled: 'select__item--disabled',
itemSelectable: 'select__item--selectable',
list: 'select__list',
listDropdown: 'select__dropdown',
listItems: 'select__list--multiple',
listSingle: 'select__list--single',
loadingState: 'is-loading',
noChoices: 'has-no-choices',
noResults: 'has-no-results',
openState: 'is-open',
placeholder: 'select__placeholder',
selectedState: 'is-selected',
},
editItems: false,
itemSelectText: '',
noResultsText: this.settings.noResultsText,
removeItemButton: true,
removeItems: true,
renderSelectedChoices: 'always',
searchChoices: this.settings.search,
searchEnabled: this.settings.search,
});
this.addEventListeners();
}
addEventListeners(): void {
// Multiselect context
if (this.multiple) {
// Deselect a selected item when clicking on it.
this.input[0].addEventListener('choice', (event: CustomEvent) => {
const values: string[] | string = this.choices.getValue(true);
const value: string = event.detail.choice.value;
if (values?.includes(value)) {
setTimeout((): Choices => this.choices.removeActiveItemsByValue(value));
}
});
// Close dropdown when clicking on empty space between currently selected items in select list.
this.element.find('.select__inner > .select__list').on('click', (): void => {
this.choices.hideDropdown();
});
}
this.input[0].addEventListener('setValue', (event: CustomEvent): void => {
const values: string | string[] = event.detail;
if (typeof values !== 'undefined') {
this.setValue(event.detail);
}
});
this.input[0].addEventListener('hideDropdown', (): void => {
this.choices.hideDropdown();
// Clear search input when closing dropdown
if (this.settings.search) {
this.choices.clearInput();
}
});
this.input[0].addEventListener('showDropdown', (): void => {
// Focus search input when opening dropdown if search enabled.
if (this.settings.search) {
this.element.find('.select__dropdown .select__choices-input--cloned').trigger('focus');
}
this.choices.showDropdown();
});
}
}
<div class="textfield select is-disabled">
<label class="textfield__label select__label " for="select1">
Select label
</label>
<div class="textfield__inner">
<select name="select" id="select1" class="textfield__input select__input" disabled>
<option value="1">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Veritatis error velit quidem alias nihil mollitia temporibus, maxime dicta repellat. Perspiciatis veniam doloribus, quia corporis commodi sed omnis adipisci facilis nisi.
</option>
<option value="2">
Option 2
</option>
<option value="3">
Option 3
</option>
</select>
<svg class="icon select__icon" focusable="false">
<use href="../../inc/svg/global.4609ec92109fc41e7ad4764ef897ea8e.svg#"></use>
</svg>
</div>
</div>
{% set input %}
<select
name="{{ data.name }}"
id="{{ data.id }}"
{% if 'select--multiple' in modifier %} multiple{% endif %}
{% if 'select--search' in modifier %} data-search="true"{% endif %}
class="textfield__input select__input"
{% if data.isDisabled %} disabled{% endif %}
{% if 'select--search' in modifier %} data-no-results-text="{{ data.noResultsText }}"{% endif %}
{% if 'select--multiple' in modifier %} data-remove-item-text="{{ data.removeItemText }}"{% endif %}
{{ data.attributes }}
>
{% if data.placeholder or data.hasPlaceholder %}
<option value="placeholder" disabled selected>
{% if data.placeholder %}
{{ data.placeholder }}
{% endif %}
</option>
{% endif %}
{% for option in data.options %}
<option
value="{{ option.value }}"
{% if option.isSelected %} selected {% endif %}
>
{{ option.name }}
</option>
{% endfor %}
</select>
{% include '@icon' with { class: 'select__icon', modifier: '', name: data.iconName } %}
{% endset %}
{% include '@textfield' with {
data: data,
input: input,
class: 'select ' ~ class,
modifier: modifier,
labelClass: 'select__label' ~ ' ' ~ labelClass
} %}
{
"language": "en-US",
"data": {
"label": "Select label",
"id": "select1",
"name": "select",
"options": [
{
"name": "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Veritatis error velit quidem alias nihil mollitia temporibus, maxime dicta repellat. Perspiciatis veniam doloribus, quia corporis commodi sed omnis adipisci facilis nisi.",
"value": "1"
},
{
"name": "Option 2",
"value": "2"
},
{
"name": "Option 3",
"value": "3"
}
],
"isDisabled": true,
"value": "Tere"
}
}
.select {
position: relative;
margin: 0;
cursor: pointer;
&.is-disabled {
cursor: default;
}
}
.select__icon {
position: absolute;
top: 50%;
right: 16px;
transform: translateY(-50%);
font-size: 24px;
pointer-events: none;
.select__container.is-open + & {
transform: translateY(-50%) rotate(180deg);
}
}
.select__inner {
padding-right: calc(24px + 4px);
min-height: 32px;
height: auto;
box-shadow: $elevation-01;
display: flex;
align-items: center;
.select__container.is-focused & {
border-color: $L-border-focus;
}
}
.select__dropdown {
visibility: hidden;
z-index: 2;
position: absolute;
top: 100%;
width: 100%;
background-color: $L-background-weak;
border: none;
overflow: hidden;
word-break: break-all; /* stylelint-disable-line plugin/no-unsupported-browser-features */
will-change: visibility;
border-radius: $border-radius-base;
box-shadow: $elevation-02;
.select--search & {
padding-top: 48px;
}
.select__container.is-flipped & {
top: auto;
bottom: 100%;
.select--search & {
padding-top: 0;
padding-bottom: 48px;
}
}
.select__container.is-open & {
visibility: visible;
}
}
.select__choices-input {
background: transparent;
border: none;
appearance: none;
height: 48px;
display: none;
width: 100% !important; /* overwrite plugin inline styles */
padding: 10px 16px;
z-index: -1;
pointer-events: none;
position: absolute;
top: 100%;
left: 0;
.select__container.is-flipped & {
top: auto;
bottom: 100%;
}
.select--multiple:not(.select--search) & {
color: transparent;
outline: none;
}
.select--search .select__container.is-open & {
pointer-events: auto;
}
&:not(select) {
display: block;
z-index: 3;
}
&::-ms-expand {
display: none;
}
}
.select__list {
.select__dropdown & {
padding: 8px 0;
max-height: 220px;
overflow-y: auto;
will-change: scroll-position;
.select--search & {
border-top: 1px solid $L-border;
}
.select__container.is-flipped & {
.select--search & {
border-top: 0;
border-bottom: 1px solid $L-border;
}
}
}
}
.select__list--single,
.select__list--multiple {
overflow: hidden;
white-space: nowrap;
padding: 4px 0;
width: 100%;
height: 100%;
}
.select__list--multiple {
overflow: visible;
white-space: normal;
padding: 10px 0 4px;
line-height: 0;
}
.select__item {
padding: 8px 16px;
color: $L-text-black;
font-size: $font-size-small;
line-height: $line-height-base;
.select__list--single & {
padding: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: $L-text;
}
.select__list--multiple & {
display: inline-block;
background-color: $L-background-medium;
font-size: 12px;
padding: 2px 24px 2px 8px;
margin-bottom: 6px;
max-width: 190px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
border-radius: $border-radius-base;
position: relative;
cursor: pointer;
}
&:not(:last-child) {
.select__list--multiple & {
margin-right: 8px;
}
}
&.select__item--highlighted {
background-color: $L-background-hover;
}
&.has-no-results {
cursor: default;
&:hover {
.select__list:not(.select__list--single) & {
color: $L-text;
background-color: transparent;
}
}
}
&[data-value='placeholder'] {
color: $L-text-medium;
font-size: $font-size-small;
letter-spacing: $letter-spacing-50;
.select__dropdown & {
display: none;
}
}
}
.select__remove {
appearance: none;
background: transparent;
border: 0;
padding: 0;
margin: 0;
display: inline-flex;
align-items: center;
justify-content: center;
position: absolute;
top: 0;
right: 0;
width: 24px;
height: 100%;
opacity: .8;
cursor: pointer;
.select__list--single & {
display: none;
}
}
.select__search-icon {
display: none;
font-size: 24px;
position: absolute;
top: 12px;
right: 16px;
z-index: map-get($zindex, 'default');
.select__container.is-flipped & {
top: auto;
bottom: 12px;
}
.select--search & {
display: inline-block;
}
}
.select__close-icon {
font-size: 15px;
pointer-events: none;
}
.select__option-span {
font-size: $font-size-tiny;
}
import Choices, { Choices as ChoicesLib } from 'choices.js';
import Component from '../../component/component';
import Icon from '../../icon/icon';
import '../textfield/textfield';
import './select.scss';
export interface ISelectSettings {
noResultsText: string;
search: boolean;
removeItemText: string;
}
export default class Select extends Component {
static initSelector: string = '.select';
public settings: ISelectSettings;
public input: JQuery;
public choices: Choices;
public multiple: boolean;
constructor(target: HTMLElement) {
super(target);
this.input = this.element.find('.select__input');
if (this.input.length) {
this.settings = $.extend({
multiselect: false,
noResultsText: 'No results found',
removeItemText: 'Remove item',
search: false,
}, this.input.data());
this.multiple = !!this.input.attr('multiple');
this.init();
}
}
setValue(value: string | string[]): void {
this.choices.setChoiceByValue(value);
}
init(): void {
this.choices = new Choices(this.input[0] as HTMLInputElement, {
callbackOnCreateTemplates: (template: ChoicesLib.Types.strToEl): Partial<ChoicesLib.Templates> => ({
dropdown: (classNames: ChoicesLib.ClassNames): HTMLElement => template(`
<div class="${classNames.list} ${classNames.listDropdown}" aria-expanded="false">
${Icon.render('search', '', 'select__search-icon')}
</div>
`) as HTMLElement,
item: (classNames: ChoicesLib.ClassNames, data: ChoicesLib.Choice): HTMLElement => template(`
<div class="${classNames.item}" data-item data-id="${data.id}" data-value="${data.value}" ${data.active ? 'aria-selected="true"' : ''} ${data.disabled ? 'aria-disabled="true"' : ''} data-deletable>
${data.label}
<button type="button" class="${classNames.button}" aria-label="${this.settings.removeItemText}" data-button>${Icon.render('close', '', 'select__close-icon')}</button>
</div>
`) as HTMLElement,
}),
classNames: {
activeState: 'is-active',
button: 'select__remove',
containerInner: 'textfield__input select__inner',
containerOuter: 'select__container',
disabledState: 'is-disabled',
flippedState: 'is-flipped',
focusState: 'is-focused',
group: 'select__group',
groupHeading: 'select__group-heading',
highlightedState: 'select__item--highlighted',
input: 'select__choices-input',
inputCloned: 'select__choices-input--cloned',
item: 'select__item',
itemChoice: 'select__item--choice',
itemDisabled: 'select__item--disabled',
itemSelectable: 'select__item--selectable',
list: 'select__list',
listDropdown: 'select__dropdown',
listItems: 'select__list--multiple',
listSingle: 'select__list--single',
loadingState: 'is-loading',
noChoices: 'has-no-choices',
noResults: 'has-no-results',
openState: 'is-open',
placeholder: 'select__placeholder',
selectedState: 'is-selected',
},
editItems: false,
itemSelectText: '',
noResultsText: this.settings.noResultsText,
removeItemButton: true,
removeItems: true,
renderSelectedChoices: 'always',
searchChoices: this.settings.search,
searchEnabled: this.settings.search,
});
this.addEventListeners();
}
addEventListeners(): void {
// Multiselect context
if (this.multiple) {
// Deselect a selected item when clicking on it.
this.input[0].addEventListener('choice', (event: CustomEvent) => {
const values: string[] | string = this.choices.getValue(true);
const value: string = event.detail.choice.value;
if (values?.includes(value)) {
setTimeout((): Choices => this.choices.removeActiveItemsByValue(value));
}
});
// Close dropdown when clicking on empty space between currently selected items in select list.
this.element.find('.select__inner > .select__list').on('click', (): void => {
this.choices.hideDropdown();
});
}
this.input[0].addEventListener('setValue', (event: CustomEvent): void => {
const values: string | string[] = event.detail;
if (typeof values !== 'undefined') {
this.setValue(event.detail);
}
});
this.input[0].addEventListener('hideDropdown', (): void => {
this.choices.hideDropdown();
// Clear search input when closing dropdown
if (this.settings.search) {
this.choices.clearInput();
}
});
this.input[0].addEventListener('showDropdown', (): void => {
// Focus search input when opening dropdown if search enabled.
if (this.settings.search) {
this.element.find('.select__dropdown .select__choices-input--cloned').trigger('focus');
}
this.choices.showDropdown();
});
}
}