<div class="textfield select ">
<div class="textfield__inner">
<select name="select" id="select1" class="textfield__input select__input" data-text="Select label">
<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" selected>Option 2</option>
<option value="3">Option 3</option>
</select>
<svg class="icon select__icon">
<use xlink:href="../../inc/svg/global.49d3f64de1a3f5ccf16c763316702027.svg#chevron-bottom"></use>
</svg>
<label class="textfield__label text-button select__label" for="select1">
Select label
</label>
</div>
</div>
{% set input %}
<select
name="{{ data.name }}"
id="{{ data.id }}"
class="textfield__input select__input"
data-text="{{ data.label }}"
{% if data.isDisabled %} disabled{% endif %}
{{ data.attributes }}
>
{% 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', name: 'chevron-bottom' } %}
{% endset %}
{% include '@textfield' with {
data: data,
input: input,
class: 'select ' ~ class,
modifier: modifier,
labelClass: 'select__label'
} %}
{
"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",
"isSelected": true
},
{
"name": "Option 3",
"value": "3"
}
]
}
}
.select {
position: relative;
margin: 0;
cursor: pointer;
z-index: map-get($zindex, 'default');
&.is-disabled {
opacity: .3;
cursor: default;
}
}
.select__input {
display: none;
}
.select__icon {
position: absolute;
font-size: 24px;
top: 7px;
right: 15px;
pointer-events: none;
transition: $transition-duration $transition-easing;
transition-property: transform, fill;
fill: var(--color-text);
.select__container.is-open + & {
transform: rotate(180deg);
}
.select__container.is-focused + & {
outline: none;
fill: rgba(var(--color-text--rgb), .5);
}
@include bp(sm-min) {
right: 7px;
top: 17px;
}
}
.select__inner {
display: none;
width: 100%;
&:hover {
@media (hover: hover) {
background-color: transparent;
border-color: var(--color-brand);
color: var(--color-text);
}
&:disabled {
@media (hover: hover) {
background-color: transparent;
border-color: var(--color-brand);
color: var(--color-text);
}
}
}
.select__container.is-focused & {
outline: none;
background-color: rgba(var(--color-brand--rgb), .02);
border-color: var(--color-brand);
color: rgba(var(--color-text--rgb), .5);
&:disabled {
background-color: transparent;
border-color: var(--color-brand);
color: var(--color-text);
}
}
.select__container & {
display: block;
}
}
.select__dropdown {
visibility: hidden;
z-index: 2;
position: absolute;
top: 100%;
width: 100%;
border: none;
overflow: hidden;
word-break: break-all; /* stylelint-disable-line plugin/no-unsupported-browser-features */
will-change: visibility; /* stylelint-disable-line plugin/no-unsupported-browser-features */
background-color: var(--color-background);
.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;
border-bottom: 1px solid var(--color-brand);
}
.select__container.is-open.is-flipped & {
border-bottom: none;
border-top: 1px solid var(--color-brand);
}
}
.select__list {
.select__dropdown & {
overflow-y: auto;
will-change: scroll-position; /* stylelint-disable-line plugin/no-unsupported-browser-features */
}
}
.select__list--single {
display: flex;
align-items: center;
flex-direction: row;
overflow: hidden;
white-space: nowrap;
padding: 10px 0;
width: 100%;
height: 100%;
}
.select__item {
display: flex;
align-items: center;
padding: 8px 16px;
width: 100%;
flex-shrink: 1;
@include bp(sm-min) {
padding: 15px 24px;
}
.select__list--single & {
display: block;
padding: 0 40px 0 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
pointer-events: none;
&.select__item-label {
width: auto;
flex-shrink: 0;
white-space: pre;
padding-right: 0;
}
}
&.select__item--choice {
transition: $transition-duration $transition-easing;
transition-property: background-color, color;
&.is-selected {
background-color: rgba(var(--color-brand--rgb), .05);
}
&.select__item--highlighted {
background-color: var(--color-brand);
color: var(--color-text-inverse);
}
}
}
.select__remove {
display: none;
}
.select__label {
display: none;
}
import Choices, {Choices as ChoicesLib} from 'choices.js';
import Component from '@component';
import '@textfield';
import './select.scss';
export default class Select extends Component {
static initSelector: string = '.select';
public input: JQuery;
public choices: Choices;
public isOpen: boolean;
constructor(target: HTMLElement) {
super(target);
this.input = this.element.find('.select__input');
this.isOpen = false;
this.init();
}
init(): void {
this.choices = new Choices(this.input[0] as HTMLInputElement, {
callbackOnCreateTemplates(template: ChoicesLib.Types.strToEl): Partial<ChoicesLib.Templates> { // eslint-disable-line prefer-arrow/prefer-arrow-functions
return {
dropdown: (classNames: ChoicesLib.ClassNames): HTMLElement => template(`
<div class="${classNames.list} ${classNames.listDropdown}" aria-expanded="false"></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__input--cloned',
item: 'select__item text-button',
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',
},
itemSelectText: '',
removeItems: false,
renderSelectedChoices: 'always',
searchChoices: false,
searchEnabled: false,
});
this.addEventListeners();
requestAnimationFrame(() => this.addLabel());
}
addEventListeners(): void {
this.input.get(0).addEventListener('choice', this.updateItem.bind(this));
this.element.find('.select__inner > .select__list').on('click', (): void => {
if (this.isOpen) {
this.choices.hideDropdown();
}
});
this.input.get(0).addEventListener('hideDropdown', (): void => {
this.isOpen = false;
});
this.input.get(0).addEventListener('showDropdown', (): void => {
this.element.find('.select__dropdown .choices__item').first().focus();
this.isOpen = true;
});
}
addLabel(): void {
const span: HTMLSpanElement = document.createElement('span');
const list: HTMLElement = this.element.find('.select__list')[0];
if (list.childElementCount === 1) {
span.innerHTML = this.input.attr('data-text') + ': ';
list.children[0].classList.forEach((_class: string) => span.classList.add(_class));
span.classList.add('select__item-label');
list.insertBefore(span, list.childNodes[0]);
}
}
updateItem(): void {
requestAnimationFrame(() => this.addLabel());
}
}
<div class="textfield select is-invalid">
<div class="textfield__inner">
<select name="select" id="select1" class="textfield__input select__input" data-text="Select label">
<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" selected>Option 2</option>
<option value="3">Option 3</option>
</select>
<svg class="icon select__icon">
<use xlink:href="../../inc/svg/global.49d3f64de1a3f5ccf16c763316702027.svg#chevron-bottom"></use>
</svg>
<label class="textfield__label text-button select__label" for="select1">
Select label
</label>
</div>
<div class="textfield__error text-small">
Please check your input
</div>
</div>
{% set input %}
<select
name="{{ data.name }}"
id="{{ data.id }}"
class="textfield__input select__input"
data-text="{{ data.label }}"
{% if data.isDisabled %} disabled{% endif %}
{{ data.attributes }}
>
{% 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', name: 'chevron-bottom' } %}
{% endset %}
{% include '@textfield' with {
data: data,
input: input,
class: 'select ' ~ class,
modifier: modifier,
labelClass: 'select__label'
} %}
{
"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",
"isSelected": true
},
{
"name": "Option 3",
"value": "3"
}
],
"isInvalid": true,
"error": "Please check your input"
}
}
.select {
position: relative;
margin: 0;
cursor: pointer;
z-index: map-get($zindex, 'default');
&.is-disabled {
opacity: .3;
cursor: default;
}
}
.select__input {
display: none;
}
.select__icon {
position: absolute;
font-size: 24px;
top: 7px;
right: 15px;
pointer-events: none;
transition: $transition-duration $transition-easing;
transition-property: transform, fill;
fill: var(--color-text);
.select__container.is-open + & {
transform: rotate(180deg);
}
.select__container.is-focused + & {
outline: none;
fill: rgba(var(--color-text--rgb), .5);
}
@include bp(sm-min) {
right: 7px;
top: 17px;
}
}
.select__inner {
display: none;
width: 100%;
&:hover {
@media (hover: hover) {
background-color: transparent;
border-color: var(--color-brand);
color: var(--color-text);
}
&:disabled {
@media (hover: hover) {
background-color: transparent;
border-color: var(--color-brand);
color: var(--color-text);
}
}
}
.select__container.is-focused & {
outline: none;
background-color: rgba(var(--color-brand--rgb), .02);
border-color: var(--color-brand);
color: rgba(var(--color-text--rgb), .5);
&:disabled {
background-color: transparent;
border-color: var(--color-brand);
color: var(--color-text);
}
}
.select__container & {
display: block;
}
}
.select__dropdown {
visibility: hidden;
z-index: 2;
position: absolute;
top: 100%;
width: 100%;
border: none;
overflow: hidden;
word-break: break-all; /* stylelint-disable-line plugin/no-unsupported-browser-features */
will-change: visibility; /* stylelint-disable-line plugin/no-unsupported-browser-features */
background-color: var(--color-background);
.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;
border-bottom: 1px solid var(--color-brand);
}
.select__container.is-open.is-flipped & {
border-bottom: none;
border-top: 1px solid var(--color-brand);
}
}
.select__list {
.select__dropdown & {
overflow-y: auto;
will-change: scroll-position; /* stylelint-disable-line plugin/no-unsupported-browser-features */
}
}
.select__list--single {
display: flex;
align-items: center;
flex-direction: row;
overflow: hidden;
white-space: nowrap;
padding: 10px 0;
width: 100%;
height: 100%;
}
.select__item {
display: flex;
align-items: center;
padding: 8px 16px;
width: 100%;
flex-shrink: 1;
@include bp(sm-min) {
padding: 15px 24px;
}
.select__list--single & {
display: block;
padding: 0 40px 0 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
pointer-events: none;
&.select__item-label {
width: auto;
flex-shrink: 0;
white-space: pre;
padding-right: 0;
}
}
&.select__item--choice {
transition: $transition-duration $transition-easing;
transition-property: background-color, color;
&.is-selected {
background-color: rgba(var(--color-brand--rgb), .05);
}
&.select__item--highlighted {
background-color: var(--color-brand);
color: var(--color-text-inverse);
}
}
}
.select__remove {
display: none;
}
.select__label {
display: none;
}
import Choices, {Choices as ChoicesLib} from 'choices.js';
import Component from '@component';
import '@textfield';
import './select.scss';
export default class Select extends Component {
static initSelector: string = '.select';
public input: JQuery;
public choices: Choices;
public isOpen: boolean;
constructor(target: HTMLElement) {
super(target);
this.input = this.element.find('.select__input');
this.isOpen = false;
this.init();
}
init(): void {
this.choices = new Choices(this.input[0] as HTMLInputElement, {
callbackOnCreateTemplates(template: ChoicesLib.Types.strToEl): Partial<ChoicesLib.Templates> { // eslint-disable-line prefer-arrow/prefer-arrow-functions
return {
dropdown: (classNames: ChoicesLib.ClassNames): HTMLElement => template(`
<div class="${classNames.list} ${classNames.listDropdown}" aria-expanded="false"></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__input--cloned',
item: 'select__item text-button',
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',
},
itemSelectText: '',
removeItems: false,
renderSelectedChoices: 'always',
searchChoices: false,
searchEnabled: false,
});
this.addEventListeners();
requestAnimationFrame(() => this.addLabel());
}
addEventListeners(): void {
this.input.get(0).addEventListener('choice', this.updateItem.bind(this));
this.element.find('.select__inner > .select__list').on('click', (): void => {
if (this.isOpen) {
this.choices.hideDropdown();
}
});
this.input.get(0).addEventListener('hideDropdown', (): void => {
this.isOpen = false;
});
this.input.get(0).addEventListener('showDropdown', (): void => {
this.element.find('.select__dropdown .choices__item').first().focus();
this.isOpen = true;
});
}
addLabel(): void {
const span: HTMLSpanElement = document.createElement('span');
const list: HTMLElement = this.element.find('.select__list')[0];
if (list.childElementCount === 1) {
span.innerHTML = this.input.attr('data-text') + ': ';
list.children[0].classList.forEach((_class: string) => span.classList.add(_class));
span.classList.add('select__item-label');
list.insertBefore(span, list.childNodes[0]);
}
}
updateItem(): void {
requestAnimationFrame(() => this.addLabel());
}
}
<div class="textfield select is-dirty is-disabled">
<div class="textfield__inner">
<select name="select" id="select1" class="textfield__input select__input" data-text="Select label" 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" selected>Option 2</option>
<option value="3">Option 3</option>
</select>
<svg class="icon select__icon">
<use xlink:href="../../inc/svg/global.49d3f64de1a3f5ccf16c763316702027.svg#chevron-bottom"></use>
</svg>
<label class="textfield__label text-button select__label" for="select1">
Select label
</label>
</div>
</div>
{% set input %}
<select
name="{{ data.name }}"
id="{{ data.id }}"
class="textfield__input select__input"
data-text="{{ data.label }}"
{% if data.isDisabled %} disabled{% endif %}
{{ data.attributes }}
>
{% 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', name: 'chevron-bottom' } %}
{% endset %}
{% include '@textfield' with {
data: data,
input: input,
class: 'select ' ~ class,
modifier: modifier,
labelClass: 'select__label'
} %}
{
"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",
"isSelected": true
},
{
"name": "Option 3",
"value": "3"
}
],
"isDisabled": true,
"value": "Tere"
}
}
.select {
position: relative;
margin: 0;
cursor: pointer;
z-index: map-get($zindex, 'default');
&.is-disabled {
opacity: .3;
cursor: default;
}
}
.select__input {
display: none;
}
.select__icon {
position: absolute;
font-size: 24px;
top: 7px;
right: 15px;
pointer-events: none;
transition: $transition-duration $transition-easing;
transition-property: transform, fill;
fill: var(--color-text);
.select__container.is-open + & {
transform: rotate(180deg);
}
.select__container.is-focused + & {
outline: none;
fill: rgba(var(--color-text--rgb), .5);
}
@include bp(sm-min) {
right: 7px;
top: 17px;
}
}
.select__inner {
display: none;
width: 100%;
&:hover {
@media (hover: hover) {
background-color: transparent;
border-color: var(--color-brand);
color: var(--color-text);
}
&:disabled {
@media (hover: hover) {
background-color: transparent;
border-color: var(--color-brand);
color: var(--color-text);
}
}
}
.select__container.is-focused & {
outline: none;
background-color: rgba(var(--color-brand--rgb), .02);
border-color: var(--color-brand);
color: rgba(var(--color-text--rgb), .5);
&:disabled {
background-color: transparent;
border-color: var(--color-brand);
color: var(--color-text);
}
}
.select__container & {
display: block;
}
}
.select__dropdown {
visibility: hidden;
z-index: 2;
position: absolute;
top: 100%;
width: 100%;
border: none;
overflow: hidden;
word-break: break-all; /* stylelint-disable-line plugin/no-unsupported-browser-features */
will-change: visibility; /* stylelint-disable-line plugin/no-unsupported-browser-features */
background-color: var(--color-background);
.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;
border-bottom: 1px solid var(--color-brand);
}
.select__container.is-open.is-flipped & {
border-bottom: none;
border-top: 1px solid var(--color-brand);
}
}
.select__list {
.select__dropdown & {
overflow-y: auto;
will-change: scroll-position; /* stylelint-disable-line plugin/no-unsupported-browser-features */
}
}
.select__list--single {
display: flex;
align-items: center;
flex-direction: row;
overflow: hidden;
white-space: nowrap;
padding: 10px 0;
width: 100%;
height: 100%;
}
.select__item {
display: flex;
align-items: center;
padding: 8px 16px;
width: 100%;
flex-shrink: 1;
@include bp(sm-min) {
padding: 15px 24px;
}
.select__list--single & {
display: block;
padding: 0 40px 0 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
pointer-events: none;
&.select__item-label {
width: auto;
flex-shrink: 0;
white-space: pre;
padding-right: 0;
}
}
&.select__item--choice {
transition: $transition-duration $transition-easing;
transition-property: background-color, color;
&.is-selected {
background-color: rgba(var(--color-brand--rgb), .05);
}
&.select__item--highlighted {
background-color: var(--color-brand);
color: var(--color-text-inverse);
}
}
}
.select__remove {
display: none;
}
.select__label {
display: none;
}
import Choices, {Choices as ChoicesLib} from 'choices.js';
import Component from '@component';
import '@textfield';
import './select.scss';
export default class Select extends Component {
static initSelector: string = '.select';
public input: JQuery;
public choices: Choices;
public isOpen: boolean;
constructor(target: HTMLElement) {
super(target);
this.input = this.element.find('.select__input');
this.isOpen = false;
this.init();
}
init(): void {
this.choices = new Choices(this.input[0] as HTMLInputElement, {
callbackOnCreateTemplates(template: ChoicesLib.Types.strToEl): Partial<ChoicesLib.Templates> { // eslint-disable-line prefer-arrow/prefer-arrow-functions
return {
dropdown: (classNames: ChoicesLib.ClassNames): HTMLElement => template(`
<div class="${classNames.list} ${classNames.listDropdown}" aria-expanded="false"></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__input--cloned',
item: 'select__item text-button',
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',
},
itemSelectText: '',
removeItems: false,
renderSelectedChoices: 'always',
searchChoices: false,
searchEnabled: false,
});
this.addEventListeners();
requestAnimationFrame(() => this.addLabel());
}
addEventListeners(): void {
this.input.get(0).addEventListener('choice', this.updateItem.bind(this));
this.element.find('.select__inner > .select__list').on('click', (): void => {
if (this.isOpen) {
this.choices.hideDropdown();
}
});
this.input.get(0).addEventListener('hideDropdown', (): void => {
this.isOpen = false;
});
this.input.get(0).addEventListener('showDropdown', (): void => {
this.element.find('.select__dropdown .choices__item').first().focus();
this.isOpen = true;
});
}
addLabel(): void {
const span: HTMLSpanElement = document.createElement('span');
const list: HTMLElement = this.element.find('.select__list')[0];
if (list.childElementCount === 1) {
span.innerHTML = this.input.attr('data-text') + ': ';
list.children[0].classList.forEach((_class: string) => span.classList.add(_class));
span.classList.add('select__item-label');
list.insertBefore(span, list.childNodes[0]);
}
}
updateItem(): void {
requestAnimationFrame(() => this.addLabel());
}
}