Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,9 @@ node_modules
.env
dist
/.direnv

# local backups
*.bak


test-results/
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"cSpell.words": ["coachings", "Morphcoin", "morphcoins", "Recaptcha", "TOTP"],
"vue.codeActions.enabled": false
"vue.codeActions.enabled": false,
"typescript.tsdk": "node_modules\\typescript\\lib"
}
9 changes: 7 additions & 2 deletions app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@
<NuxtPage />

<CookiePolicy />
<Modal v-if="dialog && dialog.show" @backdrop="handleDialogOnBackdrop()">
<Dialog :dialog="dialog" />
<Modal
v-if="dialog && dialog.show"
@backdrop="handleDialogOnBackdrop()"
:labelId="dialog.labelId"
:descriptionId="dialog.descriptionId"
>
<Dialog :dialog="dialog" :labelId="dialog.labelId" :descriptionId="dialog.descriptionId" />
</Modal>
<Snackbar class="z-[999]" />

Expand Down
32 changes: 19 additions & 13 deletions components/Btn.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
</template>

<script lang="ts">
import { defineComponent, computed } from "vue";

export default defineComponent({
props: {
full: { type: Boolean, default: false },
Expand All @@ -17,19 +19,17 @@ export default defineComponent({
tertiary: { type: Boolean, default: false },
icon: { type: Object, default: null },
iconRight: { type: Boolean, default: false },
disabled: { type: Boolean, default: false },
// bgColor and borderColor ar no longer used directly in the computed classes, as the primary/secondary logic handle the color based on the theme.
// However, they are kept as props for backward compatibility if needed.
bgColor: { type: String, default: "bg-accent" },
borderColor: { type: String, default: "border-accent" },
disabled: { type: Boolean, default: false },
},
emits: ["click"],
setup(props, { emit }) {
function onclick() {
if (!props.disabled) emit("click", true);
}

const textColor = computed(() => {
return props.bgColor.includes("warning") ? "text-primary" : "text-white";
});
const classes = computed(() => {
return [
{
Expand All @@ -40,15 +40,20 @@ export default defineComponent({
"text-center justify-center w-full": props.full,
disabled: props.disabled,
},
/*
props.primary && !props.secondary && !props.tertiary
? `primary ${props.bgColor} text-primary enabled:hover:${props.bgColor} border ${props.borderColor} enabled:hover:ring-4 md:enabled:hover:ring-8 enabled:hover:ring-tertiary`
: "",
: '',
props.secondary
? `secondary bg-transparent text-heading enabled:hover:bg-transparent border ${props.borderColor} enabled:hover:ring-4 md:enabled:hover:ring-8 enabled:hover:ring-tertiary`
: "",
: '',
props.tertiary
? `tertiary bg-transparent text-heading enabled:hover:bg-transparent enabled:hover:scale-105 border border-transparent enabled:hover:ring-4 md:enabled:hover:ring-8 enabled:hover:ring-transparent`
: "",
: '',
*/
props.primary && !props.secondary && !props.tertiary ? `primary` : "",
props.secondary ? `secondary` : "",
props.tertiary ? `tertiary` : "",
];
});
return { classes, onclick };
Expand All @@ -66,15 +71,16 @@ button:disabled {
}

/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ STYLE */
/* .primary {
@apply bg-accent hover:bg-accent border border-accent focus:ring-8 focus:ring-tertiary;
.primary {
/* Apply specific classes that match desired look. */
@apply rounded bg-info px-4 py-2 font-bold text-white hover:bg-info;
}
.secondary {
@apply bg-transparent hover:bg-transparent border border-accent focus:ring-8 focus:ring-tertiary;
@apply border border-accent bg-transparent hover:bg-transparent focus:ring-8 focus:ring-tertiary;
}
.tertiary {
@apply bg-transparent hover:bg-transparent hover:scale-105 border border-transparent focus:ring-8 focus:ring-transparent;
} */
@apply border border-transparent bg-transparent hover:scale-105 hover:bg-transparent focus:ring-8 focus:ring-transparent;
}

/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SIZE */
.sm {
Expand Down
149 changes: 94 additions & 55 deletions components/CookiePolicy.vue
Original file line number Diff line number Diff line change
@@ -1,75 +1,114 @@
<template>
<div>
<Modal v-if="!agreed && route.name != 'docs-privacy'">
<Dialog
:dialog="dialog"
role="dialog"
aria-labelledby="cookie-dialog-title"
aria-describedby="cookie-dialog-description"
aria-modal="true"
>
<template #content="{ t }">
<p id="cookie-dialog-description" class="text-body-1 m-0 text-body font-body mt-box">
{{ t("Body.CookiePolicy") }}
<!-- removed the dialog component and put the content into the modal component. -->
<!-- template renders the content directly (labelId, descriptionId) and transfers the ARIA attributes (role="dialog", aria-modal="true" and aria-labelledby) to the modal component via the props. -->
<Modal
v-if="!agreed && route.name !== 'docs-privacy'"
:label-id="titleId"
:description-id="descId"
>
<!-- Panel -->
<section
class="w-[560px] max-w-[92vw] overflow-hidden rounded-lg bg-primary text-white shadow-lg"
:aria-labelledby="titleId"
:aria-describedby="descId"
>
<!-- Header: Icon + Heading -->
<div class="flex items-start gap-3 p-6 pb-3">
<!-- Icon -->
<span class="relative inline-flex h-7 w-7 shrink-0">
<!-- circle -->
<span class="absolute inset-0 rounded-full bg-info" aria-hidden="true" />
<!-- 'i' with Panel-background-color -->
<span
class="relative m-auto font-bold italic leading-none"
style="color: var(--color-primary, transparent)"
aria-hidden="true"
>
i
</span>
</span>

<NuxtLink to="/docs/privacy" class="underline-link inline-block w-fit">
{{ t("Body.CookiePolicyLink") }}
</NuxtLink>
</p>
</template>
</Dialog>
</Modal>
</div>
<h2 :id="titleId" class="m-0 text-lg font-semibold leading-6">
{{ t("Headings.CookiePolicy") }}
</h2>
</div>

<!-- Body -->
<div class="px-6 pb-4">
<p :id="descId" class="m-0 text-sm leading-relaxed">
{{ t("Body.CookiePolicy") }}
<NuxtLink
to="/docs/privacy"
class="border-b border-[#38f7c7] no-underline transition-colors hover:border-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#ff2d55]"
>
{{ t("Body.CookiePolicyLink") }}
</NuxtLink>
</p>
</div>

<!-- Footer -->
<div class="bg-white/5 flex justify-end px-6 py-4">
<!-- Button -->
<Btn
:primary="true"
:md="true"
class="focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#ff2d55]"
@click="agreed = true"
>
{{ t("Buttons.CookiePolicy") }}
</Btn>
</div>
</section>
</Modal>
</template>

<script lang="ts">
import { defineComponent, onMounted } from "vue";
import { defineComponent, computed } from "vue";
import { useI18n } from "vue-i18n";
import Gleap from "gleap";
import Btn from "./Btn.vue";
import Modal from "./Modal.vue";

export default defineComponent({
components: { Btn, Modal },
setup() {
const dialog = computed(() => {
return {
type: "info",
heading: "Headings.CookiePolicy",
body: "",
primaryBtn: {
label: "Buttons.CookiePolicy",
onclick: () => {
agreed.value = true;
},
},
secondaryBtn: {
label: "",
onclick: () => {},
},
};
});
const { t } = useI18n();
const config = useRuntimeConfig().public;

// Consent-Cookie
const cookie_agreedToCookiePolicy = useCookie<boolean>("agreedToCookiePolicy");

const router = useRouter();
const route = useRoute();

const agreed = computed({
get() {
return cookie_agreedToCookiePolicy.value || false;
},
set(data: boolean) {
cookie_agreedToCookiePolicy.value = data;
// Zustimmungs-Flag
const agreed = computed<boolean>({
get: () => cookie_agreedToCookiePolicy.value || false,
set: (v: boolean) => {
cookie_agreedToCookiePolicy.value = v;
if (v && import.meta.client) {
if (!(window as any).__gleapInited) {
Gleap.initialize(config.Gleap_API_KEY as string);
(window as any).__gleapInited = true;
}
}
},
});

onMounted(() => {
const dialogElement = document.querySelector('[role="dialog"]');
if (dialogElement && dialogElement instanceof HTMLElement) {
dialogElement.setAttribute("tabindex", "-1");
dialogElement.focus();
}
});
// ARIA-Hooks
// The IDs are defined here to link the h2 and p tags to the Modal component
const titleId = "cookie-dialog-title";
const descId = "cookie-dialog-description";

return { agreed, dialog, route };
return { t, agreed, route, titleId, descId };
},
});
</script>

<style scoped></style>
<style scoped>
:root {
--color-primary: inherit;
}

section[aria-labelledby="cookie-dialog-title"] {
--color-primary: #0e1b2b;
}
</style>
22 changes: 12 additions & 10 deletions components/Dialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
>
<div class="card grid grid-cols-[auto_1fr] gap-x-4 md:gap-x-6">
<component
:is="theme.icon"
class="row-span-2 h-10 w-10 md:row-span-3"
:class="[theme.fill]"
:is="theme.icon"
></component>
/>

<h6 class="text-heading-2 text-heading font-heading">
{{ t(heading) }}
Expand All @@ -25,8 +25,8 @@
<div class="card flex flex-wrap justify-end bg-[#1c3250] gap-card">
<Btn
v-if="!!secondaryBtn.label"
:bgColor="theme.bg"
:borderColor="theme.border"
:bg-color="theme.bg"
:border-color="theme.border"
secondary
@click="
secondaryBtn.onclick();
Expand All @@ -37,8 +37,8 @@
</Btn>
<Btn
v-if="!!primaryBtn.label"
:bgColor="theme.bg"
:borderColor="theme.border"
:bg-color="theme.bg"
:border-color="theme.border"
@click="
primaryBtn.onclick();
closeDialog();
Expand All @@ -63,15 +63,17 @@ import type { PropType } from "vue";
import { useI18n } from "vue-i18n";

export default defineComponent({
props: {
dialog: { type: Object as PropType<any>, default: null },
},
components: {
ExclamationCircleIcon,
InformationCircleIcon,
XCircleIcon,
CheckCircleIcon,
},
props: {
dialog: { type: Object as PropType<any>, default: null },
labelId: { type: String, required: true },
descriptionId: { type: String, required: false, default: null },
},
setup(props) {
const { t } = useI18n();

Expand Down Expand Up @@ -143,7 +145,7 @@ export default defineComponent({

return {
label: label,
onclick: !!label ? props.dialog.secondaryBtn.onclick : () => {},
onclick: label ? props.dialog.secondaryBtn.onclick : () => {},
};
});

Expand Down
Loading