import { h } from '@ledge/jsx';
import { NgInputController, NgInputOptions } from './shared';
import { NgAttributes } from '../attributes';
import { NgService } from '../service';
class SelectController extends NgInputController {
public static readonly SinglePlaceholder = '----Select One----';
public static readonly MultiplePlaceholder = '----Select All That Apply----';
public static IsMultiple($attrs: NgAttributes) {
return $attrs.hasOwnProperty('multiple') || $attrs.type === 'multiple';
}
public static GetPlaceholder($attrs: NgAttributes) {
return $attrs.placeholder ||
SelectController.IsMultiple($attrs)
? SelectController.MultiplePlaceholder
: SelectController.SinglePlaceholder;
}
protected isListOpen = false;
protected list!: any[];
protected searchList!: any[];
private _text!: string;
private _value!: string;
public get text() {
if (typeof this._text !== 'string') {
const { text = 'Text' } = this.$attrs;
this._text = text;
}
return this._text;
}
public get value() {
if (typeof this._value !== 'string') {
const { value = 'Value' } = this.$attrs;
this._value = value;
}
return this._value;
}
private destroyCurrentWatcher!: () => void;
private get isMultiple() {
return SelectController.IsMultiple(this.$attrs);
}
public $onInit() {
if (this.isMobile) {
this.searchList = this.list;
return;
}
const container = this.$element.querySelector('.select-container') as HTMLDivElement;
const dropdown = this.$element.querySelector('.select-dropdown') as HTMLDivElement;
const dropdownlist = this.$element.querySelector('.select-dropdown-list') as HTMLDivElement;
const input = this.$element.querySelector('input') as HTMLInputElement;
const updateSearchList = () => {
if (input.value) {
const inputValueSplit = input.value.split('');
this.searchList = this.getSearchList(this.list)
.filter(x => typeof x[this.text] === 'string')
.filter(x => {
const xText = x[this.text].toLowerCase() as string;
const xIndices = inputValueSplit.map(y => xText.indexOf(y));
if (xIndices.some(y => y === -1)) {
return false;
}
const xIndicesUnique = [];
for (const index of xIndices) {
if (xIndicesUnique.indexOf(index) === -1) {
xIndicesUnique.push(index);
} else {
const lastIndex = xIndicesUnique[xIndicesUnique.lastIndexOf(index)];
const nextIndex = xText.indexOf(xText.charAt(index), lastIndex + 1);
if (nextIndex !== -1 && xIndicesUnique.indexOf(nextIndex) === -1) {
xIndicesUnique.push(nextIndex);
}
}
}
return xIndicesUnique.length === xIndices.length;
})
.sort((x, y) => {
const xText = x[this.text].toLowerCase();
const yText = y[this.text].toLowerCase();
return (
inputValueSplit.reduce((a, b) => a + xText.indexOf(b), 0) -
inputValueSplit.reduce((a, b) => a + yText.indexOf(b), 0)
);
});
} else {
this.searchList = window.angular.copy(this.list);
}
this.$scope.$applyAsync();
};
input.oninput = () => {
updateSearchList();
};
input.onblur = e => {
if (e.relatedTarget !== container) {
input.hidden = true;
dropdownlist.hidden = true;
dropdown.classList.add('border-bottom-0');
}
};
container.onclick = ({ target }) => {
if (target instanceof HTMLSelectElement) {
return;
}
if (target instanceof HTMLButtonElement) {
input.hidden = false;
}
input.hidden = (
!this.isMultiple ||
target instanceof HTMLDivElement &&
(
target.classList.contains('placeholder') ||
target.classList.contains('select-list')
)
) && !input.hidden;
if (input.hidden) {
dropdown.classList.add('border-bottom-0');
} else {
dropdown.classList.remove('border-bottom-0');
input.focus();
}
dropdownlist.hidden = input.hidden;
};
dropdownlist.onclick = e => {
let { target } = e as unknown as { target: HTMLElement };
if (target.nodeName === '#text') {
target = target.parentElement as HTMLElement;
}
const targetIsItem =
target instanceof HTMLDivElement &&
target.classList.contains('select-item') &&
target.parentElement instanceof HTMLDivElement &&
target.parentElement.classList.contains('select-dropdown-list');
if (targetIsItem) {
input.value = '';
this.select(target.dataset.value);
}
};
this.$scope.$watchCollection(
() => this.list,
_ => updateSearchList(),
);
}
public $onDestroy() {
if (typeof this.destroyCurrentWatcher === 'function') {
this.destroyCurrentWatcher();
}
}
public getDisplayText(value: any) {
if (Array.isArray(value)) {
return;
}
if (value == null) {
return SelectController.GetPlaceholder(this.$attrs);
}
// tslint:disable-next-line:triple-equals
const [item] = this.list.filter(x => x[this.value] == value);
return item == null ? this.clear() : item[this.text];
}
public remove(item: any) {
// tslint:disable-next-line:triple-equals
this.ngModel = this.ngModel.filter((x: any) => x != item);
this.searchList = this.getSearchList(this.list);
}
public clear() {
if (this.isMultiple ? Array.isArray(this.ngModel) && this.ngModel.length > 0 : this.ngModel !== undefined) {
this.ngModel = this.isMultiple ? [] : undefined;
}
this.searchList = this.getSearchList(this.list);
}
public select(value: any) {
if (this.isMultiple) {
this.ngModel = Array.isArray(this.ngModel)
? this.ngModel.indexOf(value) !== -1
? this.ngModel
: this.ngModel.concat(value)
: [value];
} else {
this.ngModel = value;
}
this.searchList = this.getSearchList(this.list);
this.$scope.$applyAsync();
}
private getSearchList(list: any[]) {
// tslint:disable:triple-equals
return Array.isArray(this.ngModel)
? list.filter(x => this.ngModel.every((y: any) => x[this.value] != y))
: this.ngModel == null
? window.angular.copy(list)
: list.filter(x => x[this.value] != this.ngModel);
// tslint:enable:triple-equals
}
}
export const selectList: NgInputOptions = {
type: 'input',
render() {
const select = ;
const isMultiple = SelectController.IsMultiple(this.$attrs);
if (isMultiple) {
select.setAttribute('multiple', 'true');
} else {
const placeholder = as HTMLOptionElement;
placeholder.setAttribute('placeholder', 'true');
placeholder.text = SelectController.GetPlaceholder(this.$attrs);
placeholder.value = '';
select.appendChild(placeholder);
}
if (NgService.IsMobile()) {
select.classList.remove('d-none');
select.setAttribute(
'ng-options',
'item[\'{{$ctrl.value}}\'] as item[\'{{$ctrl.text}}\'] for item in $ctrl.searchList track by $index',
);
return select;
}
const innerlist =
;
const inner = {select}{innerlist}
;
const selected = ;
const btn =
;
if (isMultiple) {
const sbtn = btn.cloneNode() as HTMLButtonElement;
sbtn.setAttribute('ng-attr-aria-label', 'Remove item: \'{{$ctrl.getDisplayText(item)}}\'');
sbtn.setAttribute('ng-click', '$ctrl.remove(item)');
selected.setAttribute('ng-repeat', 'item in $ctrl.ngModel track by $index');
selected.setAttribute('aria-selected', 'true');
selected.innerHTML = `{{$ctrl.getDisplayText(item)}}${sbtn.outerHTML}`;
const placeholder = ;
placeholder.innerText = SelectController.GetPlaceholder(this.$attrs);
innerlist.appendChild(placeholder);
} else {
selected.setAttribute('ng-class', '{ \'placeholder\': $ctrl.ngModel == null }');
selected.innerText = '{{$ctrl.getDisplayText($ctrl.ngModel)}}';
}
const item = ;
item.innerText = '{{item[$ctrl.text]}}';
const list = ;
const input = ;
const type = `select-${isMultiple ? 'multiple' : 'one'}`;
const container =
;
innerlist.appendChild(selected);
innerlist.appendChild(btn);
list.appendChild(item);
return container;
},
controller: SelectController,
bindings: {
list: '<',
},
};
const stylesheet = document.createElement('style');
export function addSelectListStylesheet() {
if (stylesheet.parentElement === document.head) {
return;
}
stylesheet.textContent = `
.select-container {
border: 1px solid #ced4da;
cursor: pointer;
position: relative;
}
.select-container:last-child {
margin-bottom: 0;
}
.select-container.is-disabled .select-input {
background-color: #eaeaea;
cursor: not-allowed;
user-select: none;
}
.select-container.is-disabled .select-item {
cursor: not-allowed;
}
.select-container[data-type='select-one']:after {
content: '';
height: 0;
width: 0;
border-style: solid;
border-color: #333 transparent transparent transparent;
border-width: 5px;
position: absolute;
right: 11.5px;
top: 50%;
margin-top: -2.5px;
pointer-events: none;
}
.select-container[data-type='select-one']:after.is-open {
border-color: transparent transparent #333 transparent;
margin-top: -7.5px;
}
.select-container > .select-inner-container {
display: inline-block;
vertical-align: top;
width: 100%;
border-radius: 0.23rem;
overflow: hidden;
}
.select-container .select-input {
display: block;
width: 100%;
padding: 10px;
margin: 0;
border: 0;
border-bottom: 1px solid #ced4da;
border-top: 1px solid #ced4da;
border-radius: 0;
}
.select-container .select-button {
background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==);
border: 0;
background-color: transparent;
background-repeat: no-repeat;
background-position: center;
cursor: pointer;
display: inline-flex;
position: relative;
padding: 0;
background-size: 0.75rem;
height: 1rem;
color: #000;
width: 0.75rem;
opacity: 0.5;
}
.select-container .select-button:hover,
.select-container .select-button:focus {
opacity: 1;
}
.select-container .select-button:focus {
outline: none;
}
.select-container .select-item.placeholder {
color: gray;
}
.select-container .select-item:hover > .select-button {
opacity: 1;
}
.select-container .select-list,
.select-container .select-dropdown {
display: flex;
align-items: center;
}
.select-container .select-list.single,
.select-container .single.select-dropdown {
padding: 0.275rem;
}
.select-container .select-list.single > .select-item,
.select-container .single.select-dropdown > .select-item {
padding-left: 0.23rem;
width: 100%;
}
.select-container .select-list.single > .select-button,
.select-container .single.select-dropdown > .select-button {
right: 1.5rem;
}
.select-container .select-list.multiple,
.select-container .multiple.select-dropdown {
flex-wrap: wrap;
padding: 0.275rem;
}
.select-container .select-list.multiple > .select-item,
.select-container .multiple.select-dropdown > .select-item {
border-radius: 1rem;
background-color: darkblue;
color: #fff;
display: inline-flex;
align-items: center;
word-break: break-all;
padding: 0 0.75rem;
margin: 0 0.1rem;
opacity: 1;
width: max-content;
}
.select-container .select-list.multiple > .select-item.is-highlighted,
.select-container .multiple.select-dropdown > .select-item.is-highlighted,
.select-container .select-list.multiple > .select-item:hover,
.select-container .multiple.select-dropdown > .select-item:hover {
background-color: blue;
}
.select-container .select-list.multiple > .select-item.placeholder,
.select-container .multiple.select-dropdown > .select-item.placeholder {
color: gray;
background-color: inherit;
border: none;
padding: 0;
width: 100%;
}
.is-disabled .select-container .select-list.multiple > .select-item,
.is-disabled .select-container .multiple.select-dropdown > .select-item {
background-color: #aaa;
border: 1px solid #919191;
}
.select-container .select-list.multiple > .select-item > .select-button,
.select-container .multiple.select-dropdown > .select-item > .select-button {
margin-left: 0.5rem;
align-self: center;
opacity: 0.75;
background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjRkZGIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==);
}
.select-container .select-list.multiple > .select-button,
.select-container .multiple.select-dropdown > .select-button {
position: absolute;
right: 1rem;
}
.select-container .select-dropdown {
z-index: 1;
position: absolute;
width: 100%;
background-color: #fff;
border-bottom: 1px solid #ced4da;
border-left: 1px solid #ced4da;
border-right: 1px solid #ced4da;
margin-top: -1px;
border-bottom-left-radius: 0.23rem;
border-bottom-right-radius: 0.23rem;
overflow: hidden;
word-break: break-all;
display: block;
}
.select-container .select-dropdown > .select-dropdown-list {
flex-direction: column;
position: relative;
max-height: 300px;
overflow: auto;
-webkit-overflow-scrolling: touch;
will-change: scroll-position;
}
.select-container .select-dropdown > .select-dropdown-list > .select-item {
position: relative;
padding: 10px;
width: 100%;
}
@media (min-width: 640px) {
.select-container .select-dropdown > .select-dropdown-list > .select-item:after {
content: attr(data-select-text);
opacity: 0;
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
}
}
.select-container .select-dropdown > .select-dropdown-list > .select-item.is-highlighted,
.select-container .select-dropdown > .select-dropdown-list > .select-item:hover {
background-color: #f2f2f2;
}
.select-container .select-dropdown > .select-dropdown-list > .select-item.is-highlighted:after,
.select-container .select-dropdown > .select-dropdown-list > .select-item:hover:after {
opacity: 0.5;
}
`;
document.head.appendChild(stylesheet);
}