mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
update tabindex + langswitch + fix stat card prop
* fix stat card prop that changed from "value" to "stat" on component * now contentIndex is set by default for components outside dashboard components * enhance lang switch style * enhance menu style
This commit is contained in:
parent
8bec560b53
commit
94bbc0f0a1
18 changed files with 698 additions and 636 deletions
|
|
@ -106,7 +106,7 @@ body {
|
|||
}
|
||||
|
||||
.content-container {
|
||||
@apply xl:pl-75 w-full px-2 sm:px-6 pb-0 pt-20 sm:pt-3 min-h-[85vh] flex flex-col justify-between;
|
||||
@apply xl:pl-72 w-full px-2 sm:px-6 pb-0 pt-20 sm:pt-3 min-h-[85vh] flex flex-col justify-between;
|
||||
}
|
||||
|
||||
.content-wrap {
|
||||
|
|
@ -494,7 +494,7 @@ body {
|
|||
}
|
||||
|
||||
.menu-container {
|
||||
@apply transition-all mt-[4.5rem] fixed flex flex-col justify-between inset-y-0 max-h-screen w-full p-0 my-4 antialiased duration-200 bg-white border-0 shadow-xl dark:shadow-none dark:bg-slate-850 dark:brightness-110 max-w-64 z-[1000] xl:ml-6 rounded-2xl xl:left-0;
|
||||
@apply transition-all mt-[4.5rem] fixed flex flex-col justify-between inset-y-0 max-h-screen w-full p-0 mb-2 antialiased duration-200 bg-white border-0 shadow-xl dark:shadow-none dark:bg-slate-850 dark:brightness-110 max-w-64 z-[1000] xl:ml-2 rounded-2xl xl:left-0;
|
||||
}
|
||||
|
||||
.no-banner.menu-container {
|
||||
|
|
@ -807,6 +807,16 @@ body {
|
|||
@apply capitalize-first hover:italic hover:brightness-90 block sm:px-4 py-1 lg:pt-1 lg:pb-1 text-sm tracking-wide font-normal transition duration-300 ease-in-out text-white dark:text-white;
|
||||
}
|
||||
|
||||
/* LANG */
|
||||
|
||||
.lang-switch-container {
|
||||
@apply fixed bottom-0 left-1 z-[1100];
|
||||
}
|
||||
|
||||
.lang-switch-list {
|
||||
@apply max-h-[300px] overflow-auto;
|
||||
}
|
||||
|
||||
/* STATUS COMPONENT */
|
||||
|
||||
.status-svg-container {
|
||||
|
|
@ -874,7 +884,7 @@ body {
|
|||
/* LAYOUT COMPONENT */
|
||||
|
||||
.card {
|
||||
@apply relative transition dark:brightness-110 shadow-md bg-white dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border hover:scale-[1.01] transform duration-300 ease-in-out;
|
||||
@apply relative transition dark:brightness-110 shadow-md bg-white dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border transform duration-300 ease-in-out;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -14,14 +14,16 @@ const bannerStore = useBannerStore();
|
|||
|
||||
const banner = reactive({
|
||||
visibleId: 1,
|
||||
default : [
|
||||
default: [
|
||||
{
|
||||
title: "Get the most of BunkerWeb by upgrading to the PRO version. More info and free trial",
|
||||
title:
|
||||
"Get the most of BunkerWeb by upgrading to the PRO version. More info and free trial",
|
||||
link: "https://panel.bunkerweb.io/?utm_campaign=self&utm_source=ui#pro",
|
||||
linkText: "here",
|
||||
},
|
||||
{
|
||||
title: "Need premium support or tailored consulting around BunkerWeb ? Check out our",
|
||||
title:
|
||||
"Need premium support or tailored consulting around BunkerWeb ? Check out our",
|
||||
link: "https://panel.bunkerweb.io/?utm_campaign=self&utm_source=ui#services",
|
||||
linkText: "professional services.",
|
||||
},
|
||||
|
|
@ -33,15 +35,18 @@ const banner = reactive({
|
|||
],
|
||||
isTabIndex: false,
|
||||
api: [],
|
||||
apiFormat : computed(() => {
|
||||
if(banner.api.length === 0) return [];
|
||||
apiFormat: computed(() => {
|
||||
if (banner.api.length === 0) return [];
|
||||
// deep copy
|
||||
const data = JSON.parse(JSON.stringify(banner.api));
|
||||
data.forEach((item, index) => {
|
||||
data.forEach((item, index) => {
|
||||
// I want to match everything inside class and replace it
|
||||
data[index].content = item.content.replace(/class='(.+?)'|class="(.+?)"/g, 'class="banner-item-text"');
|
||||
data[index].content = item.content.replace(
|
||||
/class='(.+?)'|class="(.+?)"/g,
|
||||
'class="banner-item-text"'
|
||||
);
|
||||
});
|
||||
return data
|
||||
return data;
|
||||
}),
|
||||
});
|
||||
|
||||
|
|
@ -50,45 +55,44 @@ const banner = reactive({
|
|||
function setupBanner() {
|
||||
// Check if data, and if case, that data is not older than one hour
|
||||
// Case it is, refetch
|
||||
if (sessionStorage.getItem("bannerRefetch") !== null) {
|
||||
const storeStamp = sessionStorage.getItem("bannerRefetch");
|
||||
const nowStamp = Math.round(new Date().getTime() / 1000);
|
||||
if (+nowStamp > storeStamp) {
|
||||
sessionStorage.removeItem("bannerRefetch");
|
||||
sessionStorage.removeItem("bannerNews");
|
||||
}
|
||||
if (sessionStorage.getItem("bannerRefetch") !== null) {
|
||||
const storeStamp = sessionStorage.getItem("bannerRefetch");
|
||||
const nowStamp = Math.round(new Date().getTime() / 1000);
|
||||
if (+nowStamp > storeStamp) {
|
||||
sessionStorage.removeItem("bannerRefetch");
|
||||
sessionStorage.removeItem("bannerNews");
|
||||
}
|
||||
// Case we already have the data
|
||||
if (sessionStorage.getItem("bannerNews") !== null) {
|
||||
banner.api =
|
||||
JSON.parse(sessionStorage.getItem("bannerNews"))
|
||||
banner.default = [];
|
||||
runBanner();
|
||||
return;
|
||||
}
|
||||
// Try to fetch api data
|
||||
fetch("https://www.bunkerweb.io/api/bw-ui-news")
|
||||
.then((res) => {
|
||||
return res.json();
|
||||
})
|
||||
.then((res) => {
|
||||
sessionStorage.setItem("bannerNews", JSON.stringify(res.data[0].data));
|
||||
// Refetch after one hour
|
||||
sessionStorage.setItem(
|
||||
"bannerRefetch",
|
||||
Math.round(new Date().getTime() / 1000) + 3600,
|
||||
);
|
||||
banner.api = res.data[0].data;
|
||||
if(banner.api.length > 0) {
|
||||
banner.default = [];
|
||||
}
|
||||
runBanner();
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
runBanner();
|
||||
});
|
||||
}
|
||||
// Case we already have the data
|
||||
if (sessionStorage.getItem("bannerNews") !== null) {
|
||||
banner.api = JSON.parse(sessionStorage.getItem("bannerNews"));
|
||||
banner.default = [];
|
||||
runBanner();
|
||||
return;
|
||||
}
|
||||
// Try to fetch api data
|
||||
fetch("https://www.bunkerweb.io/api/bw-ui-news")
|
||||
.then((res) => {
|
||||
return res.json();
|
||||
})
|
||||
.then((res) => {
|
||||
sessionStorage.setItem("bannerNews", JSON.stringify(res.data[0].data));
|
||||
// Refetch after one hour
|
||||
sessionStorage.setItem(
|
||||
"bannerRefetch",
|
||||
Math.round(new Date().getTime() / 1000) + 3600
|
||||
);
|
||||
banner.api = res.data[0].data;
|
||||
if (banner.api.length > 0) {
|
||||
banner.default = [];
|
||||
}
|
||||
runBanner();
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
runBanner();
|
||||
});
|
||||
}
|
||||
|
||||
// Banner animation effect
|
||||
function runBanner() {
|
||||
|
|
@ -97,7 +101,7 @@ function runBanner() {
|
|||
// Switch item every interval and
|
||||
setTimeout(() => {
|
||||
const prev = banner.visibleId;
|
||||
banner.visibleId = banner.visibleId === 3 ? 1 : banner.visibleId + 1;
|
||||
banner.visibleId = banner.visibleId === 2 ? 0 : banner.visibleId + 1;
|
||||
const next = banner.visibleId;
|
||||
|
||||
// Hide previous one
|
||||
|
|
@ -124,10 +128,9 @@ function runBanner() {
|
|||
|
||||
runBanner();
|
||||
}, nextDelay);
|
||||
|
||||
}
|
||||
// Observe banner and set is visible or not to
|
||||
// Update float button and menu position
|
||||
// Observe banner and set is visible or not to
|
||||
// Update float button and menu position
|
||||
function observeBanner() {
|
||||
const options = {
|
||||
root: null,
|
||||
|
|
@ -148,31 +151,38 @@ function observeBanner() {
|
|||
function noTabindex() {
|
||||
const bannerItems = document.querySelectorAll(".banner-item");
|
||||
bannerItems.forEach((item) => {
|
||||
item.classList.remove("banner-tabindex-highlight", 'banner-tabindex-hide');
|
||||
item.classList.remove("banner-tabindex-highlight", "banner-tabindex-hide");
|
||||
});
|
||||
}
|
||||
|
||||
function isTabindex() {
|
||||
const activeElement = document.activeElement;
|
||||
const bannerItems = document.querySelectorAll(".banner-item");
|
||||
bannerItems.forEach((item) => {
|
||||
item.classList.add("banner-tabindex-hide");
|
||||
item.classList.remove("banner-tabindex-highlight");
|
||||
});
|
||||
// Higher z-index for the focused element
|
||||
activeElement.closest('.banner-item').classList.add("banner-tabindex-highlight");
|
||||
activeElement.closest('.banner-item').classList.remove("banner-tabindex-hide");
|
||||
const bannerItems = document.querySelectorAll(".banner-item");
|
||||
bannerItems.forEach((item) => {
|
||||
item.classList.add("banner-tabindex-hide");
|
||||
item.classList.remove("banner-tabindex-highlight");
|
||||
});
|
||||
// Higher z-index for the focused element
|
||||
activeElement
|
||||
.closest(".banner-item")
|
||||
.classList.add("banner-tabindex-highlight");
|
||||
activeElement
|
||||
.closest(".banner-item")
|
||||
.classList.remove("banner-tabindex-hide");
|
||||
}
|
||||
|
||||
|
||||
// Focus with tabindex break banner animation
|
||||
// When a banner is focused, we need to add in front of the current banner the focus element
|
||||
// And remove it when the focus is lost
|
||||
function handleTabIndex() {
|
||||
// Get the active element after tabindex click
|
||||
document.addEventListener("keyup", (e) => {
|
||||
if(e.key !== "Tab" && !document.activeElement.classList.contains("banner-item-text")) return;
|
||||
if(document.activeElement.classList.contains("banner-item-text")) {
|
||||
if (
|
||||
e.key !== "Tab" &&
|
||||
!document.activeElement.classList.contains("banner-item-text")
|
||||
)
|
||||
return;
|
||||
if (document.activeElement.classList.contains("banner-item-text")) {
|
||||
isTabindex();
|
||||
return;
|
||||
} else {
|
||||
|
|
@ -181,7 +191,6 @@ function handleTabIndex() {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
observeBanner();
|
||||
setupBanner();
|
||||
|
|
@ -200,7 +209,7 @@ onMounted(() => {
|
|||
:class="[index === 1 ? 'left-0' : 'left-full opacity-0']"
|
||||
>
|
||||
<p class="banner-item-text">
|
||||
{{ bannerEl.title }}
|
||||
{{ bannerEl.title }}
|
||||
<a
|
||||
:tabindex="bannerIndex"
|
||||
class="banner-item-link"
|
||||
|
|
@ -217,8 +226,7 @@ onMounted(() => {
|
|||
class="banner-item"
|
||||
:class="[index === 1 ? 'left-0' : 'left-full opacity-0']"
|
||||
>
|
||||
<div
|
||||
v-html="bannerEl.content"></div>
|
||||
<div v-html="bannerEl.content"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -20,12 +20,12 @@ function updateLangStorage(lang) {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="fixed bottom-0 left-1 z-[800]">
|
||||
<div class="lang-switch-container">
|
||||
<ul
|
||||
id="switch-lang"
|
||||
role="radiogroup"
|
||||
v-show="lang.isOpen"
|
||||
class="max-h-[300px] overflow-auto"
|
||||
class="lang-switch-list"
|
||||
>
|
||||
<li
|
||||
v-for="(locale, id) in $i18n.availableLocales"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import Container from "@components/Widget/Container.vue";
|
|||
import Header from "@components/Forms/Header/Field.vue";
|
||||
import ErrorField from "@components/Forms/Error/Field.vue";
|
||||
|
||||
|
||||
/**
|
||||
@name Forms/Field/Checkbox.vue
|
||||
@description This component is used to create a complete checkbox field input with error handling and label.
|
||||
|
|
@ -33,69 +32,69 @@ import ErrorField from "@components/Forms/Error/Field.vue";
|
|||
@param {string} [containerClass=""]
|
||||
@param {string} [headerClass=""]
|
||||
@param {string} [inpClass=""]
|
||||
@param {string|number} [tabId=""]
|
||||
@param {string|number} [tabId=contentIndex] - The tabindex of the field, by default it is the contentIndex
|
||||
*/
|
||||
|
||||
const props = defineProps({
|
||||
// id && value && method
|
||||
id: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
columns: {
|
||||
type: [Object, Boolean],
|
||||
required: false,
|
||||
default : false
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
version: {
|
||||
type: String,
|
||||
required: false,
|
||||
default : ""
|
||||
},
|
||||
hideLabel: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
containerClass : {
|
||||
type: String,
|
||||
required: false,
|
||||
default : ""
|
||||
},
|
||||
headerClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default : ""
|
||||
},
|
||||
inpClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default : ""
|
||||
},
|
||||
tabId: {
|
||||
type: [String, Number],
|
||||
required: false,
|
||||
default: ""
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
columns: {
|
||||
type: [Object, Boolean],
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
version: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "",
|
||||
},
|
||||
hideLabel: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
containerClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "",
|
||||
},
|
||||
headerClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "",
|
||||
},
|
||||
inpClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "",
|
||||
},
|
||||
tabId: {
|
||||
type: [String, Number],
|
||||
required: false,
|
||||
default: contentIndex,
|
||||
},
|
||||
});
|
||||
|
||||
const checkboxEl = ref(null);
|
||||
|
|
@ -119,44 +118,53 @@ onMounted(() => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<Container :containerClass="`w-full m-1 p-1 ${props.containerClass}`" :columns="props.columns">
|
||||
<Header :required="props.required" :name="props.name" :label="props.label" :hideLabel="props.hideLabel" :headerClass="props.headerClass" />
|
||||
|
||||
<div class="relative z-10 flex flex-col items-start">
|
||||
<input
|
||||
ref="checkboxEl"
|
||||
:tabindex="props.tabId || contentIndex"
|
||||
@keyup.enter="$emit('inp', updateValue())"
|
||||
@click="$emit('inp', updateValue())"
|
||||
:id="props.id"
|
||||
<Container
|
||||
:containerClass="`w-full m-1 p-1 ${props.containerClass}`"
|
||||
:columns="props.columns"
|
||||
>
|
||||
<Header
|
||||
:required="props.required"
|
||||
:name="props.name"
|
||||
:disabled="props.disabled || false"
|
||||
:checked="checkbox.value === 'yes' ? true : false"
|
||||
:class="[
|
||||
'checkbox',
|
||||
checkbox.value === 'yes' ? 'check' : '',
|
||||
checkbox.isValid ? 'valid' : 'invalid',
|
||||
props.inpClass,
|
||||
]"
|
||||
type="checkbox"
|
||||
:value="checkbox.value"
|
||||
:required="props.required || false"
|
||||
:label="props.label"
|
||||
:hideLabel="props.hideLabel"
|
||||
:headerClass="props.headerClass"
|
||||
/>
|
||||
|
||||
<svg
|
||||
role="img"
|
||||
aria-hidden="true"
|
||||
v-show="checkbox.value === 'yes'"
|
||||
class="checkbox-svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
>
|
||||
<path
|
||||
class="pointer-events-none"
|
||||
d="M470.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L192 338.7 425.4 105.4c12.5-12.5 32.8-12.5 45.3 0z"
|
||||
></path>
|
||||
</svg>
|
||||
<ErrorField :isValid="checkbox.isValid" :isValue="checkbox.isValid" />
|
||||
</div>
|
||||
</Container>
|
||||
</template>
|
||||
<div class="relative z-10 flex flex-col items-start">
|
||||
<input
|
||||
ref="checkboxEl"
|
||||
:tabindex="props.tabId"
|
||||
@keyup.enter="$emit('inp', updateValue())"
|
||||
@click="$emit('inp', updateValue())"
|
||||
:id="props.id"
|
||||
:name="props.name"
|
||||
:disabled="props.disabled || false"
|
||||
:checked="checkbox.value === 'yes' ? true : false"
|
||||
:class="[
|
||||
'checkbox',
|
||||
checkbox.value === 'yes' ? 'check' : '',
|
||||
checkbox.isValid ? 'valid' : 'invalid',
|
||||
props.inpClass,
|
||||
]"
|
||||
type="checkbox"
|
||||
:value="checkbox.value"
|
||||
:required="props.required || false"
|
||||
/>
|
||||
|
||||
<svg
|
||||
role="img"
|
||||
aria-hidden="true"
|
||||
v-show="checkbox.value === 'yes'"
|
||||
class="checkbox-svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
>
|
||||
<path
|
||||
class="pointer-events-none"
|
||||
d="M470.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L192 338.7 425.4 105.4c12.5-12.5 32.8-12.5 45.3 0z"
|
||||
></path>
|
||||
</svg>
|
||||
<ErrorField :isValid="checkbox.isValid" :isValue="checkbox.isValid" />
|
||||
</div>
|
||||
</Container>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -40,56 +40,56 @@ import "@assets/css/flatpickr.dark.css";
|
|||
@param {boolean} [required=false]
|
||||
@param {string} [headerClass=""]
|
||||
@param {string} [containerClass=""]
|
||||
@param {string|number} [tabId=""]
|
||||
@param {string|number} [tabId=contentIndex] - The tabindex of the field, by default it is the contentIndex
|
||||
*/
|
||||
|
||||
const props = defineProps({
|
||||
// id && type && disabled && required && value
|
||||
id: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
name : {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
hideLabel: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
headerClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "",
|
||||
},
|
||||
containerClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "",
|
||||
},
|
||||
inpClass: {
|
||||
id: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
hideLabel: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
headerClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "",
|
||||
},
|
||||
containerClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "",
|
||||
},
|
||||
inpClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "",
|
||||
},
|
||||
|
||||
columns: {
|
||||
type: [Object, Boolean],
|
||||
required: false,
|
||||
default : false
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
columns: {
|
||||
type: [Object, Boolean],
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
defaultDate: {
|
||||
type: [String, Number, Date],
|
||||
required: false,
|
||||
|
|
@ -110,7 +110,7 @@ const props = defineProps({
|
|||
tabId: {
|
||||
type: [String, Number],
|
||||
required: false,
|
||||
default: ""
|
||||
default: contentIndex,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -134,7 +134,7 @@ onMounted(() => {
|
|||
time_24hr: true,
|
||||
minuteIncrement: 1,
|
||||
onChange(selectedDates, dateStr, instance) {
|
||||
if(!dateStr && props.required) return date.isValid = false;
|
||||
if (!dateStr && props.required) return (date.isValid = false);
|
||||
//Check if date is in interval
|
||||
try {
|
||||
const currStamp = Date.parse(dateStr);
|
||||
|
|
@ -193,7 +193,7 @@ onMounted(() => {
|
|||
function setMonthSelect(calendar, id) {
|
||||
// Hide default select and optionss
|
||||
const defaultSelect = calendar.querySelector(
|
||||
".flatpickr-monthDropdown-months",
|
||||
".flatpickr-monthDropdown-months"
|
||||
);
|
||||
defaultSelect.classList.add("hidden");
|
||||
defaultSelect.setAttribute("aria-hidden", "true");
|
||||
|
|
@ -210,7 +210,7 @@ function setMonthSelect(calendar, id) {
|
|||
container.classList.add(
|
||||
"flatpickr-monthDropdown-months",
|
||||
"inline",
|
||||
"relative",
|
||||
"relative"
|
||||
);
|
||||
// Select-like
|
||||
const selectCustom = document.createElement("button");
|
||||
|
|
@ -239,7 +239,7 @@ function setMonthSelect(calendar, id) {
|
|||
"text-white",
|
||||
"py-1",
|
||||
"hover:brightness-125",
|
||||
"focus:brightness-125",
|
||||
"focus:brightness-125"
|
||||
);
|
||||
opt.setAttribute("data-month", option.value);
|
||||
opt.setAttribute("data-value", option.value);
|
||||
|
|
@ -268,7 +268,7 @@ function setPickerAtt(calendarEl, id = false) {
|
|||
}
|
||||
|
||||
const inps = calendarEl.querySelectorAll(
|
||||
'input.numInput[type="number"][maxlength]',
|
||||
'input.numInput[type="number"][maxlength]'
|
||||
);
|
||||
inps.forEach((inp) => {
|
||||
inp.setAttribute("data-maxlength", inp.getAttribute("maxlength"));
|
||||
|
|
@ -315,7 +315,7 @@ function handleEvents(calendarEl, id, datepicker) {
|
|||
) {
|
||||
// Get update value
|
||||
const selectDefault = calendarEl.querySelector(
|
||||
"select.flatpickr-monthDropdown-months",
|
||||
"select.flatpickr-monthDropdown-months"
|
||||
);
|
||||
|
||||
let monthValue;
|
||||
|
|
@ -379,7 +379,7 @@ function handleEvents(calendarEl, id, datepicker) {
|
|||
if (e.key === "Tab" && e.shiftKey) {
|
||||
e.preventDefault();
|
||||
const currActive = calendarEl.querySelector(
|
||||
'[data-tabindex-active="true"]',
|
||||
'[data-tabindex-active="true"]'
|
||||
);
|
||||
if (!currActive) return;
|
||||
|
||||
|
|
@ -432,7 +432,7 @@ function handleEvents(calendarEl, id, datepicker) {
|
|||
//Focus previous element with a tabindex
|
||||
const currIndex = datepicker.input.getAttribute("tabindex");
|
||||
const elements = document.querySelectorAll(
|
||||
`input[tabindex="${currIndex}"]`,
|
||||
`input[tabindex="${currIndex}"]`
|
||||
);
|
||||
// Remove disabled elements
|
||||
const filtered = [];
|
||||
|
|
@ -472,7 +472,7 @@ function handleEvents(calendarEl, id, datepicker) {
|
|||
//Focus next element with a tabindex
|
||||
const currIndex = datepicker.input.getAttribute("tabindex");
|
||||
const elements = document.querySelectorAll(
|
||||
`input[tabindex="${currIndex}"]`,
|
||||
`input[tabindex="${currIndex}"]`
|
||||
);
|
||||
// Remove disabled elements
|
||||
const filtered = [];
|
||||
|
|
@ -509,7 +509,7 @@ function toggleSelect(calendar, id, e) {
|
|||
optCtnr.classList.toggle("hidden");
|
||||
optCtnr.setAttribute(
|
||||
"aria-hidden",
|
||||
optCtnr.classList.contains("hidden") ? "true" : "false",
|
||||
optCtnr.classList.contains("hidden") ? "true" : "false"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -544,7 +544,7 @@ function updateMonth(calendar, id, e, datepicker) {
|
|||
selectCustom.focus();
|
||||
// Click on default select to update
|
||||
const selectDefault = calendar.querySelector(
|
||||
"select.flatpickr-monthDropdown-months",
|
||||
"select.flatpickr-monthDropdown-months"
|
||||
);
|
||||
selectDefault.querySelectorAll("option").forEach((option) => {
|
||||
if (option.value === e.target.getAttribute("data-month")) {
|
||||
|
|
@ -586,14 +586,14 @@ function setIndex(calendarEl, tabindex) {
|
|||
const prevMonth = calendarEl.querySelector(".flatpickr-prev-month");
|
||||
const year = calendarEl.querySelector(".cur-year");
|
||||
const monthSelect = calendarEl.querySelector(
|
||||
".flatpickr-monthDropdown-months",
|
||||
".flatpickr-monthDropdown-months"
|
||||
);
|
||||
prevMonth.setAttribute("tabindex", tabindex);
|
||||
nextMonth.setAttribute("tabindex", tabindex);
|
||||
year.setAttribute("tabindex", tabindex);
|
||||
monthSelect.setAttribute("tabindex", tabindex);
|
||||
const months = calendarEl.querySelectorAll(
|
||||
".flatpickr-monthDropdown-month",
|
||||
".flatpickr-monthDropdown-month"
|
||||
);
|
||||
months.forEach((month) => {
|
||||
month.setAttribute("tabindex", tabindex);
|
||||
|
|
@ -613,45 +613,54 @@ function setIndex(calendarEl, tabindex) {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<Container :containerClass="`w-full m-1 p-1 ${props.containerClass}`" :columns="props.columns">
|
||||
<Header :required="props.required" :name="props.name" :label="props.label" :hideLabel="props.hideLabel" :headerClass="props.headerClass" />
|
||||
|
||||
<div class="relative flex flex-col items-start">
|
||||
<input
|
||||
:tabindex="props.tabId || contentIndex"
|
||||
:aria-controls="props.id"
|
||||
:aria-selected="picker.isOpen ? 'true' : 'false'"
|
||||
type="text"
|
||||
:class="[
|
||||
date.isValid ? 'valid' : 'invalid',
|
||||
'input-regular',
|
||||
props.inpClass,
|
||||
props.disabled ? 'cursor-not-allowed' : 'cursor-pointer',
|
||||
]"
|
||||
:id="props.id"
|
||||
:required="props.required || false"
|
||||
:disabled="props.disabled || false"
|
||||
<Container
|
||||
:containerClass="`w-full m-1 p-1 ${props.containerClass}`"
|
||||
:columns="props.columns"
|
||||
>
|
||||
<Header
|
||||
:required="props.required"
|
||||
:name="props.name"
|
||||
:placeholder="'mm/dd/yyyy h:m:s'"
|
||||
pattern="/^(0[1-9]|1[0-2])\/(0[1-9]|1\d|2\d|3[01])\/\d{4}$/g"
|
||||
:label="props.label"
|
||||
:hideLabel="props.hideLabel"
|
||||
:headerClass="props.headerClass"
|
||||
/>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="w-6 h-6 stroke-gray-600 opacity-50 pointer-events-none absolute top-1 md:top-1.5 right-2"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5"
|
||||
|
||||
<div class="relative flex flex-col items-start">
|
||||
<input
|
||||
:tabindex="props.tabId"
|
||||
:aria-controls="props.id"
|
||||
:aria-selected="picker.isOpen ? 'true' : 'false'"
|
||||
type="text"
|
||||
:class="[
|
||||
date.isValid ? 'valid' : 'invalid',
|
||||
'input-regular',
|
||||
props.inpClass,
|
||||
props.disabled ? 'cursor-not-allowed' : 'cursor-pointer',
|
||||
]"
|
||||
:id="props.id"
|
||||
:required="props.required || false"
|
||||
:disabled="props.disabled || false"
|
||||
:name="props.name"
|
||||
:placeholder="'mm/dd/yyyy h:m:s'"
|
||||
pattern="/^(0[1-9]|1[0-2])\/(0[1-9]|1\d|2\d|3[01])\/\d{4}$/g"
|
||||
/>
|
||||
</svg>
|
||||
<ErrorField :isValid="date.isValid" :isValue="!!date.value" />
|
||||
</div>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="w-6 h-6 stroke-gray-600 opacity-50 pointer-events-none absolute top-1 md:top-1.5 right-2"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5"
|
||||
/>
|
||||
</svg>
|
||||
<ErrorField :isValid="date.isValid" :isValue="!!date.value" />
|
||||
</div>
|
||||
</Container>
|
||||
</template>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -39,92 +39,90 @@ import ErrorField from "@components/Forms/Error/Field.vue";
|
|||
@param {string} [containerClass=""]
|
||||
@param {string} [inpClass=""]
|
||||
@param {string} [headerClass=""]
|
||||
@param {string|number} [tabId=""]
|
||||
@param {string|number} [tabId=contentIndex] - The tabindex of the field, by default it is the contentIndex
|
||||
*/
|
||||
|
||||
const props = defineProps({
|
||||
// id && value && method
|
||||
id: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
columns : {
|
||||
type : [Object, Boolean],
|
||||
required: false,
|
||||
default : false
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
pattern: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
clipboard: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
version: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
hideLabel: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
containerClass : {
|
||||
type: String,
|
||||
required: false,
|
||||
default : ""
|
||||
},
|
||||
headerClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default : ""
|
||||
},
|
||||
inpClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default : ""
|
||||
},
|
||||
tabId: {
|
||||
type: [String, Number],
|
||||
required: false,
|
||||
default : ""
|
||||
},
|
||||
|
||||
id: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
columns: {
|
||||
type: [Object, Boolean],
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
pattern: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
clipboard: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
version: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
hideLabel: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
containerClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "",
|
||||
},
|
||||
headerClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "",
|
||||
},
|
||||
inpClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "",
|
||||
},
|
||||
tabId: {
|
||||
type: [String, Number],
|
||||
required: false,
|
||||
default: contentIndex,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
const inputEl = ref(null);
|
||||
|
||||
const inp = reactive({
|
||||
|
|
@ -168,110 +166,119 @@ onMounted(() => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<Container :containerClass="`w-full m-1 p-1 ${props.containerClass}`" :columns="props.columns">
|
||||
<Header :required="props.required" :name="props.name" :label="props.label" :hideLabel="props.hideLabel" :headerClass="props.headerClass" />
|
||||
|
||||
<div class="relative flex flex-col items-start">
|
||||
<input
|
||||
:tabindex="props.tabId || contentIndex"
|
||||
ref="inputEl"
|
||||
v-model="inp.value"
|
||||
@input="
|
||||
() => {
|
||||
inp.isValid = inputEl.checkValidity();
|
||||
$emit('inp', inp.value);
|
||||
}
|
||||
"
|
||||
:id="props.id"
|
||||
:class="[
|
||||
'input-regular',
|
||||
inp.isValid ? 'valid' : 'invalid',
|
||||
props.inpClass,
|
||||
]"
|
||||
:required="props.required || false"
|
||||
:readonly="props.readonly || false"
|
||||
:disabled="props.disabled || false"
|
||||
:placeholder="props.placeholder || ''"
|
||||
:pattern="props.pattern || '(?s).*'"
|
||||
<Container
|
||||
:containerClass="`w-full m-1 p-1 ${props.containerClass}`"
|
||||
:columns="props.columns"
|
||||
>
|
||||
<Header
|
||||
:required="props.required"
|
||||
:name="props.name"
|
||||
:value="inp.value"
|
||||
:type="
|
||||
props.type === 'password'
|
||||
? inp.showInp
|
||||
? 'text'
|
||||
: 'password'
|
||||
: props.type
|
||||
"
|
||||
:label="props.label"
|
||||
:hideLabel="props.hideLabel"
|
||||
:headerClass="props.headerClass"
|
||||
/>
|
||||
<div
|
||||
v-if="props.clipboard && inp.isClipAllow"
|
||||
:class="[props.type === 'password' ? 'pw-input' : 'no-pw-input']"
|
||||
class="input-clipboard-container"
|
||||
>
|
||||
<button
|
||||
:tabindex="contentIndex"
|
||||
@click="copyClipboard()"
|
||||
:class="[props.disabled ? 'disabled' : 'enabled']"
|
||||
class="input-clipboard-button"
|
||||
:aria-describedby="`${props.id}-clipboard-text`"
|
||||
|
||||
<div class="relative flex flex-col items-start">
|
||||
<input
|
||||
:tabindex="props.tabId"
|
||||
ref="inputEl"
|
||||
v-model="inp.value"
|
||||
@input="
|
||||
() => {
|
||||
inp.isValid = inputEl.checkValidity();
|
||||
$emit('inp', inp.value);
|
||||
}
|
||||
"
|
||||
:id="props.id"
|
||||
:class="[
|
||||
'input-regular',
|
||||
inp.isValid ? 'valid' : 'invalid',
|
||||
props.inpClass,
|
||||
]"
|
||||
:required="props.required || false"
|
||||
:readonly="props.readonly || false"
|
||||
:disabled="props.disabled || false"
|
||||
:placeholder="props.placeholder || ''"
|
||||
:pattern="props.pattern || '(?s).*'"
|
||||
:name="props.name"
|
||||
:value="inp.value"
|
||||
:type="
|
||||
props.type === 'password'
|
||||
? inp.showInp
|
||||
? 'text'
|
||||
: 'password'
|
||||
: props.type
|
||||
"
|
||||
/>
|
||||
<div
|
||||
v-if="props.clipboard && inp.isClipAllow"
|
||||
:class="[props.type === 'password' ? 'pw-input' : 'no-pw-input']"
|
||||
class="input-clipboard-container"
|
||||
>
|
||||
<span :id="`${props.id}-clipboard-text`" class="sr-only"
|
||||
>{{ $t("inp_input_clipboard_desc") }}
|
||||
</span>
|
||||
<svg
|
||||
role="img"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="input-clipboard-svg"
|
||||
<button
|
||||
:tabindex="contentIndex"
|
||||
@click="copyClipboard()"
|
||||
:class="[props.disabled ? 'disabled' : 'enabled']"
|
||||
class="input-clipboard-button"
|
||||
:aria-describedby="`${props.id}-clipboard-text`"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M15.666 3.888A2.25 2.25 0 0 0 13.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 0 1-.75.75H9a.75.75 0 0 1-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 0 1-2.25 2.25H6.75A2.25 2.25 0 0 1 4.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 0 1 1.927-.184"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<span :id="`${props.id}-clipboard-text`" class="sr-only"
|
||||
>{{ $t("inp_input_clipboard_desc") }}
|
||||
</span>
|
||||
<svg
|
||||
role="img"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="input-clipboard-svg"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M15.666 3.888A2.25 2.25 0 0 0 13.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 0 1-.75.75H9a.75.75 0 0 1-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 0 1-2.25 2.25H6.75A2.25 2.25 0 0 1 4.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 0 1 1.927-.184"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="props.type === 'password'" class="input-pw-container">
|
||||
<button
|
||||
:tabindex="contentIndex"
|
||||
:aria-description="$t('inp_input_password_desc')"
|
||||
:aria-controls="props.id"
|
||||
@click="inp.showInp = inp.showInp ? false : true"
|
||||
:class="[props.disabled ? 'disabled' : 'enabled']"
|
||||
class="input-pw-button"
|
||||
>
|
||||
<svg
|
||||
role="img"
|
||||
aria-hidden="true"
|
||||
v-if="!inp.showInp"
|
||||
class="input-pw-svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 576 512"
|
||||
>
|
||||
<path
|
||||
d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM432 256c0 79.5-64.5 144-144 144s-144-64.5-144-144s64.5-144 144-144s144 64.5 144 144zM288 192c0 35.3-28.7 64-64 64c-11.5 0-22.3-3-31.6-8.4c-.2 2.8-.4 5.5-.4 8.4c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6z"
|
||||
/>
|
||||
</svg>
|
||||
<svg
|
||||
role="img"
|
||||
aria-hidden="true"
|
||||
v-if="inp.showInp"
|
||||
class="input-pw-svg scale-110"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 640 512"
|
||||
>
|
||||
<path
|
||||
d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c5.2-11.8 8-24.8 8-38.5c0-53-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zm223.1 298L373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<ErrorField :isValid="inp.isValid" :isValue="!!inp.value" />
|
||||
</div>
|
||||
<div v-if="props.type === 'password'" class="input-pw-container">
|
||||
<button
|
||||
:tabindex="contentIndex"
|
||||
:aria-description="$t('inp_input_password_desc')"
|
||||
:aria-controls="props.id"
|
||||
@click="inp.showInp = inp.showInp ? false : true"
|
||||
:class="[props.disabled ? 'disabled' : 'enabled']"
|
||||
class="input-pw-button"
|
||||
>
|
||||
<svg
|
||||
role="img"
|
||||
aria-hidden="true"
|
||||
v-if="!inp.showInp"
|
||||
class="input-pw-svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 576 512"
|
||||
>
|
||||
<path
|
||||
d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM432 256c0 79.5-64.5 144-144 144s-144-64.5-144-144s64.5-144 144-144s144 64.5 144 144zM288 192c0 35.3-28.7 64-64 64c-11.5 0-22.3-3-31.6-8.4c-.2 2.8-.4 5.5-.4 8.4c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6z"
|
||||
/>
|
||||
</svg>
|
||||
<svg
|
||||
role="img"
|
||||
aria-hidden="true"
|
||||
v-if="inp.showInp"
|
||||
class="input-pw-svg scale-110"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 640 512"
|
||||
>
|
||||
<path
|
||||
d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c5.2-11.8 8-24.8 8-38.5c0-53-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zm223.1 298L373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<ErrorField :isValid="inp.isValid" :isValue="!!inp.value" />
|
||||
</div>
|
||||
</Container>
|
||||
</template>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import Container from "@components/Widget/Container.vue";
|
|||
import Header from "@components/Forms/Header/Field.vue";
|
||||
import ErrorField from "@components/Forms/Error/Field.vue";
|
||||
|
||||
|
||||
/**
|
||||
@name Forms/Field/Select.vue
|
||||
@description This component is used to create a complete select field input with error handling and label.
|
||||
|
|
@ -36,82 +35,80 @@ import ErrorField from "@components/Forms/Error/Field.vue";
|
|||
@param {string} [containerClass=""]
|
||||
@param {string} [inpClass=""]
|
||||
@param {string} [headerClass=""]
|
||||
@param {string|number} [tabId=""]
|
||||
@param {string|number} [tabId=contentIndex] - The tabindex of the field, by default it is the contentIndex
|
||||
*/
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
// id && value && method
|
||||
id: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
columns: {
|
||||
type: [Object, Boolean],
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
values: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
requiredValues : {
|
||||
type: Array,
|
||||
required: false,
|
||||
default : []
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
version: {
|
||||
type: String,
|
||||
required: false,
|
||||
default : ""
|
||||
},
|
||||
hideLabel: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
containerClass : {
|
||||
type: String,
|
||||
required: false,
|
||||
default : ""
|
||||
},
|
||||
headerClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default : ""
|
||||
},
|
||||
inpClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default : ""
|
||||
},
|
||||
tabId: {
|
||||
type: [String, Number],
|
||||
required: false,
|
||||
default: ""
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
columns: {
|
||||
type: [Object, Boolean],
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
values: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
requiredValues: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: [],
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
version: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "",
|
||||
},
|
||||
hideLabel: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
containerClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "",
|
||||
},
|
||||
headerClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "",
|
||||
},
|
||||
inpClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "",
|
||||
},
|
||||
tabId: {
|
||||
type: [String, Number],
|
||||
required: false,
|
||||
default: contentIndex,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
// 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)
|
||||
|
|
@ -129,7 +126,13 @@ const select = reactive({
|
|||
// If we use select.value : props.value
|
||||
// Component will not re-render after props.value change
|
||||
value: "",
|
||||
isValid: !props.required ? true : props.requiredValues.length <= 0 ? true : props.requiredValues.includes(props.value) ? true : false,
|
||||
isValid: !props.required
|
||||
? true
|
||||
: props.requiredValues.length <= 0
|
||||
? true
|
||||
: props.requiredValues.includes(props.value)
|
||||
? true
|
||||
: false,
|
||||
});
|
||||
|
||||
const selectBtn = ref();
|
||||
|
|
@ -149,7 +152,13 @@ function changeValue(newValue) {
|
|||
// Then send the new value to parent
|
||||
select.value = newValue;
|
||||
// Check if value is required and if it is in requiredValues
|
||||
select.isValid = !props.required ? true : props.requiredValues.length <= 0 ? true : props.requiredValues.includes(newValue) ? true : false;
|
||||
select.isValid = !props.required
|
||||
? true
|
||||
: props.requiredValues.length <= 0
|
||||
? true
|
||||
: props.requiredValues.includes(newValue)
|
||||
? true
|
||||
: false;
|
||||
closeSelect();
|
||||
return newValue;
|
||||
}
|
||||
|
|
@ -187,86 +196,110 @@ const emits = defineEmits(["inp"]);
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<Container :containerClass="`w-full m-1 p-1 ${props.containerClass}`" :columns="props.columns">
|
||||
<Header :required="props.required" :name="props.name" :label="props.label" :hideLabel="props.hideLabel" :headerClass="props.headerClass" />
|
||||
|
||||
<select :name="props.name" class="hidden">
|
||||
<option
|
||||
v-for="(value, id) in props.values"
|
||||
:key="id"
|
||||
:value="value"
|
||||
@click="$emit('inp', changeValue(value))"
|
||||
:selected="select.value && select.value === value || !select.value && value === props.value ? true : false"
|
||||
<Container
|
||||
:containerClass="`w-full m-1 p-1 ${props.containerClass}`"
|
||||
:columns="props.columns"
|
||||
>
|
||||
{{ value }}
|
||||
</option>
|
||||
</select>
|
||||
<!-- end default select -->
|
||||
<Header
|
||||
:required="props.required"
|
||||
:name="props.name"
|
||||
:label="props.label"
|
||||
:hideLabel="props.hideLabel"
|
||||
:headerClass="props.headerClass"
|
||||
/>
|
||||
|
||||
<!--custom-->
|
||||
<div class="relative">
|
||||
<button
|
||||
:name="`${props.name}-custom`"
|
||||
:tabindex="props.tabId || contentIndex"
|
||||
ref="selectBtn"
|
||||
:aria-controls="`${props.id}-custom`"
|
||||
:aria-expanded="select.isOpen ? 'true' : 'false'"
|
||||
:aria-description="$t('inp_select_dropdown_button_desc')"
|
||||
data-select-dropdown
|
||||
:disabled="props.disabled || false"
|
||||
@click="toggleSelect()"
|
||||
:class="['select-btn', select.isValid ? 'valid' : 'invalid',
|
||||
props.inpClass]"
|
||||
>
|
||||
<span :id="`${props.id}-text`" class="select-btn-name">
|
||||
{{ select.value || props.value }}
|
||||
</span>
|
||||
<!-- chevron -->
|
||||
<svg
|
||||
role="img"
|
||||
aria-hidden="true"
|
||||
:class="[select.isOpen ? '-rotate-180' : '']"
|
||||
class="select-btn-svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
>
|
||||
<path
|
||||
d="M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z"
|
||||
/>
|
||||
</svg>
|
||||
<!-- end chevron -->
|
||||
</button>
|
||||
<!-- dropdown-->
|
||||
<div
|
||||
role="radiogroup"
|
||||
:style="{ width: selectWidth }"
|
||||
:id="`${props.id}-custom`"
|
||||
:class="[select.isOpen ? 'flex' : 'hidden']"
|
||||
class="select-dropdown-container"
|
||||
:aria-description="$t('inp_select_dropdown_desc')"
|
||||
>
|
||||
<button
|
||||
:tabindex="contentIndex"
|
||||
v-for="(value, id) in props.values"
|
||||
role="radio"
|
||||
@click="$emit('inp', changeValue(value))"
|
||||
:class="[
|
||||
id === 0 ? 'first' : '',
|
||||
id === props.values.length - 1 ? 'last' : '',
|
||||
value === select.value && select.value === value || !select.value && value === props.value ? 'active' : '',
|
||||
'select-dropdown-btn',
|
||||
]"
|
||||
:aria-controls="`${props.id}-text`"
|
||||
:aria-checked="select.value && select.value === value || !select.value && value === props.value ? 'true' : 'false'"
|
||||
>
|
||||
{{ value }}
|
||||
</button>
|
||||
</div>
|
||||
<ErrorField :isValid="select.isValid" :isValue="true" />
|
||||
<select :name="props.name" class="hidden">
|
||||
<option
|
||||
v-for="(value, id) in props.values"
|
||||
:key="id"
|
||||
:value="value"
|
||||
@click="$emit('inp', changeValue(value))"
|
||||
:selected="
|
||||
(select.value && select.value === value) ||
|
||||
(!select.value && value === props.value)
|
||||
? true
|
||||
: false
|
||||
"
|
||||
>
|
||||
{{ value }}
|
||||
</option>
|
||||
</select>
|
||||
<!-- end default select -->
|
||||
|
||||
<!-- end dropdown-->
|
||||
</div>
|
||||
<!-- end custom-->
|
||||
</Container>
|
||||
|
||||
</template>
|
||||
<!--custom-->
|
||||
<div class="relative">
|
||||
<button
|
||||
:name="`${props.name}-custom`"
|
||||
:tabindex="props.tabId"
|
||||
ref="selectBtn"
|
||||
:aria-controls="`${props.id}-custom`"
|
||||
:aria-expanded="select.isOpen ? 'true' : 'false'"
|
||||
:aria-description="$t('inp_select_dropdown_button_desc')"
|
||||
data-select-dropdown
|
||||
:disabled="props.disabled || false"
|
||||
@click="toggleSelect()"
|
||||
:class="[
|
||||
'select-btn',
|
||||
select.isValid ? 'valid' : 'invalid',
|
||||
props.inpClass,
|
||||
]"
|
||||
>
|
||||
<span :id="`${props.id}-text`" class="select-btn-name">
|
||||
{{ select.value || props.value }}
|
||||
</span>
|
||||
<!-- chevron -->
|
||||
<svg
|
||||
role="img"
|
||||
aria-hidden="true"
|
||||
:class="[select.isOpen ? '-rotate-180' : '']"
|
||||
class="select-btn-svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
>
|
||||
<path
|
||||
d="M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z"
|
||||
/>
|
||||
</svg>
|
||||
<!-- end chevron -->
|
||||
</button>
|
||||
<!-- dropdown-->
|
||||
<div
|
||||
role="radiogroup"
|
||||
:style="{ width: selectWidth }"
|
||||
:id="`${props.id}-custom`"
|
||||
:class="[select.isOpen ? 'flex' : 'hidden']"
|
||||
class="select-dropdown-container"
|
||||
:aria-description="$t('inp_select_dropdown_desc')"
|
||||
>
|
||||
<button
|
||||
:tabindex="contentIndex"
|
||||
v-for="(value, id) in props.values"
|
||||
role="radio"
|
||||
@click="$emit('inp', changeValue(value))"
|
||||
:class="[
|
||||
id === 0 ? 'first' : '',
|
||||
id === props.values.length - 1 ? 'last' : '',
|
||||
(value === select.value && select.value === value) ||
|
||||
(!select.value && value === props.value)
|
||||
? 'active'
|
||||
: '',
|
||||
'select-dropdown-btn',
|
||||
]"
|
||||
:aria-controls="`${props.id}-text`"
|
||||
:aria-checked="
|
||||
(select.value && select.value === value) ||
|
||||
(!select.value && value === props.value)
|
||||
? 'true'
|
||||
: 'false'
|
||||
"
|
||||
>
|
||||
{{ value }}
|
||||
</button>
|
||||
</div>
|
||||
<ErrorField :isValid="select.isValid" :isValue="true" />
|
||||
|
||||
<!-- end dropdown-->
|
||||
</div>
|
||||
<!-- end custom-->
|
||||
</Container>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ import Icons from "@components/Widget/Icons.vue";
|
|||
@param {string} [iconName=""] - Name in lowercase of icons store on /Icons. If falsy value, no icon displayed.
|
||||
@param {string} [iconColor=""]
|
||||
@param {object} [eventAttr={}] - Store event on click {"store" : <store_name>, "default" : <default_value>, "value" : <value_stored_on_click>, "target"<optional> : <target_id_element>, "valueExpanded" : "expanded_value"}
|
||||
@param {string|number} [tabId=""]
|
||||
@param {string|number} [tabId=contentIndex] - The tabindex of the field, by default it is the contentIndex
|
||||
*/
|
||||
|
||||
const eventStore = useEventStore();
|
||||
|
|
@ -99,16 +99,16 @@ const props = defineProps({
|
|||
required: false,
|
||||
default: {},
|
||||
},
|
||||
tabId: {
|
||||
type: [String, Number],
|
||||
required: false,
|
||||
default: "",
|
||||
},
|
||||
containerClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "",
|
||||
},
|
||||
tabId: {
|
||||
type: [String, Number],
|
||||
required: false,
|
||||
default: contentIndex,
|
||||
},
|
||||
});
|
||||
|
||||
const btnEl = ref();
|
||||
|
|
@ -174,7 +174,7 @@ function updateData(isClick = false) {
|
|||
ref="btnEl"
|
||||
@click="updateData(true)"
|
||||
:id="props.id"
|
||||
:tabindex="props.tabId || contentIndex"
|
||||
:tabindex="props.tabId"
|
||||
:class="[buttonClass]"
|
||||
:disabled="props.disabled || false"
|
||||
:aria-describedby="`text-${props.id}`"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<script setup>
|
||||
import { computed, ref, onMounted } from "vue";
|
||||
import { contentIndex } from "@utils/tabindex.js";
|
||||
|
||||
/**
|
||||
@name Widget/GridLayout.vue
|
||||
|
|
@ -19,6 +20,7 @@ import { computed, ref, onMounted } from "vue";
|
|||
@param {string} [link=""] - Will transform the container tag from a div to an a tag with the link as href. Useful with card type.
|
||||
@param {object} [columns={"pc": 12, "tablet": 12, "mobile": 12}] - Work with grid system { pc: 12, tablet: 12, mobile: 12}
|
||||
@param {string} [gridLayoutClass="items-start"] - Additional class
|
||||
@param {string} [tabId=contentIndex] - Case the container is converted to an anchor with a link, we can define the tabId, by default it is the contentIndex
|
||||
*/
|
||||
|
||||
const props = defineProps({
|
||||
|
|
@ -37,6 +39,11 @@ const props = defineProps({
|
|||
required: false,
|
||||
default: "",
|
||||
},
|
||||
tabId: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: contentIndex,
|
||||
},
|
||||
columns: {
|
||||
type: Object,
|
||||
required: false,
|
||||
|
|
@ -68,6 +75,7 @@ onMounted(() => {
|
|||
if (props.link) {
|
||||
gridLayoutEl.value.setAttribute("href", props.link);
|
||||
gridLayoutEl.value.setAttribute("rel", "noopener");
|
||||
gridLayoutEl.value.setAttribute("tabindex", props.tabId);
|
||||
}
|
||||
|
||||
if (props.link && props.link.startsWith("http")) {
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ const props = defineProps({
|
|||
type: String,
|
||||
required: true,
|
||||
},
|
||||
value: {
|
||||
stat: {
|
||||
type: [String, Number],
|
||||
required: true,
|
||||
},
|
||||
|
|
@ -75,7 +75,7 @@ const props = defineProps({
|
|||
]"
|
||||
>
|
||||
<TitleStat :title="props.title" />
|
||||
<ContentStat :value="props.value" />
|
||||
<ContentStat :stat="props.stat" />
|
||||
<SubtitleStat
|
||||
v-if="props.subtitle"
|
||||
:subtitle="props.subtitle"
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ onBeforeMount(() => {
|
|||
// title: "home_version",
|
||||
// subtitle: "home_all_features_available" if is_pro_version else "home_upgrade_pro",
|
||||
// subtitleColor: "success" is is_pro_version else "warning",
|
||||
// value: "home_pro" if is_pro_version else "home_free",
|
||||
// stat: "home_pro" if is_pro_version else "home_free",
|
||||
// iconName: "crown" if is_pro_version else "core",
|
||||
// iconColor: "amber",
|
||||
// },
|
||||
|
|
@ -54,7 +54,7 @@ onBeforeMount(() => {
|
|||
// title: "home_version_number",
|
||||
// subtitle: "home_latest_version" if is_latest_version else "home_upgrade_available",
|
||||
// subtitleColor: "success" if is_latest_version else "warning",
|
||||
// value: <current_version>,
|
||||
// stat: <current_version>,
|
||||
// iconName: "wire",
|
||||
// iconColor: "teal",
|
||||
// },
|
||||
|
|
@ -72,7 +72,7 @@ onBeforeMount(() => {
|
|||
// title: "home_instances",
|
||||
// subtitle: "home_total_number",
|
||||
// subtitleColor: "info",
|
||||
// value: "<instances_total>",
|
||||
// stat: "<instances_total>",
|
||||
// iconName: "box",
|
||||
// iconColor: "dark",
|
||||
// },
|
||||
|
|
@ -90,7 +90,7 @@ onBeforeMount(() => {
|
|||
// title: "home_services",
|
||||
// subtitle: "home_all_methods_included",
|
||||
// subtitleColor: "info",
|
||||
// value: "<services_total>",
|
||||
// stat: "<services_total>",
|
||||
// iconName: "disk",
|
||||
// iconColor: "orange",
|
||||
// },
|
||||
|
|
@ -108,7 +108,7 @@ onBeforeMount(() => {
|
|||
// title: "home_plugins",
|
||||
// subtitle: "home_no_error" if all_plugins_ok else "home_errors_found",
|
||||
// subtitleColor: "success" if all_plugins_ok else "error",
|
||||
// value: "<plugins_total>",
|
||||
// stat: "<plugins_total>",
|
||||
// iconName: "puzzle",
|
||||
// iconColor: "yellow",
|
||||
// },
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
data-server-flash='[{"type" : "success", "title" : "title", "message" : "Success feedback"}, {"type" : "error", "title" : "title", "message" : "Error feedback"}, {"type" : "warning", "title" : "title", "message" : "Warning feedback"}, {"type" : "info", "title" : "title", "message" : "Info feedback"}]'>
|
||||
</div>
|
||||
<div class="hidden"
|
||||
data-server-builder='[{"type":"card", "link" : "/services", "containerClass":"","containerColumns":{"pc":4,"tablet":6,"mobile":12},"widgets":[{"type":"Stat", "link": "https://github.com/bunkerity/bunkerweb","data":{"title":"home_version","subtitle":"home_all_features_available","subtitleColor":"success","value":"home_pro","iconName":"crown","iconColor":"amber"}}]},{"type":"card","containerClass":"","containerColumns":{"pc":4,"tablet":6,"mobile":12},"widgets":[{"type":"Stat","data":{"title":"home_version_number","subtitle":"home_latest_version","subtitleColor":"success","value":"1.5.7","iconName":"wire","iconColor":"teal"}}]},{"type":"card","containerClass":"","containerColumns":{"pc":4,"tablet":6,"mobile":12},"widgets":[{"type":"Stat","data":{"title":"home_instances","subtitle":"home_total_number","subtitleColor":"info", "value":"1","iconName":"box","iconColor":"dark"}}]}, {"type":"card","containerClass":"","containerColumns":{"pc":4,"tablet":6,"mobile":12},"widgets":[{"type":"Stat","data":{"title":"home_services","subtitle":"home_all_methods_included","subtitleColor":"info","value":"2","iconName":"disk","iconColor":"orange"}}]}, {"type":"card","containerClass":"","containerColumns":{"pc":4,"tablet":6,"mobile":12},"widgets":[{"type":"Stat","data":{"title":"home_plugins","subtitle":"home_no_error","subtitleColor":"success","value":"42","iconName":"puzzle","iconColor":"yellow"}}]}]'>
|
||||
data-server-builder='[{"type":"card", "link" : "/services", "containerClass":"","containerColumns":{"pc":4,"tablet":6,"mobile":12},"widgets":[{"type":"Stat", "link": "https://github.com/bunkerity/bunkerweb","data":{"title":"home_version","subtitle":"home_all_features_available","subtitleColor":"success","stat":"home_pro","iconName":"crown","iconColor":"amber"}}]},{"type":"card","containerClass":"","containerColumns":{"pc":4,"tablet":6,"mobile":12},"widgets":[{"type":"Stat","data":{"title":"home_version_number","subtitle":"home_latest_version","subtitleColor":"success","stat":"1.5.7","iconName":"wire","iconColor":"teal"}}]},{"type":"card","containerClass":"","containerColumns":{"pc":4,"tablet":6,"mobile":12},"widgets":[{"type":"Stat","data":{"title":"home_instances","subtitle":"home_total_number","subtitleColor":"info", "stat":"1","iconName":"box","iconColor":"dark"}}]}, {"type":"card","containerClass":"","containerColumns":{"pc":4,"tablet":6,"mobile":12},"widgets":[{"type":"Stat","data":{"title":"home_services","subtitle":"home_all_methods_included","subtitleColor":"info","stat":"2","iconName":"disk","iconColor":"orange"}}]}, {"type":"card","containerClass":"","containerColumns":{"pc":4,"tablet":6,"mobile":12},"widgets":[{"type":"Stat","data":{"title":"home_plugins","subtitle":"home_no_error","subtitleColor":"success","stat":"42","iconName":"puzzle","iconColor":"yellow"}}]}]'>
|
||||
</div>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="home.js"></script>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import { createI18n } from "vue-i18n";
|
||||
import en from "@lang/en.json" assert { type: "json" };
|
||||
import fr from "@lang/fr.json" assert { type: "json" };
|
||||
|
||||
/**
|
||||
@name lang.js
|
||||
|
|
@ -8,9 +10,6 @@ import { createI18n } from "vue-i18n";
|
|||
We need to instanciate the i18n object in the main file of the application inside /pages.
|
||||
*/
|
||||
|
||||
import en from "@lang/en.json" assert { type: "json" };
|
||||
import fr from "@lang/fr.json" assert { type: "json" };
|
||||
|
||||
const availablesLangs = ["en", "fr"];
|
||||
|
||||
function getAllLang() {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
1
vuejs/static/css/flag-icons.min.css
vendored
1
vuejs/static/css/flag-icons.min.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1,17 +0,0 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/x-icon" href="/images/favicon.ico" />
|
||||
<link rel="stylesheet" href="/css/style.css" />
|
||||
<link rel="stylesheet" href="/css/flag-icons.min.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>BunkerWeb | TEST</title>
|
||||
<script type="module" crossorigin src="/assets/test-e985cac8.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div data-default-value="DEFAULT TITLE" data-flask="{{ flask_data }}" class="hidden"></div>
|
||||
<div id="app"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in a new issue