mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
update forms and select-like + fields validity
* avoid invalid popup when rendering input by setting valid by default and then check for validity * remove useless props check for select and combobox * add combobox for templates and plugins on Advanced forms
This commit is contained in:
parent
c18c23e785
commit
351da6e99a
7 changed files with 120 additions and 63 deletions
|
|
@ -4,8 +4,10 @@ import Container from "@components/Widget/Container.vue";
|
|||
import Fields from "@components/Form/Fields.vue";
|
||||
import Title from "@components/Widget/Title.vue";
|
||||
import Subtitle from "@components/Widget/Subtitle.vue";
|
||||
import Combobox from "@components/Forms/Field/Combobox.vue";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
/**
|
||||
/**
|
||||
@name Form/Advanced.vue
|
||||
@description This component is used to create a complete advanced form with plugin selection.
|
||||
@example
|
||||
|
|
@ -13,7 +15,6 @@ import Subtitle from "@components/Widget/Subtitle.vue";
|
|||
{
|
||||
name: "plugin name",
|
||||
type: "pro",
|
||||
is_activate: true,
|
||||
description: "plugin description",
|
||||
page: "/page",
|
||||
settings: [
|
||||
|
|
@ -43,7 +44,6 @@ import Subtitle from "@components/Widget/Subtitle.vue";
|
|||
},
|
||||
];
|
||||
@param {object} forms - List of advanced forms that contains settings.
|
||||
@param {boolean} [isActive=true] - Check if the form is active, it will display the form if true
|
||||
*/
|
||||
|
||||
const props = defineProps({
|
||||
|
|
@ -53,39 +53,108 @@ const props = defineProps({
|
|||
required: true,
|
||||
default: {},
|
||||
},
|
||||
isActive: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
|
||||
const comboboxPlugin = {
|
||||
id: uuidv4(),
|
||||
name: uuidv4(),
|
||||
disabled: false,
|
||||
required: false,
|
||||
label: "dashboard_plugins",
|
||||
tabId: "1",
|
||||
columns: { pc: 4, tablet: 6, mobile: 12 },
|
||||
};
|
||||
|
||||
const comboboxTemplate = {
|
||||
id: uuidv4(),
|
||||
name: uuidv4(),
|
||||
disabled: false,
|
||||
required: false,
|
||||
label: "dashboard_templates",
|
||||
tabId: "1",
|
||||
columns: { pc: 4, tablet: 6, mobile: 12 },
|
||||
};
|
||||
|
||||
const data = reactive({
|
||||
currTemplate: "",
|
||||
currPlugin: "",
|
||||
});
|
||||
|
||||
function getFirstTemplate() {
|
||||
return Object.keys(props.forms)[0];
|
||||
}
|
||||
|
||||
function getTemplateNames() {
|
||||
return Object.keys(props.forms);
|
||||
}
|
||||
|
||||
function getFirstPlugin(form) {
|
||||
return form[0]["name"];
|
||||
}
|
||||
|
||||
function getPluginNames(form) {
|
||||
const pluginNames = [];
|
||||
// Loop on each dict from form list
|
||||
for (const plugin of form) {
|
||||
// Return the first plugin
|
||||
pluginNames.push(plugin.name);
|
||||
}
|
||||
return pluginNames;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// Get first props.forms template name
|
||||
data.currTemplate = getFirstTemplate();
|
||||
// Get first plugin name
|
||||
data.currPlugin = getFirstPlugin(props.forms[data.currTemplate]);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Container
|
||||
v-if="props.isActive"
|
||||
:tag="'form'"
|
||||
method="POST"
|
||||
:containerClass="`col-span-12 w-full m-1 p-1`"
|
||||
:columns="props.columns"
|
||||
>
|
||||
<Container v-for="form in props.forms">
|
||||
<Container class="col-span-12 w-full" v-for="plugin in form">
|
||||
<Title type="card" :title="plugin.name" />
|
||||
<Subtitle type="card" :subtitle="plugin.description" />
|
||||
|
||||
<Container
|
||||
style="max-height: 300px; overflow: auto"
|
||||
class="grid grid-cols-12 w-full relative"
|
||||
>
|
||||
<template
|
||||
v-for="(setting, name, index) in plugin.settings"
|
||||
:key="index"
|
||||
<template v-for="(template, template_name) in props.forms">
|
||||
<Container
|
||||
:containerClass="`col-span-12 grid grid-cols-12`"
|
||||
v-if="template_name === data.currTemplate"
|
||||
>
|
||||
<Combobox
|
||||
v-bind="comboboxTemplate"
|
||||
:value="getFirstTemplate()"
|
||||
:values="getTemplateNames()"
|
||||
@inp="data.currPlugin = $event"
|
||||
/>
|
||||
<Combobox
|
||||
v-bind="comboboxPlugin"
|
||||
:value="getFirstPlugin(template)"
|
||||
:values="getPluginNames(template)"
|
||||
@inp="data.currPlugin = $event"
|
||||
/>
|
||||
<template v-for="plugin in template">
|
||||
<Container
|
||||
v-if="plugin.name === data.currPlugin"
|
||||
class="col-span-12 w-full"
|
||||
>
|
||||
<Fields :setting="setting" />
|
||||
</template>
|
||||
</Container>
|
||||
</Container>
|
||||
</Container>
|
||||
<Title type="card" :title="plugin.name" />
|
||||
<Subtitle type="card" :subtitle="plugin.description" />
|
||||
|
||||
<Container
|
||||
style="max-height: 300px; overflow: auto"
|
||||
class="grid grid-cols-12 w-full relative"
|
||||
>
|
||||
<template
|
||||
v-for="(setting, name, index) in plugin.settings"
|
||||
:key="index"
|
||||
>
|
||||
<Fields :setting="setting" />
|
||||
</template>
|
||||
</Container>
|
||||
</Container>
|
||||
</template> </Container
|
||||
></template>
|
||||
</Container>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script setup>
|
||||
import { reactive, defineProps, onMounted, ref } from "vue";
|
||||
import { reactive, defineProps, ref, onMounted } from "vue";
|
||||
import { contentIndex } from "@utils/tabindex.js";
|
||||
import Container from "@components/Widget/Container.vue";
|
||||
import Header from "@components/Forms/Header/Field.vue";
|
||||
|
|
@ -118,7 +118,7 @@ const checkboxEl = ref(null);
|
|||
|
||||
const checkbox = reactive({
|
||||
value: props.value,
|
||||
isValid: false,
|
||||
isValid: true,
|
||||
});
|
||||
|
||||
const emits = defineEmits(["inp"]);
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import { v4 as uuidv4 } from "uuid";
|
|||
iconColor: "info",
|
||||
},]
|
||||
}
|
||||
@param {string} [id=uuidv4()] - Unique id
|
||||
@param {string} [id=uuidv4()] - Unique id
|
||||
@param {string} label - The label of the field. Can be a translation key or by default raw text.
|
||||
@param {string} name - The name of the field. Case no label, this is the fallback. Can be a translation key or by default raw text.
|
||||
@param {string} value
|
||||
|
|
@ -124,19 +124,9 @@ const props = defineProps({
|
|||
},
|
||||
});
|
||||
|
||||
// When mounted or when props changed, we want select to display new props values
|
||||
// When component value change itself, we want to switch to select.value
|
||||
// To avoid component to send and stick to props values (bad behavior)
|
||||
// Trick is to use select.value || props.value on template
|
||||
watch(props, (newProp, oldProp) => {
|
||||
if (newProp.value !== select.value) {
|
||||
select.value = "";
|
||||
}
|
||||
});
|
||||
|
||||
const inp = reactive({
|
||||
value: "",
|
||||
isValid: false,
|
||||
isValid: true,
|
||||
});
|
||||
|
||||
const inputEl = ref();
|
||||
|
|
@ -253,6 +243,7 @@ watch(select, () => {
|
|||
});
|
||||
|
||||
onMounted(() => {
|
||||
inp.isValid = inputEl.value.checkValidity();
|
||||
selectWidth.value = `${selectBtn.value.clientWidth}px`;
|
||||
window.addEventListener("resize", () => {
|
||||
try {
|
||||
|
|
@ -353,12 +344,7 @@ const emits = defineEmits(["inp"]);
|
|||
ref="inputEl"
|
||||
v-model="inp.value"
|
||||
:placeholder="$t('inp_combobox_placeholder')"
|
||||
@input="
|
||||
() => {
|
||||
inp.isValid = inputEl.checkValidity();
|
||||
$emit('inp', inp.value);
|
||||
}
|
||||
"
|
||||
@input="inp.isValid = inputEl.checkValidity()"
|
||||
:aria-controls="`${props.id}-list`"
|
||||
:id="`${props.id}-combobox`"
|
||||
:class="[
|
||||
|
|
@ -373,9 +359,15 @@ const emits = defineEmits(["inp"]);
|
|||
/>
|
||||
<div
|
||||
class="select-dropdown-btn"
|
||||
v-if="!props.values.some((str) => str.includes(inp.value))"
|
||||
v-if="
|
||||
!props.values.some((str) =>
|
||||
str.toLowerCase().includes(inp.value.toLowerCase())
|
||||
)
|
||||
"
|
||||
:aria-hidden="
|
||||
!props.values.some((str) => str.includes(inp.value))
|
||||
!props.values.some((str) =>
|
||||
str.toLowerCase().includes(inp.value.toLowerCase())
|
||||
)
|
||||
? 'true'
|
||||
: 'false'
|
||||
"
|
||||
|
|
@ -395,11 +387,15 @@ const emits = defineEmits(["inp"]);
|
|||
>
|
||||
<template v-for="(value, id) in props.values">
|
||||
<button
|
||||
v-if="value.includes(inp.value)"
|
||||
:aria-hidden="value.includes(inp.value) ? 'false' : 'true'"
|
||||
v-if="value.toLowerCase().includes(inp.value.toLowerCase())"
|
||||
:aria-hidden="
|
||||
value.toLowerCase().includes(inp.value.toLowerCase())
|
||||
? 'false'
|
||||
: 'true'
|
||||
"
|
||||
:tabindex="
|
||||
select.isOpen
|
||||
? value.includes(inp.value)
|
||||
? value.toLowerCase().includes(inp.value.toLowerCase())
|
||||
? props.tabId
|
||||
: '-1'
|
||||
: '-1'
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ const props = defineProps({
|
|||
});
|
||||
|
||||
const date = reactive({
|
||||
isValid: false,
|
||||
isValid: true,
|
||||
format: "m/d/Y H:i:S",
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ const inp = reactive({
|
|||
value: props.value,
|
||||
showInp: false,
|
||||
isClipAllow: false,
|
||||
isValid: false,
|
||||
isValid: true,
|
||||
});
|
||||
|
||||
const emits = defineEmits(["inp"]);
|
||||
|
|
@ -170,6 +170,7 @@ function copyClipboard() {
|
|||
|
||||
onMounted(() => {
|
||||
inp.isValid = inputEl.value.checkValidity();
|
||||
|
||||
// Clipboard not allowed on http
|
||||
if (!window.location.href.startsWith("https://")) return;
|
||||
|
||||
|
|
|
|||
|
|
@ -125,16 +125,6 @@ const props = defineProps({
|
|||
},
|
||||
});
|
||||
|
||||
// When mounted or when props changed, we want select to display new props values
|
||||
// When component value change itself, we want to switch to select.value
|
||||
// To avoid component to send and stick to props values (bad behavior)
|
||||
// Trick is to use select.value || props.value on template
|
||||
watch(props, (newProp, oldProp) => {
|
||||
if (newProp.value !== select.value) {
|
||||
select.value = "";
|
||||
}
|
||||
});
|
||||
|
||||
const select = reactive({
|
||||
isOpen: false,
|
||||
// On mounted value is null to display props value
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
"dashboard_services": "services",
|
||||
"dashboard_configs": "configs",
|
||||
"dashboard_plugins": "plugins",
|
||||
"dashboard_templates": "templates",
|
||||
"dashboard_jobs": "jobs",
|
||||
"dashboard_bans": "bans",
|
||||
"dashboard_actions": "actions",
|
||||
|
|
|
|||
Loading…
Reference in a new issue