60 lines
2.1 KiB
TypeScript
60 lines
2.1 KiB
TypeScript
import Vue, {VNodeDirective} from 'vue';
|
|
//tslint:disable:strict-boolean-expressions
|
|
type Option = { value: string | null, disabled: boolean, text: string, label: string, options: Option[]} | string | number;
|
|
|
|
function rebuild(e: HTMLElement, binding: VNodeDirective): void {
|
|
const el = <HTMLSelectElement>e;
|
|
if(binding.oldValue === binding.value) return;
|
|
if(!binding.value) console.error('Must provide a value');
|
|
const value = <Option[]>binding.value;
|
|
|
|
function _isObject(val: any): val is object { //tslint:disable-line:no-any
|
|
return val !== null && typeof val === 'object';
|
|
}
|
|
|
|
function clearOptions(): void {
|
|
let i = el.options.length;
|
|
while(i--) {
|
|
const opt = el.options[i];
|
|
const parent = opt.parentNode!;
|
|
if(parent === el) parent.removeChild(opt);
|
|
else {
|
|
el.removeChild(parent);
|
|
i = el.options.length;
|
|
}
|
|
}
|
|
}
|
|
|
|
function buildOptions(parent: HTMLElement, options: Option[]): void {
|
|
let newEl: (HTMLOptionElement & {'_value'?: string | null});
|
|
for(let i = 0, l = options.length; i < l; i++) {
|
|
const op = options[i];
|
|
if(!_isObject(op) || !op.options) {
|
|
newEl = document.createElement('option');
|
|
if(typeof op === 'string' || typeof op === 'number')
|
|
newEl.text = newEl.value = op as string;
|
|
else {
|
|
if(op.value !== null && !_isObject(op.value))
|
|
newEl.value = op.value;
|
|
newEl['_value'] = op.value;
|
|
newEl.text = op.text || '';
|
|
if(op.disabled)
|
|
newEl.disabled = true;
|
|
}
|
|
} else {
|
|
newEl = <any>document.createElement('optgroup'); //tslint:disable-line:no-any
|
|
newEl.label = op.label;
|
|
buildOptions(newEl, op.options);
|
|
}
|
|
parent.appendChild(newEl);
|
|
}
|
|
}
|
|
|
|
clearOptions();
|
|
buildOptions(el, value);
|
|
}
|
|
|
|
export default Vue.directive('select', {
|
|
inserted: rebuild,
|
|
update: rebuild
|
|
}); |