ln.vue/js/ln.vue.components.js

450 lines
10 KiB
JavaScript

(function(){
let nextTooltipKey = 0;
var tooltipData = {};
var tootlipVisible = false;
var tooltipEl = LNVue.$(`<div class="ln-tooltip"></div>`);
function findTooltipData(el){
let tooltip = null;
while (!tooltip)
{
tooltip = tooltipData[el.dataset['tooltip']];
el = el.parentElement;
}
return tooltip;
}
function tooltipShow(ev,tooltip){
if (!tooltipEl.parentElement)
{
tooltipEl.style.visibility = "hidden";
tooltipEl.style.opacity = 0.0;
tooltipEl.style.visibility = "visible";
}
document.body.appendChild(tooltipEl);
tooltipEl.innerText = tooltip.value;
tooltipEl.style.opacity = 1.0;
tootlipVisible = true;
}
function tooltipHide(ev,tooltip){
tooltipEl.style.opacity = 0.0;
setTimeout(()=>{
document.body.removeChild(tooltipEl);
}, 600);
tootlipVisible = false;
}
function tooltipMouseOver(ev){
let tooltip = findTooltipData(ev.target);
if (!tooltip)
console.log(ev);
if (tooltip.timeout)
clearTimeout(tooltip.timeout);
if (tootlipVisible)
tooltipHide(ev,tooltip);
else
tooltip.timeout = setTimeout(() => {
tooltipShow(ev,tooltip);
}, 800);
if (tooltipEl){
tooltipEl.style.left = `${ev.x+3}px`;
tooltipEl.style.top = `${ev.y+3}px`;
}
}
function tooltipMouseOut(ev){
let tooltip = findTooltipData(ev.target);
if (tooltip.timeout)
clearTimeout(tooltip.timeout);
tooltipHide(ev,tooltip);
}
Vue.directive('tooltip',{
bind: function(el, binding, vnode){
let tooltip = {
value: binding.value,
timeout: null,
};
let tooltipKey = nextTooltipKey++;
tooltipData[tooltipKey] = tooltip;
el.dataset['tooltip'] = tooltipKey;
el.addEventListener('mousemove',tooltipMouseOver);
el.addEventListener('mouseout',tooltipMouseOut);
},
inserted: function(el, binding, vnode){
},
update: function(el, binding, vnode, oldVnode){
},
componentUpdated: function(el, binding, vnode, oldVnode){
},
unbind: function(el, binding, vnode){
el.removeEventListener('mouseover',tooltipMouseOver);
el.removeEventListener('mouseout',tooltipMouseOut);
},
});
Vue.component('ln-navitem',{
props: {
href: {
type: String,
required: true,
},
},
template: `
<router-link
:to="href"
class="ln-navitem"unbind
v-slot="{ href, route, navigate, isActive, isExactActive }"
>
<div class="ln-navitem">
<a :href="href" @click="navigate">
<slot></slot>
</a>
<slot name="children"></slot>
</div>
</router-link>
`,
});
Vue.component('ln-navbar',{
props: {
navitems: {
type: Object,
required: true,
}
},
template: `
<div class="ln-navbar">
<ln-navitem
v-for="navitem in navitems"
v-bind:href="navitem.href"
>{{ navitem.label || navitem.href }}</ln-navitem>
</div>`
});
Vue.component('ln-textfield',{
props: {
value: {
type: String,
required: true,
},
},
template: `<input
type="text"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)">`,
});
Vue.component('ln-textarea',{
props: {
value: {
type: String,
required: true,
},
},
template: `<textarea v-on:input="$emit('input', $event.target.value)">{{ value }}</textarea>`,
});
Vue.component('ln-number',{
model: {
prop: "value",
event: "input",
},
props: {
value: {
required: false,
type: Number,
},
float: {
type: Boolean,
default: false,
},
},
template: `
<input
type="number"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value )"
v-bind:step="float ? 'any' : 1"
>`,
});
Vue.component('ln-slider',{
model: {
prop: 'value',
event: 'change',
},
props: {
value: {
type: Number,
default: 1,
},
min: {
type: Number,
default: 0,
},
max: {
type: Number,
default: 100,
},
step: {
type: Number,
default: 1,
},
islogarithmic: {
type: Boolean,
default: false,
},
values: {
default: null,
}
},
data: function(){
let d = {
};
if (this.values instanceof Array){
this.min = 0;
this.max = this.values.length - 1;
this.step = 1;
} else if (this.values instanceof Object){
this.min = 0;
this.max = Object.keys(this.values).length - 1;
this.step = 1;
} else if (this.islogarithmic){
/* ToDo: Implement */
} else {
}
// console.log(`min=${this.min} max=${this.max} step=${this.step} value=${this.value}`);
return d;
},
computed: {
keys: function(){
if (this.values instanceof Array)
{
let keys = new Array(this.values.length);
for (let n=0;n<keys.length;n++)
keys[n] = n;
return keys;
} else if (this.values instanceof Object)
{
return Object.keys(this.values);
}
console.log("ln-slider.keys: ???");
},
displayValue: function(){
if (this.values)
{
return this.values[this.value];
}
return this.mapFromRange(this.value);
},
sliderValue: {
get: function(){ return this.mapToRange(this.value); },
set: function(v){ this.value = this.mapFromRange(v); this.$emit('change', this.value); },
}
},
methods: {
mapToRange: function(v){
if (this.values)
{
if (this.values)
{
for (let idx=0;idx<this.keys.length;idx++)
{
if (v == this.keys[idx])
{
return idx;
}
}
}
console.log("slider position not found!");
console.log(this.values);
console.log(v);
return 0;
}
return this.islogarithmic ? null : v;
},
mapFromRange: function(v){
if (this.values)
{
return this.keys[v];
}
return this.islogarithmic ? null : v;
},
fireChange: function(){
this.$emit('change',this.value);
}
},
template: `
<div class='ln-slider'>
<input
class="ln-slider"
type="range"
v-bind:min="min"
v-bind:max="max"
v-bind:step="step"
v-model="sliderValue"
@change="fireChange"
>{{ displayValue }}
</div>`,
});
Vue.component("ln-file",{
props: {
file: {
type: Object,
required: true,
},
remove: {
type: Function,
required: false,
}
},
methods: {
},
template: `<div
class="ln-upload-file"
>{{ file.name }} ({{ file.size }}bytes)
<sym
@click="remove && remove(file)"
class="trash"
style="position: absolute; right: 2px;"
></sym>
</div>`
});
Vue.component('ln-upload',{
model: {
prop: "files",
event: "change",
},
props: {
maxSize: {
type: Number,
default: 0,
},
files: {
type: Array,
default: [],
}
},
data: function(){ return { maxSize: this.maxSize, files: this.files, }; },
methods: {
drop: function(dropEvent){
dropEvent.preventDefault();
for (let n=0;n<dropEvent.dataTransfer.files.length;n++)
{
//let file = dropEvent.dataTransfer.files[n];
let item = dropEvent.dataTransfer.items[n];
let file = item.getAsFile();
this.files.push({
name: file.name,
size: file.size,
type: file.type,
file: file,
});
}
this.$emit("change",this.files);
console.log("files dropped...");
},
remove(file){
console.log(file);
let idx = this.files.indexOf(file);
if (idx != -1)
{
this.files.splice(idx,1);
this.$emit("change",this.files);
}
}
},
template: `
<div
class="ln-upload"
@drop="drop"
ondragover="event.preventDefault();"
>Dateien hierher ziehen oder auswählen:<br>
<input type="file" value=""><br>
<ln-file
v-for="file in files"
v-bind:file="file"
v-bind:remove="remove"
></ln-file>
</div>
`,
});
Vue.component('ln-select',{
model: {
prop: "value",
event: "change",
},
props: {
items: {
required: true,
},
value: {
required: true,
},
empty: {
type: Boolean,
default: true,
},
},
computed: {
prepared: {
get: function(){
let prepared = {};
LNVue.each(this.items,(element,index) => {
prepared[index] = element;
});
return prepared;
},
},
},
methods: {
changed: function(ev){
this.$emit("change", ev.target.value );
},
},
template: `<div class="ln-select">
<select @change="changed">
<option
v-if="empty"
value=""
></option>
<option
v-for="(item,key) in prepared"
:value="key"
:selected="key == value"
>{{ item }}</option>
</select>
</div>
`,
});
})();