mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
start modal
* add modal base layout * add backdrop click close modal logic * add modal close btn logic * update button to handle no css style and avoid svg to be clickable inside it * update utils that allow to hide/show an element using attributs
This commit is contained in:
parent
a7be545baa
commit
a7723a2140
8 changed files with 116 additions and 38 deletions
|
|
@ -490,6 +490,31 @@ body {
|
|||
@apply col-span-12 grid grid-cols-12 w-full relative;
|
||||
}
|
||||
|
||||
.layout-card {
|
||||
@apply overflow-visible relative transition dark:brightness-110 shadow-md bg-white dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border transform duration-300 ease-in-out;
|
||||
}
|
||||
|
||||
.layout-modal-container {
|
||||
@apply z-[9999] fixed top-0 left-0 w-full h-full;
|
||||
}
|
||||
|
||||
.layout-backdrop {
|
||||
@apply z-[10] fixed top-0 left-0 w-full h-full bg-black bg-opacity-50;
|
||||
}
|
||||
|
||||
.layout-modal-wrap {
|
||||
@apply relative z-[20] flex justify-center items-center w-full h-full;
|
||||
}
|
||||
|
||||
.layout-modal {
|
||||
@apply relative min-w-[50vw] w-full max-w-screen-xl max-h-[80vh] min-h-[200px] transition dark:brightness-110 shadow-md bg-white dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border transform duration-300 ease-in-out;
|
||||
}
|
||||
|
||||
.layout-modal-button-container {
|
||||
@apply absolute right-2 top-2 z-[30];
|
||||
}
|
||||
|
||||
|
||||
.layout-settings {
|
||||
@apply py-4 sm:gap-x-8 gap-y-8 col-span-12 grid grid-cols-12 w-full relative;
|
||||
}
|
||||
|
|
@ -1163,9 +1188,6 @@ body {
|
|||
|
||||
/* CARD */
|
||||
|
||||
.card {
|
||||
@apply overflow-visible relative transition dark:brightness-110 shadow-md bg-white dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border transform duration-300 ease-in-out;
|
||||
}
|
||||
|
||||
.card-info-text {
|
||||
@apply col-span-12 mb-0 leading-normal text-sm text-gray-700 dark:text-gray-500 ml-2 mr-1.5;
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -36,17 +36,17 @@ onMounted(() => {
|
|||
<template>
|
||||
<span :id="icon.id" class="sr-only">{{ $t("icons_cross_desc") }}</span>
|
||||
<svg
|
||||
data-svg="cross"
|
||||
role="img"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
:aria-labelledby="icon.id"
|
||||
data-svg="cross"
|
||||
role="img"
|
||||
:class="['icon-svg', props.iconClass, props.iconColor]"
|
||||
:aria-labelledby="icon.id"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm13.36-1.814a.75.75 0 1 0-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 0 0-1.06 1.06l2.25 2.25a.75.75 0 0 0 1.14-.094l3.75-5.25Z"
|
||||
d="M5.47 5.47a.75.75 0 0 1 1.06 0L12 10.94l5.47-5.47a.75.75 0 1 1 1.06 1.06L13.06 12l5.47 5.47a.75.75 0 1 1-1.06 1.06L12 13.06l-5.47 5.47a.75.75 0 0 1-1.06-1.06L10.94 12 5.47 6.53a.75.75 0 0 1 0-1.06Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
|
|
|
|||
|
|
@ -86,6 +86,11 @@ const props = defineProps({
|
|||
required: false,
|
||||
default: "",
|
||||
},
|
||||
iconClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "sm",
|
||||
},
|
||||
attrs: {
|
||||
type: Object,
|
||||
required: false,
|
||||
|
|
@ -110,6 +115,7 @@ const btn = reactive({
|
|||
const btnEl = ref();
|
||||
|
||||
const buttonClass = computed(() => {
|
||||
if (props.color === "transparent") return `${props.size}`;
|
||||
return `btn ${props.color} ${props.size}`;
|
||||
});
|
||||
|
||||
|
|
@ -150,7 +156,7 @@ onMounted(() => {
|
|||
<Icons
|
||||
v-if="props.iconName"
|
||||
:iconName="props.iconName"
|
||||
:iconClass="'btn-svg'"
|
||||
:iconClass="`${props.iconClass} pointer-events-none`"
|
||||
:iconColor="props.iconColor"
|
||||
/>
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
<script setup>
|
||||
import { computed, ref, onMounted } from "vue";
|
||||
import { computed, ref, onMounted, reactive } from "vue";
|
||||
import Button from "@components/Widget/Button.vue";
|
||||
import { contentIndex } from "@utils/tabindex.js";
|
||||
import { useUUID } from "@utils/global.js";
|
||||
|
||||
/**
|
||||
@name Widget/GridLayout.vue
|
||||
|
|
@ -15,7 +17,8 @@ import { contentIndex } from "@utils/tabindex.js";
|
|||
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} [type="card"] - Type of layout component, we can have "card" or "modal"
|
||||
@param {string} [id=uuidv4()] - Id of the layout component, will be used to identify the component.
|
||||
@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 {string} [link=""] - Will transform the container tag from a div to an a tag with the link as href. Useful with card type.
|
||||
@param {object} [columns={"pc": 12, "tablet": 12, "mobile": 12}] - Work with grid system { pc: 12, tablet: 12, mobile: 12}
|
||||
|
|
@ -29,6 +32,11 @@ const props = defineProps({
|
|||
required: false,
|
||||
default: "card",
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: "",
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: false,
|
||||
|
|
@ -60,8 +68,13 @@ const props = defineProps({
|
|||
},
|
||||
});
|
||||
|
||||
const container = reactive({
|
||||
id: props.id,
|
||||
});
|
||||
|
||||
const containerClass = computed(() => {
|
||||
if (props.type === "card") return "card";
|
||||
if (props.type === "card") return "layout-card";
|
||||
if (props.type === "modal") return "layout-modal";
|
||||
return "";
|
||||
});
|
||||
|
||||
|
|
@ -69,32 +82,62 @@ const gridClass = computed(() => {
|
|||
return ` col-span-${props.columns.mobile} md:col-span-${props.columns.tablet} lg:col-span-${props.columns.pc}`;
|
||||
});
|
||||
|
||||
const gridLayoutEl = ref();
|
||||
const flowEl = ref();
|
||||
|
||||
onMounted(() => {
|
||||
container.id = useUUID(container.id);
|
||||
if (!props.link) return;
|
||||
gridLayoutEl.value.setAttribute("href", props.link);
|
||||
gridLayoutEl.value.setAttribute("rel", "noopener");
|
||||
gridLayoutEl.value.setAttribute("tabindex", props.tabId);
|
||||
flowEl.value.setAttribute("href", props.link);
|
||||
flowEl.value.setAttribute("rel", "noopener");
|
||||
flowEl.value.setAttribute("tabindex", props.tabId);
|
||||
|
||||
if (!props.link.startsWith("http")) return;
|
||||
|
||||
gridLayoutEl.value.setAttribute("target", "_blank");
|
||||
flowEl.value.setAttribute("target", "_blank");
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component
|
||||
ref="gridLayoutEl"
|
||||
:is="props.link ? 'a' : 'div'"
|
||||
data-grid-layout
|
||||
:class="[
|
||||
containerClass,
|
||||
gridClass,
|
||||
props.gridLayoutClass,
|
||||
'layout-grid-layout',
|
||||
]"
|
||||
>
|
||||
<slot></slot>
|
||||
</component>
|
||||
<!-- modal -->
|
||||
<template v-if="props.type === 'modal'">
|
||||
<div class="layout-modal-container" :id="container.id">
|
||||
<div class="layout-backdrop"></div>
|
||||
<div class="layout-modal-wrap" :data-hide-el="container.id">
|
||||
<div class="layout-modal">
|
||||
<div class="layout-modal-button-container">
|
||||
<Button
|
||||
:attrs="{ 'data-hide-el': container.id }"
|
||||
:text="'action_close_modal'"
|
||||
:hideText="true"
|
||||
:iconName="'cross'"
|
||||
:iconColor="'dark'"
|
||||
:iconClass="'lg'"
|
||||
:color="'transparent'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- end modal -->
|
||||
|
||||
<!-- card or elements on the document flow -->
|
||||
<template v-if="props.type !== 'modal'">
|
||||
<component
|
||||
ref="flowEl"
|
||||
:id="container.id"
|
||||
:is="props.link ? 'a' : 'div'"
|
||||
data-grid-layout
|
||||
:class="[
|
||||
containerClass,
|
||||
gridClass,
|
||||
props.gridLayoutClass,
|
||||
'layout-grid-layout',
|
||||
]"
|
||||
>
|
||||
<slot></slot>
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<!-- end card or elements on the document flow -->
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -142,6 +142,7 @@
|
|||
"action_save": "save {name}",
|
||||
"action_add": "add {name}",
|
||||
"action_close": "close {name}",
|
||||
"action_close_modal": "close modal",
|
||||
"action_delete": "delete {name}",
|
||||
"action_link": "link",
|
||||
"action_edit": "edit {name}",
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import { reactive, onBeforeMount, onMounted } from "vue";
|
||||
import DashboardLayout from "@components/Dashboard/Layout.vue";
|
||||
import BuilderPlugins from "@components/Builder/Plugins.vue";
|
||||
import { useGlobal } from "@utils/global.js";
|
||||
|
||||
/**
|
||||
@name Page/PLugins.vue
|
||||
|
|
@ -82,10 +83,14 @@ onBeforeMount(() => {
|
|||
});
|
||||
|
||||
onMounted(() => {
|
||||
useGlobal();
|
||||
redirectPlugin();
|
||||
deletePlugin();
|
||||
});
|
||||
const builder = [
|
||||
{
|
||||
type: "modal",
|
||||
},
|
||||
{
|
||||
type: "card",
|
||||
widgets: [
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ function useGlobal() {
|
|||
window.addEventListener(
|
||||
"click",
|
||||
(e) => {
|
||||
console.log("click", e.target);
|
||||
// Update some states
|
||||
useShowEl(e);
|
||||
useHideEl(e);
|
||||
|
|
@ -35,13 +36,13 @@ function useGlobal() {
|
|||
@param {Event} e - The event object.
|
||||
*/
|
||||
function useShowEl(e) {
|
||||
if (!e.target.closest("button").hasAttribute("data-show-el")) return;
|
||||
if (!e.target.hasAttribute("data-show-el")) return;
|
||||
// show
|
||||
const showElId = e.target.closest("button").getAttribute("data-show-el");
|
||||
const showElId = e.target.getAttribute("data-show-el");
|
||||
document.getElementById(showElId).classList.remove("hidden");
|
||||
// Update a11y attributes
|
||||
e.target.closest("button").setAttribute("aria-controls", showElId);
|
||||
e.target.closest("button").setAttribute("aria-expanded", "true");
|
||||
e.target.setAttribute("aria-controls", showElId);
|
||||
e.target.setAttribute("aria-expanded", "true");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -56,13 +57,13 @@ function useShowEl(e) {
|
|||
@param {Event} e - The event object.
|
||||
*/
|
||||
function useHideEl(e) {
|
||||
if (!e.target.closest("button").hasAttribute("data-show-close")) return;
|
||||
if (!e.target.hasAttribute("data-hide-el")) return;
|
||||
// hide
|
||||
const hideElId = e.target.closest("button").getAttribute("data-show-close");
|
||||
const hideElId = e.target.getAttribute("data-hide-el");
|
||||
document.getElementById(hideElId).classList.add("hidden");
|
||||
// Update a11y attributes
|
||||
e.target.closest("button").setAttribute("aria-controls", hideElId);
|
||||
e.target.closest("button").setAttribute("aria-expanded", "false");
|
||||
e.target.setAttribute("aria-controls", hideElId);
|
||||
e.target.setAttribute("aria-expanded", "false");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue