mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
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:
parent
d3ff909019
commit
d74521e7e6
62 changed files with 1488 additions and 2911 deletions
181
jsdoc/Select.js
181
jsdoc/Select.js
|
|
@ -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"]);
|
||||
BIN
jsdoc/Select.md
BIN
jsdoc/Select.md
Binary file not shown.
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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
|
|
@ -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>
|
||||
|
|
|
|||
31
vuejs/client/src/components/Icons/Box.vue
Normal file
31
vuejs/client/src/components/Icons/Box.vue
Normal 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>
|
||||
|
|
@ -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>
|
||||
32
vuejs/client/src/components/Icons/Carton.vue
Normal file
32
vuejs/client/src/components/Icons/Carton.vue
Normal 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>
|
||||
38
vuejs/client/src/components/Icons/Core.vue
Normal file
38
vuejs/client/src/components/Icons/Core.vue
Normal 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>
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
>
|
||||
31
vuejs/client/src/components/Icons/Disk.vue
Normal file
31
vuejs/client/src/components/Icons/Disk.vue
Normal 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>
|
||||
31
vuejs/client/src/components/Icons/Flag.vue
Normal file
31
vuejs/client/src/components/Icons/Flag.vue
Normal 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>
|
||||
31
vuejs/client/src/components/Icons/Gear.vue
Normal file
31
vuejs/client/src/components/Icons/Gear.vue
Normal 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>
|
||||
|
|
@ -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"
|
||||
>
|
||||
32
vuejs/client/src/components/Icons/House.vue
Normal file
32
vuejs/client/src/components/Icons/House.vue
Normal 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>
|
||||
36
vuejs/client/src/components/Icons/Key.vue
Normal file
36
vuejs/client/src/components/Icons/Key.vue
Normal 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>
|
||||
|
|
@ -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"
|
||||
31
vuejs/client/src/components/Icons/List.vue
Normal file
31
vuejs/client/src/components/Icons/List.vue
Normal 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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
38
vuejs/client/src/components/Icons/Plus.vue
Normal file
38
vuejs/client/src/components/Icons/Plus.vue
Normal 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>
|
||||
32
vuejs/client/src/components/Icons/Puzzle.vue
Normal file
32
vuejs/client/src/components/Icons/Puzzle.vue
Normal 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>
|
||||
31
vuejs/client/src/components/Icons/Settings.vue
Normal file
31
vuejs/client/src/components/Icons/Settings.vue
Normal 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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
32
vuejs/client/src/components/Icons/Task.vue
Normal file
32
vuejs/client/src/components/Icons/Task.vue
Normal 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>
|
||||
31
vuejs/client/src/components/Icons/Trespass.vue
Normal file
31
vuejs/client/src/components/Icons/Trespass.vue
Normal 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>
|
||||
|
|
@ -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"
|
||||
>
|
||||
|
|
@ -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" />
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
79
vuejs/client/src/components/Widget/Icons.vue
Normal file
79
vuejs/client/src/components/Widget/Icons.vue
Normal 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>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue