icons are now global + widget icons

* now icons are not related to a specific component but are global
* each icon can have any define color
* each widget and components using a icon have a class to set the right icon style
* create widget icons that contains all available icons
This commit is contained in:
Jordan Blasenhauer 2024-05-22 13:06:51 +02:00
parent d3ff909019
commit d74521e7e6
62 changed files with 1488 additions and 2911 deletions

View file

@ -1,181 +0,0 @@
import { ref, reactive, watch, onMounted, defineEmits, defineProps } from "vue";
import { contentIndex } from "@utils/tabindex.js";
import Container from "@components/Widget/Container.vue";
import Header from "@components/Forms/Header/Field.vue";
import ErrorField from "@components/Forms/Error/Field.vue";
/**
@name Select.vue
@description This component is used to create a complete select field input with error handling and label.
We can be more precise by adding values that need to be selected to be valid.
We can also add popover to display more information.
It is mainly use in forms.
@example
{
id: 'test-input',
value: 'yes',
values : ['yes', 'no'],
name: 'test-input',
disabled: false,
required: true,
requiredValues : ['no'], // need required to be checked
label: 'Test select',
}
@param {string} id
@param {string} text - content of the button
@param {string} [type="button"] - button || submit
@param {boolean} [disabled=false]
@param {boolean} [hideText=false] - hide text to only display icon
@param {string} [color="primary"]
@param {string} [size="normal"] - sm || normal || lg || xl
@param {string} [iconName=""] - store on components/Icons/Button
@param {string} [iconColor=""]
@param {object} [eventAttr={}] - {"store" : <store_name>, "default" : <default_value>, "value" : <value_stored_on_click>, "target"<optional> : <target_id_element>, "valueExpanded" : "expanded_value"}
@param {string|number} [tabId=""]
*/
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: ""
},
});
// 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
// Then on new select we will switch to select.value
// 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,
});
const selectBtn = ref();
const selectWidth = ref("");
// EVENTS
function toggleSelect() {
select.isOpen = select.isOpen ? false : true;
}
function closeSelect() {
select.isOpen = false;
}
function changeValue(newValue) {
// Allow on template to switch from prop value to component own value
// 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;
closeSelect();
return newValue;
}
// Close select dropdown when clicked outside element
watch(select, () => {
if (select.isOpen) {
document.querySelector("body").addEventListener("click", closeOutside);
} else {
document.querySelector("body").removeEventListener("click", closeOutside);
}
});
// Close select when clicked outside logic
function closeOutside(e) {
try {
if (e.target !== selectBtn.value) {
select.isOpen = false;
}
} catch (err) {
select.isOpen = false;
}
}
onMounted(() => {
selectWidth.value = `${selectBtn.value.clientWidth}px`;
window.addEventListener("resize", () => {
try {
selectWidth.value = `${selectBtn.value.clientWidth}px`;
} catch (err) {}
});
});
const emits = defineEmits(["inp"]);

Binary file not shown.

View file

@ -1,68 +0,0 @@
<script setup>
import { reactive, onBeforeMount } from "vue";
import Checkbox from "@components/Forms/Field/Checkbox.vue";
import Select from "@components/Forms/Field/Select.vue";
import Input from "@components/Forms/Field/Input.vue";
import Datepicker from "@components/Forms/Field/Datepicker.vue";
import Button from "@components/Widget/Button.vue";
import GridLayout from "@components/Widget/GridLayout.vue";
import Grid from "@components/Widget/Grid.vue";
/**
@name Builder.vue
@description This component is a wrapper to create a complete page using containers and widgets.
We have to define each container and each widget inside it.
This is an abstract component that will be used to create any kind of page content (base dashboard elements like menu and news excluded)
@example
[
{
"type": "card", // this can be a "card", "modal", "table"... etc
"containerClass": "", // tailwind css grid class (items-start, ...)
"containerColumns" : {"pc": 12, "tablet": 12, "mobile": 12},
"title" : "My awesome card", // container title
// Each widget need a name (here type) and associated data
// We need to send specific data for each widget type
widgets: [
{
type : "Checkbox",
data : {containerClass : "", columns : {"pc": 6, "tablet": 12, "mobile": 12}, id:"test-check", value: "yes", label: "Checkbox", name: "checkbox", required: true, version: "v1.0.0", hideLabel: false, headerClass: "text-red-500" }
}, {
type : "Select",
data : {containerClass : "", columns : {"pc": 6, "tablet": 12, "mobile": 12}, id: 'test-select', value: 'yes', values: ['yes', 'no'], name: 'test-select', disabled: false, required: true, label: 'Test select', tabId: '1',}
}
]
}
]
@param {array} builder - Array of containers and widgets
*/
const props = defineProps({
builder : {
type: Array,
required: true,
},
})
</script>
<template>
<div class="grid grid-cols-12">
<!-- top level grid (layout) -->
<GridLayout v-for="(container, index) in props.builder" :key="index"
:gridLayoutClass="container.containerClass"
:type="container.type"
:title="container.title"
:columns="container.containerColumns">
<!-- widget grid -->
<Grid>
<!-- widget element -->
<template v-for="(widget, index) in container.widgets" :key="index">
<Checkbox v-if="widget.type === 'Checkbox'" v-bind="widget.data"></Checkbox>
<Select v-if="widget.type === 'Select'" v-bind="widget.data"></Select>
<Input v-if="widget.type === 'Input'" v-bind="widget.data"></Input>
<Datepicker v-if="widget.type === 'Datepicker'" v-bind="widget.data"></Datepicker>
<Button v-if="widget.type === 'Button'" v-bind="widget.data"></Button>
</template>
</Grid>
</GridLayout>
</div>
</template>

View file

@ -1,68 +0,0 @@
<script setup>
import { reactive, onBeforeMount } from "vue";
import Checkbox from "@components/Forms/Field/Checkbox.vue";
import Select from "@components/Forms/Field/Select.vue";
import Input from "@components/Forms/Field/Input.vue";
import Datepicker from "@components/Forms/Field/Datepicker.vue";
import Button from "@components/Widget/Button.vue";
import GridLayout from "@components/Widget/GridLayout.vue";
import Grid from "@components/Widget/Grid.vue";
/**
@name Builder.vue
@description This component is a wrapper to create a complete page using containers and widgets.
We have to define each container and each widget inside it.
This is an abstract component that will be used to create any kind of page content (base dashboard elements like menu and news excluded)
@example
[
{
"type": "card", // this can be a "card", "modal", "table"... etc
"containerClass": "", // tailwind css grid class (items-start, ...)
"containerColumns" : {"pc": 12, "tablet": 12, "mobile": 12},
"title" : "My awesome card", // container title
// Each widget need a name (here type) and associated data
// We need to send specific data for each widget type
widgets: [
{
type : "Checkbox",
data : {containerClass : "", columns : {"pc": 6, "tablet": 12, "mobile": 12}, id:"test-check", value: "yes", label: "Checkbox", name: "checkbox", required: true, version: "v1.0.0", hideLabel: false, headerClass: "text-red-500" }
}, {
type : "Select",
data : {containerClass : "", columns : {"pc": 6, "tablet": 12, "mobile": 12}, id: 'test-select', value: 'yes', values: ['yes', 'no'], name: 'test-select', disabled: false, required: true, label: 'Test select', tabId: '1',}
}
]
}
]
@param {array} builder - Array of containers and widgets
*/
const props = defineProps({
builder : {
type: Array,
required: true,
},
})
</script>
<template>
<div class="grid grid-cols-12">
<!-- top level grid (layout) -->
<GridLayout v-for="(container, index) in props.builder" :key="index"
:gridLayoutClass="container.containerClass"
:type="container.type"
:title="container.title"
:columns="container.containerColumns">
<!-- widget grid -->
<Grid>
<!-- widget element -->
<template v-for="(widget, index) in container.widgets" :key="index">
<Checkbox v-if="widget.type === 'Checkbox'" v-bind="widget.data"></Checkbox>
<Select v-if="widget.type === 'Select'" v-bind="widget.data"></Select>
<Input v-if="widget.type === 'Input'" v-bind="widget.data"></Input>
<Datepicker v-if="widget.type === 'Datepicker'" v-bind="widget.data"></Datepicker>
<Button v-if="widget.type === 'Button'" v-bind="widget.data"></Button>
</template>
</Grid>
</GridLayout>
</div>
</template>

View file

@ -1,43 +0,0 @@
<script setup>
/**
@name Forms/Error/Field.vue
@description This component is used to display a feedback message to user when a field is invalid.
It is used with /Forms/Field components.
@example
{
isValid: false,
isValue: false,
}
@param {boolean} [isValid=false] - Check if the field is valid
@param {boolean} [isValue=false] - Check if the field has a value, display a different message if the field is empty or not
*/
const props = defineProps({
isValid: {
type: Boolean,
required: false,
},
isValue : {
type: Boolean,
required: false,
}
})
</script>
<template>
<p
:aria-hidden="props.isValid ? 'true' : 'false'"
role="alert"
:class="[props.isValid ? 'hidden' : '']"
class="input-error-msg"
>
{{
props.isValid
? $t("inp_input_valid")
: !props.isValue
? $t("inp_input_error_required")
: $t("inp_input_error")
}}
</p>
</template>

View file

@ -1,162 +0,0 @@
<script setup>
import { reactive, defineProps, onMounted, ref } from "vue";
import { contentIndex } from "@utils/tabindex.js";
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.
We can also add popover to display more information.
It is mainly use in forms.
@example
{
columns : {"pc": 6, "tablet": 12, "mobile": 12},
id:"test-check",
value: "yes",
label: "Checkbox",
name: "checkbox",
required: true,
hideLabel: false,
headerClass: "text-red-500"
}
@param {string} id
@param {string} name
@param {string} label
@param {string} value
@param {boolean} [disabled=false]
@param {boolean} [required=false]
@param {object} [columns={"pc": "12", "tab": "12", "mob": "12}]
@param {boolean} [hideLabel=false]
@param {string} [containerClass=""]
@param {string} [headerClass=""]
@param {string} [inpClass=""]
@param {string|number} [tabId=""]
*/
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: ""
},
});
const checkboxEl = ref(null);
const checkbox = reactive({
value: props.value,
isValid: false,
});
const emits = defineEmits(["inp"]);
function updateValue() {
checkbox.value = checkbox.value === "yes" ? "no" : "yes";
checkbox.isValid = checkboxEl.value.checkValidity();
return checkbox.value;
}
onMounted(() => {
checkbox.isValid = checkboxEl.value.checkValidity();
});
</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"
: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>

View file

@ -1,657 +0,0 @@
<script setup>
import { reactive, defineEmits, defineProps, onMounted } from "vue";
import { contentIndex } from "@utils/tabindex.js";
import Container from "@components/Widget/Container.vue";
import Header from "@components/Forms/Header/Field.vue";
import ErrorField from "@components/Forms/Error/Field.vue";
import flatpickr from "flatpickr";
import "@assets/css/datepicker-foundation.css";
import "@assets/css/flatpickr.css";
import "@assets/css/flatpickr.dark.css";
/**
@name Forms/Field/Datepicker.vue
@description This component is used to create a complete datepicker field input with error handling and label.
You can define a default date, a min and max date, and a format.
We can also add popover to display more information.
It is mainly use in forms.
@example
{
id: 'test-date',
columns : {"pc": 6, "tablet": 12, "mobile": 12},
disabled: false,
required: true,
defaultDate: 1735682600000,
noPickBeforeStamp: 1735682600000,
noPickAfterStamp: 1735689600000,
inpClass: "text-center",
}
@param {string} id
@param {string} name
@param {string} label
@param {string|number|date} [defaultDate=null] - Default date when instanciate
@param {string|number} [noPickBeforeStamp=""] - Impossible to pick a date before this date
@param {string|number} [noPickAfterStamp=""] - Impossible to pick a date after this date
@param {boolean} [hideLabel=false]
@param {object|boolean} [columns={"pc": "12", "tab": "12", "mob": "12}]
@param {boolean} [disabled=false]
@param {boolean} [required=false]
@param {string} [headerClass=""]
@param {string} [containerClass=""]
@param {string|number} [tabId=""]
*/
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: {
type: String,
required: false,
default: "",
},
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,
default: null,
},
// Impossible to pick a date before this date
noPickBeforeStamp: {
type: [String, Number],
required: false,
default: "",
},
// Impossible to pick a date after this date
noPickAfterStamp: {
type: [String, Number],
required: false,
default: "",
},
tabId: {
type: [String, Number],
required: false,
default: ""
},
});
const date = reactive({
isValid: false,
format: "m/d/Y H:i:S",
});
const picker = reactive({
isOpen: false,
});
let datepicker;
onMounted(() => {
datepicker = flatpickr(`#${props.id}`, {
locale: "en",
dateFormat: date.format,
defaultDate: props.defaultDate || "",
enableTime: true,
enableSeconds: true,
time_24hr: true,
minuteIncrement: 1,
onChange(selectedDates, dateStr, instance) {
if(!dateStr && props.required) return date.isValid = false;
//Check if date is in interval
try {
const currStamp = Date.parse(dateStr);
// Check pick is before min allow
if (props.noPickBeforeStamp && currStamp < props.noPickBeforeStamp) {
return instance.setDate(props.noPickBeforeStamp);
}
// Check pick is after min allow
if (props.noPickAfterStamp && currStamp > props.noPickAfterStamp) {
return instance.setDate(props.noPickAfterStamp);
}
// Run whatever, if invalid this will override
date.isValid = true;
} catch (err) {}
},
onOpen(selectedDates, dateStr, instance) {
picker.isOpen = true;
// Focus on current date and update tabindex
try {
setIndex(instance.calendarContainer, contentIndex);
const baseFocus =
instance.calendarContainer.querySelector(".flatpickr-day.today") ||
instance.calendarContainer.querySelector(".flatpickr-day");
baseFocus.setAttribute("data-tabindex-active", true);
setTimeout(() => {
baseFocus.focus();
}, 50);
} catch (err) {}
},
onClose(selectedDates, dateStr, instance) {
picker.isOpen = false;
setIndex(instance.calendarContainer, "-1");
},
});
// Check if multiple or not
let datepickerEl = null;
if (Array.isArray(datepicker)) {
datepickerEl = datepicker[datepicker.length - 1];
} else {
datepickerEl = datepicker;
}
// Set valid date state
if (!datepickerEl.selectedDates[0] && props.required) date.isValid = false;
if (!datepickerEl.selectedDates[0] && !props.required) date.isValid = true;
const calendar = datepickerEl.calendarContainer;
// Impossible to use default select month dropdown with keyboard
// We need to create our own and link calendar to it
setMonthSelect(calendar, props.id);
// Override default behavior that go to input el instead of previous calendat element on tab + maj
handleEvents(calendar, props.id, datepickerEl);
setPickerAtt(calendar, props.id);
});
function setMonthSelect(calendar, id) {
// Hide default select and optionss
const defaultSelect = calendar.querySelector(
".flatpickr-monthDropdown-months",
);
defaultSelect.classList.add("hidden");
defaultSelect.setAttribute("aria-hidden", "true");
defaultSelect.setAttribute("tabindex", "-1");
defaultSelect.querySelectorAll("option").forEach((option) => {
option.classList.add("hidden");
option.setAttribute("tabindex", "-1");
option.setAttribute("aria-hidden", "true");
});
// Create custom select
// Container
const container = document.createElement("div");
container.classList.add(
"flatpickr-monthDropdown-months",
"inline",
"relative",
);
// Select-like
const selectCustom = document.createElement("button");
selectCustom.setAttribute("data-interactive", "");
selectCustom.setAttribute("aria-label", "Month");
selectCustom.setAttribute("data-months-select", "");
selectCustom.setAttribute("aria-controls", `${id}-custom`);
container.appendChild(selectCustom);
// Options container
const optCtnr = document.createElement("div");
optCtnr.setAttribute("role", "radiogroup");
optCtnr.setAttribute("id", `${id}-custom`);
optCtnr.classList.add("select-dropdown-container", "hidden", "flex");
container.appendChild(optCtnr);
// Options
calendar
.querySelector(".flatpickr-monthDropdown-months")
.querySelectorAll("option")
.forEach((option) => {
// Prepare options
const opt = document.createElement("button");
opt.classList.add(
"flatpickr-monthDropdown-month",
"rounded-none",
"text-white",
"py-1",
"hover:brightness-125",
"focus:brightness-125",
);
opt.setAttribute("data-month", option.value);
opt.setAttribute("data-value", option.value);
opt.setAttribute("data-interactive", "");
opt.setAttribute("role", "radio");
opt.setAttribute("aria-checked", option.selected ? "true" : "false");
opt.setAttribute("aria-label", option.textContent);
opt.setAttribute("aria-controls", `${id}-custom`);
opt.textContent = option.textContent;
// Set select as button content
if (option.selected) {
selectCustom.textContent = option.textContent;
}
// Append options
optCtnr.appendChild(opt);
});
// Insert as sibling of select
defaultSelect.parentNode.insertBefore(container, defaultSelect.nextSibling);
}
function setPickerAtt(calendarEl, id = false) {
// change error non-standard attributes
if (id) {
calendarEl.setAttribute("id", id);
}
const inps = calendarEl.querySelectorAll(
'input.numInput[type="number"][maxlength]',
);
inps.forEach((inp) => {
inp.setAttribute("data-maxlength", inp.getAttribute("maxlength"));
inp.removeAttribute("maxlength");
});
// set role button
calendarEl.querySelectorAll(".flatpickr-day").forEach((el) => {
el.setAttribute("role", "button");
});
calendarEl
.querySelector(".flatpickr-prev-month")
.setAttribute("role", "button");
calendarEl
.querySelector(".flatpickr-next-month")
.setAttribute("role", "button");
// Prevent svg to be focusable
calendarEl.querySelectorAll("svg").forEach((svg) => {
svg.classList.add("pointer-events-none");
});
}
function handleEvents(calendarEl, id, datepicker) {
calendarEl.addEventListener("click", (e) => {
// Close dropdown month select if click outside
closeSelectByDefault(calendarEl, id, e);
// Remove prev focus el and replace by click one if is tabindex element
updateIndex(calendarEl, e.target);
// When month change, update tabindex and update custom select
if (
e.target.classList.contains("flatpickr-prev-month") ||
e.target.classList.contains("flatpickr-next-month") ||
e.target.classList.contains("flatpickr-monthDropdown-month")
) {
setIndex(calendarEl, contentIndex);
}
// When click on next or prev month button
// Update custom select and options
if (
e.target.classList.contains("flatpickr-prev-month") ||
e.target.classList.contains("flatpickr-next-month")
) {
// Get update value
const selectDefault = calendarEl.querySelector(
"select.flatpickr-monthDropdown-months",
);
let monthValue;
let monthName;
selectDefault.querySelectorAll("option").forEach((option) => {
if (option.selected) {
monthValue = option.value;
monthName = option.textContent;
}
});
// Update options
calendarEl.querySelectorAll("[data-month]").forEach((el) => {
el.setAttribute("aria-checked", "false");
el.classList.remove("active");
if (el.getAttribute("data-month") === monthValue) {
el.setAttribute("aria-checked", "true");
el.classList.add("active");
}
});
// Update select text
const selectCustom = calendarEl.querySelector("[data-months-select]");
selectCustom.textContent = monthName;
selectCustom.focus();
}
// When click on custom select toggle
toggleSelect(calendarEl, id, e);
// When click on custom select option
updateMonth(calendarEl, id, e, datepicker);
});
calendarEl.addEventListener("keydown", (e) => {
// Space or enter logic
if (
(e.key !== "Tab" && !e.shiftKey && e.keyCode === 13) ||
(e.key !== "Tab" && !e.shiftKey && e.keyCode === 32)
) {
// Prev or next month button
if (
e.target.classList.contains("flatpickr-prev-month") ||
e.target.classList.contains("flatpickr-next-month")
) {
e.preventDefault();
e.target.click();
}
// Close dropdown month select if target isn't select
closeSelectByDefault(calendarEl, id, e);
// Custom select toggle
toggleSelect(calendarEl, id, e);
// Custom select option
updateMonth(calendarEl, id, e, datepicker);
}
let prevEl = null;
// Override default tab + maj behavior that focus input instead of previous calendar element
if (e.key === "Tab" && e.shiftKey) {
e.preventDefault();
const currActive = calendarEl.querySelector(
'[data-tabindex-active="true"]',
);
if (!currActive) return;
try {
// Case day, get prev day or next month el if no day remaining
if (currActive.classList.contains("flatpickr-day"))
prevEl =
currActive.previousElementSibling ||
calendarEl.querySelector(".flatpickr-next-month") ||
null;
// Case months
if (currActive.classList.contains("flatpickr-next-month"))
prevEl = calendarEl.querySelector(".cur-year") || null;
if (currActive.hasAttribute("data-months-select"))
prevEl = calendarEl.querySelector(".flatpickr-prev-month") || null;
if (currActive.hasAttribute("data-month"))
prevEl =
currActive.previousElementSibling ||
calendarEl.querySelector("[data-months-select]") ||
null;
// Case first datepicker element, go to input
if (currActive.classList.contains("flatpickr-prev-month"))
prevEl = null;
// Case year
if (currActive.classList.contains("cur-year"))
prevEl = calendarEl.querySelector("[data-months-select]") || null;
// Case hours
if (currActive.classList.contains("flatpickr-hour"))
prevEl =
calendarEl.querySelector(".dayContainer").lastElementChild || null;
// Case minutes
if (currActive.classList.contains("flatpickr-minute"))
prevEl = calendarEl.querySelector(".flatpickr-hour") || null;
// Case minutes
if (currActive.classList.contains("flatpickr-second"))
prevEl = calendarEl.querySelector(".flatpickr-minute") || null;
// Focus or close
if (prevEl) prevEl.focus();
if (!prevEl) {
//Focus previous element with a tabindex
const currIndex = datepicker.input.getAttribute("tabindex");
const elements = document.querySelectorAll(
`input[tabindex="${currIndex}"]`,
);
// Remove disabled elements
const filtered = [];
elements.forEach((el) => {
if (el === datepicker.input) return filtered.push(el);
if (
el.hasAttribute("disabled") ||
el.className.includes("flatpickr")
)
return;
filtered.push(el);
});
// Get previous element
let focusEl;
filtered.forEach((el, id) => {
if (el !== datepicker.input) return;
focusEl = filtered[id - 1];
});
// Focus new one
datepicker.close();
setTimeout(() => {
focusEl.focus();
}, 50);
}
} catch (e) {}
}
// Override when seconds
if (
e.keyCode === "Tab" &&
!e.shiftKey &&
calendarEl
.querySelector('[data-tabindex-active="true"]')
.classList.contains("flatpickr-second")
) {
try {
//Focus next element with a tabindex
const currIndex = datepicker.input.getAttribute("tabindex");
const elements = document.querySelectorAll(
`input[tabindex="${currIndex}"]`,
);
// Remove disabled elements
const filtered = [];
elements.forEach((el) => {
if (el === datepicker.input) return filtered.push(el);
if (el.hasAttribute("disabled") || el.className.includes("flatpickr"))
return;
filtered.push(el);
});
// Get next element
let focusEl;
filtered.forEach((el, id) => {
if (el !== datepicker.input) return;
focusEl = filtered[id + 1];
});
// Focus new one
datepicker.close();
setTimeout(() => {
focusEl.focus();
}, 50);
} catch (e) {}
}
// Global
setPickerAtt(calendarEl, false);
setIndex(calendarEl, contentIndex);
return updateIndex(calendarEl, prevEl || document.activeElement);
});
}
function toggleSelect(calendar, id, e) {
if (e.target.hasAttribute("data-months-select")) {
const optCtnr = calendar.querySelector(`#${id}-custom`);
optCtnr.classList.toggle("hidden");
optCtnr.setAttribute(
"aria-hidden",
optCtnr.classList.contains("hidden") ? "true" : "false",
);
}
}
function closeSelectByDefault(calendar, id, e) {
if (!e.target.hasAttribute("data-months-select")) {
const optCtnr = calendar.querySelector(`#${id}-custom`);
if (!optCtnr.classList.contains("hidden")) {
optCtnr.classList.add("hidden");
optCtnr.setAttribute("aria-hidden", "true");
}
}
}
function updateMonth(calendar, id, e, datepicker) {
if (e.target.hasAttribute("data-month")) {
// Close dropdown
const optCtnr = calendar.querySelector(`#${id}-custom`);
optCtnr.classList.add("hidden");
optCtnr.setAttribute("aria-hidden", "true");
// Update options
calendar.querySelectorAll("data-month").forEach((el) => {
el.setAttribute("aria-checked", "false");
el.classList.remove("active");
});
e.target.setAttribute("aria-checked", "true");
e.target.classList.add("active");
// Update select text
const selectCustom = calendar.querySelector("[data-months-select]");
selectCustom.textContent = e.target.textContent;
selectCustom.focus();
// Click on default select to update
const selectDefault = calendar.querySelector(
"select.flatpickr-monthDropdown-months",
);
selectDefault.querySelectorAll("option").forEach((option) => {
if (option.value === e.target.getAttribute("data-month")) {
datepicker.changeMonth(parseInt(option.value, 10) - 1, false);
option.selected = true;
}
});
}
}
function updateIndex(calendarEl, target) {
if (target.hasAttribute("tabindex")) {
calendarEl.querySelectorAll("[data-tabindex-active]").forEach((el) => {
el.removeAttribute("data-tabindex-active");
});
target.setAttribute("data-tabindex-active", true);
}
}
function setIndex(calendarEl, tabindex) {
try {
const days = calendarEl.querySelectorAll(".flatpickr-day");
days.forEach((day) => {
day.setAttribute("tabindex", tabindex);
});
} catch (e) {}
try {
const customSelectEls = calendarEl.querySelectorAll("[data-interactive]");
customSelectEls.forEach((el) => {
el.setAttribute("tabindex", tabindex);
});
} catch (err) {}
try {
const nextMonth = calendarEl.querySelector(".flatpickr-next-month");
const prevMonth = calendarEl.querySelector(".flatpickr-prev-month");
const year = calendarEl.querySelector(".cur-year");
const monthSelect = calendarEl.querySelector(
".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",
);
months.forEach((month) => {
month.setAttribute("tabindex", tabindex);
});
} catch (e) {}
try {
const hour = calendarEl.querySelector(".numInput.flatpickr-hour");
const minute = calendarEl.querySelector(".numInput.flatpickr-minute");
const second = calendarEl.querySelector(".numInput.flatpickr-second");
hour.setAttribute("tabindex", tabindex);
minute.setAttribute("tabindex", tabindex);
second.setAttribute("tabindex", tabindex);
} catch (e) {}
}
</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"
: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
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>

View file

@ -1,276 +0,0 @@
<script setup>
import { reactive, ref, defineEmits, onMounted, defineProps } from "vue";
import { contentIndex } from "@utils/tabindex.js";
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/Input.vue
@description This component is used to create a complete input field input with error handling and label.
We can add a clipboard button to copy the input value.
We can also add a password button to show the password.
We can also add popover to display more information.
It is mainly use in forms.
@example
{
id: 'test-input',
value: 'yes',
type: "text",
name: 'test-input',
disabled: false,
required: true,
label: 'Test input',
pattern : "(test)",
}
@param {string} id
@param {string} name
@param {string} type - text, email, password, number, tel, url
@param {string} value
@param {string} label
@param {boolean} [disabled=false]
@param {boolean} [required=false]
@param {string} [placeholder=""]
@param {string} [pattern="(?.*)"]
@param {boolean} [clipboard=false] - allow to copy the input value
@param {boolean} [readonly=false] - allow to read only the input value
@param {boolean} [hideLabel=false]
@param {string} [containerClass=""]
@param {string} [inpClass=""]
@param {string} [headerClass=""]
@param {string|number} [tabId=""]
*/
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 : ""
},
});
const inputEl = ref(null);
const inp = reactive({
value: props.value,
showInp: false,
isClipAllow: false,
isValid: false,
});
const emits = defineEmits(["inp"]);
function copyClipboard() {
if (!inp.clipboard || !inp.isClipAllow) return;
navigator.permissions.query({ name: "clipboard-write" }).then((result) => {
if (result.state === "granted" || result.state === "prompt") {
/* write to the clipboard now */
inputEl.select();
inputEl.setSelectionRange(0, 99999); // For mobile devices
// Copy the text inside the text field
return navigator.clipboard.writeText(inputEl.value);
}
});
}
onMounted(() => {
inp.isValid = inputEl.value.checkValidity();
// Clipboard not allowed on http
if (!window.location.href.startsWith("https://")) return;
// Check clipboard permission
navigator.permissions.query({ name: "clipboard-write" }).then((result) => {
if (result.state === "granted" || result.state === "prompt") {
inp.isClipAllow = true;
return;
}
});
});
</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).*'"
: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"
>
<button
:tabindex="contentIndex"
@click="copyClipboard()"
:class="[props.disabled ? 'disabled' : 'enabled']"
class="input-clipboard-button"
:aria-describedby="`${props.id}-clipboard-text`"
>
<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>
</Container>
</template>

View file

@ -1,272 +0,0 @@
<script setup>
import { ref, reactive, watch, onMounted, defineEmits, defineProps } from "vue";
import { contentIndex } from "@utils/tabindex.js";
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.
We can be more precise by adding values that need to be selected to be valid.
We can also add popover to display more information.
It is mainly use in forms.
@example
{
id: 'test-input',
value: 'yes',
values : ['yes', 'no'],
name: 'test-input',
disabled: false,
required: true,
requiredValues : ['no'], // need required to be checked
label: 'Test select',
}
@param {string} id
@param {string} name
@param {string} value
@param {string} label
@param {array} values
@param {boolean} [disabled=false]
@param {boolean} [required=false]
@param {array} [requiredValues=[]] - values that need to be selected to be valid, works only if required is true
@param {object|boolean} [columns={"pc": "12", "tab": "12", "mob": "12}]
@param {boolean} [hideLabel=false]
@param {string} [containerClass=""]
@param {string} [inpClass=""]
@param {string} [headerClass=""]
@param {string|number} [tabId=""]
*/
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: ""
},
});
// 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
// Then on new select we will switch to select.value
// 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,
});
const selectBtn = ref();
const selectWidth = ref("");
// EVENTS
function toggleSelect() {
select.isOpen = select.isOpen ? false : true;
}
function closeSelect() {
select.isOpen = false;
}
function changeValue(newValue) {
// Allow on template to switch from prop value to component own value
// 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;
closeSelect();
return newValue;
}
// Close select dropdown when clicked outside element
watch(select, () => {
if (select.isOpen) {
document.querySelector("body").addEventListener("click", closeOutside);
} else {
document.querySelector("body").removeEventListener("click", closeOutside);
}
});
// Close select when clicked outside logic
function closeOutside(e) {
try {
if (e.target !== selectBtn.value) {
select.isOpen = false;
}
} catch (err) {
select.isOpen = false;
}
}
onMounted(() => {
selectWidth.value = `${selectBtn.value.clientWidth}px`;
window.addEventListener("resize", () => {
try {
selectWidth.value = `${selectBtn.value.clientWidth}px`;
} catch (err) {}
});
});
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"
>
{{ value }}
</option>
</select>
<!-- end default select -->
<!--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" />
<!-- end dropdown-->
</div>
<!-- end custom-->
</Container>
</template>

View file

@ -1,66 +0,0 @@
<script setup>
import { defineProps } from "vue";
/**
@name Forms/Header/Field.vue
@description This component is used with field in order to link a label to field type.
We can add popover to display more information.
Always use with field component.
@example
{
label: 'Test',
version : "0.1.0",
name: 'test-input',
required: true,
}
@param {string} label
@param {string} name
@param {boolean} [required=false]
@param {boolean} [hideLabel=false]
@param {string} [headerClass=""]
*/
const props = defineProps({
label: {
type: String,
required: true,
},
name: {
type: String,
required: true,
},
required: {
type: Boolean,
required: false,
},
version: {
type: String,
required: false,
},
hideLabel: {
type: Boolean,
required: false,
},
headerClass: {
type: String,
required: false,
},
});
</script>
<template>
<div :class="['relative', props.hideLabel ? 'hidden' : '', props.headerClass]">
<label
:class="[props.label ? '' : 'sr-only']"
:for="props.name"
class="relative lowercase capitalize-first my-1 transition duration-300 ease-in-out text-sm sm:text-md font-bold m-0 dark:text-gray-300"
>
{{ props.label ? props.label : props.name }} <span v-if="props.version">{{ props.version }}</span>
</label>
<span
v-if="props.required"
class="font-bold text-red-500 absolute ml-1"
>*
</span>
</div>
</template>

View file

@ -1,37 +0,0 @@
<script setup>
import { computed } from 'vue';
/**
@name Icons/Button/Add.vue
@description This component is used to create a complete svg icon for a button.
This svg is related to a add action button.
@example
{
iconColor: 'white',
}
@param {string} [iconColor="white"]
*/
const props = defineProps({
iconColor : {
type: String,
required: false,
default: "white",
}
})
const svgClass = computed(() => {
return `w-6.5 h-6.5 ${props.iconColor}`
})
</script>
<template>
<svg xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
:class="[svgClass]">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v6m3-3H9m12 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
</template>

View file

@ -1,216 +0,0 @@
<script setup>
import { computed, ref, watch, onBeforeMount, onMounted } from 'vue';
import { contentIndex } from "@utils/tabindex.js";
import { useEventStore } from "@store/event.js";
import Container from "@components/Widget/Container.vue";
import IconAdd from "@components/Icons/Button/Add.vue";
/**
@name Widget/Button.vue
@description This component is a standard button.
You can link this button to the event store on click with eventAttr.
This will allow you to share a value with other components, for example switching form on a click.
The eventAttr object must contain the store name and the value to send on click at least.
It can also contain the target id element and the expanded value, this will add additionnal accessibility attributs to the button.
@example
{
id: "open-modal-btn",
text: "Open modal",
disabled: false,
hideText: true,
color: "green",
size: "normal",
iconName: "modal",
iconColor: "white",
eventAttr: {"store" : "modal", "value" : "open", "target" : "modal_id", "valueExpanded" : "open"},7
}
@param {string} id
@param {string} text - Content of the button
@param {string} [type="button"] - Can be of type button || submit
@param {boolean} [disabled=false]
@param {boolean} [hideText=false] - Hide text to only display icon
@param {string} [color="primary"]
@param {string} [size="normal"] - Can be of size sm || normal || lg || xl
@param {string} [iconName=""] - Name in lowercase of icons store on /Icons/Button
@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=""]
*/
/*
COMPONENT DESCRIPTION
*
*
This button component is a standard button.
We can link this button to a store on click with eventAttr.
Stores allow to share a value with other components, for example switching form on a click.
We need to determine the store name and the value to send on click.
*
*
PROPS ARGUMENTS
*
*
id: string,
text: string,
type: string<"button"|"submit">,
disabled: boolean,
hideText: boolean,
color: string,
size: string<"sm"|"normal"|"lg"|"xl">,
iconName: string,
iconColor: string,
eventAttr: object,
tabId: string || number,
*
*
PROPS EXAMPLE
*
*
{
id: "open-modal-btn",
text: "Open modal",
disabled: false,
hideText: true,
color: "green",
size: "normal",
iconName: "modal",
iconColor: "white",
eventAttr: {"store" : "modal", "value" : "open", "target" : "modal_id", "valueExpanded" : "open"},7
}
*
*
*/
const eventStore = useEventStore();
const props = defineProps({
id : {
type: String,
required: true,
},
// valid || delete || info
text : {
type: String,
required: true
},
type : {
type: String,
required: false,
default : "button"
},
disabled : {
type: Boolean,
required: false,
default : false
},
// case we want only icon but we need to add accessibility data
hideText : {
type: Boolean,
required: false,
default : false
},
color: {
type: String,
required: false,
default : "primary"
},
// sm || normal || lg || xl
size: {
type: String,
required: false,
default : "normal"
},
// Store on components/Icons/Button
// Check import ones
iconName : {
type: String,
required: false,
default : "",
},
// Defined on input.css
iconColor : {
type: String,
required: false,
default : ""
},
// {"store" : <store_name>, "default" : <default_value>, "value" : <value_stored_on_click>, "target"<optional> : <target_id_element>, "valueExpanded" : "expanded_value"}
// type will add additionnal accessibility attributs to the button
// for example, if button open a modal : {"store" : "modal", "value" : "open", "target" : "modal_id", "valueExpanded" : "open"}
eventAttr: {
type: Object,
required: false,
default : {}
},
tabId : {
type: [String, Number],
required: false,
default : ""
}
});
const btnEl = ref();
const buttonClass = computed(() => {
return `btn btn-${props.color} btn-${props.size}`
})
onMounted(() => {
updateData();
})
watch(eventStore,() => {
updateData();
})
function updateData(isClick = false) {
const isStore = props.eventAttr?.store ? true : false;
const isValue = props.eventAttr?.value ? true : false;
const isDefault = props.eventAttr?.default ? true : false;
if(!isStore || !isValue || !isDefault) return;
isClick ? eventStore.updateEvent(props.eventAttr.store, props.eventAttr.value) : eventStore.addEvent(props.eventAttr.store, props.eventAttr.default);
try {
const expanded = props.eventAttr?.valueExpanded ? props.eventAttr.valueExpanded === eventStore.getEvent(props.eventAttr.store) ? 'true' : 'false' : false;
if(expanded) {
btnEl.value.setAttribute('aria-expanded', expanded);
}
if(!expanded) {
btnEl.value.removeAttribute('aria-expanded');
}
}catch(e) {
}
try {
const controls = props.eventAttr?.target ? props.eventAttr.target : false;
if(controls) {
btnEl.value.setAttribute('aria-controls', controls);
}
if(!controls) {
btnEl.value.removeAttribute('aria-controls');
}
}catch(e) {
}
}
</script>
<template>
<Container :containerClass="`w-full m-2 ${props.containerClass}`" :columns="props.columns">
<button :type="props.type" ref="btnEl" @click="updateData(true)" :id="props.id"
:tabindex="props.tabId || contentIndex"
:class="[buttonClass]"
:disabled="props.disabled || false"
:aria-describedby="`${props.id}-text`"
>
<span :class="[props.hideText ? 'sr-only' : '',
props.iconName ? 'mr-2' : ''
]" :id="`${props.id}-text`">{{ props.text }}</span>
<IconAdd v-if="props.iconName === 'add'" :iconName="props.iconName" :iconColor="props.iconColor" />
</button>
</Container>
</template>

View file

@ -1,41 +0,0 @@
<script setup>
import { computed } from 'vue';
/**
@name Widget/Container.vue
@description This component is a basic container that can be used to wrap other components.
In case we are working with grid system, we can add columns to position the container.
We can define additional class too.
This component is mainly use as widget container.
@example
{
containerClass: "w-full h-full bg-white rounded shadow-md",
columns: { pc: 12, tablet: 12, mobile: 12}
}
@param {string} [containerClass=""] - Additional class
@param {object|boolean} [columns=false] - Work with grid system { pc: 12, tablet: 12, mobile: 12}
*/
const props = defineProps({
containerClass : {
type: String,
required: false,
default : ""
},
columns: {
type: [Object, Boolean],
required: false,
default: false
},
})
const gridClass = computed(() => {
return props.columns ? `col-span-${props.columns.mobile} md:col-span-${props.columns.tablet} lg:col-span-${props.columns.pc}` : '';
})
</script>
<template>
<div :class="[props.containerClass ? props.containerClass : '', gridClass]">
<slot></slot>
</div>
</template>

View file

@ -1,35 +0,0 @@
<script setup>
import { computed } from 'vue';
/**
@name Widget/Flex.vue
@description This component is a basic container that can be used to wrap other components.
Per default, it aligns the components horizontally using flex.
We can define additional class too.
This component is mainly use as widget container or for groups of widget.
@example
{
flexClass: "flex-start"
}
@param {string} [flexClass="flex-start"] - Additional class
*/
const props = defineProps({
flexClass: {
type: String,
required: false,
default : "flex-start"
},
})
const flexClass = computed(() => {
return `w-full flex ${props.flexClass}`;
})
</script>
<template>
<div :class="[flexClass]">
<slot></slot>
</div>
</template>

View file

@ -1,48 +0,0 @@
<script setup>
import { computed } from 'vue';
/**
@name Widget/Grid.vue
@description This component is a basic container that can be used to wrap other components.
This container is based on a grid system and will return a grid container with 12 columns.
In case we are working with grid system, we can add columns to position the container.
We can define additional class too.
This component is mainly use as widget container or as a child of a GridLayout.
@example
{
columns: { pc: 12, tablet: 12, mobile: 12},
gridClass: "items-start"
}
@param {string} [gridClass="items-start"] - Additional class
@param {object|boolean} [columns=false] - Work with grid system { pc: 12, tablet: 12, mobile: 12}
*/
const props = defineProps({
columns : {
type: [Object, Boolean],
required: false,
default : false,
},
gridClass : {
type: String,
required: false,
default: "items-start"
},
})
const gridClass = computed(() => {
return `grid grid-cols-12 w-full ${props.gridClass}`;
})
const columnClass = computed(() => {
return props.columns ? `col-span-${props.columns.mobile} md:col-span-${props.columns.tablet} lg:col-span-${props.columns.pc}` : ``;
})
</script>
<template>
<div :class="[gridClass, columnClass]">
<slot></slot>
</div>
</template>

View file

@ -1,70 +0,0 @@
<script setup>
import { computed } from 'vue';
/**
@name Widget/GridLayout.vue
@description This component is used for top level page layout.
This will determine the position of layout components based on the grid system.
We can create card, modal, table and others top level layout using this component.
This component is mainly use as Grid parent component.
@example
{
type: "card",
title: "Test",
columns: { pc: 12, tablet: 12, mobile: 12},
gridLayoutClass: "items-start"
}
@param {string} [type="card"] - Type of layout component, we can have : card, table, modal and others
@param {string} [title=""] - Title of the layout component, will be displayed at the top if exists. Type of layout component will determine the style of the title.
@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
*/
const props = defineProps({
type : {
type: String,
required: false,
default : "card"
},
title : {
type: String,
required: false,
default : ""
},
columns : {
type: Object,
required: false,
default : {
pc: 12,
tablet: 12,
mobile: 12}
},
gridLayoutClass : {
type: String,
required: false,
default: "items-start"
},
})
const containerClass = computed(() => {
if(props.type === 'card') return 'bg-white rounded-xl shadow-md w-full';
return '';
})
const gridClass = computed(() => {
return `grid grid-cols-12 w-full col-span-${props.columns.mobile} md:col-span-${props.columns.tablet} lg:col-span-${props.columns.pc}`;
})
const titleClass = computed(() => {
if(props.type === 'card') return 'text-2xl font-bold mb-2';
return ''
})
</script>
<template>
<div :class="[containerClass, gridClass, props.gridLayoutClass, 'p-4 m-4']">
<h1 v-if="props.title" :class="[titleClass, 'col-span-12']">{{ props.title }}</h1>
<slot></slot>
</div>
</template>

View file

@ -1,85 +0,0 @@
<script setup>
import { reactive, onMounted, defineProps } from "vue";
import { contentIndex } from "@utils/tabindex.js";
const props = defineProps({
id: {
type: String,
required: true,
},
content: {
type: String,
required: false,
},
icon : {
type: String,
required: false,
},
iconColor: {
type: String,
required: false,
},
// Sometimes we can't have a button tag (like popover on another btn)
tag: {
type: String,
required: false,
default: "button",
},
});
// Determine popover need to be display
const popover = reactive({
isOpen: false,
isHover: false,
});
function showPopover() {
popover.isHover = true;
setTimeout(() => {
popover.isOpen = popover.isHover ? true : false;
}, 450);
}
function hidePopover() {
popover.isHover = false;
popover.isOpen = false;
}
</script>
<template>
<component
:tabindex="contentIndex"
:aria-controls="`${props.id}-popover-text`"
:aria-expanded="popover.isOpen ? 'true' : 'false'"
:aria-describedby="`${props.id}-popover-text`"
:is="props.tag"
role="button"
@focusin="showPopover()"
@focusout="hidePopover()"
@pointerover="showPopover()"
@pointerleave="hidePopover()"
class="cursor-pointer flex justify-start w-full"
>
<svg
role="img"
aria-hidden="true"
class="popover-settings-svg"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
d="M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-144c-17.7 0-32-14.3-32-32s14.3-32 32-32s32 14.3 32 32s-14.3 32-32 32z"
/>
</svg>
</component>
<div
:id="`${props.id}-popover-container`"
role="status"
:aria-hidden="popover.isOpen ? 'false' : 'true'"
v-show="popover.isOpen"
:class="['popover-settings-container']"
:aria-description="$t('dashboard_popover_detail_desc')"
>
<p :id="`${props.id}-popover-text`" class="popover-settings-text"><slot></slot></p>
</div>
</template>

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -1,21 +1,5 @@
<script setup>
import MenuSvgHome from "@components/Icons/Menu/Home.vue";
import MenuSvgInstances from "@components/Icons/Menu/Instances.vue";
import MenuSvgGlobalConf from "@components/Icons/Menu/GlobalConf.vue";
import MenuSvgConfigs from "@components/Icons/Menu/Configs.vue";
import MenuSvgPlugins from "@components/Icons/Menu/Plugins.vue";
import MenuSvgCache from "@components/Icons/Menu/Cache.vue";
import MenuSvgJobs from "@components/Icons/Menu/Jobs.vue";
import MenuSvgBans from "@components/Icons/Menu/Bans.vue";
import MenuSvgReports from "@components/Icons/Menu/Reports.vue";
import MenuSvgLogs from "@components/Icons/Menu/Logs.vue";
import MenuSvgCore from "@components/Icons/Menu/Core.vue";
import MenuSvgPro from "@components/Icons/Menu/Pro.vue";
import MenuSvgTwitter from "@components/Icons/Menu/Twitter.vue";
import MenuSvgLinkedin from "@components/Icons/Menu/Linkedin.vue";
import MenuSvgDiscord from "@components/Icons/Menu/Discord.vue";
import MenuSvgServices from "@components/Icons/Menu/Services.vue";
import MenuSvgGithub from "@components/Icons/Menu/Github.vue";
import Icons from "@components/Widget/Icons.vue";
import { reactive, onMounted, onBeforeMount } from "vue";
import { menuIndex, menuFloatIndex } from "@/utils/tabindex.js";
import { useBannerStore } from "@store/global.js";
@ -34,46 +18,53 @@ const bannerStore = useBannerStore();
// Navigation with components
// resolveComponent allow to replace a tag by a real Vue component
const navList = [
{ tag: "home", svg: MenuSvgHome, path: "/home" },
{ tag: "home", svg: "house", svgColor : "cyan", path: "/home" },
{
tag: "instances",
svg: MenuSvgInstances,
svg: "box",
svgColor: "dark",
path: "/instances",
},
{
tag: "global_config",
svg: MenuSvgGlobalConf,
svg: "settings",
svgColor : "blue",
path: "/global-config",
},
{
tag: "services",
svg: MenuSvgServices,
svg: "disk",
svgColor : "orange",
path: "/services",
},
{
tag: "configs",
svg: MenuSvgConfigs,
svg: "gear",
svgColor : "purple",
path: "/configs",
},
{
tag: "plugins",
svg: MenuSvgPlugins,
svg: "puzzle",
svgColor : "yellow",
path: "/plugins",
},
{
tag: "cache",
svg: MenuSvgCache,
svg: "carton",
svgColor : "purple",
path: "/cache",
},
{
tag: "reports",
svg: MenuSvgReports,
svg: "flag",
svgColor : "amber",
path: "/reports",
},
{ tag: "bans", svg: MenuSvgBans, path: "/bans" },
{ tag: "jobs", svg: MenuSvgJobs, path: "/jobs" },
{ tag: "logs", svg: MenuSvgLogs, path: "/jobs" },
{ tag: "bans", svg: "trespass", svgColor : "red", path: "/bans" },
{ tag: "jobs", svg: "task", svgColor : "emerald", path: "/jobs" },
{ tag: "logs", svg: "list", svgColor : "dark", path: "/logs" },
];
// Social links
@ -81,22 +72,26 @@ const socialList = [
{
tag: "twitter",
href: "https://twitter.com/bunkerity",
svg: MenuSvgTwitter,
svg: "twitter",
svgColor : "twitter"
},
{
tag: "linkedin",
href: "https://www.linkedin.com/company/bunkerity/",
svg: MenuSvgLinkedin,
svg: "linkedin",
svgColor : "linkedin"
},
{
tag: "discord",
href: "https://discord.gg/fTf46FmtyD",
svg: MenuSvgDiscord,
svg: "discord",
svgColor : "discord"
},
{
tag: "github",
href: "https://github.com/bunkerity",
svg: MenuSvgGithub,
svg: "github",
svgColor: "github"
},
];
@ -291,7 +286,7 @@ onBeforeMount(() => {
"
>
<div class="menu-nav-item-container">
<component :is="item.svg"></component>
<Icons :iconName="item.svg" :iconClass="'menu-svg'" :iconColor="item.svgColor" />
</div>
<span class="menu-nav-item-title">
{{ $t(`dashboard_${item.tag}`) }}
@ -322,8 +317,7 @@ onBeforeMount(() => {
:href="`/plugins?plugin_id=${plugin.id}`"
>
<div aria-hidden="true" class="menu-page-plugin-svg-container">
<MenuSvgCore v-if="plugin.type !== 'pro'" />
<MenuSvgPro v-if="plugin.type === 'pro'" />
<Icons :iconName="plugin.type === 'pro' ? 'crown' : 'free'" :iconClass="'menu-svg'" :iconColor="plugin.type === 'pro' ? 'amber' : 'dark'" />
</div>
<span class="menu-page-plugin-name">{{ plugin.name }}</span>
</a>
@ -373,7 +367,7 @@ onBeforeMount(() => {
<span :id="`${item}-id`" class="sr-only">
{{ $t(`dashboard_menu_${item.tag}_label`) }}
</span>
<component :is="item.svg"></component>
<Icons :iconName="item.svg" :iconClass="'social-svg'" :iconColor="item.svgColor" />
</a>
</li>
</ul>

View file

@ -0,0 +1,31 @@
<script setup>
/**
@name Icons/Box.vue
@description This component is a svg icon representing box.
@example
{
iconColor: 'info',
}
@param {string} [iconClass=""]
@param {string} [iconColor="info"] - The color of the icon between some tailwind css available colors (purple, green, red, orange, blue, yellow, gray, dark, amber, emerald, teal, indigo, cyan, sky, pink...). Darker colors are also available using the base color and adding '-darker' (e.g. 'red-darker').
*/
const props = defineProps({
iconClass : {
type: String,
required: false,
default : "default-svg"
},
iconColor : {
type: String,
required: false,
default: "info",
}
})
</script>
<template>
<svg role="img"
aria-hidden="true" :class="[props.iconClass, props.iconColor]" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path d="M12.378 1.602a.75.75 0 0 0-.756 0L3 6.632l9 5.25 9-5.25-8.622-5.03ZM21.75 7.93l-9 5.25v9l8.628-5.032a.75.75 0 0 0 .372-.648V7.93ZM11.25 22.18v-9l-9-5.25v8.57a.75.75 0 0 0 .372.648l8.628 5.033Z" />
</svg>
</template>

View file

@ -1,37 +0,0 @@
<script setup>
import { computed } from 'vue';
/**
@name Icons/Button/Add.vue
@description This component is used to create a complete svg icon for a button.
This svg is related to a add action button.
@example
{
iconColor: 'white',
}
@param {string} [iconColor="white"]
*/
const props = defineProps({
iconColor : {
type: String,
required: false,
default: "white",
}
})
const svgClass = computed(() => {
return `w-6.5 h-6.5 ${props.iconColor}`
})
</script>
<template>
<svg xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
:class="[svgClass]">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v6m3-3H9m12 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
</template>

View file

@ -0,0 +1,32 @@
<script setup>
/**
@name Icons/Carton.vue
@description This component is a svg icon representing carton box.
@example
{
iconColor: 'info',
}
@param {string} [iconClass=""]
@param {string} [iconColor="info"] - The color of the icon between some tailwind css available colors (purple, green, red, orange, blue, yellow, gray, dark, amber, emerald, teal, indigo, cyan, sky, pink...). Darker colors are also available using the base color and adding '-darker' (e.g. 'red-darker').
*/
const props = defineProps({
iconClass : {
type: String,
required: false,
default : "default-svg"
},
iconColor : {
type: String,
required: false,
default: "info",
}
})
</script>
<template>
<svg role="img"
aria-hidden="true" :class="[props.iconClass, props.iconColor]" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path d="M3.375 3C2.339 3 1.5 3.84 1.5 4.875v.75c0 1.036.84 1.875 1.875 1.875h17.25c1.035 0 1.875-.84 1.875-1.875v-.75C22.5 3.839 21.66 3 20.625 3H3.375Z" />
<path fill-rule="evenodd" d="m3.087 9 .54 9.176A3 3 0 0 0 6.62 21h10.757a3 3 0 0 0 2.995-2.824L20.913 9H3.087Zm6.163 3.75A.75.75 0 0 1 10 12h4a.75.75 0 0 1 0 1.5h-4a.75.75 0 0 1-.75-.75Z" clip-rule="evenodd" />
</svg>
</template>

View file

@ -0,0 +1,38 @@
<script setup>
/**
@name Icons/Core.vue
@description This component is a svg icon representing core plugin.
@example
{
iconColor: 'info',
}
@param {string} [iconClass=""]
@param {string} [iconColor="info"] - The color of the icon between some tailwind css available colors (purple, green, red, orange, blue, yellow, gray, dark, amber, emerald, teal, indigo, cyan, sky, pink...). Darker colors are also available using the base color and adding '-darker' (e.g. 'red-darker').
*/
const props = defineProps({
iconClass : {
type: String,
required: false,
default : "default-svg"
},
iconColor : {
type: String,
required: false,
default: "info",
}
})
</script>
<template>
<svg
role="img"
aria-hidden="true"
:class="[props.iconClass, props.iconColor]"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 384 512"
>
<path
d="M0 64C0 28.7 28.7 0 64 0H224V128c0 17.7 14.3 32 32 32H384V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V64zm384 64H256V0L384 128z"
/>
</svg>
</template>

View file

@ -1,13 +1,32 @@
<script setup>
/**
@name Icons/Menu/Pro.vue
@description This component is used to create a complete svg icon for the menu.
This svg is related to the plugins pro page link in the menu.
@name Icons/Crown.vue
@description This component is a svg icon representing crown.
@example
{
iconColor: 'info',
}
@param {string} [iconClass=""]
@param {string} [iconColor="info"] - The color of the icon between some tailwind css available colors (purple, green, red, orange, blue, yellow, gray, dark, amber, emerald, teal, indigo, cyan, sky, pink...). Darker colors are also available using the base color and adding '-darker' (e.g. 'red-darker').
*/
const props = defineProps({
iconClass : {
type: String,
required: false,
default : "default-svg"
},
iconColor : {
type: String,
required: false,
default: "info",
}
})
</script>
<template>
<svg
class="h-5 w-5 dark:brightness-90 relative"
<svg role="img"
aria-hidden="true"
:class="[props.iconClass, props.iconColor]"
viewBox="0 0 48 46"
fill="none"
xmlns="http://www.w3.org/2000/svg"

View file

@ -1,16 +1,34 @@
<script setup>
/**
@name Icons/Menu/Discord.vue
@description This component is used to create a complete svg icon for the menu.
This svg is related to the social discord link in the menu.
@name Icons/Discord.vue
@description This component is a svg icon representing Discord.
@example
{
iconColor: 'info',
}
@param {string} [iconClass=""]
@param {string} [iconColor="discord"]
*/
const props = defineProps({
iconClass : {
type: String,
required: false,
default : "default-svg"
},
iconColor : {
type: String,
required: false,
default: "discord",
}
})
</script>
<template>
<svg
role="img"
aria-hidden="true"
class="hover:opacity-80"
fill="#5562EA"
fill="none"
:class="[props.iconClass, props.iconColor]"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 640 512"
>

View file

@ -0,0 +1,31 @@
<script setup>
/**
@name Icons/Disk.vue
@description This component is a svg icon representing disk.
@example
{
iconColor: 'info',
}
@param {string} [iconClass=""]
@param {string} [iconColor="info"] - The color of the icon between some tailwind css available colors (purple, green, red, orange, blue, yellow, gray, dark, amber, emerald, teal, indigo, cyan, sky, pink...). Darker colors are also available using the base color and adding '-darker' (e.g. 'red-darker').
*/
const props = defineProps({
iconClass : {
type: String,
required: false,
default : "default-svg"
},
iconColor : {
type: String,
required: false,
default: "info",
}
})</script>
<template>
<svg role="img"
aria-hidden="true" :class="[props.iconClass, props.iconColor]" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path d="M4.08 5.227A3 3 0 0 1 6.979 3H17.02a3 3 0 0 1 2.9 2.227l2.113 7.926A5.228 5.228 0 0 0 18.75 12H5.25a5.228 5.228 0 0 0-3.284 1.153L4.08 5.227Z" />
<path fill-rule="evenodd" d="M5.25 13.5a3.75 3.75 0 1 0 0 7.5h13.5a3.75 3.75 0 1 0 0-7.5H5.25Zm10.5 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm3.75-.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Z" clip-rule="evenodd" />
</svg>
</template>

View file

@ -0,0 +1,31 @@
<script setup>
/**
@name Icons/Flag.vue
@description This component is a svg icon representing flag.
@example
{
iconColor: 'info',
}
@param {string} [iconClass=""]
@param {string} [iconColor="info"] - The color of the icon between some tailwind css available colors (purple, green, red, orange, blue, yellow, gray, dark, amber, emerald, teal, indigo, cyan, sky, pink...). Darker colors are also available using the base color and adding '-darker' (e.g. 'red-darker').
*/
const props = defineProps({
iconClass : {
type: String,
required: false,
default : "default-svg"
},
iconColor : {
type: String,
required: false,
default: "info",
}
})
</script>
<template>
<svg role="img"
aria-hidden="true" :class="[props.iconClass, props.iconColor]" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path fill-rule="evenodd" d="M3 2.25a.75.75 0 0 1 .75.75v.54l1.838-.46a9.75 9.75 0 0 1 6.725.738l.108.054A8.25 8.25 0 0 0 18 4.524l3.11-.732a.75.75 0 0 1 .917.81 47.784 47.784 0 0 0 .005 10.337.75.75 0 0 1-.574.812l-3.114.733a9.75 9.75 0 0 1-6.594-.77l-.108-.054a8.25 8.25 0 0 0-5.69-.625l-2.202.55V21a.75.75 0 0 1-1.5 0V3A.75.75 0 0 1 3 2.25Z" clip-rule="evenodd" />
</svg>
</template>

View file

@ -0,0 +1,31 @@
<script setup>
/**
@name Icons/Gear.vue
@description This component is a svg icon representing gear.
@example
{
iconColor: 'info',
}
@param {string} [iconClass=""]
@param {string} [iconColor="info"] - The color of the icon between some tailwind css available colors (purple, green, red, orange, blue, yellow, gray, dark, amber, emerald, teal, indigo, cyan, sky, pink...). Darker colors are also available using the base color and adding '-darker' (e.g. 'red-darker').
*/
const props = defineProps({
iconClass : {
type: String,
required: false,
default : "default-svg"
},
iconColor : {
type: String,
required: false,
default: "info",
}
})
</script>
<template>
<svg role="img"
aria-hidden="true" :class="[props.iconClass, props.iconColor]" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path fill-rule="evenodd" d="M11.078 2.25c-.917 0-1.699.663-1.85 1.567L9.05 4.889c-.02.12-.115.26-.297.348a7.493 7.493 0 0 0-.986.57c-.166.115-.334.126-.45.083L6.3 5.508a1.875 1.875 0 0 0-2.282.819l-.922 1.597a1.875 1.875 0 0 0 .432 2.385l.84.692c.095.078.17.229.154.43a7.598 7.598 0 0 0 0 1.139c.015.2-.059.352-.153.43l-.841.692a1.875 1.875 0 0 0-.432 2.385l.922 1.597a1.875 1.875 0 0 0 2.282.818l1.019-.382c.115-.043.283-.031.45.082.312.214.641.405.985.57.182.088.277.228.297.35l.178 1.071c.151.904.933 1.567 1.85 1.567h1.844c.916 0 1.699-.663 1.85-1.567l.178-1.072c.02-.12.114-.26.297-.349.344-.165.673-.356.985-.57.167-.114.335-.125.45-.082l1.02.382a1.875 1.875 0 0 0 2.28-.819l.923-1.597a1.875 1.875 0 0 0-.432-2.385l-.84-.692c-.095-.078-.17-.229-.154-.43a7.614 7.614 0 0 0 0-1.139c-.016-.2.059-.352.153-.43l.84-.692c.708-.582.891-1.59.433-2.385l-.922-1.597a1.875 1.875 0 0 0-2.282-.818l-1.02.382c-.114.043-.282.031-.449-.083a7.49 7.49 0 0 0-.985-.57c-.183-.087-.277-.227-.297-.348l-.179-1.072a1.875 1.875 0 0 0-1.85-1.567h-1.843ZM12 15.75a3.75 3.75 0 1 0 0-7.5 3.75 3.75 0 0 0 0 7.5Z" clip-rule="evenodd" />
</svg>
</template>

View file

@ -1,16 +1,34 @@
<script setup>
/**
@name Icons/Menu/Github.vue
@description This component is used to create a complete svg icon for the menu.
This svg is related to the github social link in the menu.
@name Icons/Github.vue
@description This component is a svg icon representing Github.
@example
{
iconColor: 'info',
}
@param {string} [iconClass=""]
@param {string} [iconColor="github"]
*/
const props = defineProps({
iconClass : {
type: String,
required: false,
default : "default-svg"
},
iconColor : {
type: String,
required: false,
default: "github",
}
})
</script>
<template>
<svg
role="img"
aria-hidden="true"
class="hover:opacity-80 dark:fill-gray-600"
fill="#171A1F"
fill="none"
:class="[props.iconClass, props.iconColor]"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 496 512"
>

View file

@ -0,0 +1,32 @@
<script setup>
/**
@name Icons/House.vue
@description This component is a svg icon representing house.
@example
{
iconColor: 'info',
}
@param {string} [iconClass=""]
@param {string} [iconColor="info"] - The color of the icon between some tailwind css available colors (purple, green, red, orange, blue, yellow, gray, dark, amber, emerald, teal, indigo, cyan, sky, pink...). Darker colors are also available using the base color and adding '-darker' (e.g. 'red-darker').
*/
const props = defineProps({
iconClass : {
type: String,
required: false,
default : "default-svg"
},
iconColor : {
type: String,
required: false,
default: "info",
}
})
</script>
<template>
<svg role="img"
aria-hidden="true" :class="[props.iconClass, props.iconColor]" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path d="M11.47 3.841a.75.75 0 0 1 1.06 0l8.69 8.69a.75.75 0 1 0 1.06-1.061l-8.689-8.69a2.25 2.25 0 0 0-3.182 0l-8.69 8.69a.75.75 0 1 0 1.061 1.06l8.69-8.689Z" />
<path d="m12 5.432 8.159 8.159c.03.03.06.058.091.086v6.198c0 1.035-.84 1.875-1.875 1.875H15a.75.75 0 0 1-.75-.75v-4.5a.75.75 0 0 0-.75-.75h-3a.75.75 0 0 0-.75.75V21a.75.75 0 0 1-.75.75H5.625a1.875 1.875 0 0 1-1.875-1.875v-6.198a2.29 2.29 0 0 0 .091-.086L12 5.432Z" />
</svg>
</template>

View file

@ -0,0 +1,36 @@
<script setup>
/**
@name Icons/Key.vue
@description This component is a svg icon representing key.
@example
{
iconColor: 'info',
}
@param {string} [iconClass=""]
@param {string} [iconColor="info"] - The color of the icon between some tailwind css available colors (purple, green, red, orange, blue, yellow, gray, dark, amber, emerald, teal, indigo, cyan, sky, pink...). Darker colors are also available using the base color and adding '-darker' (e.g. 'red-darker').
*/
const props = defineProps({
iconClass : {
type: String,
required: false,
default : "default-svg"
},
iconColor : {
type: String,
required: false,
default: "info",
}
})
</script>
<template>
<svg role="img"
aria-hidden="true" :class="[props.iconClass, props.iconColor]"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 5.25a3 3 0 0 1 3 3m3 0a6 6 0 0 1-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1 1 21.75 8.25Z" />
</svg>
</template>

View file

@ -1,15 +1,34 @@
<script setup>
/**
@name Icons/Menu/Linkedin.vue
@description This component is used to create a complete svg icon for the menu.
This svg is related to the linkedin social link in the menu.
@name Icons/Linkedin.vue
@description This component is a svg icon representing Linkedin.
@example
{
iconColor: 'info',
}
@param {string} [iconClass=""]
@param {string} [iconColor="linkedin"]
*/
const props = defineProps({
iconClass : {
type: String,
required: false,
default : "default-svg"
},
iconColor : {
type: String,
required: false,
default: "linkedin",
}
})
</script>
<template>
<svg
role="img"
:class="[props.iconClass, props.iconColor]"
aria-hidden="true"
fill="#0A63BC"
fill="none"
class="hover:opacity-80 dark:brightness-110"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 448 512"

View file

@ -0,0 +1,31 @@
<script setup>
/**
@name Icons/List.vue
@description This component is a svg icon representing list.
@example
{
iconColor: 'info',
}
@param {string} [iconClass=""]
@param {string} [iconColor="info"] - The color of the icon between some tailwind css available colors (purple, green, red, orange, blue, yellow, gray, dark, amber, emerald, teal, indigo, cyan, sky, pink...). Darker colors are also available using the base color and adding '-darker' (e.g. 'red-darker').
*/
const props = defineProps({
iconClass : {
type: String,
required: false,
default : "default-svg"
},
iconColor : {
type: String,
required: false,
default: "info",
}
})
</script>
<template>
<svg :class="[props.iconClass, props.iconColor]" role="img"
aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6">
<path fill-rule="evenodd" d="M2.625 6.75a1.125 1.125 0 1 1 2.25 0 1.125 1.125 0 0 1-2.25 0Zm4.875 0A.75.75 0 0 1 8.25 6h12a.75.75 0 0 1 0 1.5h-12a.75.75 0 0 1-.75-.75ZM2.625 12a1.125 1.125 0 1 1 2.25 0 1.125 1.125 0 0 1-2.25 0ZM7.5 12a.75.75 0 0 1 .75-.75h12a.75.75 0 0 1 0 1.5h-12A.75.75 0 0 1 7.5 12Zm-4.875 5.25a1.125 1.125 0 1 1 2.25 0 1.125 1.125 0 0 1-2.25 0Zm4.875 0a.75.75 0 0 1 .75-.75h12a.75.75 0 0 1 0 1.5h-12a.75.75 0 0 1-.75-.75Z" clip-rule="evenodd" />
</svg>
</template>

View file

@ -1,19 +0,0 @@
<script setup>
/**
@name Icons/Menu/Bans.vue
@description This component is used to create a complete svg icon for the menu.
This svg is related to the bans link in the menu.
*/
</script>
<template>
<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="stroke-red-500 dark:stroke-red-500 h-6 w-6 relative">
<path stroke-linecap="round" stroke-linejoin="round" d="M18.364 18.364A9 9 0 0 0 5.636 5.636m12.728 12.728A9 9 0 0 1 5.636 5.636m12.728 12.728L5.636 5.636" />
</svg>
</template>

View file

@ -1,18 +0,0 @@
<script setup>
/**
@name Icons/Menu/Cache.vue
@description This component is used to create a complete svg icon for the menu.
This svg is related to the cache link in the menu.
*/
</script>
<template>
<svg role="img"
aria-hidden="true" class="stroke-pink-600 h-5.5 w-5.5 relative"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M20.25 7.5l-.625 10.632a2.25 2.25 0 01-2.247 2.118H6.622a2.25 2.25 0 01-2.247-2.118L3.75 7.5M10 11.25h4M3.375 7.5h17.25c.621 0 1.125-.504 1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125H3.375c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125z" />
</svg>
</template>

View file

@ -1,19 +0,0 @@
<script setup>
/**
@name Icons/Menu/Configs.vue
@description This component is used to create a complete svg icon for the menu.
This svg is related to the configs link in the menu.
*/
</script>
<template>
<svg role="img"
aria-hidden="true" class="stroke-blue-500 h-6 w-6 relative"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
</template>

View file

@ -1,20 +0,0 @@
<script setup>
/**
@name Icons/Menu/Core.vue
@description This component is used to create a complete svg icon for the menu.
This svg is related to the plugins page (core / external) link in the menu.
*/
</script>
<template>
<svg
role="img"
aria-hidden="true"
class="fill-gray-500 h-5 w-5 relative"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 384 512"
>
<path
d="M0 64C0 28.7 28.7 0 64 0H224V128c0 17.7 14.3 32 32 32H384V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V64zm384 64H256V0L384 128z"
/>
</svg>
</template>

View file

@ -1,18 +0,0 @@
<script setup>
/**
@name Icons/Menu/GlobalConf.vue
@description This component is used to create a complete svg icon for the menu.
This svg is related to the global conf link in the menu.
*/
</script>
<template>
<svg role="img"
aria-hidden="true" class="stroke-blue-400 h-6 w-6 relative"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M10.5 6h9.75M10.5 6a1.5 1.5 0 11-3 0m3 0a1.5 1.5 0 10-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-9.75 0h9.75" />
</svg>
</template>

View file

@ -1,18 +0,0 @@
<script setup>
/**
@name Icons/Menu/Home.vue
@description This component is used to create a complete svg icon for the menu.
This svg is related to the home link in the menu.
*/
</script>
<template>
<svg role="img"
aria-hidden="true" class="stroke-sky-500 h-6 w-6 relative"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
</svg>
</template>

View file

@ -1,18 +0,0 @@
<script setup>
/**
@name Icons/Menu/Instances.vue
@description This component is used to create a complete svg icon for the menu.
This svg is related to the instances link in the menu.
*/
</script>
<template>
<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="stroke-stone-500 h-5.5 w-5.5 relative">
<path stroke-linecap="round" stroke-linejoin="round" d="m21 7.5-9-5.25L3 7.5m18 0-9 5.25m9-5.25v9l-9 5.25M3 7.5l9 5.25M3 7.5v9l9 5.25m0-9v9" />
</svg>
</template>

View file

@ -1,18 +0,0 @@
<script setup>
/**
@name Icons/Menu/Jobs.vue
@description This component is used to create a complete svg icon for the menu.
This svg is related to the jobs link in the menu.
*/
</script>
<template>
<svg role="img"
aria-hidden="true" class="stroke-emerald-600 h-6 w-6 relative"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M10.125 2.25h-4.5c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125v-9M10.125 2.25h.375a9 9 0 019 9v.375M10.125 2.25A3.375 3.375 0 0113.5 5.625v1.5c0 .621.504 1.125 1.125 1.125h1.5a3.375 3.375 0 013.375 3.375M9 15l2.25 2.25L15 12" />
</svg>
</template>

View file

@ -1,19 +0,0 @@
<script setup>
/**
@name Icons/Menu/Logs.vue
@description This component is used to create a complete svg icon for the menu.
This svg is related to the logs link in the menu.
*/
</script>
<template>
<svg role="img"
aria-hidden="true" class="stroke-gray-600 dark:fill-gray-500 h-6 w-6 relative"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 6.75h12M8.25 12h12m-12 5.25h12M3.75 6.75h.007v.008H3.75V6.75zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zM3.75 12h.007v.008H3.75V12zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm-.375 5.25h.007v.008H3.75v-.008zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z" />
</svg>
</template>

View file

@ -1,18 +0,0 @@
<script setup>
/**
@name Icons/Menu/Plugins.vue
@description This component is used to create a complete svg icon for the menu.
This svg is related to the plugins link in the menu.
*/
</script>
<template>
<svg role="img"
aria-hidden="true" class="stroke-yellow-400 h-6 w-6 relative"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M14.25 6.087c0-.355.186-.676.401-.959.221-.29.349-.634.349-1.003 0-1.036-1.007-1.875-2.25-1.875s-2.25.84-2.25 1.875c0 .369.128.713.349 1.003.215.283.401.604.401.959v0a.64.64 0 01-.657.643 48.39 48.39 0 01-4.163-.3c.186 1.613.293 3.25.315 4.907a.656.656 0 01-.658.663v0c-.355 0-.676-.186-.959-.401a1.647 1.647 0 00-1.003-.349c-1.036 0-1.875 1.007-1.875 2.25s.84 2.25 1.875 2.25c.369 0 .713-.128 1.003-.349.283-.215.604-.401.959-.401v0c.31 0 .555.26.532.57a48.039 48.039 0 01-.642 5.056c1.518.19 3.058.309 4.616.354a.64.64 0 00.657-.643v0c0-.355-.186-.676-.401-.959a1.647 1.647 0 01-.349-1.003c0-1.035 1.008-1.875 2.25-1.875 1.243 0 2.25.84 2.25 1.875 0 .369-.128.713-.349 1.003-.215.283-.4.604-.4.959v0c0 .333.277.599.61.58a48.1 48.1 0 005.427-.63 48.05 48.05 0 00.582-4.717.532.532 0 00-.533-.57v0c-.355 0-.676.186-.959.401-.29.221-.634.349-1.003.349-1.035 0-1.875-1.007-1.875-2.25s.84-2.25 1.875-2.25c.37 0 .713.128 1.003.349.283.215.604.401.96.401v0a.656.656 0 00.658-.663 48.422 48.422 0 00-.37-5.36c-1.886.342-3.81.574-5.766.689a.578.578 0 01-.61-.58v0z" />
</svg>
</template>

View file

@ -1,18 +0,0 @@
<script setup>
/**
@name Icons/Menu/Reports.vue
@description This component is used to create a complete svg icon for the menu.
This svg is related to the reports link in the menu.
*/
</script>
<template>
<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="stroke-amber-500 dark:stroke-amber-500 h-6 w-6 relative">
<path stroke-linecap="round" stroke-linejoin="round" d="M3 3v1.5M3 21v-6m0 0 2.77-.693a9 9 0 0 1 6.208.682l.108.054a9 9 0 0 0 6.086.71l3.114-.732a48.524 48.524 0 0 1-.005-10.499l-3.11.732a9 9 0 0 1-6.085-.711l-.108-.054a9 9 0 0 0-6.208-.682L3 4.5M3 15V4.5" />
</svg>
</template>

View file

@ -1,18 +0,0 @@
<script setup>
/**
@name Icons/Menu/Services.vue
@description This component is used to create a complete svg icon for the menu.
This svg is related to the services link in the menu.
*/
</script>
<template>
<svg role="img"
aria-hidden="true" class="stroke-orange-500 h-6 w-6 relative"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M21.75 17.25v-.228a4.5 4.5 0 00-.12-1.03l-2.268-9.64a3.375 3.375 0 00-3.285-2.602H7.923a3.375 3.375 0 00-3.285 2.602l-2.268 9.64a4.5 4.5 0 00-.12 1.03v.228m19.5 0a3 3 0 01-3 3H5.25a3 3 0 01-3-3m19.5 0a3 3 0 00-3-3H5.25a3 3 0 00-3 3m16.5 0h.008v.008h-.008v-.008zm-3 0h.008v.008h-.008v-.008z" />
</svg>
</template>

View file

@ -0,0 +1,38 @@
<script setup>
/**
@name Icons/Plus.vue
@description This component is a svg icon representing addition (+).
@example
{
iconColor: 'info',
}
@param {string} [iconClass=""]
@param {string} [iconColor="info"] - The color of the icon between some tailwind css available colors (purple, green, red, orange, blue, yellow, gray, dark, amber, emerald, teal, indigo, cyan, sky, pink...). Darker colors are also available using the base color and adding '-darker' (e.g. 'red-darker').
*/
const props = defineProps({
iconClass : {
type: String,
required: false,
default : "default-svg"
},
iconColor : {
type: String,
required: false,
default: "info",
}
})
</script>
<template>
<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="[props.iconClass, props.iconColor]">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v6m3-3H9m12 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
</template>

View file

@ -0,0 +1,32 @@
<script setup>
/**
@name Icons/Puzzle.vue
@description This component is a svg icon representing puzzle.
@example
{
iconColor: 'info',
}
@param {string} [iconClass=""]
@param {string} [iconColor="info"] - The color of the icon between some tailwind css available colors (purple, green, red, orange, blue, yellow, gray, dark, amber, emerald, teal, indigo, cyan, sky, pink...). Darker colors are also available using the base color and adding '-darker' (e.g. 'red-darker').
*/
const props = defineProps({
iconClass : {
type: String,
required: false,
default : "default-svg"
},
iconColor : {
type: String,
required: false,
default: "info",
}
})
</script>
<template>
<svg :class="[props.iconClass, props.iconColor]" role="img"
aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path d="M11.25 5.337c0-.355-.186-.676-.401-.959a1.647 1.647 0 0 1-.349-1.003c0-1.036 1.007-1.875 2.25-1.875S15 2.34 15 3.375c0 .369-.128.713-.349 1.003-.215.283-.401.604-.401.959 0 .332.278.598.61.578 1.91-.114 3.79-.342 5.632-.676a.75.75 0 0 1 .878.645 49.17 49.17 0 0 1 .376 5.452.657.657 0 0 1-.66.664c-.354 0-.675-.186-.958-.401a1.647 1.647 0 0 0-1.003-.349c-1.035 0-1.875 1.007-1.875 2.25s.84 2.25 1.875 2.25c.369 0 .713-.128 1.003-.349.283-.215.604-.401.959-.401.31 0 .557.262.534.571a48.774 48.774 0 0 1-.595 4.845.75.75 0 0 1-.61.61c-1.82.317-3.673.533-5.555.642a.58.58 0 0 1-.611-.581c0-.355.186-.676.401-.959.221-.29.349-.634.349-1.003 0-1.035-1.007-1.875-2.25-1.875s-2.25.84-2.25 1.875c0 .369.128.713.349 1.003.215.283.401.604.401.959a.641.641 0 0 1-.658.643 49.118 49.118 0 0 1-4.708-.36.75.75 0 0 1-.645-.878c.293-1.614.504-3.257.629-4.924A.53.53 0 0 0 5.337 15c-.355 0-.676.186-.959.401-.29.221-.634.349-1.003.349-1.036 0-1.875-1.007-1.875-2.25s.84-2.25 1.875-2.25c.369 0 .713.128 1.003.349.283.215.604.401.959.401a.656.656 0 0 0 .659-.663 47.703 47.703 0 0 0-.31-4.82.75.75 0 0 1 .83-.832c1.343.155 2.703.254 4.077.294a.64.64 0 0 0 .657-.642Z" />
</svg>
</template>

View file

@ -0,0 +1,31 @@
<script setup>
/**
@name Icons/Settings.vue
@description This component is a svg icon representing settings.
@example
{
iconColor: 'info',
}
@param {string} [iconClass=""]
@param {string} [iconColor="info"] - The color of the icon between some tailwind css available colors (purple, green, red, orange, blue, yellow, gray, dark, amber, emerald, teal, indigo, cyan, sky, pink...). Darker colors are also available using the base color and adding '-darker' (e.g. 'red-darker').
*/
const props = defineProps({
iconClass : {
type: String,
required: false,
default : "default-svg"
},
iconColor : {
type: String,
required: false,
default: "info",
}
})
</script>
<template>
<svg role="img"
aria-hidden="true" :class="[props.iconClass, props.iconColor]" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path d="M18.75 12.75h1.5a.75.75 0 0 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM12 6a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 6ZM12 18a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 18ZM3.75 6.75h1.5a.75.75 0 1 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM5.25 18.75h-1.5a.75.75 0 0 1 0-1.5h1.5a.75.75 0 0 1 0 1.5ZM3 12a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 3 12ZM9 3.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5ZM12.75 12a2.25 2.25 0 1 1 4.5 0 2.25 2.25 0 0 1-4.5 0ZM9 15.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Z" />
</svg>
</template>

View file

@ -1,18 +0,0 @@
<script setup>
/**
@name Icons/Stat/Crown.vue
@description This component is used to create a complete svg icon for stat card.
This svg is a representation of a crown, mainly used for premium version stat.
*/
</script>
<template>
<svg role="img"
aria-hidden="true" class="leading-none text-lg relative scale-[0.6]"
viewBox="0 0 48 46"
fill="none"
xmlns="http://www.w3.org/2000/svg">
<path class="fill-white" d="M43.218 28.2327L43.6765 23.971C43.921 21.6973 44.0825 20.1957 43.9557 19.2497L44 19.25C46.071 19.25 47.75 17.5711 47.75 15.5C47.75 13.4289 46.071 11.75 44 11.75C41.929 11.75 40.25 13.4289 40.25 15.5C40.25 16.4366 40.5935 17.2931 41.1613 17.9503C40.346 18.4535 39.2805 19.515 37.6763 21.1128C36.4405 22.3438 35.8225 22.9593 35.1333 23.0548C34.7513 23.1075 34.3622 23.0532 34.0095 22.898C33.373 22.6175 32.9485 21.8567 32.0997 20.335L27.6262 12.3135C27.1025 11.3747 26.6642 10.5889 26.2692 9.95662C27.89 9.12967 29 7.44445 29 5.5C29 2.73857 26.7615 0.5 24 0.5C21.2385 0.5 19 2.73857 19 5.5C19 7.44445 20.11 9.12967 21.7308 9.95662C21.3358 10.589 20.8975 11.3746 20.3738 12.3135L15.9002 20.335C15.0514 21.8567 14.627 22.6175 13.9905 22.898C13.6379 23.0532 13.2487 23.1075 12.8668 23.0548C12.1774 22.9593 11.5595 22.3438 10.3238 21.1128C8.71968 19.515 7.6539 18.4535 6.83882 17.9503C7.4066 17.2931 7.75 16.4366 7.75 15.5C7.75 13.4289 6.07107 11.75 4 11.75C1.92893 11.75 0.25 13.4289 0.25 15.5C0.25 17.5711 1.92893 19.25 4 19.25L4.04428 19.2497C3.91755 20.1957 4.07905 21.6973 4.32362 23.971L4.782 28.2327C5.03645 30.5982 5.24802 32.849 5.50717 34.875H42.4928C42.752 32.849 42.9635 30.5982 43.218 28.2327Z" fill="#1C274C" />
<path class="fill-white" d="M21.2803 45.5H26.7198C33.8098 45.5 37.3545 45.5 39.7198 43.383C40.7523 42.4588 41.4057 40.793 41.8775 38.625H6.1224C6.59413 40.793 7.24783 42.4588 8.2802 43.383C10.6454 45.5 14.1903 45.5 21.2803 45.5Z" fill="#1C274C" />
</svg>
</template>

View file

@ -1,19 +0,0 @@
<script setup>
/**
@name Icons/Stat/Free.vue
@description This component is used to create a complete svg icon for stat card.
This svg is a representation of a free version stat.
*/
</script>
<template>
<svg role="img"
aria-hidden="true" class="leading-none fill-white text-yellow-500 text-lg relative scale-[0.6]"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 5.25a3 3 0 0 1 3 3m3 0a6 6 0 0 1-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1 1 21.75 8.25Z" />
</svg>
</template>

View file

@ -1,19 +0,0 @@
<script setup>
/**
@name Icons/Stat/Plugins.vue
@description This component is used to create a complete svg icon for stat card.
This svg is a representation of the plugins of the application.
*/
</script>
<template>
<svg role="img"
aria-hidden="true" class="scale-75 leading-none text-lg relative fill-white"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M14.25 6.087c0-.355.186-.676.401-.959.221-.29.349-.634.349-1.003 0-1.036-1.007-1.875-2.25-1.875s-2.25.84-2.25 1.875c0 .369.128.713.349 1.003.215.283.401.604.401.959v0a.64.64 0 01-.657.643 48.39 48.39 0 01-4.163-.3c.186 1.613.293 3.25.315 4.907a.656.656 0 01-.658.663v0c-.355 0-.676-.186-.959-.401a1.647 1.647 0 00-1.003-.349c-1.036 0-1.875 1.007-1.875 2.25s.84 2.25 1.875 2.25c.369 0 .713-.128 1.003-.349.283-.215.604-.401.959-.401v0c.31 0 .555.26.532.57a48.039 48.039 0 01-.642 5.056c1.518.19 3.058.309 4.616.354a.64.64 0 00.657-.643v0c0-.355-.186-.676-.401-.959a1.647 1.647 0 01-.349-1.003c0-1.035 1.008-1.875 2.25-1.875 1.243 0 2.25.84 2.25 1.875 0 .369-.128.713-.349 1.003-.215.283-.4.604-.4.959v0c0 .333.277.599.61.58a48.1 48.1 0 005.427-.63 48.05 48.05 0 00.582-4.717.532.532 0 00-.533-.57v0c-.355 0-.676.186-.959.401-.29.221-.634.349-1.003.349-1.035 0-1.875-1.007-1.875-2.25s.84-2.25 1.875-2.25c.37 0 .713.128 1.003.349.283.215.604.401.96.401v0a.656.656 0 00.658-.663 48.422 48.422 0 00-.37-5.36c-1.886.342-3.81.574-5.766.689a.578.578 0 01-.61-.58v0z" />
</svg>
</template>

View file

@ -1,19 +0,0 @@
<script setup>
/**
@name Icons/Stat/Services.vue
@description This component is used to create a complete svg icon for stat card.
This svg is a representation of the services of the application.
*/
</script>
<template>
<svg role="img"
aria-hidden="true" class="scale-[0.6] stroke-orange-500 leading-none text-lg relative fill-white"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M21.75 17.25v-.228a4.5 4.5 0 00-.12-1.03l-2.268-9.64a3.375 3.375 0 00-3.285-2.602H7.923a3.375 3.375 0 00-3.285 2.602l-2.268 9.64a4.5 4.5 0 00-.12 1.03v.228m19.5 0a3 3 0 01-3 3H5.25a3 3 0 01-3-3m19.5 0a3 3 0 00-3-3H5.25a3 3 0 00-3 3m16.5 0h.008v.008h-.008v-.008zm-3 0h.008v.008h-.008v-.008z" />
</svg>
</template>

View file

@ -1,16 +0,0 @@
<script setup>
/**
@name Icons/Stat/Version.vue
@description This component is used to create a complete svg icon for stat card.
This svg is a representation of the version of the application.
*/
</script>
<template>
<svg role="img"
aria-hidden="true" class="translate-x-0.5 -translate-y-0.5 scale-50 leading-none text-lg relative fill-white"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 448 512">
<path d="M80 104c13.3 0 24-10.7 24-24s-10.7-24-24-24S56 66.7 56 80s10.7 24 24 24zm80-24c0 32.8-19.7 61-48 73.3v87.8c18.8-10.9 40.7-17.1 64-17.1h96c35.3 0 64-28.7 64-64v-6.7C307.7 141 288 112.8 288 80c0-44.2 35.8-80 80-80s80 35.8 80 80c0 32.8-19.7 61-48 73.3V160c0 70.7-57.3 128-128 128H176c-35.3 0-64 28.7-64 64v6.7c28.3 12.3 48 40.5 48 73.3c0 44.2-35.8 80-80 80s-80-35.8-80-80c0-32.8 19.7-61 48-73.3V352 153.3C19.7 141 0 112.8 0 80C0 35.8 35.8 0 80 0s80 35.8 80 80zm232 0c0-13.3-10.7-24-24-24s-24 10.7-24 24s10.7 24 24 24s24-10.7 24-24zM80 456c13.3 0 24-10.7 24-24s-10.7-24-24-24s-24 10.7-24 24s10.7 24 24 24z" />
</svg>
</template>

View file

@ -0,0 +1,32 @@
<script setup>
/**
@name Icons/Task.vue
@description This component is a svg icon representing task.
@example
{
iconColor: 'info',
}
@param {string} [iconClass=""]
@param {string} [iconColor="info"] - The color of the icon between some tailwind css available colors (purple, green, red, orange, blue, yellow, gray, dark, amber, emerald, teal, indigo, cyan, sky, pink...). Darker colors are also available using the base color and adding '-darker' (e.g. 'red-darker').
*/
const props = defineProps({
iconClass : {
type: String,
required: false,
default : "default-svg"
},
iconColor : {
type: String,
required: false,
default: "info",
}
})
</script>
<template>
<svg :class="[props.iconClass, props.iconColor]" role="img"
aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path fill-rule="evenodd" d="M9 1.5H5.625c-1.036 0-1.875.84-1.875 1.875v17.25c0 1.035.84 1.875 1.875 1.875h12.75c1.035 0 1.875-.84 1.875-1.875V12.75A3.75 3.75 0 0 0 16.5 9h-1.875a1.875 1.875 0 0 1-1.875-1.875V5.25A3.75 3.75 0 0 0 9 1.5Zm6.61 10.936a.75.75 0 1 0-1.22-.872l-3.236 4.53L9.53 14.47a.75.75 0 0 0-1.06 1.06l2.25 2.25a.75.75 0 0 0 1.14-.094l3.75-5.25Z" clip-rule="evenodd" />
<path d="M12.971 1.816A5.23 5.23 0 0 1 14.25 5.25v1.875c0 .207.168.375.375.375H16.5a5.23 5.23 0 0 1 3.434 1.279 9.768 9.768 0 0 0-6.963-6.963Z" />
</svg>
</template>

View file

@ -0,0 +1,31 @@
<script setup>
/**
@name Icons/Trespass.vue
@description This component is a svg icon representing no trespassing.
@example
{
iconColor: 'info',
}
@param {string} [iconClass=""]
@param {string} [iconColor="info"] - The color of the icon between some tailwind css available colors (purple, green, red, orange, blue, yellow, gray, dark, amber, emerald, teal, indigo, cyan, sky, pink...). Darker colors are also available using the base color and adding '-darker' (e.g. 'red-darker').
*/
const props = defineProps({
iconClass : {
type: String,
required: false,
default : "default-svg"
},
iconColor : {
type: String,
required: false,
default: "info",
}
})
</script>
<template>
<svg role="img"
aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" :class="[props.iconClass, props.iconColor]">
<path fill-rule="evenodd" d="m6.72 5.66 11.62 11.62A8.25 8.25 0 0 0 6.72 5.66Zm10.56 12.68L5.66 6.72a8.25 8.25 0 0 0 11.62 11.62ZM5.105 5.106c3.807-3.808 9.98-3.808 13.788 0 3.808 3.807 3.808 9.98 0 13.788-3.807 3.808-9.98 3.808-13.788 0-3.808-3.807-3.808-9.98 0-13.788Z" clip-rule="evenodd" />
</svg>
</template>

View file

@ -1,16 +1,34 @@
<script setup>
/**
@name Icons/Menu/Twitter.vue
@description This component is used to create a complete svg icon for the menu.
This svg is related to the twitter social link in the menu.
@name Icons/Twiiter.vue
@description This component is a svg icon representing Twiiter.
@example
{
iconColor: 'info',
}
@param {string} [iconClass=""]
@param {string} [iconColor="twitter"]
*/
const props = defineProps({
iconClass : {
type: String,
required: false,
default : "default-svg"
},
iconColor : {
type: String,
required: false,
default: "twitter",
}
})
</script>
<template>
<svg
role="img"
aria-hidden="true"
class="hover:opacity-80"
fill="#1DA1F2"
:class="[props.iconClass, props.iconColor]"
fill="none"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>

View file

@ -1,14 +1,31 @@
<script setup>
/**
@name Icons/Stat/Instances.vue
@description This component is used to create a complete svg icon for stat card.
This svg is a representation of the instances stat.
@name Icons/Wire.vue
@description This component is a svg icon representing wire.
@example
{
iconColor: 'info',
}
@param {string} [iconClass=""]
@param {string} [iconColor="info"] - The color of the icon between some tailwind css available colors (purple, green, red, orange, blue, yellow, gray, dark, amber, emerald, teal, indigo, cyan, sky, pink...). Darker colors are also available using the base color and adding '-darker' (e.g. 'red-darker').
*/
const props = defineProps({
iconClass : {
type: String,
required: false,
default : "default-svg"
},
iconColor : {
type: String,
required: false,
default: "info",
}
})
</script>
<template>
<svg role="img"
aria-hidden="true" class="translate-x-0.5 -translate-y-0.5 scale-50 leading-none text-lg relative fill-white"
aria-hidden="true" :class="[props.iconClass, props.iconColor]"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 448 512">
<path d="M80 104c13.3 0 24-10.7 24-24s-10.7-24-24-24S56 66.7 56 80s10.7 24 24 24zm80-24c0 32.8-19.7 61-48 73.3v87.8c18.8-10.9 40.7-17.1 64-17.1h96c35.3 0 64-28.7 64-64v-6.7C307.7 141 288 112.8 288 80c0-44.2 35.8-80 80-80s80 35.8 80 80c0 32.8-19.7 61-48 73.3V160c0 70.7-57.3 128-128 128H176c-35.3 0-64 28.7-64 64v6.7c28.3 12.3 48 40.5 48 73.3c0 44.2-35.8 80-80 80s-80-35.8-80-80c0-32.8 19.7-61 48-73.3V352 153.3C19.7 141 0 112.8 0 80C0 35.8 35.8 0 80 0s80 35.8 80 80zm232 0c0-13.3-10.7-24-24-24s-24 10.7-24 24s10.7 24 24 24s24-10.7 24-24zM80 456c13.3 0 24-10.7 24-24s-10.7-24-24-24s-24 10.7-24 24s10.7 24 24 24z" />

View file

@ -3,7 +3,7 @@ import { computed, ref, watch, onBeforeMount, onMounted } from 'vue';
import { contentIndex } from "@utils/tabindex.js";
import { useEventStore } from "@store/event.js";
import Container from "@components/Widget/Container.vue";
import IconAdd from "@components/Icons/Button/Add.vue";
import Icons from "@components/Widget/Icons.vue";
/**
@name Widget/Button.vue
@ -31,7 +31,7 @@ import IconAdd from "@components/Icons/Button/Add.vue";
@param {boolean} [hideText=false] - Hide text to only display icon
@param {string} [color="primary"]
@param {string} [size="normal"] - Can be of size sm || normal || lg || xl
@param {string} [iconName=""] - Name in lowercase of icons store on /Icons/Button
@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=""]
@ -107,7 +107,7 @@ const props = defineProps({
const btnEl = ref();
const buttonClass = computed(() => {
return `btn btn-${props.color} btn-${props.size}`
return `btn ${props.color} ${props.size}`
})
onMounted(() => {
@ -165,7 +165,7 @@ function updateData(isClick = false) {
<span :class="[props.hideText ? 'sr-only' : '',
props.iconName ? 'mr-2' : ''
]" :id="`${props.id}-text`">{{ $t(props.text, props.text) }}</span>
<IconAdd v-if="props.iconName === 'add'" :iconName="props.iconName" :iconColor="props.iconColor" />
<Icons :iconName="props.iconName" :iconClass="'btn-svg'" :iconColor="props.iconColor" />
</button>
</Container>
</template>

View file

@ -0,0 +1,79 @@
<script setup>
import Box from "@components/Icons/Box.vue";
import Carton from "@components/Icons/Carton.vue";
import Core from "@components/Icons/Core.vue";
import Crown from "@components/Icons/Crown.vue";
import Discord from "@components/Icons/Discord.vue";
import Disk from "@components/Icons/Disk.vue";
import Flag from "@components/Icons/Flag.vue";
import Gear from "@components/Icons/Gear.vue";
import Github from "@components/Icons/Github.vue";
import House from "@components/Icons/House.vue";
import List from "@components/Icons/List.vue";
import Key from "@components/Icons/Key.vue";
import Linkedin from "@components/Icons/Linkedin.vue";
import Plus from "@components/Icons/Plus.vue";
import Puzzle from "@components/Icons/Puzzle.vue";
import Settings from "@components/Icons/Settings.vue";
import Task from "@components/Icons/Task.vue";
import Trespass from "@components/Icons/Trespass.vue";
import Twitter from "@components/Icons/Twitter.vue";
import Wire from "@components/Icons/Wire.vue";
import { computed } from "vue";
/**
@name Widget/Icons.vue
@description This component is a wrapper that contains all the icons available in the application (Icons folder).
This component is used to display the icon based on the icon name.
This component is mainly use inside others widgets.
@example
{
iconName: 'box',
iconClass: 'stat-svg',
iconColor: 'amber',
}
@param {string} iconName - The name of the icon to display. The icon name is the name of the file without the extension on lowercase.
@param {string} [iconClass="default-svg"] - Class to apply to the icon. In case the icon is related to a widget, the widget will set the right class automatically.
@param {string} [iconColor="info"] - The color of the icon between some tailwind css available colors (purple, green, red, orange, blue, yellow, gray, dark, amber, emerald, teal, indigo, cyan, sky, pink...). Darker colors are also available using the base color and adding '-darker' (e.g. 'red-darker').
*/
const props = defineProps({
iconName: {
type: String,
required: true,
},
iconClass: {
type: String,
required: false,
default: "default-svg",
},
iconColor: {
type: String,
required: false,
default: "info",
},
});
</script>
<template>
<Box v-if="props.iconName === 'box'" :iconClass="props.iconClass" :iconColor="props.iconColor" />
<Carton v-if="props.iconName === 'carton'" :iconClass="props.iconClass" :iconColor="props.iconColor" />
<Core v-if="props.iconName === 'core'" :iconClass="props.iconClass" :iconColor="props.iconColor" />
<Crown v-if="props.iconName === 'crown'" :iconClass="props.iconClass" :iconColor="props.iconColor" />
<Discord v-if="props.iconName === 'discord'" :iconClass="props.iconClass" :iconColor="props.iconColor" />
<Disk v-if="props.iconName === 'disk'" :iconClass="props.iconClass" :iconColor="props.iconColor" />
<Flag v-if="props.iconName === 'flag'" :iconClass="props.iconClass" :iconColor="props.iconColor" />
<Gear v-if="props.iconName === 'gear'" :iconClass="props.iconClass" :iconColor="props.iconColor" />
<Github v-if="props.iconName === 'github'" :iconClass="props.iconClass" :iconColor="props.iconColor" />
<House v-if="props.iconName === 'house'" :iconClass="props.iconClass" :iconColor="props.iconColor" />
<List v-if="props.iconName === 'list'" :iconClass="props.iconClass" :iconColor="props.iconColor" />
<Key v-if="props.iconName === 'key'" :iconClass="props.iconClass" :iconColor="props.iconColor" />
<Linkedin v-if="props.iconName === 'linkedin'" :iconClass="props.iconClass" :iconColor="props.iconColor" />
<Plus v-if="props.iconName === 'plus'" :iconClass="props.iconClass" :iconColor="props.iconColor" />
<Puzzle v-if="props.iconName === 'puzzle'" :iconClass="props.iconClass" :iconColor="props.iconColor" />
<Settings v-if="props.iconName === 'settings'" :iconClass="props.iconClass" :iconColor="props.iconColor" />
<Task v-if="props.iconName === 'task'" :iconClass="props.iconClass" :iconColor="props.iconColor" />
<Trespass v-if="props.iconName === 'trespass'" :iconClass="props.iconClass" :iconColor="props.iconColor" />
<Twitter v-if="props.iconName === 'twitter'" :iconClass="props.iconClass" :iconColor="props.iconColor" />
<Wire v-if="props.iconName === 'wire'" :iconClass="props.iconClass" :iconColor="props.iconColor" />
</template>

View file

@ -1,11 +1,6 @@
<script setup>
import { onMounted, ref } from 'vue';
import IconCrown from '@components/Icons/Stat/Crown.vue';
import IconFree from '@components/Icons/Stat/Free.vue';
import IconInstances from '@components/Icons/Stat/Instances.vue';
import IconPlugins from '@components/Icons/Stat/Plugins.vue';
import IconServices from '@components/Icons/Stat/Services.vue';
import IconVersion from '@components/Icons/Stat/Version.vue';
import IconList from '@components/Icons/List.vue';
/**
@name Widget/Stat.vue
@ -25,7 +20,7 @@ import IconVersion from '@components/Icons/Stat/Version.vue';
@param {string} title - The title of the stat. Can be a translation key or by default raw text.
@param {string|number} value - The value of the stat
@param {string} [subtitle=""] - The subtitle of the stat. Can be a translation key or by default raw text.
@param {string} [icon=""] - A top-right icon to display between icon available in Icons/Stat. Case falsy value, no icon displayed. The icon name is the name of the file without the extension on lowercase.
@param {string} [iconName=""] - A top-right icon to display between icon available in Icons/Stat. Case falsy value, no icon displayed. The icon name is the name of the file without the extension on lowercase.
@param {string} [iconColor="sky"] - The color of the icon between some tailwind css available colors (purple, green, red, orange, blue, yellow, gray, dark, amber, emerald, teal, indigo, cyan, sky, pink, lime, rose, fuchsia, violet, lightBlue, warmGray, trueGray, coolGray, blueGray, white, black)
@param {string} [link=""] - A link to redirect when the card is clicked. Case falsy value, element is a div, , else it is an anchor.
@param {string} [subtitleColor="info"] - The color of the subtitle between error, success, warning, info
@ -46,7 +41,7 @@ const props = defineProps({
required: false,
default : ""
},
icon: {
iconName: {
type: String,
required: false,
default : ""
@ -102,12 +97,7 @@ onMounted(() => {
<div v-if="props.icon" role="img"
aria-label="version"
:class="['stat-svg-container', props.iconColor]">
<IconCrown v-if="props.icon === 'crown'" />
<IconFree v-else-if="props.icon === 'free'" />
<IconInstances v-else-if="props.icon === 'instances'" />
<IconPlugins v-else-if="props.icon === 'plugins'" />
<IconServices v-else-if="props.icon === 'services'" />
<IconVersion v-else-if="props.icon === 'version'" />
<IconList :iconName="props.icon" :iconClass="'stat-svg'" :iconColor="props.iconColor" />
</div>
<!-- end icon -->
</component>

View file

@ -69,7 +69,7 @@ const buttonSVGData = {
eventAttr: {"store" : "modal", "default" : "close", "value" : "open", "target" : "modal_id", "valueExpanded" : "open"},
columns: {"pc": 12, "tablet": 12, "mobile": 12},
tabId: '1',
iconName : "add"
iconName : "plus"
}
const buttonOnlySVGData = {
@ -81,7 +81,7 @@ const buttonOnlySVGData = {
eventAttr: {"store" : "modal", "default" : "close", "value" : "open", "target" : "modal_id", "valueExpanded" : "open"},
columns: {"pc": 12, "tablet": 12, "mobile": 12},
tabId: '1',
iconName : "add"
iconName : "plus"
}
</script>