135 lines
3.0 KiB
HTML
135 lines
3.0 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
|
|
<style>
|
|
.clear {
|
|
background: none;
|
|
outline: none;
|
|
}
|
|
|
|
.clear:invalid+hr {
|
|
border: #f00 solid 1px !important;
|
|
}
|
|
|
|
body {
|
|
position: relative;
|
|
width: 100%;
|
|
}
|
|
|
|
hr {
|
|
margin: auto;
|
|
position: absolute;
|
|
bottom: -1px;
|
|
border: rgba(128, 128, 128, 0.2) solid 1px;
|
|
transition: all 0.2s linear;
|
|
}
|
|
|
|
body:hover hr {
|
|
border: rgba(128, 128, 128, 9) solid 1px;
|
|
width: 100% !important;
|
|
left: 0rem !important;
|
|
}
|
|
|
|
|
|
.vinput-core {
|
|
width: calc(100% - 1rem);
|
|
padding-left: 1rem;
|
|
min-width: 3rem;
|
|
}
|
|
|
|
input[type="text"]:focus {}
|
|
</style>
|
|
|
|
<body class='relative'>
|
|
<div class="flex relative">
|
|
<div v-if='label' class="flex-shrink-0" :style="{width: label_width}">{{label}}</div>
|
|
<textarea vdom='vref' :rows='opts?.rows' v-if="type==='textarea'" class="clear vinput-core grow" @input='sync_evt'
|
|
!value :placeholder !required>
|
|
</textarea>
|
|
<div v-else-if='type==="bool"'>
|
|
<div @click='value=!value'>{{value?'true':'false'}}</div>
|
|
</div>
|
|
<input vdom='vref' v-else-if='type==="number"' class="clear vinput-core grow" type="number" @input='sync_evt'
|
|
!value='value||0' :placeholder !required />
|
|
<input-select v:value v-else-if='type==="select"' class="vinput-core grow" :input='sync' :options='opts.options'
|
|
:placeholder></input-select>
|
|
<input vdom='vref' v-else class="clear vinput-core grow" type="text" @input='sync_evt' !value="value||''"
|
|
:placeholder !required />
|
|
<hr v-if='type!="select"'
|
|
:style="label?`width:calc(100% - 2rem - ${label_width});left: calc(1rem + ${label_width})`:'width:calc(100% - 2rem);left:1rem'">
|
|
</div>
|
|
</body>
|
|
<script setup>
|
|
vref = null
|
|
type = 'text' // text textarea
|
|
required = false
|
|
value = ''
|
|
label = ''
|
|
label_width = '6rem'
|
|
placeholder = ''
|
|
opts = {
|
|
rows: 5,
|
|
options: []
|
|
}
|
|
|
|
validate = (v) => true
|
|
sync_evt = (e) => {
|
|
sync(e.target.value)
|
|
}
|
|
sync = (v) => {
|
|
if (!$data.check(v)) {
|
|
$data.vref?.setCustomValidity("invalid");
|
|
return
|
|
}
|
|
$data.vref?.setCustomValidity('');
|
|
if ($data.type === 'textarea') {
|
|
$data.value = v
|
|
} else if ($data.type === 'number') {
|
|
$data.value = Number(v)
|
|
} else if ($data.type === 'bool') {
|
|
$data.value = v === 'true'
|
|
} else if ($data.type === 'select') {
|
|
$data.value = v
|
|
} else {
|
|
// type text
|
|
$data.value = v
|
|
}
|
|
}
|
|
check = (v) => {
|
|
if (!v) {
|
|
return !$data.required
|
|
}
|
|
if (!$data.validate) {
|
|
return true
|
|
} else if ($data.validate instanceof RegExp) {
|
|
if ($data.validate.test(v)) {
|
|
return true
|
|
}
|
|
} else if (typeof $data.validate === 'function') {
|
|
if ($data.validate(v)) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
</script>
|
|
<script>
|
|
if (!$data.value) {
|
|
switch ($data.type) {
|
|
case 'number':
|
|
sync(0)
|
|
break
|
|
case 'bool':
|
|
sync(false)
|
|
break
|
|
case 'select':
|
|
sync('')
|
|
break
|
|
default:
|
|
sync('')
|
|
}
|
|
}
|
|
</script>
|
|
|
|
</html>
|