mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
remove event store for vanilla JS
This commit is contained in:
parent
180a014644
commit
4ddceff055
5 changed files with 78 additions and 104 deletions
|
|
@ -1,7 +1,6 @@
|
|||
<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 Icons from "@components/Widget/Icons.vue";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
|
@ -11,9 +10,7 @@ import { v4 as uuidv4 } from "uuid";
|
|||
@description This component is a standard button.
|
||||
You can link this button to the event store with the clickAttr object.
|
||||
This will allow you to share a value with other components, for example switching form on a click.
|
||||
The clickAttr object must contain at least the key, defaultValue and value to work with the event store.
|
||||
It can also contain the targetId element in case the button is linked to another element, like a modal or a sidebar.
|
||||
We can specify a valueExpanded value, we will check the current value in the store and the valueExpanded, this will update the aria-expanded attribute of the button.
|
||||
The prop attrs is an object that can contain multiple attributes to add to the button.
|
||||
@example
|
||||
{
|
||||
id: "open-modal-btn",
|
||||
|
|
@ -24,7 +21,9 @@ import { v4 as uuidv4 } from "uuid";
|
|||
size: "normal",
|
||||
iconName: "modal",
|
||||
iconColor: "white",
|
||||
clickAttr: {"key" : "modal-config", "defaultValue" : "close", "clickValue" : "open", "targetId" : "modal_id", "valueExpanded" : "open"},
|
||||
attrs: [
|
||||
{ key: "modal", defaultValue: "close", clickValue: "open", targetId: "modal_id", valueExpanded: "open" },
|
||||
],
|
||||
}
|
||||
@param {string} [id=uuid()] - Unique id of the button
|
||||
@param {string} text - Content of the button. Can be a translation key or by default raw text.
|
||||
|
|
@ -35,13 +34,10 @@ import { v4 as uuidv4 } from "uuid";
|
|||
@param {string} [size="normal"] - Can be of size sm || normal || lg || xl
|
||||
@param {string} [iconName=""] - Name in lowercase of icons store on /Icons. If falsy value, no icon displayed.
|
||||
@param {string} [iconColor=""]
|
||||
@param {object} [clickAttr={}] - Click event manage with event store {"key" : <key_name>, "defaultValue" : <defaultValue = "set this value to store if not done">, "clickValue" : <value_set_on_click>, "targetId"<optional> : <targetId_element="id of element link to the button event">, "valueExpanded<optional>" : <expanded_value="check current value in store and this value to determine a expanded true or false">}
|
||||
@param {array} [staticAttr=[]] - Static attributes that can be useful to do some check with script (for example {"data-attr" : "value"} will add data-attr="value" to the button)
|
||||
@param {Object} [attrs={}] - List of attributs to add to the button. Some attributs will conduct to additionnal script
|
||||
@param {string|number} [tabId=contentIndex] - The tabindex of the field, by default it is the contentIndex
|
||||
*/
|
||||
|
||||
const eventStore = useEventStore();
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: String,
|
||||
|
|
@ -94,13 +90,7 @@ const props = defineProps({
|
|||
required: false,
|
||||
default: "",
|
||||
},
|
||||
// Example of button opening a modal : {"key" : "modal", "defaultValue" : "close", "clickValue" : "open", "targetId" : "modal_id", "valueExpanded" : "open"}
|
||||
clickAttr: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: {},
|
||||
},
|
||||
staticAttr: {
|
||||
attrs: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: {},
|
||||
|
|
@ -124,59 +114,14 @@ const buttonClass = computed(() => {
|
|||
});
|
||||
|
||||
onMounted(() => {
|
||||
setStaticAttr();
|
||||
updateData();
|
||||
setAttrs();
|
||||
});
|
||||
|
||||
watch(eventStore, () => {
|
||||
updateData();
|
||||
});
|
||||
|
||||
function setStaticAttr() {
|
||||
for (const [key, value] of Object.entries(props.staticAttr)) {
|
||||
function setAttrs() {
|
||||
for (const [key, value] of Object.entries(props.attrs)) {
|
||||
btnEl.value.setAttribute(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
function updateData(isClick = false) {
|
||||
const isKey = props.clickAttr?.key ? true : false;
|
||||
const isValue = props.clickAttr?.clickValue ? true : false;
|
||||
const isDefault = props.clickAttr?.defaultValue ? true : false;
|
||||
if (!isKey || !isValue || !isDefault) return;
|
||||
|
||||
isClick
|
||||
? eventStore.updateEvent(props.clickAttr.key, props.clickAttr.clickValue)
|
||||
: eventStore.addEvent(props.clickAttr.key, props.clickAttr.defaultValue);
|
||||
|
||||
try {
|
||||
const expanded = props.clickAttr?.valueExpanded
|
||||
? props.clickAttr.valueExpanded ===
|
||||
eventStore.getEvent(props.clickAttr.key)
|
||||
? "true"
|
||||
: "false"
|
||||
: false;
|
||||
if (expanded) {
|
||||
btnEl.value.setAttribute("aria-expanded", expanded);
|
||||
}
|
||||
|
||||
if (!expanded) {
|
||||
btnEl.value.removeAttribute("aria-expanded");
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
try {
|
||||
const controls = props.clickAttr?.targetId
|
||||
? props.clickAttr.targetId
|
||||
: false;
|
||||
if (controls) {
|
||||
btnEl.value.setAttribute("aria-controls", controls);
|
||||
}
|
||||
|
||||
if (!controls) {
|
||||
btnEl.value.removeAttribute("aria-controls");
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -187,15 +132,19 @@ function updateData(isClick = false) {
|
|||
<button
|
||||
:type="props.type"
|
||||
ref="btnEl"
|
||||
@click="updateData(true)"
|
||||
:id="props.id"
|
||||
@click.prevent=""
|
||||
:tabindex="props.tabId"
|
||||
:class="[buttonClass]"
|
||||
:disabled="props.disabled || false"
|
||||
:aria-describedby="`text-${props.id}`"
|
||||
>
|
||||
<span
|
||||
:class="[props.hideText ? 'sr-only' : '', props.iconName ? 'mr-2' : '']"
|
||||
:class="[
|
||||
props.hideText ? 'sr-only' : '',
|
||||
props.iconName ? 'mr-2' : '',
|
||||
'pointer-events-none',
|
||||
]"
|
||||
:id="`text-${props.id}`"
|
||||
>{{ $t(props.text, props.text) }}</span
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
<script setup>
|
||||
import { reactive, onBeforeMount } from "vue";
|
||||
import { reactive, onBeforeMount, onMounted } from "vue";
|
||||
import DashboardLayout from "@components/Dashboard/Layout.vue";
|
||||
import Builder from "@components/Builder.vue";
|
||||
import { useGlobal } from "@utils/global.js";
|
||||
|
||||
/**
|
||||
@name Page/Home.vue
|
||||
|
|
@ -24,6 +25,10 @@ onBeforeMount(() => {
|
|||
home.builder = data;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
useGlobal();
|
||||
});
|
||||
|
||||
// const data = [
|
||||
// {
|
||||
// type: "Instance",
|
||||
|
|
@ -51,5 +56,6 @@ onBeforeMount(() => {
|
|||
<template>
|
||||
<DashboardLayout>
|
||||
<Builder v-if="home.builder" :builder="home.builder" />
|
||||
<div id="test-el"></div>
|
||||
</DashboardLayout>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
data-server-flash='[{"type" : "success", "title" : "title", "message" : "Success feedback"}, {"type" : "error", "title" : "title", "message" : "Error feedback"}, {"type" : "warning", "title" : "title", "message" : "Warning feedback"}, {"type" : "info", "title" : "title", "message" : "Info feedback"}]'>
|
||||
</div>
|
||||
<div class="hidden"
|
||||
data-server-builder='[{"type":"card","link":"/services","containerClass":"","containerColumns":{"pc":6,"tablet":6,"mobile":12},"widgets":[{"type":"Instance","data":{"details":[{"key":"instances_hostname","value":"www.example.com"},{"key":"instances_method","value":"dashboard_ui"},{"key":"instances_port","value":"1084"},{"key":"instances_status","value":"instances_active"}],"status":"success","title":"www.example.com","buttons":[{"type" : "submit", "text":"action_edit","color":"edit","size":"normal"}]}}]},{"type":"card","link":"/services","containerClass":"","containerColumns":{"pc":6,"tablet":6,"mobile":12},"widgets":[{"type":"Instance","data":{"details":[{"key":"instances_hostname","value":"www.example.com"},{"key":"instances_method","value":"dashboard_ui"},{"key":"instances_port","value":"1084"},{"key":"instances_status","value":"instances_active"}],"status":"error","title":"www.example.com","buttons":[{"type" : "submit", "text":"action_start","color":"valid","size":"normal"}]}}]}]'>
|
||||
data-server-builder='[{"type":"card","link":"/services","containerClass":"","containerColumns":{"pc":6,"tablet":6,"mobile":12},"widgets":[{"type":"Instance","data":{"details":[{"key":"instances_hostname","value":"www.example.com"},{"key":"instances_method","value":"dashboard_ui"},{"key":"instances_port","value":"1084"},{"key":"instances_status","value":"instances_active"}],"status":"success","title":"www.example.com","buttons":[{"attrs" : {"aria-controls" : "test-el", "aria-expanded" : "false"}, "text":"action_edit","color":"edit","size":"normal"}]}}]},{"type":"card","link":"/services","containerClass":"","containerColumns":{"pc":6,"tablet":6,"mobile":12},"widgets":[{"type":"Instance","data":{"details":[{"key":"instances_hostname","value":"www.example.com"},{"key":"instances_method","value":"dashboard_ui"},{"key":"instances_port","value":"1084"},{"key":"instances_status","value":"instances_active"}],"status":"error","title":"www.example.com","buttons":[{ "text":"action_start","color":"valid","size":"normal"}]}}]}]'>
|
||||
</div>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="instances.js"></script>
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
import { data } from "autoprefixer";
|
||||
import { defineStore } from "pinia";
|
||||
import { ref } from "vue";
|
||||
|
||||
/**
|
||||
@name useEventStore
|
||||
@description Store to share data between components related to events (click, change, ...).
|
||||
We can toggle a component visibility, change a value, etc... using this store.
|
||||
Be aware that this store better work using primitive values (string, number, boolean) and not objects.
|
||||
*/
|
||||
export const useEventStore = defineStore("event", () => {
|
||||
const event = ref({});
|
||||
|
||||
// add only if the event is not already in the store
|
||||
function addEvent(name, value) {
|
||||
if (!(name in event.value)) event.value[name] = value;
|
||||
}
|
||||
|
||||
function updateEvent(name, value) {
|
||||
event.value[name] = value;
|
||||
}
|
||||
|
||||
function getEvent(name) {
|
||||
return event.value[name];
|
||||
}
|
||||
|
||||
function deleteEvent(name) {
|
||||
delete event.value[name];
|
||||
}
|
||||
|
||||
function $reset() {
|
||||
data.value = {};
|
||||
}
|
||||
|
||||
return { event, $reset, addEvent, updateEvent, deleteEvent, getEvent };
|
||||
});
|
||||
55
vuejs/client/src/utils/global.js
Normal file
55
vuejs/client/src/utils/global.js
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
@name global.js
|
||||
@description This file contains global utils that will be used in the application by default.
|
||||
This file contains functions related to accessibilities, cookies, and other global utils.
|
||||
*/
|
||||
|
||||
// This function is a wrapper that contains all the global utils functions.
|
||||
function useGlobal() {
|
||||
window.addEventListener("click", (e) => {
|
||||
updateExpanded();
|
||||
});
|
||||
}
|
||||
|
||||
// This function updates the aria-expanded attribute of an element in case we have an aria-controls attribute.
|
||||
function updateExpanded() {
|
||||
// Wait for previous event and element visibility update
|
||||
setTimeout(() => {
|
||||
// Check state for all elements that have aria-controls and aria-expanded attributes
|
||||
const controlEls = document.querySelectorAll(
|
||||
"[aria-controls][aria-expanded]"
|
||||
);
|
||||
if (!controlEls) return;
|
||||
controlEls.forEach((el) => {
|
||||
try {
|
||||
const targetEl = document.getElementById(
|
||||
el.getAttribute("aria-controls")
|
||||
);
|
||||
if (!targetEl) return el.setAttribute("aria-expanded", "false");
|
||||
el.setAttribute(
|
||||
"aria-expanded",
|
||||
isElHidden(targetEl) ? "false" : "true"
|
||||
);
|
||||
} catch (err) {}
|
||||
});
|
||||
}, 50);
|
||||
}
|
||||
|
||||
// Check all the possible ways to hide an element
|
||||
function isElHidden(el) {
|
||||
return el.hasAttribute("aria-hidden")
|
||||
? el.getAttribute("aria-hidden") === "true"
|
||||
? true
|
||||
: false
|
||||
: el.classList.contains("hidden")
|
||||
? true
|
||||
: el.style.display === "none"
|
||||
? true
|
||||
: el.style.visibility === "hidden"
|
||||
? true
|
||||
: el.classList.contains("!hidden")
|
||||
? true
|
||||
: false;
|
||||
}
|
||||
|
||||
export { useGlobal };
|
||||
Loading…
Reference in a new issue