mirror of
https://github.com/AzuraCast/AzuraCast.git
synced 2024-06-14 13:16:37 +00:00
172 lines
4.0 KiB
Vue
172 lines
4.0 KiB
Vue
<template>
|
|
<b-form-group
|
|
v-bind="$attrs"
|
|
:label-for="id"
|
|
:state="fieldState"
|
|
>
|
|
<template #default>
|
|
<slot
|
|
name="default"
|
|
v-bind="{ id, field, state: fieldState }"
|
|
>
|
|
<b-form-textarea
|
|
v-if="inputType === 'textarea'"
|
|
v-bind="inputAttrs"
|
|
:id="id"
|
|
ref="$input"
|
|
v-model="modelValue"
|
|
:name="name"
|
|
:required="isRequired"
|
|
:number="isNumeric"
|
|
:trim="inputTrim"
|
|
:autofocus="autofocus"
|
|
:state="fieldState"
|
|
/>
|
|
<b-form-input
|
|
v-else
|
|
v-bind="inputAttrs"
|
|
:id="id"
|
|
ref="$input"
|
|
v-model="modelValue"
|
|
:type="inputType"
|
|
:name="name"
|
|
:required="isRequired"
|
|
:number="isNumeric"
|
|
:trim="inputTrim"
|
|
:autofocus="autofocus"
|
|
:state="fieldState"
|
|
/>
|
|
</slot>
|
|
|
|
<b-form-invalid-feedback :state="fieldState">
|
|
<vuelidate-error :field="field" />
|
|
</b-form-invalid-feedback>
|
|
</template>
|
|
|
|
<template #label="slotProps">
|
|
<slot
|
|
v-bind="slotProps"
|
|
name="label"
|
|
/>
|
|
<span
|
|
v-if="isRequired"
|
|
class="text-danger"
|
|
>
|
|
<span aria-hidden="true">*</span>
|
|
<span class="sr-only">Required</span>
|
|
</span>
|
|
<span
|
|
v-if="advanced"
|
|
class="badge small badge-primary ml-2"
|
|
>
|
|
{{ $gettext('Advanced') }}
|
|
</span>
|
|
</template>
|
|
<template #description="slotProps">
|
|
<slot
|
|
v-bind="slotProps"
|
|
name="description"
|
|
/>
|
|
</template>
|
|
|
|
<template
|
|
v-for="(_, slot) of filteredSlots"
|
|
#[slot]="scope"
|
|
>
|
|
<slot
|
|
:name="slot"
|
|
v-bind="scope"
|
|
/>
|
|
</template>
|
|
</b-form-group>
|
|
</template>
|
|
|
|
<script setup>
|
|
import VuelidateError from "./VuelidateError";
|
|
import {computed, ref} from "vue";
|
|
import useSlotsExcept from "~/functions/useSlotsExcept";
|
|
import {has} from "lodash";
|
|
|
|
const props = defineProps({
|
|
id: {
|
|
type: String,
|
|
required: true
|
|
},
|
|
name: {
|
|
type: String,
|
|
required: true
|
|
},
|
|
field: {
|
|
type: Object,
|
|
required: true
|
|
},
|
|
inputType: {
|
|
type: String,
|
|
default: 'text'
|
|
},
|
|
inputNumber: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
inputTrim: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
inputEmptyIsNull: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
inputAttrs: {
|
|
type: Object,
|
|
default() {
|
|
return {};
|
|
}
|
|
},
|
|
autofocus: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
advanced: {
|
|
type: Boolean,
|
|
default: false
|
|
}
|
|
});
|
|
|
|
const modelValue = computed({
|
|
get() {
|
|
return props.field.$model;
|
|
},
|
|
set(newValue) {
|
|
if ((props.isNumeric || props.inputEmptyIsNull) && '' === newValue) {
|
|
newValue = null;
|
|
}
|
|
|
|
props.field.$model = newValue;
|
|
}
|
|
});
|
|
|
|
const filteredSlots = useSlotsExcept(['default', 'label', 'description']);
|
|
|
|
const fieldState = computed(() => {
|
|
return props.field.$dirty ? !props.field.$error : null;
|
|
});
|
|
|
|
const isRequired = computed(() => {
|
|
return has(props.field, 'required');
|
|
});
|
|
|
|
const isNumeric = computed(() => {
|
|
return props.inputNumber || props.inputType === "number";
|
|
});
|
|
|
|
const $input = ref(); // Input
|
|
|
|
const focus = () => {
|
|
$input.value?.focus();
|
|
};
|
|
|
|
defineExpose({
|
|
focus
|
|
});
|
|
</script>
|