zwischenstand
This commit is contained in:
@@ -1,5 +1,17 @@
|
||||
<script lang="ts">
|
||||
import { location } from "./lib/store"
|
||||
import { onDestroy } from "svelte"
|
||||
import { getEventCallbacks, registerEventCallback, unregisterEventCallback } from "./lib/functions/eventBus"
|
||||
import { loadModulesAndSetStore } from "./lib/functions/fetch/loadModules"
|
||||
import { location, pages, rerender } from "./lib/store"
|
||||
import { Route, Router, links } from "../../vendor/svelte-routing"
|
||||
import Footer from "./lib/components/Footer.svelte"
|
||||
import Header from "./lib/components/Header.svelte"
|
||||
import Page from "./routes/Page.svelte"
|
||||
import ScrollTop from "./lib/components/ScrollTop.svelte"
|
||||
import ScrollBottom from "./lib/components/ScrollBottom.svelte"
|
||||
import { loadContentAndSetStores } from "./lib/functions/fetch/loadContentAndSetStores"
|
||||
import { loadNavigationAndSetStores } from "./lib/functions/fetch/loadNavigationAndSetStores"
|
||||
import { loadLibraryAndSetStore } from "./lib/functions/fetch/loadLibraryAndSetStores"
|
||||
|
||||
export let url = ""
|
||||
|
||||
@@ -15,8 +27,102 @@
|
||||
pop: false,
|
||||
}
|
||||
}
|
||||
function registerEvent() {
|
||||
let callbacks = getEventCallbacks("navigate")
|
||||
if (callbacks && callbacks["rerender"]) return
|
||||
registerEventCallback("navigate", "rerender", () => {
|
||||
$rerender = !$rerender
|
||||
return true
|
||||
})
|
||||
}
|
||||
registerEvent()
|
||||
onDestroy(() => {
|
||||
let callbacks = getEventCallbacks("navigate")
|
||||
if (callbacks["rerender"]) unregisterEventCallback("navigate", "rerender")
|
||||
})
|
||||
loadModulesAndSetStore()
|
||||
|
||||
if (typeof window !== "undefined") console.log("App initialized")
|
||||
loadNavigationAndSetStores()
|
||||
loadLibraryAndSetStore()
|
||||
loadContentAndSetStores()
|
||||
</script>
|
||||
|
||||
<h1>Hello World</h1>
|
||||
<main use:links>
|
||||
<Header></Header>
|
||||
<Router {url}>
|
||||
<Route path="/" component="{Page}" />
|
||||
<Route path="/*path" let:params>
|
||||
{#key $rerender}
|
||||
<Page path="/{params?.path}" isHomepage="{false}"></Page>
|
||||
{/key}
|
||||
</Route>
|
||||
</Router>
|
||||
<Footer></Footer>
|
||||
</main>
|
||||
|
||||
<ScrollTop />
|
||||
<ScrollBottom />
|
||||
|
||||
<style lang="less" global>
|
||||
@import "./lib/assets/css/variables.less";
|
||||
@import "./lib/assets/css/main.less";
|
||||
@import "swiper/swiper-bundle.min.css";
|
||||
@import "swiper/modules/effect-fade/effect-fade";
|
||||
@import "swiper/modules/navigation/navigation";
|
||||
@import "swiper/modules/pagination/pagination";
|
||||
.swiper-button-prev,
|
||||
.swiper-button-next {
|
||||
transform-origin: left;
|
||||
color: #333;
|
||||
transform: scale(0.3);
|
||||
background-color: rgba(255, 255, 255, 0.6);
|
||||
top: 50%;
|
||||
padding: 10px;
|
||||
height: 70px;
|
||||
width: 70px;
|
||||
border-radius: 50px;
|
||||
}
|
||||
.swiper-button-prev {
|
||||
left: 6%;
|
||||
}
|
||||
.swiper-button-next {
|
||||
right: 6%;
|
||||
transform-origin: right;
|
||||
}
|
||||
html,
|
||||
body {
|
||||
font-family: "Libre Franklin", sans-serif;
|
||||
button {
|
||||
font-family: "Libre Franklin", sans-serif;
|
||||
}
|
||||
@media @mobile {
|
||||
font-size: @bodyfontsize;
|
||||
line-height: 1.4;
|
||||
}
|
||||
@media @tablet {
|
||||
font-size: @bodyfontsize_desktop;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
align-items: center;
|
||||
}
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: "Libre Franklin";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url("./lib/assets/fonts/libre-franklin-v13-latin-regular.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: "Libre Caslon Text";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url("./lib/assets/fonts/LibreCaslonText-Regular.woff2") format("woff2");
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,6 +2,7 @@ import * as sentry from "./sentry"
|
||||
import configClient from "../../api/hooks/config-client"
|
||||
|
||||
export const apiBaseURL = "/api/"
|
||||
export const baseURL = "https://tibi_starter.code.testversion.online.de"
|
||||
export const release = configClient.release
|
||||
|
||||
export const sentryDSN = "https://5063f9b5564d0fdece4e47a8e2e63672@sentry.basehosts.de/3"
|
||||
|
||||
@@ -0,0 +1,252 @@
|
||||
@import "../../../variables.less";
|
||||
.my-form {
|
||||
input,
|
||||
select,
|
||||
textarea,
|
||||
.data-protection {
|
||||
margin: 5px 0px;
|
||||
box-shadow: 0 0 25px 10px rgba(24, 24, 24, 0.05);
|
||||
}
|
||||
|
||||
.success-message {
|
||||
h1 {
|
||||
color: @main-color;
|
||||
font-size: 36px;
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
.invalidBlocks {
|
||||
border: 2px solid rgb(255, 0, 0) !important;
|
||||
position: relative;
|
||||
&::after {
|
||||
font-size: 0.9rem !important;
|
||||
color: red !important;
|
||||
position: absolute;
|
||||
bottom: 2px;
|
||||
content: "Bitte wähle entweder eine Kartenanzahl oder einen Wunschbetrag aus.";
|
||||
}
|
||||
}
|
||||
|
||||
.border-red {
|
||||
border-color: red !important;
|
||||
}
|
||||
|
||||
.no-margin {
|
||||
margin-top: 15px !important;
|
||||
}
|
||||
|
||||
.invalid {
|
||||
border: 2px solid red !important;
|
||||
}
|
||||
.error-message {
|
||||
font-size: 0.9rem !important;
|
||||
color: red !important;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
input[type="number"]::-webkit-inner-spin-button,
|
||||
input[type="number"]::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
input[type="number"] {
|
||||
-moz-appearance: textfield;
|
||||
appearance: textfield;
|
||||
}
|
||||
|
||||
.checkit {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.checkit-span {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
border: 1px solid grey;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
[type="checkbox"]:checked + span:before {
|
||||
content: "\2714";
|
||||
position: absolute;
|
||||
transform-origin: bottom;
|
||||
}
|
||||
|
||||
.form-rows {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
width: 100%;
|
||||
h3 {
|
||||
font-weight: bold !important;
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
@media @tablet {
|
||||
gap: 1.5rem;
|
||||
}
|
||||
.form-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
gap: 1.5rem;
|
||||
|
||||
.date {
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
label {
|
||||
width: 100% !important;
|
||||
font-size: inherit;
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
width: 100%;
|
||||
font-size: inherit;
|
||||
}
|
||||
}
|
||||
.form-cols {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.form-column {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
|
||||
width: 100%;
|
||||
gap: 1rem;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--hover-color);
|
||||
}
|
||||
|
||||
input,
|
||||
select,
|
||||
textarea,
|
||||
.data-protection {
|
||||
padding: 10px 20px;
|
||||
border: 0px solid white;
|
||||
border-bottom: 3px solid @main-color;
|
||||
outline: 0px solid white;
|
||||
color: black;
|
||||
background-color: @background-color;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.data-protection {
|
||||
display: flex;
|
||||
|
||||
flex-wrap: nowrap;
|
||||
gap: 5px !important;
|
||||
justify-content: start;
|
||||
flex-direction: row !important;
|
||||
}
|
||||
|
||||
input[type="date"] {
|
||||
font-family: inherit;
|
||||
width: 100% !important;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
select {
|
||||
padding: 10px 20px;
|
||||
border: 0;
|
||||
border-bottom: 3px solid @main-color;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
select:focus {
|
||||
border-bottom-color: @main-color;
|
||||
}
|
||||
|
||||
#time-select {
|
||||
appearance: none;
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 20px center;
|
||||
background-size: 18px;
|
||||
|
||||
option {
|
||||
padding: 10px 20px;
|
||||
background-color: @background-color;
|
||||
}
|
||||
}
|
||||
@media @mobile {
|
||||
.date {
|
||||
width: 100vw !important;
|
||||
}
|
||||
}
|
||||
@media @tablet {
|
||||
.date {
|
||||
width: 100% !important;
|
||||
}
|
||||
.form-cols {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
}
|
||||
i .max-width {
|
||||
max-width: @body-maxwidth !important;
|
||||
}
|
||||
|
||||
.datasec {
|
||||
display: flex;
|
||||
align-items: center !important;
|
||||
.link {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: flex-end !important;
|
||||
text-decoration: underline;
|
||||
margin-right: 3px;
|
||||
color: rgb(14, 91, 146);
|
||||
}
|
||||
}
|
||||
|
||||
.additional {
|
||||
display: flex;
|
||||
@media @mobile {
|
||||
flex-direction: column !important;
|
||||
}
|
||||
@media @tablet {
|
||||
flex-direction: row !important;
|
||||
}
|
||||
gap: 2.5rem;
|
||||
div {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
}
|
||||
.submit-request {
|
||||
flex: 1;
|
||||
box-shadow: 0 0 25px 10px rgba(0, 0, 0, 0.05);
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
background-color: @main-color;
|
||||
color: @background-color;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
99
frontend/src/lib/assets/css/main.less
Normal file
99
frontend/src/lib/assets/css/main.less
Normal file
@@ -0,0 +1,99 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
html {
|
||||
background-color: black;
|
||||
}
|
||||
body {
|
||||
color: #343a40 !important;
|
||||
height: 100%;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
}
|
||||
.boxes {
|
||||
.content {
|
||||
ul {
|
||||
padding-left: 20px;
|
||||
list-style-type: disc;
|
||||
}
|
||||
}
|
||||
}
|
||||
ol {
|
||||
list-style-type: decimal;
|
||||
}
|
||||
|
||||
/* Links */
|
||||
a {
|
||||
text-decoration: none;
|
||||
font-weight: 700;
|
||||
color: inherit;
|
||||
font-weight: normal;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Tabellen */
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
* {
|
||||
transition:
|
||||
background-color 0.5s ease,
|
||||
border-color 0.5s ease,
|
||||
color 0.5s ease,
|
||||
max-height 0.5s,
|
||||
height 0.5s ease,
|
||||
width 0.5s ease,
|
||||
flex 0.5s ease,
|
||||
opacity 0.5s ease,
|
||||
top 0.5s ease,
|
||||
bottom 0.5s ease,
|
||||
left 0.5s ease,
|
||||
right 0.5s ease,
|
||||
transform 0.5s ease;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: inherit;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: inherit;
|
||||
color: #343a40;
|
||||
}
|
||||
input,
|
||||
select {
|
||||
color: #343a40;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar {
|
||||
width: 10px; /* width of the entire scrollbar */
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-track {
|
||||
background: rgb(0, 0, 0); /* color of the tracking area */
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb {
|
||||
background-color: rgb(255, 255, 255); /* color of the scroll thumb */
|
||||
border-radius: 20px; /* roundness of the scroll thumb */
|
||||
//border: 3px solid black; /* creates padding around scroll thumb */
|
||||
}
|
||||
|
||||
* {
|
||||
scrollbar-width: thin; /* "auto" or "thin" */
|
||||
scrollbar-color: rgb(255, 255, 255) rgb(0, 0, 0); /* scroll thumb and track */
|
||||
}
|
||||
|
||||
a {
|
||||
color: black;
|
||||
text-decoration: underline;
|
||||
}
|
||||
18
frontend/src/lib/assets/css/variables.less
Normal file
18
frontend/src/lib/assets/css/variables.less
Normal file
@@ -0,0 +1,18 @@
|
||||
@main-color: #b5103f;
|
||||
@hover-color: #920a31;
|
||||
@background-color: white;
|
||||
@bodyfontsize: 16px;
|
||||
@bodyfontsize_desktop: 18px;
|
||||
@headingfontsize: 24px;
|
||||
@headingfontsize_desktop: 32px;
|
||||
|
||||
@font-family: "Open Sans", sans-serif;
|
||||
|
||||
@desktop: ~"only screen and (min-width: 1024px)";
|
||||
@tablet: ~"only screen and (min-width: 768px)";
|
||||
@mobile: ~"only screen and (min-width: 100px)";
|
||||
|
||||
@body-maxwidth: 1300px;
|
||||
|
||||
:root {
|
||||
}
|
||||
BIN
frontend/src/lib/assets/fonts/LibreCaslonText-Regular.woff2
Normal file
BIN
frontend/src/lib/assets/fonts/LibreCaslonText-Regular.woff2
Normal file
Binary file not shown.
Binary file not shown.
60
frontend/src/lib/components/CookieSet.svelte
Normal file
60
frontend/src/lib/components/CookieSet.svelte
Normal file
@@ -0,0 +1,60 @@
|
||||
<script lang="ts">
|
||||
export let cookieName: string
|
||||
export let backgroundUrl = ""
|
||||
export let textPosition = "center"
|
||||
export let background = ""
|
||||
let contentShown = false
|
||||
const positions = {
|
||||
oben: "flex-start",
|
||||
mitte: "center",
|
||||
unten: "flex-end",
|
||||
}
|
||||
|
||||
window.addEventListener("ccAccept", (e) => {
|
||||
if ((e as CustomEvent).detail[1] == cookieName) contentShown = true
|
||||
})
|
||||
//isCookieSet isnt really precise
|
||||
function checkCookie(cookieName: string) {
|
||||
// Get all cookies
|
||||
var allCookies = decodeURIComponent(document.cookie)
|
||||
// Split into individual cookies
|
||||
var cookies = allCookies.split(";")
|
||||
var ccTagCookies: string[] = []
|
||||
cookies.forEach((e) => {
|
||||
e.includes("ccTags") ? (ccTagCookies = e.split(",")) : void 0
|
||||
})
|
||||
for (var i = 0; i < ccTagCookies.length; i++) {
|
||||
var c = ccTagCookies[i]
|
||||
// Trim whitespace
|
||||
while (c.charAt(0) == " ") c = c.substring(1)
|
||||
// If the cookie's name matches the given name
|
||||
if (c == cookieName) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Verwendung
|
||||
if (checkCookie(cookieName)) contentShown = true
|
||||
</script>
|
||||
|
||||
{#if contentShown}
|
||||
<slot />
|
||||
{:else}
|
||||
<div
|
||||
style="display: flex;
|
||||
justify-content: center;
|
||||
align-items: {positions[textPosition]};
|
||||
background-image: url({backgroundUrl});
|
||||
background-size: cover;
|
||||
width: 100%;
|
||||
align-self: stretch;
|
||||
flex-grow: 1;
|
||||
"
|
||||
>
|
||||
<div style="background-color: rgba(255,255,255,0.7); padding: 20px; background: {background}; width: 100%;">
|
||||
<p>Cookie ist nicht aktiviert. Bitte aktivieren Sie ihn.</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style lang="less"></style>
|
||||
123
frontend/src/lib/components/Footer.svelte
Normal file
123
frontend/src/lib/components/Footer.svelte
Normal file
@@ -0,0 +1,123 @@
|
||||
<script lang="ts">
|
||||
import { serviceNavigation, content } from "../store"
|
||||
import { navigateWrapper } from "../functions/utils"
|
||||
</script>
|
||||
|
||||
<div class="footer">
|
||||
<div class="infos">
|
||||
<h3>webmakers Erfurt</h3>
|
||||
<div class="infos-inner">
|
||||
<div class="upper">
|
||||
<p>Inh. Marc Oschmann</p>
|
||||
<p>Marktstraße 34</p>
|
||||
<p>99084 Erfurt</p>
|
||||
</div>
|
||||
<div class="lower">
|
||||
<p>Tel.: 0361 6021502</p>
|
||||
<p>Fax.: 0361 6021502</p>
|
||||
<p>Email: webmakers@gmail.de</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="social">
|
||||
<a href="">
|
||||
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M11.7 3h12.6c4.8 0 8.7 3.9 8.7 8.7v12.6a8.7 8.7 0 0 1-8.7 8.7H11.7C6.9 33 3 29.1 3 24.3V11.7A8.7 8.7 0 0 1 11.7 3zm-.3 3A5.4 5.4 0 0 0 6 11.4v13.2c0 2.985 2.415 5.4 5.4 5.4h13.2a5.4 5.4 0 0 0 5.4-5.4V11.4C30 8.415 27.585 6 24.6 6H11.4zm14.475 2.25a1.875 1.875 0 1 1 0 3.75 1.875 1.875 0 0 1 0-3.75zM18 10.5a7.5 7.5 0 1 1 0 15 7.5 7.5 0 0 1 0-15zm0 3a4.5 4.5 0 1 0 0 9 4.5 4.5 0 0 0 0-9z"
|
||||
fill="#333333"
|
||||
></path>
|
||||
</svg>
|
||||
</a>
|
||||
<a href="">
|
||||
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M18 3.06c-8.25 0-15 6.735-15 15.03 0 7.5 5.49 13.725 12.66 14.85v-10.5h-3.81v-4.35h3.81v-3.315c0-3.765 2.235-5.835 5.67-5.835 1.635 0 3.345.285 3.345.285v3.705h-1.89c-1.86 0-2.445 1.155-2.445 2.34v2.82h4.17l-.675 4.35H20.34v10.5A15 15 0 0 0 33 18.09c0-8.295-6.75-15.03-15-15.03z"
|
||||
fill="#333333"
|
||||
></path>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="services">
|
||||
<ul>
|
||||
{#each $serviceNavigation || [] as service, i (i)}
|
||||
<li>
|
||||
<button
|
||||
on:click="{() => {
|
||||
navigateWrapper(`${$content[service?.page || '']?.path}`)
|
||||
}}"
|
||||
><a class="nav-title-m" href="{$content?.[service?.page || '']?.path}" on:click|preventDefault
|
||||
>{service?.name}</a
|
||||
></button
|
||||
>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<style lang="less">
|
||||
@import "../assets/css/variables.less";
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
.social {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.footer {
|
||||
margin-top: 60px;
|
||||
width: 100vw;
|
||||
min-height: fit-content;
|
||||
font-size: 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
max-width: calc(@body-maxwidth - min(9vw, 200px));
|
||||
background-color: @hover-color;
|
||||
padding: min(4.5vw, 100px) min(4.5vw, 100px) 0px min(4.5vw, 100px);
|
||||
}
|
||||
.infos {
|
||||
display: flex;
|
||||
height: 70%;
|
||||
.infos-inner {
|
||||
gap: 10px;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.services {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
button {
|
||||
font-size: inherit;
|
||||
padding: 6px 0px;
|
||||
text-align: start;
|
||||
}
|
||||
}
|
||||
|
||||
@media @mobile {
|
||||
.infos {
|
||||
flex-direction: column;
|
||||
justify-content: start;
|
||||
|
||||
@media @mobile {
|
||||
gap: 20px;
|
||||
}
|
||||
@media @tablet {
|
||||
gap: 40px;
|
||||
}
|
||||
.infos-inner {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
.footer {
|
||||
}
|
||||
}
|
||||
@media @desktop {
|
||||
.footer {
|
||||
height: 350px;
|
||||
.infos-inner {
|
||||
gap: 5vw;
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
382
frontend/src/lib/components/Header.svelte
Normal file
382
frontend/src/lib/components/Header.svelte
Normal file
@@ -0,0 +1,382 @@
|
||||
<script lang="ts">
|
||||
import { navigateWrapper } from "../functions/utils"
|
||||
import { content, navigation } from "../store"
|
||||
|
||||
let opened = -1
|
||||
let openMenu = false
|
||||
$: {
|
||||
if (openMenu) document.body.style.overflow = "hidden"
|
||||
else document.body.style.overflow = "auto"
|
||||
}
|
||||
</script>
|
||||
|
||||
<header class="header">
|
||||
<div class="container">
|
||||
<button class="logo" on:keydown on:click="{() => navigateWrapper('/')}">
|
||||
<img src="/media/logoquer.png" alt="Logo Quer" />
|
||||
</button>
|
||||
<nav class="navigation">
|
||||
{#each $navigation as nav, i}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<ul class="nav-element">
|
||||
<li>
|
||||
<button
|
||||
class="nav-title"
|
||||
on:click="{(e) => {
|
||||
if (nav.page) navigateWrapper($content?.[nav.page]?.path)
|
||||
}}"
|
||||
>
|
||||
<a href="{$content?.[nav?.page || '']?.path}" on:click|preventDefault> {nav.name}</a>
|
||||
</button>
|
||||
</li>
|
||||
{#if !nav?.endpoint}
|
||||
<ul class="subNav" style="{$navigation.length - 1 == i ? 'right: 0px;' : ''}'}">
|
||||
{#each nav.elements || [] as subNav}
|
||||
<li>
|
||||
<button
|
||||
class="nav-subtitle"
|
||||
on:click="{(e) => {
|
||||
if (subNav.page) navigateWrapper($content?.[subNav.page]?.path)
|
||||
}}"
|
||||
>
|
||||
<a href="{$content?.[subNav?.page || '']?.path}" on:click|preventDefault>
|
||||
{subNav.name}</a
|
||||
>
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
</ul>
|
||||
{/each}
|
||||
</nav>
|
||||
|
||||
<button
|
||||
class="button-three"
|
||||
id="button-burger"
|
||||
on:click="{(e) => {
|
||||
let button = document.getElementsByClassName('button-three')[0]
|
||||
const currentState = button.getAttribute('data-state')
|
||||
if (!currentState || currentState === 'closed') {
|
||||
button.setAttribute('data-state', 'opened')
|
||||
button.setAttribute('aria-expanded', 'true')
|
||||
} else {
|
||||
button.setAttribute('data-state', 'closed')
|
||||
button.setAttribute('aria-expanded', 'false')
|
||||
}
|
||||
openMenu = !openMenu
|
||||
}}"
|
||||
aria-controls="primary-navigation"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<svg stroke="white" fill="none" class="hamburger" viewBox="-10 -10 120 120" width="45">
|
||||
<path
|
||||
class="line"
|
||||
stroke-width="10"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="m 20 40 h 60 a 1 1 0 0 1 0 20 h -60 a 1 1 0 0 1 0 -40 h 30 v 70"
|
||||
>
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<nav class="navigation-m" class:open="{openMenu}">
|
||||
{#each $navigation as nav, i}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<ul class="nav-element-m" style="padding-top: {i == 0 ? '15px' : '0px'}">
|
||||
<li>
|
||||
<button
|
||||
on:click="{(e) => {
|
||||
if (opened == i) opened = -1
|
||||
else opened = i
|
||||
if (nav.endpoint) {
|
||||
const burger = document.getElementById('button-burger')
|
||||
if (burger) burger.click()
|
||||
|
||||
if (nav.page) navigateWrapper($content?.[nav.page]?.path)
|
||||
}
|
||||
}}"
|
||||
><a class="nav-title-m" href="{$content?.[nav?.page || '']?.path}" on:click|preventDefault>
|
||||
<div>{@html nav.name}</div>
|
||||
{#if !nav?.endpoint}
|
||||
<div class="">
|
||||
{#if i == opened}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
fill="currentColor"
|
||||
class="bi bi-chevron-up"
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M7.646 4.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1-.708.708L8 5.707l-5.646 5.647a.5.5 0 0 1-.708-.708l6-6z"
|
||||
></path>
|
||||
</svg>
|
||||
{:else}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
fill="currentColor"
|
||||
class="bi bi-chevron-down"
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z"
|
||||
></path>
|
||||
</svg>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}</a
|
||||
>
|
||||
</button>
|
||||
</li>
|
||||
{#if !nav?.endpoint}
|
||||
<ul class="subNav-m {i == opened ? 'visible' : 'hidden'}">
|
||||
{#each nav.elements || [] as subNav}
|
||||
<li>
|
||||
<button
|
||||
class="nav-subtitle-m"
|
||||
on:click="{(e) => {
|
||||
const burger = document.getElementById('button-burger')
|
||||
if (burger) burger.click()
|
||||
if (subNav.page) navigateWrapper($content?.[subNav.page]?.path)
|
||||
}}"
|
||||
>
|
||||
<a href="{$content?.[subNav?.page || '']?.path}" on:click|preventDefault>
|
||||
{subNav.name}</a
|
||||
>
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
</ul>
|
||||
{/each}
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<style lang="less">
|
||||
@import "../assets/css/variables.less";
|
||||
@switchit: 1000px;
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
}
|
||||
.header {
|
||||
min-height: 60px;
|
||||
height: 100%;
|
||||
background-color: @main-color;
|
||||
width: 100%;
|
||||
color: white;
|
||||
position: relative;
|
||||
button {
|
||||
color: white;
|
||||
}
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
.desktop-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2vw;
|
||||
}
|
||||
|
||||
@media @mobile {
|
||||
font-size: @headingfontsize;
|
||||
}
|
||||
|
||||
@media (min-width: 1000px) and (max-width: 1500px) {
|
||||
font-size: 15px;
|
||||
padding: 10px;
|
||||
gap: 10px;
|
||||
}
|
||||
@media (min-width: 1250px) and (max-width: 1500px) {
|
||||
font-size: 17px;
|
||||
padding: 10px;
|
||||
gap: 10px;
|
||||
}
|
||||
.button-three {
|
||||
--button-color: #333;
|
||||
overflow: hidden;
|
||||
background: transparent;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
margin-top: 8px;
|
||||
@media (min-width: 1001px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.button-three .hamburger {
|
||||
transition:
|
||||
translate 400ms,
|
||||
rotate 400ms;
|
||||
}
|
||||
|
||||
.button-three[aria-expanded="true"] .hamburger {
|
||||
translate: 1px -1px;
|
||||
rotate: 0.125turn;
|
||||
}
|
||||
|
||||
.button-three .line {
|
||||
transition: 1s;
|
||||
stroke-dasharray: 60 31 60 300;
|
||||
}
|
||||
|
||||
.button-three[aria-expanded="true"] .line {
|
||||
stroke-dasharray: 60 105 60 300;
|
||||
stroke-dashoffset: -90;
|
||||
}
|
||||
.container {
|
||||
cursor: pointer;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
max-width: @body-maxwidth;
|
||||
align-items: center;
|
||||
@media @mobile {
|
||||
padding: 0px 20px;
|
||||
}
|
||||
@media @tablet {
|
||||
padding: 0px 10px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
.symbol {
|
||||
margin-right: min(35px, 2vw);
|
||||
}
|
||||
}
|
||||
.navigation {
|
||||
display: flex;
|
||||
@media (max-width: @switchit) {
|
||||
display: none;
|
||||
}
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 20px;
|
||||
flex-grow: 1;
|
||||
height: 85px;
|
||||
padding: 0px 20px;
|
||||
@media (min-width: 1000px) and (max-width: 1500px) {
|
||||
font-size: 14px;
|
||||
padding: 10px;
|
||||
gap: 10px;
|
||||
}
|
||||
@media (min-width: 1250px) and (max-width: 1500px) {
|
||||
font-size: 17px;
|
||||
padding: 10px;
|
||||
gap: 10px;
|
||||
}
|
||||
.nav-element {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
.nav-title {
|
||||
padding: 20px 0px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.subNav {
|
||||
opacity: 0;
|
||||
top: 99%;
|
||||
height: 0px;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
position: absolute;
|
||||
background: #333e52c0;
|
||||
max-width: 450px;
|
||||
width: fit-content;
|
||||
min-width: 100%;
|
||||
z-index: 9999;
|
||||
.nav-subtitle {
|
||||
width: fit-content;
|
||||
max-width: 450px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
.subNav {
|
||||
opacity: 1;
|
||||
height: fit-content;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigation-m {
|
||||
position: absolute;
|
||||
z-index: 9999;
|
||||
background-color: @main-color;
|
||||
top: 100%;
|
||||
left: -100vw;
|
||||
|
||||
width: 100%;
|
||||
height: 0px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
transition:
|
||||
height 0ms,
|
||||
left 400ms;
|
||||
@media @mobile {
|
||||
padding: 0px 20px;
|
||||
}
|
||||
@media @tablet {
|
||||
padding: 0px 10px;
|
||||
}
|
||||
|
||||
&.open {
|
||||
left: 0px;
|
||||
height: 100vh;
|
||||
}
|
||||
button {
|
||||
width: 100%;
|
||||
.nav-title-m {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
|
||||
width: 100%;
|
||||
padding: 15px 0px;
|
||||
justify-content: space-between;
|
||||
font-weight: 700;
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
}
|
||||
.subNav-m {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
.nav-subtitle-m {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
font-size: 1rem;
|
||||
}
|
||||
&.hidden {
|
||||
max-height: 0px;
|
||||
}
|
||||
&.visible {
|
||||
max-height: 300px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
71
frontend/src/lib/components/ScrollBottom.svelte
Normal file
71
frontend/src/lib/components/ScrollBottom.svelte
Normal file
@@ -0,0 +1,71 @@
|
||||
<script>
|
||||
import { onMount, onDestroy } from "svelte"
|
||||
import { rerender } from "../store"
|
||||
|
||||
let showButton = true
|
||||
|
||||
const checkScroll = () => {
|
||||
// Change the visibility of the button based on the scroll position
|
||||
showButton = window.pageYOffset < 100
|
||||
}
|
||||
|
||||
const jumpDown = () => {
|
||||
if (typeof window !== "undefined") {
|
||||
// Jump down by 100vh
|
||||
window.scrollTo({ top: window.innerHeight, behavior: "smooth" })
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
// Attach scroll event listener when component is mounted
|
||||
window.addEventListener("scroll", checkScroll)
|
||||
}
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
// Remove scroll event listener when component is destroyed
|
||||
window.removeEventListener("scroll", checkScroll)
|
||||
}
|
||||
})
|
||||
let force = true
|
||||
if (typeof window !== "undefined") {
|
||||
setInterval(() => {
|
||||
if (location.pathname != "/") {
|
||||
force = false
|
||||
} else force = true
|
||||
}, 1000)
|
||||
}
|
||||
$: {
|
||||
if (typeof window !== "undefined") {
|
||||
if ($rerender) {
|
||||
if (location.pathname != "/") {
|
||||
force = false
|
||||
} else force = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if showButton && force}
|
||||
<button on:click="{jumpDown}" class="jump-down"
|
||||
><span> SCROLL </span>
|
||||
<img src="/media/chev-d.svg" alt="arrow" />
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.jump-down {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: fixed;
|
||||
color: #4f4f4f;
|
||||
z-index: 100;
|
||||
gap: 5px;
|
||||
bottom: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
</style>
|
||||
52
frontend/src/lib/components/ScrollTop.svelte
Normal file
52
frontend/src/lib/components/ScrollTop.svelte
Normal file
@@ -0,0 +1,52 @@
|
||||
<script>
|
||||
import { onMount, onDestroy } from "svelte"
|
||||
|
||||
let showButton = false
|
||||
|
||||
const checkScroll = () => {
|
||||
// Change the visibility of the button based on the scroll position
|
||||
showButton = window.pageYOffset > 200
|
||||
}
|
||||
|
||||
const scrollToTop = () => {
|
||||
if (typeof window !== "undefined") {
|
||||
// Scroll smoothly to the top
|
||||
window.scrollTo({ top: 0, behavior: "smooth" })
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
// Attach scroll event listener when component is mounted
|
||||
window.addEventListener("scroll", checkScroll)
|
||||
}
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
// Remove scroll event listener when component is destroyed
|
||||
window.removeEventListener("scroll", checkScroll)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if showButton}
|
||||
<button on:click="{scrollToTop}" class="scroll-to-top"><img src="/media/ToTop.svg" alt="toTop" /> </button>
|
||||
{/if}
|
||||
|
||||
<style lang="less">
|
||||
@import "../assets/css/variables.less";
|
||||
.scroll-to-top {
|
||||
/* Place your styles here */
|
||||
position: fixed;
|
||||
bottom: 5px;
|
||||
z-index: 9999;
|
||||
transform: scale(0.5);
|
||||
right: 5px;
|
||||
@media @tablet {
|
||||
bottom: 60px;
|
||||
right: 60px;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
27
frontend/src/lib/components/pagebuilder/GoogleMaps.svelte
Normal file
27
frontend/src/lib/components/pagebuilder/GoogleMaps.svelte
Normal file
@@ -0,0 +1,27 @@
|
||||
<script lang="ts">
|
||||
import CookieSet from "../CookieSet.svelte"
|
||||
const setHeight = (element: HTMLElement) => {
|
||||
element.style.height = (element.offsetWidth / 16) * 9 + "px"
|
||||
}
|
||||
</script>
|
||||
|
||||
<CookieSet cookieName="{'googleMaps'}" textPosition="{'unten'}" background="{'rgba(44, 44, 44, 0.4)'}">
|
||||
<iframe
|
||||
title="Google Maps"
|
||||
use:setHeight
|
||||
id="iframe"
|
||||
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d2512.077224708092!2d11.023318016007138!3d50.97776317955154!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x47a4729650047377%3A0xca0e03f621729448!2sWebmakers%20GmbH!5e0!3m2!1sde!2sde!4v1571866765252!5m2!1sde!2sde"
|
||||
style="border:0;"
|
||||
allowfullscreen="{true}"
|
||||
loading="lazy"
|
||||
referrerpolicy="no-referrer-when-downgrade"
|
||||
></iframe>
|
||||
</CookieSet>
|
||||
|
||||
<style>
|
||||
iframe {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
border: 0;
|
||||
}
|
||||
</style>
|
||||
96
frontend/src/lib/components/pagebuilder/Image.svelte
Normal file
96
frontend/src/lib/components/pagebuilder/Image.svelte
Normal file
@@ -0,0 +1,96 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte"
|
||||
import { register } from "swiper/element/bundle"
|
||||
import { apiBaseURL } from "../../../config"
|
||||
import { mediaLibrary } from "../../store"
|
||||
export let images: string[]
|
||||
|
||||
register(false)
|
||||
let swiper: any
|
||||
|
||||
onMount(async () => {
|
||||
if (swiper !== undefined) {
|
||||
const response = await fetch("/dist/index.css")
|
||||
const cssText = await response.text()
|
||||
|
||||
const params = {
|
||||
injectStyles: [cssText],
|
||||
}
|
||||
|
||||
Object.assign(swiper, params)
|
||||
swiper.initialize()
|
||||
}
|
||||
})
|
||||
let image = images[0]
|
||||
</script>
|
||||
|
||||
{#if images.length > 1}
|
||||
<div class="flex">
|
||||
<swiper-container
|
||||
bind:this="{swiper}"
|
||||
slides-per-view="1"
|
||||
loop="{true}"
|
||||
direction="horizontal"
|
||||
effect="slide"
|
||||
autoplay-delay="2500"
|
||||
mousewheel="{true}"
|
||||
navigation="{true}"
|
||||
init="{false}"
|
||||
speed="600"
|
||||
class="relative"
|
||||
>
|
||||
{#each images as image, i (i)}
|
||||
<swiper-slide class="relative">
|
||||
<div class="image-container">
|
||||
<img
|
||||
src="{`${apiBaseURL}medialib/${image}/${$mediaLibrary?.[image]?.file?.src}?filter=${
|
||||
window.innerWidth > 500 ? 'xl' : 'm'
|
||||
}`}"
|
||||
alt="Bild"
|
||||
/>
|
||||
</div>
|
||||
</swiper-slide>
|
||||
{/each}
|
||||
</swiper-container>
|
||||
</div>
|
||||
{:else if image}
|
||||
<div class="image-container single flex">
|
||||
<img
|
||||
src="{`${apiBaseURL}medialib/${image}/${$mediaLibrary?.[image]?.file?.src}?filter=${
|
||||
window.innerWidth > 500 ? 'xl' : 'm'
|
||||
}`}"
|
||||
alt="Bild"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style lang="less">
|
||||
@import "../../assets/css/variables.less";
|
||||
|
||||
swiper-container {
|
||||
height: auto;
|
||||
width: 100%;
|
||||
}
|
||||
swiper-slide {
|
||||
width: 100% !important;
|
||||
}
|
||||
.flex {
|
||||
flex: 2 !important;
|
||||
}
|
||||
|
||||
.image-container {
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
max-width: @body-maxwidth;
|
||||
&:hover img {
|
||||
transform: scale(1.05);
|
||||
transform-origin: center;
|
||||
}
|
||||
img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
14
frontend/src/lib/components/pagebuilder/Modules.svelte
Normal file
14
frontend/src/lib/components/pagebuilder/Modules.svelte
Normal file
@@ -0,0 +1,14 @@
|
||||
<script lang="ts">
|
||||
import { modules } from "../../store"
|
||||
import Formular from "./form/Formular.svelte"
|
||||
|
||||
export let col: { contentType: "moduleImport"; moduleImport: string }
|
||||
export let pageId: string
|
||||
|
||||
let module = $modules[col.moduleImport] || {}
|
||||
$: module = $modules[col.moduleImport] || {}
|
||||
</script>
|
||||
|
||||
{#if module.type == "form"}
|
||||
<Formular {col} {pageId} form="{module}" />
|
||||
{/if}
|
||||
158
frontend/src/lib/components/pagebuilder/Pagebuilder.svelte
Normal file
158
frontend/src/lib/components/pagebuilder/Pagebuilder.svelte
Normal file
@@ -0,0 +1,158 @@
|
||||
<script lang="ts">
|
||||
import { navigateWrapper } from "../../functions/utils"
|
||||
import Image from "./Image.svelte"
|
||||
import Text from "./Text.svelte"
|
||||
import Modules from "./Modules.svelte"
|
||||
import GoogleMaps from "./GoogleMaps.svelte"
|
||||
export let row: Row
|
||||
export let pageId: string
|
||||
export let bright: boolean
|
||||
export let isHP: boolean
|
||||
export let i: number
|
||||
export let page: Content
|
||||
</script>
|
||||
|
||||
{#if Object.keys(row).length}
|
||||
{#if page.pageTitle && i == 0}
|
||||
<h1>{page.pageTitle}</h1>
|
||||
{/if}
|
||||
{#if row.title}
|
||||
<h2 class="">{row.title}</h2>
|
||||
{/if}
|
||||
{#if row?.columns?.length}
|
||||
<div class="row">
|
||||
{#each row?.columns as col}
|
||||
<div class="col">
|
||||
{#if col.contentType == "images"}
|
||||
<Image images="{col.images}" />
|
||||
{:else if col.contentType == "text"}
|
||||
<Text text="{col.text}" />
|
||||
{:else if col.contentType == "form" || col.contentType == "moduleImport"}
|
||||
<Modules {col} {pageId} />
|
||||
{:else if col.contentType == "googleMaps"}
|
||||
<GoogleMaps />
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>{/if}
|
||||
{/if}
|
||||
|
||||
<style lang="less">
|
||||
@import "../../assets/css/variables.less";
|
||||
|
||||
h3 {
|
||||
font-weight: 500;
|
||||
margin-bottom: 15px;
|
||||
@media @tablet {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
h2 {
|
||||
font-weight: 500;
|
||||
font-size: 1.7rem !important;
|
||||
}
|
||||
h1 {
|
||||
font-weight: 500;
|
||||
font-size: 2rem;
|
||||
}
|
||||
.top-header {
|
||||
img {
|
||||
width: 20px;
|
||||
}
|
||||
font-size: 0.7rem;
|
||||
@media @tablet {
|
||||
font-size: 1.2rem;
|
||||
img {
|
||||
width: 48px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@media @tablet {
|
||||
h3 {
|
||||
font-size: 1.3rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
img {
|
||||
width: 40px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
&.subheading {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
}
|
||||
h2 {
|
||||
font-size: 2rem !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
h1 {
|
||||
font-size: 3.5rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
@media @desktop {
|
||||
h2 {
|
||||
font-size: 2.3rem !important;
|
||||
}
|
||||
h1 {
|
||||
font-size: 5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.row {
|
||||
padding-top: 40px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
|
||||
&.dominant {
|
||||
@media (max-width: 1024px) {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
flex-direction: column-reverse;
|
||||
&.normalWrap {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
& > .col {
|
||||
max-width: 100%;
|
||||
&.dominant {
|
||||
@media (max-width: 1024px) {
|
||||
min-width: 80% !important;
|
||||
}
|
||||
flex: 5 !important;
|
||||
}
|
||||
|
||||
min-width: 40% !important;
|
||||
|
||||
@media @desktop {
|
||||
min-width: 30% !important;
|
||||
}
|
||||
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
&.twoToThree {
|
||||
& > .col:nth-child(1) {
|
||||
flex: 2 !important;
|
||||
}
|
||||
& > .col:nth-child(2) {
|
||||
flex: 3 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hph3 {
|
||||
text-transform: uppercase;
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
.nmh3 {
|
||||
font-size: 0.9rem;
|
||||
@media @tablet {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
10
frontend/src/lib/components/pagebuilder/Text.svelte
Normal file
10
frontend/src/lib/components/pagebuilder/Text.svelte
Normal file
@@ -0,0 +1,10 @@
|
||||
<script lang="ts">
|
||||
export let text: string
|
||||
</script>
|
||||
|
||||
<div class="text">
|
||||
{@html text}
|
||||
</div>
|
||||
|
||||
<style lang="less">
|
||||
</style>
|
||||
@@ -12,7 +12,3 @@
|
||||
<input type="checkbox" bind:checked="{bindValue}" {id} {name} bind:this="{formValues[name]}" />
|
||||
<span>{label}</span>
|
||||
</label>
|
||||
|
||||
<style>
|
||||
/* Sie können hier den Stil für Ihre Checkbox anpassen */
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<script lang="ts">
|
||||
import type { Writable } from "svelte/store"
|
||||
export let groupTitle: string
|
||||
export let checkboxes: { name: string; emailName: string }[]
|
||||
export let checkboxes: { textTitle: string; emailTitle: string }[]
|
||||
|
||||
export let formValues: Writable<FormValues>
|
||||
export let rowNr: number
|
||||
</script>
|
||||
@@ -14,10 +15,10 @@
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox-input checkit"
|
||||
bind:this="{$formValues[`checkbox_${groupTitle}_${rowNr}_${checkbox.emailName}`]}"
|
||||
bind:this="{$formValues[`checkbox_${groupTitle}_${rowNr}_${checkbox.emailTitle}`]}"
|
||||
/>
|
||||
<span class="checkit-span"></span>
|
||||
{checkbox.name}
|
||||
{checkbox.textTitle}
|
||||
</label>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import { CalendarView } from "fluent-svelte"
|
||||
import type { Writable } from "svelte/store"
|
||||
|
||||
|
||||
export let groupTitle: string
|
||||
export let datePickerProps: DatePickerProps
|
||||
export let formValues: Writable<FormValues>
|
||||
@@ -112,22 +111,410 @@
|
||||
</div>
|
||||
|
||||
<style lang="less">
|
||||
@import url("https://unpkg.com/fluent-svelte/theme.css");
|
||||
/* Global Variables */
|
||||
:root {
|
||||
/* Accent Colors */
|
||||
--fds-accent-light-3: 191, 98%, 80%;
|
||||
--fds-accent-light-2: 199, 99%, 69%;
|
||||
--fds-accent-light-1: 205, 100%, 49%;
|
||||
--fds-accent-base: 206, 100%, 42%;
|
||||
--fds-accent-dark-1: 209, 100%, 36%;
|
||||
--fds-accent-dark-2: 215, 100%, 29%;
|
||||
--fds-accent-dark-3: 226, 100%, 20%;
|
||||
|
||||
/* Font Families */
|
||||
--fds-font-family-fallback: "Segoe UI", -apple-system, ui-sans-serif, system-ui, BlinkMacSystemFont, Helvetica,
|
||||
Arial, sans-serif;
|
||||
--fds-font-family-text: "Segoe UI Variable Text", "Seoge UI Variable Static Text",
|
||||
var(--fds-font-family-fallback);
|
||||
--fds-font-family-small: "Segoe UI Variable Small", "Seoge UI Variable Static Small",
|
||||
var(--fds-font-family-fallback);
|
||||
--fds-font-family-display: "Segoe UI Variable Display", "Seoge UI Variable Static Display",
|
||||
var(--fds-font-family-fallback);
|
||||
|
||||
/* Font Size */
|
||||
--fds-caption-font-size: 12px;
|
||||
--fds-body-font-size: 14px;
|
||||
--fds-body-large-font-size: 18px;
|
||||
--fds-subtitle-font-size: 20px;
|
||||
--fds-title-font-size: 28px;
|
||||
--fds-title-large-font-size: 40px;
|
||||
--fds-display-font-size: 68px;
|
||||
|
||||
/* Roundness */
|
||||
--fds-control-corner-radius: 4px;
|
||||
--fds-overlay-corner-radius: 8px;
|
||||
|
||||
/* Duration */
|
||||
--fds-control-slow-duration: 333ms;
|
||||
--fds-control-normal-duration: 250ms;
|
||||
--fds-control-fast-duration: 167ms;
|
||||
/* --fds-control-fast-after-duration: 168ms; */
|
||||
--fds-control-faster-duration: 83ms;
|
||||
|
||||
/* Easing */
|
||||
--fds-control-fast-out-slow-in-easing: cubic-bezier(0, 0, 0, 1);
|
||||
|
||||
/* Focus Stroke */
|
||||
--fds-focus-stroke: 0 0 0 1px var(--fds-focus-stroke-inner), 0 0 0 3px var(--fds-focus-stroke-outer);
|
||||
|
||||
/* Acrylic */
|
||||
--fds-acrylic-noise-asset: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAMAAABrrFhUAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABJQTFRF////zMzMmZmZZmZmMzMzAAAA8496aQAADC1JREFUeNrsXYl2IjkMLPn4/18eq0oGkgABwtFtzbxdAg3dtnWUZFmWUaxaKaW2WmqtrY1Xq7W08VLj/fjW+KEVv1qt+UW/Pl5rsQa+LU0fxxfm70v3d9Wgp/Nqi1v4K3/W+Gb8pKK2+Dh+28a18Xj/rbeH8f24an7VL3gfR/+squvGx3l3/OnNr3Iohx4Wvu3+gjYa6OwS+zh+DI6zFJi16Kd3wr9S+xxyr/qZd6U4cfx9Lfo5++ddFzXU9e5t1qoe6z92hNQZdzS/gwMbA7LRgFN1UJLEbIVfV46S9GjjFz5A9sSbbfEXfCY7YD3GIiaQGLWTHsZHagD8xI45P8cDUCrU4mhhUFsD4RW/z9hZsouNwz9whEZaNe9XLzWIYJ0yRKmB02Z0y+IWf7YV1KCx88/IJ5HH7w4aDnYPhgy6+F+RytgfUtAH1TTK+MumG6XYpaCEkPpYyCGJlDrJRxQxZXxAiKKJWaQriShZEtM5kBK3Gvs+aMZGXKRakyw4tUySIHkycUmPERP8LhOjeHsdLHSuh6qJdMFgqcsUMeMrJd6VpjYN3h9sFKouHrpUhPRJtRsbpdpQCztbEXtcKwZFyYfmEmFNelo4Um/SHy75JqEGb5o3XSe14gapnKkpf07rVBS2TF6wx8QDaQ5lofINyWYWgnBgQuMFXZIqkyROioAnH2gjYJXWxeHm/4y6Y8FxEZf8NJGTNOOzGviYAwf8/85BF7GAcus6y0uUr+ikdQFMM/GtCZ6cN2NIMP2s6ZnBA6c00YuXnJPG4XM83RnrqkKUc4b6i/+2ERtRhWn+RZdchbwMGaKSOA+79ISICf7nTGhUr+YjozIIE12uKX2lBfSTQhxUI86SlENM4GiikQh42UUQRVyIqgSENB5GoRehYQ2im0MLaoBsK5MxlFA+0koHxy49lEHhP3++C6rQtQbxZZiCNwhLZbOLk9YOTb0J1x1sZSWmNDUDUXRnZouwIPgoTZruQNbiMqQQRkEdFzp1wcoE8mHtbIhnpYbSCvjv4P2dWu/dFADFY1xIJ/UlpRbgRI45LQl11uuUvqrvQnn1jzBY4sG6P1hugYYCMlI4TC1vsfAypkgMVvfaBH3eb3HWToyja6fRXLsdCLEf8lDIcH09hL/Lh2FXm9UJczLlTnx1c3a2hviV+CgLS2PuxIF12SN+3w/i7So+2NlqoDCI3tSiEj8oUw/aBNMqlXMsadEMYXL6FIQw9hcuf6V3s+kQOCW6t9WkwjJmYZWMNsDhFTEi/CIhTZ0aVINgnlJvQvZJElmjo3Qa2THtbijhxCsaTT2cJkQAS16CZKhidqN0lE52GKUCtBQnYmVl4reFAzOtZVPHqaE+YAsDKfzl4DUyfBB/dCMI9jYRXYJNTsqhcAWTg1rDgyxyXw/uamfzLkGjGchznYBicmslf8Qd/m30Bk34saSHS5Y10dKO3h8VrZJMRvs9pCqvAaCWQAMpoX36AMF1WOjZkL/2VkMe1bM65WPqpH8hn5dYUOShl4pycLSIZ+7tyzdqE34bHRWQSBRt6l4LuoQ0hg/V5ZGHHreJ3+H7tzbxWi6NHB1jJ/hdqdO/NXQLD14dIbH7ELEeKC7Xp5C5FqAv+ZGKzrnRXt1pbEsjwzMWSouLPtYhvnWaDWpWrRP8IQe/Tlde7Tryk0p1BjlKzAe/z/vwNo8jLIhzRcOhKTmY/vkGVFPNxEEjxtkB2SsYIYm8N5pN1+k6TEPrrjcnFd3CxS+cypeQcIl7Fa/cAlZ/LXL7882L8KRJFaMZjSOm0EjPRPMe0yywIVDfh2N1jE5QTOtnnHLsJXITc3i9JzBNvf6bIw+PiUW7sipB43CFLGKiMus+Zp/w0iOAsFq+q4UpMolIWOcJvHW7gTcIF9111c9LBIbFxV4xg1qSfpc6GZ4aUEocrU7FvscAKy7NkqJfmvv0UPLaTlxwee7hqStUpz6FAzRt0zGqHtRoIVOlznB8uAhhRWgw6LXQ+3S4B0lgEQrkJGeqA2hHIR8/7DtVysDeCe3DcmpmpSmUnErELMhCvOoM79NfrTGJnapwkO0SY5MTUmKmKnPr428RciPd4uFWtuh0YXew/eRYCvbCqVe5xygHnZwMJrBKX8PrgybbXeASbG5Cnr1bEZxbLKgxfdUg7ejJyZMWqRcJJ2PX0YwnzJ3woml2yIxk28XeZL1ocUv42KEDTTihQVJeGOYgisqxjDjqK5ax8cFwXBGY+Lj4HeHR3WoB2yTha+dqWH/173rAC9vwx2rrc77SGN6rbLHPkLkWjgMLFY+P4DrKFPaqEKCCh3NCZgp3KjBxDAyUNhe68WaCby7CiL347K+SEewrn+P5UQHsMrPpiSsEeEvYZcPrp9hElsIHVwmRLCvuh5eNxxaV15lBYhOL9B+kJ3ae4PHn0GXm3AgNY1MrlR/IUEWGJIhrOU5IHQwYtMLqydC/xTexaYR6g4RgW/GZ94cLkHtdyFdy11rsvXvegP1a8OfAKnaU2v8acE6/X+CNu1M2mUiFF6TecU5RbVrQtukcDLx7+rm1KDw+MP/Y1B4L7Htd5+9L2thJOt/L1l2wnEzfGZJEYidQ9Egx6b+S1ISl8l0e8Gfxss04O6EaMqbGnU7HsBFV/BgRkZf38t6xRBWEP8wzkXGbzOlyKBK6Pl9gDSus7vxFUbGMLD+4tI+EeVFf4j5Yd933tjkqVtj49Jf0baREvhMXBbmqBfxc3kWamlkXvMX/+wXSyn6kNiD3qoD72mmjgZrLI2+GYEj7hrJWP7JsiJxhkONyO7ZU1ekTgWQkyYe8CIbYZfWjJ6ZgYP0KAdcXppEnLfo8tGOdwniPQSiyVI+9hGbY6K72tyXjIHM4rM7l8UWk+SHbjGUD/jcG1f+X1Ew9EfAbd+zFPiWRGLnrSjOctwqePyalyLxXwNmNxBV0COLY+H6Gl/uS2NdS5vM1Fcn9IEU3lqyMcGMIBnvI4njlOhmS75yVX7F0iYhfhAxpamhfEA6sthHyXscBa2P8764NsmSEXgoQIeHpWl8Kd6Emr6uLjBXkTlcYkXRF7BBLRLraWd8QDLtL734yuCJJ+eCLKaNY/UjN33LXkDsorsLKyZzfr/4Csm2T+55thlUDHbdqCHKtAvx0v5C6fAZ959yn7NAmbDmd/+W0xE6n8U+bjSJ1YXFOAxdMe7mnkAoSWbyzXMRKFWIfyVTBTg6EepmUYZXtb4/ay//5ASW7CCw9uBv8QeQ9cVjhR+Q+ao2ltHIvj2LBLQB3BZex2GLvA5OpxSpC3Ot7YunJ/g1zYqSD/W9LZBk3iXxJskKajMgLUxUk3S953DW27o7A29wB5AuDfs02QsqjZk/yCrC9mg7vrWmLHdQ+fmkeL9JsDrqwjQ8Jd8x/cfGw5hTn9vA6FtwMeteCDFYulniL3CC1CaArnKZ25HmHFfsvifi3RRy890yX7WWk/0+Xt+T75rBimcx7nAssvR/mhiQjJE4Upypj5cMTbnExkCIN5IqLgpL8sDFY8nN3UZKLAGrysDDWDXbdpsXIcaTaZQFElrNFL0710leQyL1nrGrX2D5k9TVhCSQ+bpTxVqxRCOPxZVlkOlLnnLyiJC8vD0ueJ4YViyLcE23AukWzbzPayFlN+AhdSHrg9MGhRqYawudW7ZH8uEGOcL2iAHc40NjuGVDvgSRsKmHnA64ictWL+Dlpw3oxrvviBsh0tN45S46MS+KnYoscR6pdlmLk3TApmMTuKyL+cTM7avIzNrBQ6v9Dc26kKyT8zUogGrCs5wxg7YO1f5/dYOmV3xu8YmRKjD4X4cHK+4FuWZLGenlf98UW8fFMxQ9vpsBKbu0j9S+w333fz4lhIlVa6Bl4QOa9Ak4EbDOH+33IiJocBVGTV1PDHjc5PFNBsPeDk/9qmJC4pjINEdYPfF8XOlhyVxBpVkAuCAKyHanxPQ0ECbJhryafYMdHBD2FeEhSLOYiYGVOERSE5j56Pbq9K9ftyTEELFQZ7yHNQsq9gidLM8iUC3DOvmH9jXHX18yww51uT7VGyJYS810NkHTL8CEAjBWLI93jTCctoncM+SBdFdVv4VbU7DKw9LHSNyxlIMec73IGLFLavpPFT1jy2RBWKpX/yJo0UuTCXYnoIFda5E8TjzX8uceX8LB+4dzrzPsnwABuGHwbUzm+xwAAAABJRU5ErkJggg==");
|
||||
--fds-acrylic-blur-factor: blur(60px);
|
||||
}
|
||||
|
||||
/* Reduced Motion Support */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
:root {
|
||||
--fds-control-slow-duration: 0ms;
|
||||
--fds-control-normal-duration: 0ms;
|
||||
--fds-control-fast-duration: 0ms;
|
||||
/* --fds-control-fast-after-duration: 0ms; */
|
||||
--fds-control-faster-duration: 0ms;
|
||||
}
|
||||
}
|
||||
|
||||
/* Light Theme Colors */
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
/* Text */
|
||||
--fds-text-primary: hsla(0, 0%, 0%, 89.56%);
|
||||
--fds-text-secondary: hsla(0, 0%, 0%, 60.63%);
|
||||
--fds-text-tertiary: hsla(0, 0%, 0%, 44.58%);
|
||||
--fds-text-disabled: hsla(0, 0%, 0%, 36.14%);
|
||||
|
||||
/* Accent */
|
||||
--fds-accent-default: hsl(var(--fds-accent-dark-1));
|
||||
--fds-accent-secondary: hsla(var(--fds-accent-dark-1), 90%);
|
||||
--fds-accent-tertiary: hsla(var(--fds-accent-dark-1), 80%);
|
||||
--fds-accent-disabled: hsla(0, 0%, 0%, 21.69%);
|
||||
|
||||
/* Accent Text */
|
||||
--fds-accent-text-primary: hsl(var(--fds-accent-dark-2));
|
||||
--fds-accent-text-secondary: hsl(var(--fds-accent-dark-3));
|
||||
--fds-accent-text-tertiary: hsl(var(--fds-accent-dark-1));
|
||||
--fds-accent-text-disabled: hsla(0, 0%, 0%, 36.14%);
|
||||
|
||||
/* Text on Accent */
|
||||
--fds-text-on-accent-primary: hsl(0, 0%, 100%);
|
||||
--fds-text-on-accent-secondary: hsla(0, 0%, 100%, 70%);
|
||||
--fds-text-on-accent-disabled: var(--fds-text-on-accent-secondary);
|
||||
--fds-text-on-accent-selected: var(--fds-text-on-accent-primary);
|
||||
|
||||
/* Control Fill */
|
||||
--fds-control-fill-transparent: transparent;
|
||||
--fds-control-fill-default: hsla(0, 0%, 100%, 70%);
|
||||
--fds-control-fill-secondary: hsla(0, 0%, 98%, 50%);
|
||||
--fds-control-fill-tertiary: hsla(0, 0%, 98%, 30%);
|
||||
--fds-control-fill-disabled: var(--fds-control-fill-tertiary);
|
||||
--fds-control-fill-input-active: hsl(0, 0%, 100%);
|
||||
|
||||
/* Control Strong Fill */
|
||||
--fds-control-strong-fill-default: hsla(0, 0%, 0%, 44.58%);
|
||||
--fds-control-strong-fill-disabled: hsla(0, 0%, 0%, 31.73%);
|
||||
|
||||
/* Control Solid Fill */
|
||||
--fds-control-solid-fill-default: hsl(0, 0%, 100%);
|
||||
|
||||
/* Control Alt Fill */
|
||||
--fds-control-alt-fill-transparent: transparent;
|
||||
--fds-control-alt-fill-secondary: hsla(0, 0%, 0%, 2.41%);
|
||||
--fds-control-alt-fill-tertiary: hsla(0, 0%, 0%, 5.78%);
|
||||
--fds-control-alt-fill-quarternary: hsla(0, 0%, 0%, 9.24%);
|
||||
--fds-control-alt-fill-disabled: var(--fds-control-alt-fill-transparent);
|
||||
|
||||
/* Control on Image Fill */
|
||||
--fds-control-on-image-fill-default: hsla(0, 0%, 100%, 79%);
|
||||
--fds-control-on-image-fill-secondary: hsl(0, 0%, 95%);
|
||||
--fds-control-on-image-fill-tertiary: hsl(0, 0%, 92%);
|
||||
--fds-control-on-image-fill-disabled: transparent;
|
||||
|
||||
/* Subtle Fill */
|
||||
--fds-subtle-fill-transparent: transparent;
|
||||
--fds-subtle-fill-secondary: hsla(0, 0%, 0%, 3.73%);
|
||||
--fds-subtle-fill-tertiary: hsla(0, 0%, 0%, 2.41%);
|
||||
--fds-subtle-fill-disabled: var(--fds-subtle-fill-transparent);
|
||||
|
||||
/* Control Stroke */
|
||||
--fds-control-stroke-default: hsla(0, 0%, 0%, 5.78%);
|
||||
--fds-control-stroke-secondary: hsla(0, 0%, 0%, 16.22%);
|
||||
|
||||
/* Control Strong Stroke */
|
||||
--fds-control-strong-stroke-default: hsla(0, 0%, 0%, 44.58%);
|
||||
--fds-control-strong-stroke-disabled: hsla(0, 0%, 0%, 21.69%);
|
||||
|
||||
/* Control Stroke on Accent */
|
||||
--fds-control-stroke-on-accent-default: hsla(0, 0%, 100%, 8%);
|
||||
--fds-control-stroke-on-accent-secondary: hsla(0, 0%, 0%, 40%);
|
||||
--fds-control-stroke-on-accent-tertiary: hsla(0, 0%, 0%, 21.69%);
|
||||
--fds-control-stroke-on-accent-disabled: var(--fds-control-stroke-on-accent-default);
|
||||
|
||||
/* Control Strong Stroke on Image */
|
||||
--fds-control-strong-stroke-on-image-default: hsla(0, 0%, 100%, 35%);
|
||||
|
||||
/* Card Stroke */
|
||||
--fds-card-stroke-default: hsla(0, 0%, 0%, 5.78%);
|
||||
--fds-card-stroke-default-solid: hsl(0, 0%, 92%);
|
||||
|
||||
/* Surface Stroke */
|
||||
--fds-surface-stroke-default: hsla(0, 0%, 46%, 40%);
|
||||
--fds-surface-stroke-flyout: hsla(0, 0%, 0%, 5.78%);
|
||||
|
||||
/* Divider Stroke */
|
||||
--fds-divider-stroke-default: hsla(0, 0%, 0%, 8.03%);
|
||||
|
||||
/* Focus Stroke */
|
||||
--fds-focus-stroke-outer: hsla(0, 0%, 0%, 89.56%);
|
||||
--fds-focus-stroke-inner: hsl(0, 0%, 100%);
|
||||
|
||||
/* Card Background */
|
||||
--fds-card-background-default: hsla(0, 0%, 100%, 70%);
|
||||
--fds-card-background-secondary: hsla(0, 0%, 96%, 50%);
|
||||
/* --fds-card-background-tertiary: hsl(0, 0%, 100%); */
|
||||
|
||||
/* Smoke Background */
|
||||
--fds-smoke-background-default: hsla(0, 0%, 0%, 30%);
|
||||
|
||||
/* Layer */
|
||||
--fds-layer-background-default: hsla(0, 0%, 100%, 50%);
|
||||
--fds-layer-background-alt: hsl(0, 0%, 100%);
|
||||
|
||||
/* Layer on Acrylic */
|
||||
--fds-layer-on-acrylic-background-default: hsla(0, 0%, 100%, 25%);
|
||||
--fds-layer-on-accent-acrylic-background-default: var(--fds-layer-on-acrylic-background-default);
|
||||
|
||||
/* Solid Background */
|
||||
--fds-solid-background-base: hsl(0, 0%, 95%);
|
||||
--fds-solid-background-secondary: hsl(0, 0%, 93%);
|
||||
--fds-solid-background-tertiary: hsl(0, 0%, 98%);
|
||||
--fds-solid-background-quarternary: hsl(0, 0%, 100%);
|
||||
|
||||
/* Mica Background */
|
||||
/* --fds-mica-background-base: linear-gradient(0deg, hsla(0, 0%, 95%, 0.5), hsla(0, 0%, 95%, 0.5)), hsl(0, 0%, 95%); */
|
||||
|
||||
/* Acrylic Background */
|
||||
--fds-acrylic-background-default: transparent, rgba(252, 252, 252, 85%);
|
||||
--fds-acrylic-background-base: transparent, rgba(243, 243, 243, 90%);
|
||||
|
||||
/* Accent Acrylic Background */
|
||||
/* --fds-accent-acrylic-background-base: url("NoiseAsset_256.png"), linear-gradient(0deg, rgba(153, 235, 255, 80%), rgba(153, 235, 255, 80%)), rgba(153, 235, 255, 90%); */
|
||||
/* --fds-accent-acrylic-background-default: url("NoiseAsset_256.png"), linear-gradient(0deg, rgba(153, 235, 255, 80%), rgba(153, 235, 255, 80%)), rgba(153, 235, 255, 90%); */
|
||||
|
||||
/* System */
|
||||
--fds-system-attention: hsl(209, 100%, 36%);
|
||||
--fds-system-success: hsl(120, 78%, 27%);
|
||||
--fds-system-caution: hsl(36, 100%, 31%);
|
||||
--fds-system-critical: hsl(5, 75%, 44%);
|
||||
--fds-system-neutral: hsla(0, 0%, 0%, 44.58%);
|
||||
|
||||
/* System Solid */
|
||||
--fds-system-solid-neutral: hsl(0, 0%, 54%);
|
||||
|
||||
/* System Background */
|
||||
--fds-system-background-attention: hsla(0, 0%, 96%, 50%);
|
||||
--fds-system-background-success: hsl(115, 58%, 92%);
|
||||
--fds-system-background-caution: hsl(47, 100%, 90%);
|
||||
--fds-system-background-critical: hsl(355, 85%, 95%);
|
||||
|
||||
/* System Background Solid */
|
||||
--fds-system-background-solid-attention: hsl(0, 0%, 97%);
|
||||
--fds-system-background-solid-neutral: hsl(0, 0%, 95%);
|
||||
|
||||
/* Borders */
|
||||
--fds-control-border-default: var(--fds-control-stroke-default) var(--fds-control-stroke-default)
|
||||
var(--fds-control-stroke-secondary) var(--fds-control-stroke-default);
|
||||
|
||||
/* Shadows */
|
||||
--fds-card-shadow: 0px 2px 4px hsla(0, 0%, 0%, 4%);
|
||||
--fds-tooltip-shadow: 0px 4px 8px hsla(0, 0%, 0%, 14%);
|
||||
--fds-flyout-shadow: 0px 8px 16px hsla(0, 0%, 0%, 14%);
|
||||
--fds-dialog-shadow: 0px 32px 64px hsla(0, 0%, 0%, 18.76%), 0px 2px 21px hsl(0, 0%, 0%, 14.74%);
|
||||
|
||||
/* Shell Shadows */
|
||||
--fds-inactive-window-shadow: 0px 16px 32px hsla(0, 0%, 0%, 18%), 0px 2px 10.67px hsla(0, 0%, 0%, 0.1474);
|
||||
--fds-active-window-shadow: 0px 32px 64px hsla(0, 0%, 0%, 28%), 0px 2px 21px hsla(0, 0%, 0%, 22%);
|
||||
}
|
||||
}
|
||||
|
||||
/* Dark Theme Colors */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
/* Text */
|
||||
--fds-text-primary: hsla(0, 0%, 100%, 100%);
|
||||
--fds-text-secondary: hsla(0, 0%, 100%, 78.6%);
|
||||
--fds-text-tertiary: hsla(0, 0%, 100%, 54.42%);
|
||||
--fds-text-disabled: hsla(0, 0%, 100%, 36.28%);
|
||||
|
||||
/* Accent */
|
||||
--fds-accent-default: hsla(var(--fds-accent-light-2));
|
||||
--fds-accent-secondary: hsla(var(--fds-accent-light-2), 90%);
|
||||
--fds-accent-tertiary: hsla(var(--fds-accent-light-2), 80%);
|
||||
--fds-accent-disabled: hsla(0, 0%, 100%, 15.81%);
|
||||
|
||||
/* Accent Text */
|
||||
--fds-accent-text-primary: hsl(var(--fds-accent-light-3));
|
||||
--fds-accent-text-secondary: hsl(var(--fds-accent-light-3));
|
||||
--fds-accent-text-tertiary: hsl(var(--fds-accent-light-2));
|
||||
--fds-accent-text-disabled: hsla(0, 0%, 100%, 36.28%);
|
||||
|
||||
/* Text on Accent */
|
||||
--fds-text-on-accent-primary: hsl(0, 0%, 0%);
|
||||
--fds-text-on-accent-secondary: hsla(0, 0%, 0%, 0.5);
|
||||
--fds-text-on-accent-disabled: hsla(0, 0%, 100%, 0.53);
|
||||
--fds-text-on-accent-selected: hsl(0, 0%, 100%);
|
||||
|
||||
/* Control Fill */
|
||||
--fds-control-fill-transparent: transparent;
|
||||
--fds-control-fill-default: hsla(0, 0%, 100%, 0.061);
|
||||
--fds-control-fill-secondary: hsla(0, 0%, 100%, 0.084);
|
||||
--fds-control-fill-tertiary: hsla(0, 0%, 100%, 0.033);
|
||||
--fds-control-fill-disabled: hsla(0, 0%, 100%, 0.042);
|
||||
--fds-control-fill-input-active: hsla(0, 0%, 12%, 70%);
|
||||
|
||||
/* Control Strong Fill */
|
||||
--fds-control-strong-fill-default: hsla(0, 0%, 100%, 54.42%);
|
||||
--fds-control-strong-fill-disabled: hsla(0, 0%, 100%, 24.65%);
|
||||
|
||||
/* Control Solid Fill */
|
||||
--fds-control-solid-fill-default: hsl(0, 0%, 27%);
|
||||
|
||||
/* Control Alt Fill */
|
||||
--fds-control-alt-fill-transparent: transparent;
|
||||
--fds-control-alt-fill-secondary: hsla(0, 0%, 0%, 0.1);
|
||||
--fds-control-alt-fill-tertiary: hsla(0, 0%, 100%, 0.042);
|
||||
--fds-control-alt-fill-quarternary: hsla(0, 0%, 100%, 0.07);
|
||||
--fds-control-alt-fill-disabled: var(--fds-control-fill-transparent);
|
||||
|
||||
/* Control on Image Fill */
|
||||
--fds-control-on-image-fill-default: hsla(0, 0%, 11%, 70%);
|
||||
--fds-control-on-image-fill-secondary: hsl(0, 0%, 10%);
|
||||
--fds-control-on-image-fill-tertiary: hsl(0, 0%, 7%);
|
||||
--fds-control-on-image-fill-disabled: transparent;
|
||||
|
||||
/* Subtle Fill */
|
||||
--fds-subtle-fill-transparent: transparent;
|
||||
--fds-subtle-fill-secondary: hsla(0, 0%, 100%, 6.05%);
|
||||
--fds-subtle-fill-tertiary: hsla(0, 0%, 100%, 4.19%);
|
||||
--fds-subtle-fill-disabled: transparent;
|
||||
|
||||
/* Control Stroke */
|
||||
--fds-control-stroke-default: hsla(0, 0%, 100%, 6.98%);
|
||||
--fds-control-stroke-secondary: hsla(0, 0%, 100%, 9.3%);
|
||||
|
||||
/* Control Strong Stroke */
|
||||
--fds-control-strong-stroke-default: hsla(0, 0%, 100%, 54.42%);
|
||||
--fds-control-strong-stroke-disabled: hsla(0, 0%, 100%, 15.81%);
|
||||
|
||||
/* Control Stroke on Accent */
|
||||
--fds-control-stroke-on-accent-default: hsla(0, 0%, 100%, 8%);
|
||||
--fds-control-stroke-on-accent-secondary: hsla(0, 0%, 0%, 14%);
|
||||
--fds-control-stroke-on-accent-tertiary: hsla(0, 0%, 0%, 21.69%);
|
||||
--fds-control-stroke-on-accent-disabled: hsla(0, 0%, 0%, 20%);
|
||||
|
||||
/* Control Strong Stroke on Image */
|
||||
--fds-control-strong-stroke-on-image-default: hsla(0, 0%, 0%, 42%);
|
||||
|
||||
/* Card Stroke */
|
||||
--fds-card-stroke-default: hsla(0, 0%, 0%, 10%);
|
||||
--fds-card-stroke-default-solid: hsl(0, 0%, 11%);
|
||||
|
||||
/* Surface Stroke */
|
||||
--fds-surface-stroke-default: hsla(0, 0%, 46%, 40%);
|
||||
--fds-surface-stroke-flyout: hsla(0, 0%, 0%, 20%);
|
||||
|
||||
/* Divider Stroke */
|
||||
--fds-divider-stroke-default: hsla(0, 0%, 100%, 8.37%);
|
||||
|
||||
/* Focus Stroke */
|
||||
--fds-focus-stroke-outer: hsl(0, 0%, 100%);
|
||||
--fds-focus-stroke-inner: hsla(0, 0%, 0%, 70%);
|
||||
|
||||
/* Card Background */
|
||||
--fds-card-background-default: hsla(0, 0%, 100%, 5.12%);
|
||||
--fds-card-background-secondary: hsla(0, 0%, 100%, 3.26%);
|
||||
/* --fds-card-background-tertiary: unset; */
|
||||
|
||||
/* Smoke Background */
|
||||
--fds-smoke-background-default: hsla(0, 0%, 0%, 30%);
|
||||
|
||||
/* Layer */
|
||||
--fds-layer-background-default: hsla(0, 0%, 23%, 30%);
|
||||
--fds-layer-background-alt: hsla(0, 0%, 100%, 5.38%);
|
||||
|
||||
/* Layer on Acrylic */
|
||||
--fds-layer-on-acrylic-background-default: hsla(0, 0%, 100%, 3.59%);
|
||||
--fds-layer-on-accent-background-default: var(--fds-layer-on-acrylic-background-default);
|
||||
|
||||
/* Solid Background */
|
||||
--fds-solid-background-base: hsl(0, 0%, 13%);
|
||||
--fds-solid-background-secondary: hsl(0, 0%, 11%);
|
||||
--fds-solid-background-tertiary: hsl(0, 0%, 16%);
|
||||
--fds-solid-background-quarternary: hsl(0, 0%, 17%);
|
||||
|
||||
/* Mica Background */
|
||||
/* --fds-mica-background-base: linear-gradient(0deg, rgb(32, 32, 32, 0.8), rgb(32, 32, 32, 0.8)), #202020; */
|
||||
|
||||
/* Acrylic Background */
|
||||
--fds-acrylic-background-default: linear-gradient(0deg, rgb(44, 44, 44, 15%), rgb(44, 44, 44, 15%)),
|
||||
rgba(44, 44, 44, 96%);
|
||||
--fds-acrylic-background-base: linear-gradient(0deg, rgb(32, 32, 32, 50%), rgb(32, 32, 32, 50%)),
|
||||
rgba(32, 32, 32, 96%);
|
||||
|
||||
/* Accent Acrylic Background */
|
||||
/* --fds-accent-acrylic-background-default: url("NoiseAsset_256.png"), linear-gradient(0deg, rgb(0, 120, 212, 80%), rgb(0, 120, 212, 80%)), rgb(0, 120, 212, 80%); */
|
||||
/* --fds-accent-acrylic-background-base: url("NoiseAsset_256.png"), linear-gradient(0deg, rgba(0, 63, 255, 80%), rgba(0, 63, 255, 80%)), rgb(0, 63, 255, 80%); */
|
||||
|
||||
/* System */
|
||||
--fds-system-attention: hsl(199, 100%, 69%);
|
||||
--fds-system-success: hsl(113, 51%, 58%);
|
||||
--fds-system-caution: hsl(54, 100%, 49%);
|
||||
--fds-system-critical: hsl(354, 100%, 80%);
|
||||
--fds-system-neutral: hsla(0, 0%, 100%, 54.42%);
|
||||
|
||||
/* System Solid */
|
||||
--fds-system-solid-neutral: hsl(0, 0%, 62%);
|
||||
|
||||
/* System Background */
|
||||
--fds-system-background-attention: hsla(0, 0%, 100%, 3.26%);
|
||||
--fds-system-background-success: hsl(67, 39%, 17%);
|
||||
--fds-system-background-caution: hsl(40, 46%, 18%);
|
||||
--fds-system-background-critical: hsl(2, 28%, 21%);
|
||||
|
||||
/* System Background Solid */
|
||||
--fds-system-background-solid-attention: hsl(0, 0%, 18%);
|
||||
--fds-system-background-solid-neutral: hsl(0, 0%, 62%);
|
||||
|
||||
/* Borders */
|
||||
--fds-control-border-default: var(--fds-control-stroke-secondary) var(--fds-control-stroke-default)
|
||||
var(--fds-control-stroke-default) var(--fds-control-stroke-default);
|
||||
|
||||
/* Shadows */
|
||||
--fds-card-shadow: 0px 2px 4px hsla(0, 0%, 0%, 0.13);
|
||||
--fds-tooltip-shadow: 0px 4px 8px hsla(0, 0%, 0%, 0.26);
|
||||
--fds-flyout-shadow: 0px 8px 16px hsla(0, 0%, 0%, 0.14);
|
||||
--fds-dialog-shadow: 0px 32px 64px hsla(0, 0%, 0%, 0.37), 0px 2px 21px hsla(0, 0%, 0%, 0.37);
|
||||
|
||||
/* Shell Shadows */
|
||||
--fds-inactive-window-shadow: 0px 16px 32px hsla(0, 0%, 0%, 0.37), 0px 2px 10.67px hsla(0, 0%, 0%, 0.37);
|
||||
--fds-active-window-shadow: 0px 32px 64px hsla(0, 0%, 0%, 0.56), 0px 2px 21px hsla(0, 0%, 0%, 0.55);
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
--fds-solid-background-quarternary: var(--background-color);
|
||||
--fds-text-primary: var(--normal-font-color);
|
||||
--fds-text-secondary: var(--normal-font-color-80);
|
||||
--fds-text-tertiary: var(--normal-font-color-50);
|
||||
--fds-text-disabled: var(--normal-font-color-30);
|
||||
--fds-accent-disabled: var(--opposite-bg-color-5);
|
||||
--fds-control-strong-stroke-default: var(--opposite-bg-color-80);
|
||||
--fds-solid-background-quarternary: rgb(235, 221, 221);
|
||||
--fds-text-primary: #333333;
|
||||
--fds-text-secondary: rgba(51, 51, 51, 0.8);
|
||||
--fds-text-tertiary: rgba(51, 51, 51, 0.5);
|
||||
--fds-text-disabled: rgba(51, 51, 51, 0.3);
|
||||
--fds-accent-disabled: rgba(24, 24, 24, 0.05);
|
||||
--fds-control-strong-stroke-default: rgba(24, 24, 24, 0.8);
|
||||
.datepicker {
|
||||
width: fit-content;
|
||||
margin: 5px 0px;
|
||||
box-shadow: 0 0 25px 10px var(--opposite-bg-color-5);
|
||||
box-shadow: 0 0 25px 10px rgba(24, 24, 24, 0.05);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
<script lang="ts">
|
||||
import CheckboxGroup from "./CheckboxGroup.svelte"
|
||||
import Datepicker from "./Datepicker.svelte"
|
||||
import FormLabelNumberBlock from "./FormLabelNumberBlock.svelte"
|
||||
import type { Writable } from "svelte/store"
|
||||
import Select from "./Select.svelte"
|
||||
|
||||
export let formRow: FormRow
|
||||
export let index: number
|
||||
|
||||
export let formValues: Writable<FormValues>
|
||||
/* function getSortedFields(column: FormColumn) {
|
||||
const fields = [
|
||||
...(column.text.length ? [{ text: column.text, type: "text", order: column.textfieldOrder ?? 3 }] : []),
|
||||
...(column.showTimes ? [{ type: "times", order: column.timesfieldOrder ?? 3, times: column.times }] : []),
|
||||
...(column.showDate
|
||||
? [{ type: "date", order: column.datefieldOrder ?? 3, datePlaceholder: column.datePlaceholder }]
|
||||
: []),
|
||||
]
|
||||
return fields.sort((a, b) => a.order - b.order)
|
||||
}*/
|
||||
//formRow.columns = formRow.columns.map((e) => getSortedFields(e))
|
||||
function removeInvalid(e: Event) {
|
||||
let element = e.currentTarget as HTMLElement
|
||||
if (element) element.classList.remove("invalid")
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="form-row">
|
||||
<h3 style="margin-bottom: -0.5rem;">
|
||||
{#if formRow.showRowName}{formRow.rowName || ""}{/if}
|
||||
</h3>
|
||||
<div class="form-cols">
|
||||
{#each formRow.columns as column, columnIndex}
|
||||
<div class="form-column">
|
||||
<h3>{column?.title ?? ""}</h3>
|
||||
{#if column?.annotation}
|
||||
<p>{column?.annotation}</p>
|
||||
{/if}
|
||||
{#if column.showLabelNumber}
|
||||
<FormLabelNumberBlock {column} {formValues} rowIndex="{index}" />
|
||||
{/if}
|
||||
|
||||
{#if column?.showTimes}
|
||||
<label bind:this="{$formValues[`times_${column?.title ? column.title + '_' : ''}label`]}">
|
||||
<select
|
||||
id="time-select"
|
||||
bind:this="{$formValues[
|
||||
`times_${column?.title ?? 'Zeiten'}_${column.timesfieldOrder}_${index}_${
|
||||
column.emailNameTimes || columnIndex + 'invalidtimes'
|
||||
}`
|
||||
]}"
|
||||
required="{column?.timeNotRequired !== true}"
|
||||
on:change="{removeInvalid}"
|
||||
>
|
||||
<option value="" disabled selected>Bitte Uhrzeit wählen</option>
|
||||
{#each column?.times ?? [] as time}
|
||||
<option value="{time?.timeFrom}-{time?.timeTo}">
|
||||
{time?.timeFrom} - {time?.timeTo}
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
</label>
|
||||
{/if}
|
||||
|
||||
{#if column?.showSelect}
|
||||
<label bind:this="{$formValues[`select_${column?.title ? column.title + '_' : ''}label`]}">
|
||||
<select
|
||||
required="{column?.dateSelectNotRequired !== true}"
|
||||
bind:this="{$formValues[
|
||||
`select_${column?.title ?? 'Auswahl'}_${column.datefieldOrder}_${index}_${
|
||||
column?.emailNameTime || columnIndex + 'invalidtime'
|
||||
}`
|
||||
]}"
|
||||
on:change="{removeInvalid}"
|
||||
>
|
||||
<option value="" disabled selected>{column.selectTitle}</option>
|
||||
{#each column?.selectEntries as entry}
|
||||
<option value="{entry?.leftSide}-{entry?.rightSide}">
|
||||
{entry?.leftSide}-{entry?.rightSide}
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
</label>
|
||||
{/if}
|
||||
|
||||
{#if column.showDate}
|
||||
<label bind:this="{$formValues[`date_${column?.title ? column.title + '_' : ''}label`]}">
|
||||
<input
|
||||
class="date"
|
||||
type="date"
|
||||
required="{column?.dateNotRequired !== true}"
|
||||
on:change="{removeInvalid}"
|
||||
bind:this="{$formValues[
|
||||
`date_${column?.title ?? 'Datum'}_${column.datefieldOrder}_${index}_${
|
||||
column?.emailNameDate || columnIndex + 'invaliddate'
|
||||
}`
|
||||
]}"
|
||||
/>
|
||||
</label>
|
||||
{/if}
|
||||
{#if column.showNumber}
|
||||
<label
|
||||
bind:this="{$formValues[
|
||||
`input_${column.title ? column.title + '_' : ''}${column?.numberPlaceholder}_label`
|
||||
]}"
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
step="any"
|
||||
required="{column?.numberNotRequired !== true}"
|
||||
on:change="{removeInvalid}"
|
||||
placeholder="{column?.numberPlaceholder}"
|
||||
bind:this="{$formValues[
|
||||
`input_${column.title ? column.title + '_' : ''}${column?.numberPlaceholder}_${
|
||||
column.numberfieldOrder
|
||||
}_${index}_${column?.emailNameNumber || columnIndex + 'invalidnumber'}`
|
||||
]}"
|
||||
/>
|
||||
</label>
|
||||
{/if}
|
||||
|
||||
{#if column.showCheckboxGroup}
|
||||
<CheckboxGroup
|
||||
checkboxes="{column.checkboxes}"
|
||||
groupTitle="{column.groupTitle}"
|
||||
{formValues}
|
||||
rowNr="{index}"
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if column.showDatePicker}
|
||||
<Datepicker
|
||||
datePickerProps="{column.datePickerProps}"
|
||||
groupTitle="{column.groupTitle}"
|
||||
{formValues}
|
||||
rowNr="{index}"
|
||||
formCol="{column}"
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if column.showMultiSelect}
|
||||
<Select
|
||||
groupTitle="{column.groupTitle}"
|
||||
{formValues}
|
||||
rowNr="{index}"
|
||||
formCol="{column}"
|
||||
options="{column.multiSelectOptions}"
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#each column.text ?? [] as textField, textFieldIndex}
|
||||
<div>
|
||||
<h3 class="textTitle">{textField.textTitle || ""}</h3>
|
||||
{#if textField?.textArea}
|
||||
<label bind:this="{$formValues[`textarea_Nachricht_label`]}">
|
||||
<textarea
|
||||
placeholder="{textField?.textPlaceholder}"
|
||||
required="{textField?.notRequired !== true}"
|
||||
on:change="{removeInvalid}"
|
||||
bind:this="{$formValues[
|
||||
`textarea_Nachricht_${textField.textfieldOrder}_${index}_${
|
||||
textField.emailName || columnIndex + 'invalidtext' + textFieldIndex
|
||||
}`
|
||||
]}"
|
||||
></textarea>
|
||||
</label>
|
||||
{:else}
|
||||
<label
|
||||
bind:this="{$formValues[
|
||||
`input_${column.title ? column.title + '_' : ''}${textField?.textPlaceholder}_label`
|
||||
]}"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="{textField?.textPlaceholder}"
|
||||
on:change="{removeInvalid}"
|
||||
required="{textField?.notRequired !== true}"
|
||||
bind:this="{$formValues[
|
||||
`${
|
||||
textField?.telValidation
|
||||
? 'Telefon'
|
||||
: textField?.emailValidation
|
||||
? 'Email'
|
||||
: 'input'
|
||||
}_${column.title ? column.title + '_' : ''}${textField?.textPlaceholder}_${
|
||||
textField.textfieldOrder
|
||||
}_${index}_${
|
||||
textField.emailName || columnIndex + 'invalidtext' + textFieldIndex
|
||||
}`
|
||||
]}"
|
||||
/>
|
||||
</label>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
469
frontend/src/lib/components/pagebuilder/form/FormColumn.svelte
Normal file
469
frontend/src/lib/components/pagebuilder/form/FormColumn.svelte
Normal file
@@ -0,0 +1,469 @@
|
||||
<script lang="ts">
|
||||
import CheckboxGroup from "./CheckboxGroup.svelte"
|
||||
import Datepicker from "./Datepicker.svelte"
|
||||
import FormLabelNumberBlock from "./FormLabelNumberBlock.svelte"
|
||||
import type { Writable } from "svelte/store"
|
||||
import Select from "./Select.svelte"
|
||||
|
||||
export let column: FormColumn
|
||||
export let index: number
|
||||
export let columnIndex: number
|
||||
export let formValues: Writable<FormValues>
|
||||
function removeInvalid(e: Event) {
|
||||
let element = e.currentTarget as HTMLElement
|
||||
if (element) element.classList.remove("invalid")
|
||||
}
|
||||
// @ts-ignore
|
||||
const widgets: Widget[] = column.inputWidgets.map((e) => {
|
||||
if (e === "text") {
|
||||
return {
|
||||
text: column.textInput?.map((input) => {
|
||||
return {
|
||||
...input.standardInputProperties,
|
||||
...input,
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
if (e === "number") {
|
||||
return {
|
||||
number: {
|
||||
...column.numberInput.standardInputProperties,
|
||||
},
|
||||
}
|
||||
}
|
||||
if (e === "defaultCalendar") {
|
||||
return {
|
||||
date: {
|
||||
...column.dateInput.standardInputProperties,
|
||||
},
|
||||
}
|
||||
}
|
||||
if (e === "times") {
|
||||
return {
|
||||
times: {
|
||||
...column.timesInput.standardInputProperties,
|
||||
times: column.timesInput.times,
|
||||
},
|
||||
}
|
||||
}
|
||||
if (e === "checkboxGroup") {
|
||||
return {
|
||||
checkboxGroup: {
|
||||
groupTitle: column.checkboxGroupInput.groupTitle,
|
||||
checkboxes: column.checkboxGroupInput.checkboxes.map((checkbox) => {
|
||||
return {
|
||||
...checkbox.standardInputProperties,
|
||||
}
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
if (e === "customCalendar") {
|
||||
return {
|
||||
datepicker: {
|
||||
...column.datePickerInput.standardInputProperties,
|
||||
props: column.datePickerInput.props,
|
||||
},
|
||||
}
|
||||
}
|
||||
if (e === "multiSelect") {
|
||||
return {
|
||||
multiselect: {
|
||||
...column.datePickerInput.standardInputProperties,
|
||||
props: column.datePickerInput.props,
|
||||
},
|
||||
}
|
||||
}
|
||||
if (e == "labelNumber") {
|
||||
return { labelNumber: column.labelNumberInput }
|
||||
}
|
||||
if (e == "timeSelect") {
|
||||
return {
|
||||
timeSelect: {
|
||||
...column?.timeSelect?.standardInputProperties,
|
||||
...column.timeSelect,
|
||||
},
|
||||
}
|
||||
}
|
||||
if (e == "standardSelect") {
|
||||
return {
|
||||
standardSelect: {
|
||||
...column?.standardSelect?.standardInputProperties,
|
||||
...column.standardSelect,
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function getPosition(column: FormColumn, pos: number, i = 0) {
|
||||
let position = 0
|
||||
if (pos == 0) return
|
||||
if (column.inputWidgets.includes("labelNumber")) position++
|
||||
if (pos == 1) return position
|
||||
if (column.inputWidgets.includes("times")) position++
|
||||
if (pos == 2) return position
|
||||
if (column.inputWidgets.includes("timeSelect")) position++
|
||||
if (pos == 3) return position
|
||||
if (column.inputWidgets.includes("standardSelect")) position++
|
||||
if (pos == 4) return position
|
||||
if (column.inputWidgets.includes("defaultCalendar")) position++
|
||||
if (pos == 5) return position
|
||||
if (column.inputWidgets.includes("number")) position++
|
||||
if (pos == 6) return position
|
||||
if (column.inputWidgets.includes("checkboxGroup")) position++
|
||||
if (pos == 7) return position
|
||||
if (column.inputWidgets.includes("customCalendar")) position++
|
||||
if (pos == 8) return position
|
||||
if (column.inputWidgets.includes("multiSelect")) position++
|
||||
return position + i
|
||||
}
|
||||
</script>
|
||||
|
||||
{#each widgets as widget}
|
||||
{#if column?.title?.trim()}
|
||||
<h3>{column?.title ?? ""}</h3>{/if}
|
||||
{#if column?.annotation}
|
||||
<p>{column?.annotation}</p>
|
||||
{/if}
|
||||
{#if widget?.labelNumber}
|
||||
<div class="{`column-${columnIndex} position-${getPosition(column, 0)}`}">
|
||||
<FormLabelNumberBlock widget="{widget.labelNumber}" {formValues} rowIndex="{index}" />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if widget?.times}
|
||||
<div class="column-{columnIndex} position-{getPosition(column, 1)}">
|
||||
<label bind:this="{$formValues[`times_${column.emailTitle ? column.emailTitle + '_' : ''}label`]}">
|
||||
<select
|
||||
id="time-select"
|
||||
bind:this="{$formValues[
|
||||
`times_${column.emailTitle ?? 'Zeiten'}_${widget.times.fieldOrder}_${index}_${
|
||||
widget.times?.emailTitle || columnIndex + 'invalidtimes'
|
||||
}`
|
||||
]}"
|
||||
required="{widget.times.notRequired !== true}"
|
||||
on:change="{removeInvalid}"
|
||||
>
|
||||
<option value="" disabled selected>Bitte Uhrzeit wählen</option>
|
||||
{#each widget?.times.times ?? [] as time}
|
||||
<option value="{time?.from}-{time?.to}">
|
||||
{time?.from} - {time?.to}
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if widget?.timeSelect}
|
||||
<div class="column-{columnIndex} position-{getPosition(column, 2)}">
|
||||
<label bind:this="{$formValues[`select_${column?.emailTitle ? column.emailTitle + '_' : ''}label`]}">
|
||||
<select
|
||||
required="{widget.timeSelect.notRequired !== true}"
|
||||
bind:this="{$formValues[
|
||||
`select_${column?.emailTitle ?? 'Auswahl'}_${widget?.timeSelect?.fieldOrder}_${index}_${
|
||||
widget.timeSelect.emailTitle || columnIndex + 'invalidtime'
|
||||
}`
|
||||
]}"
|
||||
on:change="{removeInvalid}"
|
||||
>
|
||||
<option value="" disabled selected
|
||||
>{widget.timeSelect.textTitle || widget.timeSelect.placeholder || ""}</option
|
||||
>
|
||||
{#each widget.timeSelect.selectEntries as entry}
|
||||
<option value="{entry?.leftSide}-{entry?.rightSide}">
|
||||
{entry?.leftSide}-{entry?.rightSide}
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if widget?.standardSelect}
|
||||
<div class="column-{columnIndex} position-{getPosition(column, 3)}">
|
||||
<label
|
||||
bind:this="{$formValues[`standardSelect_${column?.emailTitle ? column.emailTitle + '_' : ''}label`]}"
|
||||
>
|
||||
<select
|
||||
required="{widget.standardSelect.notRequired !== true}"
|
||||
bind:this="{$formValues[
|
||||
`select_${column?.emailTitle ?? 'Auswahl'}_${widget?.standardSelect?.fieldOrder}_${index}_${
|
||||
widget.standardSelect.emailTitle || columnIndex + 'invalidtime'
|
||||
}`
|
||||
]}"
|
||||
on:change="{removeInvalid}"
|
||||
>
|
||||
{#each widget.standardSelect.selectEntries as entry}
|
||||
<option value="{entry.value}" selected="{entry.defaultValue}">
|
||||
{entry.shownValue}
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
{/if}
|
||||
{#if widget?.date}
|
||||
<div class="column-{columnIndex} position-{getPosition(column, 4)}">
|
||||
<label bind:this="{$formValues[`date_${column.emailTitle ? column.emailTitle + '_' : ''}label`]}">
|
||||
<input
|
||||
class="date"
|
||||
type="date"
|
||||
required="{widget.date.notRequired !== true}"
|
||||
on:change="{removeInvalid}"
|
||||
bind:this="{$formValues[
|
||||
`date_${column?.emailTitle ?? 'Datum'}_${widget.date.fieldOrder}_${index}_${
|
||||
widget.date.emailTitle || columnIndex + 'invaliddate'
|
||||
}`
|
||||
]}"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
{/if}
|
||||
{#if widget?.number}
|
||||
<div class="column-{columnIndex} position-{getPosition(column, 5)}">
|
||||
<label
|
||||
bind:this="{$formValues[
|
||||
`input_${column.emailTitle ? column.emailTitle + '_' : ''}${widget.number.placeholder}_label`
|
||||
]}"
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
step="any"
|
||||
required="{widget.number.notRequired !== true}"
|
||||
on:change="{removeInvalid}"
|
||||
placeholder="{widget.number.placeholder}"
|
||||
bind:this="{$formValues[
|
||||
`input_${column.title ? column.title + '_' : ''}${widget.number.placeholder}_${
|
||||
widget.number.fieldOrder
|
||||
}_${index}_${widget.number.emailTitle || columnIndex + 'invalidnumber'}`
|
||||
]}"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if widget?.checkboxGroup}
|
||||
<div class="column-{columnIndex} position-{getPosition(column, 6)}">
|
||||
<CheckboxGroup
|
||||
checkboxes="{widget.checkboxGroup.checkboxes}"
|
||||
groupTitle="{widget.checkboxGroup.groupTitle}"
|
||||
{formValues}
|
||||
rowNr="{index}"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if widget?.datepicker}
|
||||
<div class="column-{columnIndex} position-{getPosition(column, 7)}">
|
||||
<Datepicker
|
||||
datePickerProps="{widget.datepicker.props}"
|
||||
groupTitle="{widget.datepicker.groupTitle}"
|
||||
{formValues}
|
||||
rowNr="{index}"
|
||||
formCol="{column}"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if widget?.multiselect}
|
||||
<div class="column-{columnIndex} position-{getPosition(column, 8)}">
|
||||
<Select
|
||||
groupTitle="{widget.multiselect.groupTitle}"
|
||||
{formValues}
|
||||
rowNr="{index}"
|
||||
formCol="{column}"
|
||||
options="{widget.multiselect.options}"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{#if widget?.text}
|
||||
{#each widget.text ?? [] as textField, textFieldIndex}
|
||||
<div class="column-{columnIndex} position-{getPosition(column, 8 + textFieldIndex, textFieldIndex)}">
|
||||
<h3 class="textTitle">{textField.textTitle || ""}</h3>
|
||||
<div class="column-{columnIndex} position-{getPosition(column, 8 + textFieldIndex, textFieldIndex)}">
|
||||
{#if textField?.textArea}
|
||||
<label bind:this="{$formValues[`textarea_Nachricht_label`]}">
|
||||
<textarea
|
||||
placeholder="{textField?.placeholder}"
|
||||
required="{textField?.notRequired !== true}"
|
||||
on:change="{removeInvalid}"
|
||||
bind:this="{$formValues[
|
||||
`textarea_Nachricht_${textField.fieldOrder}_${index}_${
|
||||
textField.emailTitle || columnIndex + 'invalidtext' + textFieldIndex
|
||||
}`
|
||||
]}"
|
||||
></textarea>
|
||||
</label>
|
||||
{:else}
|
||||
<label
|
||||
bind:this="{$formValues[
|
||||
`input_${column.title ? column.title + '_' : ''}${textField?.placeholder}_label`
|
||||
]}"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="{textField?.placeholder}"
|
||||
on:change="{removeInvalid}"
|
||||
required="{textField?.notRequired !== true}"
|
||||
bind:this="{$formValues[
|
||||
`${
|
||||
textField?.telValidation
|
||||
? 'Telefon'
|
||||
: textField?.emailValidation
|
||||
? 'Email'
|
||||
: 'input'
|
||||
}_${column.title ? column.title + '_' : ''}${textField?.placeholder}_${
|
||||
textField.fieldOrder
|
||||
}_${index}_${textField.emailTitle || columnIndex + 'invalidtext' + textFieldIndex}`
|
||||
]}"
|
||||
/>
|
||||
</label>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
<style lang="less">
|
||||
@media (max-width: 768px) {
|
||||
.mobile-fields {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.form-field {
|
||||
order: 1;
|
||||
}
|
||||
|
||||
/* Order for first column */
|
||||
.column-0.position-0 {
|
||||
order: 1;
|
||||
}
|
||||
.column-0.position-1 {
|
||||
order: 5;
|
||||
}
|
||||
.column-0.position-2 {
|
||||
order: 9;
|
||||
}
|
||||
.column-0.position-3 {
|
||||
order: 13;
|
||||
}
|
||||
.column-0.position-4 {
|
||||
order: 17;
|
||||
}
|
||||
.column-0.position-5 {
|
||||
order: 21;
|
||||
}
|
||||
.column-0.position-6 {
|
||||
order: 25;
|
||||
}
|
||||
.column-0.position-7 {
|
||||
order: 29;
|
||||
}
|
||||
.column-0.position-8 {
|
||||
order: 33;
|
||||
}
|
||||
.column-0.position-9 {
|
||||
order: 37;
|
||||
}
|
||||
|
||||
/* Order for second column */
|
||||
.column-1.position-0 {
|
||||
order: 2;
|
||||
}
|
||||
.column-1.position-1 {
|
||||
order: 6;
|
||||
}
|
||||
.column-1.position-2 {
|
||||
order: 10;
|
||||
}
|
||||
.column-1.position-3 {
|
||||
order: 14;
|
||||
}
|
||||
.column-1.position-4 {
|
||||
order: 18;
|
||||
}
|
||||
.column-1.position-5 {
|
||||
order: 22;
|
||||
}
|
||||
.column-1.position-6 {
|
||||
order: 26;
|
||||
}
|
||||
.column-1.position-7 {
|
||||
order: 30;
|
||||
}
|
||||
.column-1.position-8 {
|
||||
order: 34;
|
||||
}
|
||||
.column-1.position-9 {
|
||||
order: 38;
|
||||
}
|
||||
|
||||
/* Order for third column */
|
||||
.column-2.position-0 {
|
||||
order: 3;
|
||||
}
|
||||
.column-2.position-1 {
|
||||
order: 7;
|
||||
}
|
||||
.column-2.position-2 {
|
||||
order: 11;
|
||||
}
|
||||
.column-2.position-3 {
|
||||
order: 15;
|
||||
}
|
||||
.column-2.position-4 {
|
||||
order: 19;
|
||||
}
|
||||
.column-2.position-5 {
|
||||
order: 23;
|
||||
}
|
||||
.column-2.position-6 {
|
||||
order: 27;
|
||||
}
|
||||
.column-2.position-7 {
|
||||
order: 31;
|
||||
}
|
||||
.column-2.position-8 {
|
||||
order: 35;
|
||||
}
|
||||
.column-2.position-9 {
|
||||
order: 39;
|
||||
}
|
||||
|
||||
/* Order for fourth column */
|
||||
.column-3.position-0 {
|
||||
order: 4;
|
||||
}
|
||||
.column-3.position-1 {
|
||||
order: 8;
|
||||
}
|
||||
.column-3.position-2 {
|
||||
order: 12;
|
||||
}
|
||||
.column-3.position-3 {
|
||||
order: 16;
|
||||
}
|
||||
.column-3.position-4 {
|
||||
order: 20;
|
||||
}
|
||||
.column-3.position-5 {
|
||||
order: 24;
|
||||
}
|
||||
.column-3.position-6 {
|
||||
order: 28;
|
||||
}
|
||||
.column-3.position-7 {
|
||||
order: 32;
|
||||
}
|
||||
.column-3.position-8 {
|
||||
order: 36;
|
||||
}
|
||||
.column-3.position-9 {
|
||||
order: 40;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,56 @@
|
||||
<script lang="ts">
|
||||
import type { Writable } from "svelte/store"
|
||||
import FormColumn from "./FormColumn.svelte"
|
||||
import { onMount } from "svelte"
|
||||
|
||||
export let formRow: DBFormRow
|
||||
export let index: number
|
||||
export let formValues: Writable<FormValues>
|
||||
let innerWidth = typeof window !== "undefined" ? window?.innerWidth || 0 : 0
|
||||
if (typeof window !== "undefined") {
|
||||
onMount(() => {
|
||||
const handleResize = () => {
|
||||
innerWidth = window.innerWidth
|
||||
}
|
||||
|
||||
window.addEventListener("resize", handleResize)
|
||||
|
||||
// Cleanup function
|
||||
return () => {
|
||||
window.removeEventListener("resize", handleResize)
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="form-row">
|
||||
<h3 style="margin-bottom: -0.5rem;">
|
||||
{#if formRow.title}{formRow.title || ""}{/if}
|
||||
</h3>
|
||||
{#if innerWidth < 768}
|
||||
<div class="form-cols mobile-fields">
|
||||
{#each formRow.columns as column, columnIndex}
|
||||
<FormColumn {column} {index} {columnIndex} {formValues} />
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{#if innerWidth >= 768}
|
||||
<div class="form-cols desktop-fields">
|
||||
{#each formRow.columns as column, columnIndex}
|
||||
<div class="form-column">
|
||||
<FormColumn {column} {index} {columnIndex} {formValues} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>{/if}
|
||||
</div>
|
||||
|
||||
<style lang="less">
|
||||
.mobile-fields {
|
||||
display: flex !important;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.desktop-fields {
|
||||
display: flex !important;
|
||||
}
|
||||
</style>
|
||||
@@ -1,15 +1,15 @@
|
||||
<script lang="ts">
|
||||
import type { Writable } from "svelte/store"
|
||||
export let column: FormColumn
|
||||
export let widget: LabelNumberInput[]
|
||||
export let formValues: Writable<FormValues>
|
||||
|
||||
export let rowIndex: number
|
||||
let blockContainer: HTMLDivElement
|
||||
$formValues["blockGroups"] = new Set(column.labelNumber.map((e) => e.group))
|
||||
$formValues["blockGroups"] = new Set(widget.map((e) => e.group))
|
||||
</script>
|
||||
|
||||
<div class="blockContainer" bind:this="{blockContainer}">
|
||||
{#each column.labelNumber as outerblock, i}
|
||||
{#each widget as outerblock, i}
|
||||
<div class="{`block`}" bind:this="{$formValues['blockGroups'][i]}">
|
||||
<h3>{outerblock.title}</h3>
|
||||
<div class="innterBlockContainer">
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
<script lang="ts">
|
||||
import Form from "./Form.svelte"
|
||||
import MobileForm from "./MobileForm.svelte"
|
||||
import { writable } from "svelte/store"
|
||||
import { validateFields } from "../../../functions/validateFields"
|
||||
import { sendForm } from "../../../functions/sendForm"
|
||||
import { navigate } from "svelte-routing"
|
||||
import { onMount } from "svelte"
|
||||
import { validateFields } from "../../../functions/pagebuilder/components/form/validateFields"
|
||||
import { sendForm } from "../../../functions/pagebuilder/components/form/sendForm"
|
||||
|
||||
export let col: Column
|
||||
export let siteId: string
|
||||
export let rowNr: number
|
||||
export let row: Row
|
||||
export let rows: Row[]
|
||||
import { modules } from "../../../store"
|
||||
import FormColumnWrapper from "./FormColumnWrapper.svelte"
|
||||
import { navigateWrapper } from "../../../functions/utils"
|
||||
|
||||
export let col: { contentType: "form"; moduleImport: string }
|
||||
|
||||
export let pageId: string
|
||||
// @ts-ignore
|
||||
let form: DBFormObj = $modules[col?.moduleImport]?.form
|
||||
$: form = $modules[col?.moduleImport]?.form
|
||||
let formSend = false
|
||||
let formValues = writable<FormValues>({})
|
||||
|
||||
@@ -20,8 +20,9 @@
|
||||
const values: Array<ValueEntry> = Object.entries($formValues).map((entry: ObjectEntry) => {
|
||||
const key: string = entry[0]
|
||||
const value: CustomHTMLElement = entry[1]
|
||||
|
||||
if (!key.includes("numberLabel")) {
|
||||
return [key, [value.checked || value.value, value.required]] as ValueEntry
|
||||
return [key, [value.type == "checkbox" ? value.checked : value.value, value.required]] as ValueEntry
|
||||
} else {
|
||||
return [key, [value.value, value, value.getAttribute("name"), value.required]] as ValueEntry
|
||||
}
|
||||
@@ -30,7 +31,7 @@
|
||||
const fields: Array<ValueEntry> = values.filter((entry: ValueEntry) => !entry[0].includes("label"))
|
||||
const validation = validateFields(fields)
|
||||
if (validation.length) {
|
||||
validation.forEach((error) => {
|
||||
validation.forEach((error: string[]) => {
|
||||
// @ts-ignore
|
||||
if (error[0].includes("block")) {
|
||||
// @ts-ignore
|
||||
@@ -58,344 +59,75 @@
|
||||
formObj[entry[0]] = entry[1][0]
|
||||
}
|
||||
})
|
||||
let form: any
|
||||
row.column.forEach((col) => {
|
||||
if (col.contentType == "form") form = col
|
||||
})
|
||||
if (!form) return
|
||||
formObj["formRows"] = form.formRows.map((r: FormRow) => r.rowName)
|
||||
formObj["formTitle"] = form.formEmailTitle
|
||||
if (!form) {
|
||||
return
|
||||
}
|
||||
formObj["formRows"] = form.rows.map((r) => r.emailTitle)
|
||||
formObj["formTitle"] = form.emailSubject
|
||||
formSend = true
|
||||
const hny = document.getElementById("hny") as HTMLInputElement
|
||||
if (hny) formObj["honey"] = hny.checked
|
||||
sendForm(formObj)
|
||||
}
|
||||
}
|
||||
|
||||
let innerWidth = typeof window !== "undefined" ? window?.innerWidth || 0 : 0
|
||||
if (typeof window !== "undefined") {
|
||||
onMount(() => {
|
||||
const handleResize = () => {
|
||||
innerWidth = window.innerWidth
|
||||
}
|
||||
|
||||
window.addEventListener("resize", handleResize)
|
||||
|
||||
// Cleanup function
|
||||
return () => {
|
||||
window.removeEventListener("resize", handleResize)
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if formSend}
|
||||
<div class="success-message">
|
||||
<h1>Formular erfolgreich gesendet!</h1>
|
||||
<p>Vielen Dank für Ihre Anfrage. Wir werden uns in Kürze bei Ihnen melden.</p>
|
||||
</div>
|
||||
{:else}
|
||||
<form
|
||||
class="form-rows"
|
||||
on:submit="{(e) => {
|
||||
e.preventDefault()
|
||||
submitForm()
|
||||
}}"
|
||||
>
|
||||
{#each col.formRows ?? [] as formRow, i}
|
||||
{#if innerWidth < 768}
|
||||
<MobileForm {formRow} {formValues} index="{i}" />
|
||||
{:else}
|
||||
<Form {formRow} {formValues} index="{i}" />
|
||||
{/if}
|
||||
{/each}
|
||||
<div class="row additional" style="padding: 0px;">
|
||||
<div class="data-protection">
|
||||
<label bind:this="{$formValues[`agreement_label`]}">
|
||||
<input
|
||||
required="{true}"
|
||||
class="checkit"
|
||||
type="checkbox"
|
||||
on:change="{(e) => {
|
||||
let element = e.currentTarget
|
||||
element.classList.remove('invalid')
|
||||
}}"
|
||||
bind:this="{$formValues['agreement']}"
|
||||
/>
|
||||
<span class="checkit-span"></span>
|
||||
</label>
|
||||
|
||||
<div class="datasec">
|
||||
<button on:click|preventDefault="{() => navigate('/datenschutz')}" class="link">
|
||||
Datenschutz
|
||||
</button>
|
||||
akzeptieren
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input
|
||||
type="checkbox"
|
||||
name="contact_me_by_fax_only"
|
||||
id="hny"
|
||||
value="1"
|
||||
style="display:none !important"
|
||||
tabindex="-1"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<button class="submit-request" type="submit">Anfrage absenden</button>
|
||||
<div class="my-form">
|
||||
{#if formSend}
|
||||
<div class="success-message">
|
||||
<h1>Formular erfolgreich gesendet!</h1>
|
||||
<p>Vielen Dank für Ihre Anfrage. Wir werden uns in Kürze bei Ihnen melden.</p>
|
||||
</div>
|
||||
</form>
|
||||
{/if}
|
||||
{:else if form}
|
||||
<form
|
||||
class="form-rows"
|
||||
novalidate
|
||||
on:submit="{(e) => {
|
||||
e.preventDefault()
|
||||
submitForm()
|
||||
}}"
|
||||
>
|
||||
{#each form.rows ?? [] as formRow, i}
|
||||
<FormColumnWrapper {formRow} {formValues} index="{i}" />
|
||||
{/each}
|
||||
<div class="row additional" style="padding: 0px;">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="contact_me_by_fax_only"
|
||||
id="hny"
|
||||
value="1"
|
||||
style="position: absolute; left: -9999px;"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<div class="data-protection">
|
||||
<label bind:this="{$formValues[`agreement_label`]}">
|
||||
<input
|
||||
class="checkit"
|
||||
required
|
||||
type="checkbox"
|
||||
on:change="{(e) => {
|
||||
let element = e.currentTarget
|
||||
element.classList.remove('invalid')
|
||||
}}"
|
||||
bind:this="{$formValues['agreement']}"
|
||||
/>
|
||||
<span class="checkit-span"></span>
|
||||
</label>
|
||||
|
||||
<div class="datasec">
|
||||
<button on:click|preventDefault="{() => navigateWrapper('/datenschutz')}" class="link">
|
||||
Datenschutz
|
||||
</button>
|
||||
akzeptieren
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="submit-request" type="submit">{form.sendFormBtnText || "Formular absenden"}</button>
|
||||
</div>
|
||||
</form>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="less" global>
|
||||
@import "../../../assets/css/variables.less";
|
||||
|
||||
input,
|
||||
select,
|
||||
textarea,
|
||||
.data-protection {
|
||||
margin: 5px 0px;
|
||||
box-shadow: 0 0 25px 10px var(--opposite-bg-color-5);
|
||||
}
|
||||
|
||||
.success-message {
|
||||
h1 {
|
||||
color: var(--heading-font-color);
|
||||
font-size: 36px;
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
.invalidBlocks {
|
||||
border: 2px solid rgb(255, 0, 0) !important;
|
||||
position: relative;
|
||||
&::after {
|
||||
font-size: 0.9rem !important;
|
||||
color: red !important;
|
||||
position: absolute;
|
||||
bottom: 2px;
|
||||
content: "Bitte wähle entweder eine Kartenanzahl oder einen Wunschbetrag aus.";
|
||||
}
|
||||
}
|
||||
|
||||
.border-red {
|
||||
border-color: red !important;
|
||||
}
|
||||
|
||||
.no-margin {
|
||||
margin-top: 15px !important;
|
||||
}
|
||||
|
||||
.invalid {
|
||||
border: 2px solid red !important;
|
||||
}
|
||||
.error-message {
|
||||
font-size: 0.9rem !important;
|
||||
color: red !important;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
input[type="number"]::-webkit-inner-spin-button,
|
||||
input[type="number"]::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
input[type="number"] {
|
||||
-moz-appearance: textfield;
|
||||
appearance: textfield;
|
||||
}
|
||||
|
||||
.checkit {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.checkit-span {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
border: 1px solid grey;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
[type="checkbox"]:checked + span:before {
|
||||
content: "\2714";
|
||||
position: absolute;
|
||||
transform-origin: bottom;
|
||||
}
|
||||
|
||||
.form-rows {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
width: 100%;
|
||||
h3 {
|
||||
font-weight: bold !important;
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
gap: 1.5rem;
|
||||
|
||||
.date {
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
label {
|
||||
width: 100% !important;
|
||||
font-size: inherit;
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
width: 100%;
|
||||
font-size: inherit;
|
||||
}
|
||||
}
|
||||
.form-cols {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.form-column {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
|
||||
width: 100%;
|
||||
gap: 1rem;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--hover-color);
|
||||
}
|
||||
|
||||
input,
|
||||
select,
|
||||
textarea,
|
||||
.data-protection {
|
||||
padding: 10px 20px;
|
||||
border: 0px solid var(--opposite-bg-color);
|
||||
border-bottom: 3px solid var(--heading-font-color);
|
||||
outline: 0px solid var(--opposite-bg-color);
|
||||
color: var(--opposite-bg-color);
|
||||
background-color: var(--background-color);
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.data-protection {
|
||||
display: flex;
|
||||
|
||||
flex-wrap: nowrap;
|
||||
gap: 5px !important;
|
||||
justify-content: start;
|
||||
flex-direction: row !important;
|
||||
}
|
||||
|
||||
input[type="date"] {
|
||||
font-family: inherit;
|
||||
width: 100% !important;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
select {
|
||||
padding: 10px 20px;
|
||||
border: 0;
|
||||
border-bottom: 3px solid var(--heading-font-color);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
select:focus {
|
||||
border-bottom-color: var(--heading-font-color);
|
||||
}
|
||||
|
||||
#time-select {
|
||||
appearance: none;
|
||||
background-image: url("../../../../../media/clock.svg");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 20px center;
|
||||
background-size: 18px;
|
||||
|
||||
option {
|
||||
padding: 10px 20px;
|
||||
background-color: var(--background-color);
|
||||
}
|
||||
}
|
||||
@media @mobile {
|
||||
.date {
|
||||
width: 100vw !important;
|
||||
}
|
||||
}
|
||||
@media @tablet {
|
||||
.date {
|
||||
width: 100% !important;
|
||||
}
|
||||
.form-cols {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
}
|
||||
i .max-width {
|
||||
max-width: @body-maxwidth !important;
|
||||
}
|
||||
|
||||
.datasec {
|
||||
display: flex;
|
||||
align-items: center !important;
|
||||
.link {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: flex-end !important;
|
||||
text-decoration: underline;
|
||||
margin-right: 3px;
|
||||
color: rgb(14, 91, 146);
|
||||
}
|
||||
}
|
||||
|
||||
.additional {
|
||||
display: flex;
|
||||
@media @mobile {
|
||||
flex-direction: column !important;
|
||||
}
|
||||
@media @tablet {
|
||||
flex-direction: row !important;
|
||||
}
|
||||
gap: 2.5rem;
|
||||
div {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
}
|
||||
.submit-request {
|
||||
flex: 1;
|
||||
box-shadow: 0 0 25px 10px var(--opposite-bg-color-5);
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
background-color: var(--heading-font-color);
|
||||
color: var(--background-color);
|
||||
padding: 10px 20px;
|
||||
}
|
||||
}
|
||||
@import "../../../assets/css/components/pagebuilder/form/form.less";
|
||||
</style>
|
||||
|
||||
@@ -1,351 +0,0 @@
|
||||
<script lang="ts">
|
||||
import FormLabelNumberBlock from "./FormLabelNumberBlock.svelte"
|
||||
import type { Writable } from "svelte/store"
|
||||
import CheckboxGroup from "./CheckboxGroup.svelte"
|
||||
import Datepicker from "./Datepicker.svelte"
|
||||
import Select from "./Select.svelte"
|
||||
export let formRow: FormRow
|
||||
export let formValues: Writable<FormValues>
|
||||
export let index: number
|
||||
function removeInvalid(e: Event) {
|
||||
let element = e.currentTarget as HTMLElement
|
||||
if (element) element.classList.remove("invalid")
|
||||
}
|
||||
|
||||
function getPosition(column: FormColumn, pos: number, i = 0) {
|
||||
let position = 0
|
||||
if (pos == 0) return
|
||||
if (column.showLabelNumber) position++
|
||||
if (pos == 1) return position
|
||||
if (column.showTimes) position++
|
||||
if (pos == 2) return position
|
||||
|
||||
if (column.showSelect) position++
|
||||
if (pos == 3) return position
|
||||
if (column.showDate) position++
|
||||
if (pos == 4) return position
|
||||
if (column.showNumber) position++
|
||||
return position + i
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="form-row">
|
||||
<h3 style="margin-bottom: -0.5rem;">
|
||||
{#if formRow.showRowName}{formRow.rowName || ""}{/if}
|
||||
</h3>
|
||||
<div class="form-cols mobile-fields">
|
||||
{#each formRow.columns as column, columnIndex}
|
||||
<h3>{column?.title ?? ""}</h3>
|
||||
{#if column?.annotation}
|
||||
<p>{column?.annotation}</p>
|
||||
{/if}
|
||||
{#if column.showLabelNumber}
|
||||
<div class="{`column-${columnIndex} position-${getPosition(column, 0)}`}">
|
||||
<FormLabelNumberBlock {column} {formValues} rowIndex="{index}" />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if column?.showTimes}
|
||||
<div class="column-{columnIndex} position-{getPosition(column, 1)}">
|
||||
<label bind:this="{$formValues[`times_${column?.title ? column.title + '_' : ''}label`]}">
|
||||
<select
|
||||
id="time-select"
|
||||
bind:this="{$formValues[
|
||||
`times_${column?.title ?? 'Zeiten'}_${column.timesfieldOrder}_${index}_${
|
||||
column.emailNameTimes || columnIndex + 'invalidtimes'
|
||||
}`
|
||||
]}"
|
||||
required="{column?.timeNotRequired !== true}"
|
||||
on:change="{removeInvalid}"
|
||||
>
|
||||
<option value="" disabled selected>Bitte Uhrzeit wählen</option>
|
||||
{#each column?.times ?? [] as time}
|
||||
<option value="{time?.timeFrom}-{time?.timeTo}">
|
||||
{time?.timeFrom} - {time?.timeTo}
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if column?.showSelect}
|
||||
<div class="column-{columnIndex} position-{getPosition(column, 2)}">
|
||||
<label bind:this="{$formValues[`select_${column?.title ? column.title + '_' : ''}label`]}">
|
||||
<select
|
||||
required="{column?.dateNotRequired !== true}"
|
||||
bind:this="{$formValues[
|
||||
`select_${column?.title ?? 'Auswahl'}_${column.datefieldOrder}_${index}_${
|
||||
column?.emailNameTime || columnIndex + 'invalidtime'
|
||||
}`
|
||||
]}"
|
||||
on:change="{removeInvalid}"
|
||||
>
|
||||
<option value="" disabled selected>{column.selectTitle}</option>
|
||||
{#each column?.selectEntries as entry}
|
||||
<option value="{entry?.leftSide}-{entry?.rightSide}">
|
||||
{entry?.leftSide}-{entry?.rightSide}
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if column.showDate}
|
||||
<div class="column-{columnIndex} position-{getPosition(column, 3)}">
|
||||
<label bind:this="{$formValues[`date_${column?.title ? column.title + '_' : ''}label`]}">
|
||||
<input
|
||||
class="date"
|
||||
type="date"
|
||||
required="{column?.dateNotRequired !== true}"
|
||||
on:change="{removeInvalid}"
|
||||
bind:this="{$formValues[
|
||||
`date_${column?.title ?? 'Datum'}_${column.datefieldOrder}_${index}_${
|
||||
column?.emailNameDate || columnIndex + 'invaliddate'
|
||||
}`
|
||||
]}"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
{/if}
|
||||
{#if column.showNumber}
|
||||
<div class=" column-{columnIndex} position-{getPosition(column, 4)}">
|
||||
<label
|
||||
bind:this="{$formValues[
|
||||
`input_${column.title ? column.title + '_' : ''}${column?.numberPlaceholder}_label`
|
||||
]}"
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
step="any"
|
||||
required="{column?.numberNotRequired !== true}"
|
||||
on:change="{removeInvalid}"
|
||||
placeholder="{column?.numberPlaceholder}"
|
||||
bind:this="{$formValues[
|
||||
`input_${column.title ? column.title + '_' : ''}${column?.numberPlaceholder}_${
|
||||
column.numberfieldOrder
|
||||
}_${index}_${column?.emailNameNumber || columnIndex + 'invalidnumber'}`
|
||||
]}"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
{/if}
|
||||
{#if column.showCheckboxGroup}
|
||||
<CheckboxGroup
|
||||
checkboxes="{column.checkboxes}"
|
||||
groupTitle="{column.groupTitle}"
|
||||
{formValues}
|
||||
rowNr="{index}"
|
||||
/>
|
||||
{/if}
|
||||
{#if column.showDatePicker}
|
||||
<Datepicker
|
||||
datePickerProps="{column.datePickerProps}"
|
||||
groupTitle="{column.groupTitle}"
|
||||
{formValues}
|
||||
rowNr="{index}"
|
||||
formCol="{column}"
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if column.showMultiSelect}
|
||||
<Select
|
||||
groupTitle="{column.groupTitle}"
|
||||
{formValues}
|
||||
rowNr="{index}"
|
||||
formCol="{column}"
|
||||
options="{column.multiSelectOptions}"
|
||||
/>
|
||||
{/if}
|
||||
{#each column.text ?? [] as textField, textFieldIndex}
|
||||
<div class="column-{columnIndex} position-{getPosition(column, 5 + textFieldIndex, textFieldIndex)}">
|
||||
<h3 class="textTitle">{textField.textTitle || ""}</h3>
|
||||
<div
|
||||
class="column-{columnIndex} position-{getPosition(column, 5 + textFieldIndex, textFieldIndex)}"
|
||||
>
|
||||
{#if textField?.textArea}
|
||||
<label bind:this="{$formValues[`textarea_Nachricht_label`]}">
|
||||
<textarea
|
||||
placeholder="{textField?.textPlaceholder}"
|
||||
required="{textField?.notRequired !== true}"
|
||||
on:change="{removeInvalid}"
|
||||
bind:this="{$formValues[
|
||||
`textarea_Nachricht_${textField.textfieldOrder}_${index}_${
|
||||
textField.emailName || columnIndex + 'invalidtext' + textFieldIndex
|
||||
}`
|
||||
]}"
|
||||
></textarea>
|
||||
</label>
|
||||
{:else}
|
||||
<label
|
||||
bind:this="{$formValues[
|
||||
`input_${column.title ? column.title + '_' : ''}${textField?.textPlaceholder}_label`
|
||||
]}"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="{textField?.textPlaceholder}"
|
||||
on:change="{removeInvalid}"
|
||||
required="{textField?.notRequired !== true}"
|
||||
bind:this="{$formValues[
|
||||
`${
|
||||
textField?.telValidation
|
||||
? 'Telefon'
|
||||
: textField?.emailValidation
|
||||
? 'Email'
|
||||
: 'input'
|
||||
}_${column.title ? column.title + '_' : ''}${textField?.textPlaceholder}_${
|
||||
textField.textfieldOrder
|
||||
}_${index}_${
|
||||
textField.emailName || columnIndex + 'invalidtext' + textFieldIndex
|
||||
}`
|
||||
]}"
|
||||
/>
|
||||
</label>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="less">
|
||||
.mobile-fields {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.form-field {
|
||||
order: 1;
|
||||
}
|
||||
|
||||
/* Order for first column */
|
||||
.column-0.position-0 {
|
||||
order: 1;
|
||||
}
|
||||
.column-0.position-1 {
|
||||
order: 5;
|
||||
}
|
||||
.column-0.position-2 {
|
||||
order: 9;
|
||||
}
|
||||
.column-0.position-3 {
|
||||
order: 13;
|
||||
}
|
||||
.column-0.position-4 {
|
||||
order: 17;
|
||||
}
|
||||
.column-0.position-5 {
|
||||
order: 21;
|
||||
}
|
||||
.column-0.position-6 {
|
||||
order: 25;
|
||||
}
|
||||
.column-0.position-7 {
|
||||
order: 29;
|
||||
}
|
||||
.column-0.position-8 {
|
||||
order: 33;
|
||||
}
|
||||
.column-0.position-9 {
|
||||
order: 37;
|
||||
}
|
||||
|
||||
/* Order for second column */
|
||||
.column-1.position-0 {
|
||||
order: 2;
|
||||
}
|
||||
.column-1.position-1 {
|
||||
order: 6;
|
||||
}
|
||||
.column-1.position-2 {
|
||||
order: 10;
|
||||
}
|
||||
.column-1.position-3 {
|
||||
order: 14;
|
||||
}
|
||||
.column-1.position-4 {
|
||||
order: 18;
|
||||
}
|
||||
.column-1.position-5 {
|
||||
order: 22;
|
||||
}
|
||||
.column-1.position-6 {
|
||||
order: 26;
|
||||
}
|
||||
.column-1.position-7 {
|
||||
order: 30;
|
||||
}
|
||||
.column-1.position-8 {
|
||||
order: 34;
|
||||
}
|
||||
.column-1.position-9 {
|
||||
order: 38;
|
||||
}
|
||||
|
||||
/* Order for third column */
|
||||
.column-2.position-0 {
|
||||
order: 3;
|
||||
}
|
||||
.column-2.position-1 {
|
||||
order: 7;
|
||||
}
|
||||
.column-2.position-2 {
|
||||
order: 11;
|
||||
}
|
||||
.column-2.position-3 {
|
||||
order: 15;
|
||||
}
|
||||
.column-2.position-4 {
|
||||
order: 19;
|
||||
}
|
||||
.column-2.position-5 {
|
||||
order: 23;
|
||||
}
|
||||
.column-2.position-6 {
|
||||
order: 27;
|
||||
}
|
||||
.column-2.position-7 {
|
||||
order: 31;
|
||||
}
|
||||
.column-2.position-8 {
|
||||
order: 35;
|
||||
}
|
||||
.column-2.position-9 {
|
||||
order: 39;
|
||||
}
|
||||
|
||||
/* Order for fourth column */
|
||||
.column-3.position-0 {
|
||||
order: 4;
|
||||
}
|
||||
.column-3.position-1 {
|
||||
order: 8;
|
||||
}
|
||||
.column-3.position-2 {
|
||||
order: 12;
|
||||
}
|
||||
.column-3.position-3 {
|
||||
order: 16;
|
||||
}
|
||||
.column-3.position-4 {
|
||||
order: 20;
|
||||
}
|
||||
.column-3.position-5 {
|
||||
order: 24;
|
||||
}
|
||||
.column-3.position-6 {
|
||||
order: 28;
|
||||
}
|
||||
.column-3.position-7 {
|
||||
order: 32;
|
||||
}
|
||||
.column-3.position-8 {
|
||||
order: 36;
|
||||
}
|
||||
.column-3.position-9 {
|
||||
order: 40;
|
||||
}
|
||||
</style>
|
||||
@@ -3,7 +3,7 @@
|
||||
import { onMount } from "svelte"
|
||||
import type { Writable } from "svelte/store"
|
||||
|
||||
export let options: MultiSelectOptions[] = []
|
||||
export let options: { name: string }[] = []
|
||||
export let groupTitle: string
|
||||
|
||||
export let formValues: Writable<FormValues>
|
||||
@@ -19,7 +19,7 @@
|
||||
// @ts-ignore
|
||||
$formValues[`selectMultiple_${groupTitle}_${rowNr}_${formCol.multiSelectEmailTitle}`] = {
|
||||
value: value.toString(),
|
||||
required: !formCol.multiSelectNotRequired,
|
||||
required: !formCol?.multiSelectInput?.standardInputProperties?.notRequired,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,5 +80,69 @@
|
||||
|
||||
<style lang="less" global>
|
||||
@import "../../../assets/css/variables.less";
|
||||
@import "../../../assets/css/svelte-select.less";
|
||||
.svelte-select {
|
||||
height: 55px !important;
|
||||
margin-bottom: 20px;
|
||||
|
||||
margin: 5px 0px !important;
|
||||
box-shadow: 0 0 25px 10px rgba(0, 0, 0, 0.05) !important;
|
||||
padding: 10px 20px !important;
|
||||
border: 0px solid black !important;
|
||||
border-bottom: 3px solid @main-color !important;
|
||||
outline: 0px solid black !important;
|
||||
color: black !important;
|
||||
background-color: @background-color !important;
|
||||
resize: none !important;
|
||||
.value-container {
|
||||
input {
|
||||
padding-top: 20px;
|
||||
}
|
||||
.selected-item {
|
||||
padding-top: 19px;
|
||||
}
|
||||
}
|
||||
|
||||
.multi-item {
|
||||
background-color: rgba(0, 0, 0, 0.1) !important;
|
||||
border-radius: 3px !important;
|
||||
}
|
||||
|
||||
.selected-item {
|
||||
margin-left: 3px;
|
||||
height: auto;
|
||||
line-height: initial;
|
||||
}
|
||||
&.focused {
|
||||
border-color: black !important;
|
||||
}
|
||||
|
||||
input {
|
||||
height: 100%;
|
||||
font-size: 1rem !important;
|
||||
}
|
||||
|
||||
.list-item {
|
||||
.item {
|
||||
&.active {
|
||||
background-color: transparent !important;
|
||||
color: black !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.indicators {
|
||||
color: #ccc;
|
||||
|
||||
& > * {
|
||||
border-left: 1px solid #ccc !important;
|
||||
height: 40px !important;
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
.clear-select {
|
||||
color: @hover-color !important;
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
13
frontend/src/lib/functions/eventBus.ts
Normal file
13
frontend/src/lib/functions/eventBus.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
const eventCallbacks: { [key: string]: any } = {}
|
||||
|
||||
export const registerEventCallback = (event: string | number, id: string | number, callback: any) => {
|
||||
if (!eventCallbacks[event]) eventCallbacks[event] = {}
|
||||
eventCallbacks[event][id] = callback
|
||||
}
|
||||
|
||||
export const unregisterEventCallback = (event: string | number, id: string | number) => {
|
||||
delete eventCallbacks[event][id]
|
||||
}
|
||||
export const getEventCallbacks = (event: string) => {
|
||||
return eventCallbacks[event]
|
||||
}
|
||||
16
frontend/src/lib/functions/fetch/loadContentAndSetStores.ts
Normal file
16
frontend/src/lib/functions/fetch/loadContentAndSetStores.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { api } from "../../../api"
|
||||
import { pages, content, rerender } from "../../store"
|
||||
export async function loadContentAndSetStores(): Promise<null> {
|
||||
const contentRes = await api<Content[]>("content", {})
|
||||
const contentStore: { [id: string]: Content } = {}
|
||||
const pagesStore: { [path: string]: Content } = {}
|
||||
contentRes.data.forEach((e: Content) => {
|
||||
contentStore[e.id] = e
|
||||
if (e.type === "page") {
|
||||
pagesStore[e.path] = e
|
||||
}
|
||||
})
|
||||
content.set(contentStore)
|
||||
pages.set(pagesStore)
|
||||
return null
|
||||
}
|
||||
12
frontend/src/lib/functions/fetch/loadLibraryAndSetStores.ts
Normal file
12
frontend/src/lib/functions/fetch/loadLibraryAndSetStores.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { api } from "../../../api"
|
||||
import { mediaLibrary } from "../../store"
|
||||
|
||||
export async function loadLibraryAndSetStore(): Promise<null> {
|
||||
const libraryRes = await api<MediaLibrary[]>("medialib", {})
|
||||
const libStore: { [id: string]: MediaLibrary } = {}
|
||||
libraryRes.data.forEach((el: MediaLibrary) => {
|
||||
libStore[el.id] = el
|
||||
})
|
||||
mediaLibrary.set(libStore)
|
||||
return null
|
||||
}
|
||||
11
frontend/src/lib/functions/fetch/loadModules.ts
Normal file
11
frontend/src/lib/functions/fetch/loadModules.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { api } from "../../../api"
|
||||
import { modules } from "../../store"
|
||||
export async function loadModulesAndSetStore(): Promise<{ [id: string]: Module }> {
|
||||
const module = await api<Module[]>("module", {})
|
||||
const moduleStore: { [id: string]: Module } = {}
|
||||
module.data.forEach((e: Module) => {
|
||||
moduleStore[e.id] = e
|
||||
})
|
||||
modules.set(moduleStore)
|
||||
return moduleStore
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { api } from "../../../api"
|
||||
import { navigation, serviceNavigation } from "../../store"
|
||||
export async function loadNavigationAndSetStores(): Promise<null> {
|
||||
const navigations = await api<Navigation[]>("navigation", {})
|
||||
let navigationStore: NavElement[] = []
|
||||
let serviceNavigationStore: NavElement[] = []
|
||||
navigations.data.forEach((nav: Navigation) => {
|
||||
if (nav.tree == 0) navigationStore = nav.elements
|
||||
else if (nav.tree == 1) serviceNavigationStore = nav.elements
|
||||
})
|
||||
navigation.set(navigationStore)
|
||||
serviceNavigation.set(serviceNavigationStore)
|
||||
|
||||
return null
|
||||
}
|
||||
@@ -20,7 +20,10 @@ export function validateFields(fieldsArray: ValueEntry[]): (string | (() => void
|
||||
}
|
||||
|
||||
fieldsArray.forEach(([field, value]) => {
|
||||
|
||||
|
||||
if (field === "blockGroups" || (typeof field === "string" && field.includes("numberLabel"))) {
|
||||
// number label benötigt gesonderte Validierung
|
||||
if (!field.includes("numberLabel")) return
|
||||
// @ts-ignore
|
||||
let [elementValue, element, group, boolean] = value
|
||||
|
||||
14
frontend/src/lib/functions/utils.ts
Normal file
14
frontend/src/lib/functions/utils.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { getEventCallbacks } from "./eventBus"
|
||||
import { navigate } from "../../../../vendor/svelte-routing"
|
||||
|
||||
export function navigateWrapper(path: string, props: any = null) {
|
||||
if (getEventCallbacks("navigate")) {
|
||||
let callbacks = Object.values(getEventCallbacks("navigate"))
|
||||
for (let i = 0; i < callbacks?.length; i++) {
|
||||
// do not navigate if false is returned
|
||||
if ((callbacks[i] as Function)(path, props) === false) return
|
||||
}
|
||||
}
|
||||
if (props) navigate(path, props)
|
||||
else navigate(path)
|
||||
}
|
||||
@@ -8,4 +8,11 @@ const initLoc = {
|
||||
pop: false,
|
||||
categoryPath: "",
|
||||
}
|
||||
export const location = writable(initLoc)
|
||||
export const location = writable(initLoc)
|
||||
export let content = writable<{ [id: string]: Content }>({})
|
||||
export let pages = writable<{ [path: string]: Content }>({})
|
||||
export let modules = writable<{ [id: string]: Module }>({})
|
||||
export let mediaLibrary = writable<{ [id: string]: MediaLibrary }>({})
|
||||
export let navigation = writable<NavElement[]>([])
|
||||
export let serviceNavigation = writable<NavElement[]>([])
|
||||
export let rerender = writable(false)
|
||||
|
||||
56
frontend/src/routes/NotFound.svelte
Normal file
56
frontend/src/routes/NotFound.svelte
Normal file
@@ -0,0 +1,56 @@
|
||||
<script>
|
||||
import { navigateWrapper } from "../lib/functions/utils"
|
||||
</script>
|
||||
|
||||
<div class="not-found">
|
||||
<div class="content">
|
||||
<h1>404</h1>
|
||||
<h2>Seite nicht gefunden</h2>
|
||||
<p>
|
||||
Die gesuchte Seite wurde möglicherweise entfernt, ihr Name wurde geändert oder sie ist vorübergehend nicht
|
||||
verfügbar.
|
||||
</p>
|
||||
<button on:click="{() => navigateWrapper('/')}" class="back-home">Zurück zur Startseite</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="less">
|
||||
@import "../lib/assets/css/variables.less";
|
||||
|
||||
.not-found {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
|
||||
.content {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
|
||||
h1 {
|
||||
font-size: 6rem;
|
||||
color: @main-color;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.back-home {
|
||||
text-decoration: none;
|
||||
border: 1px solid black;
|
||||
padding: 0.5rem 1rem;
|
||||
transition:
|
||||
background-color 0.2s,
|
||||
color 0.2s;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
119
frontend/src/routes/Page.svelte
Normal file
119
frontend/src/routes/Page.svelte
Normal file
@@ -0,0 +1,119 @@
|
||||
<script lang="ts">
|
||||
import { mediaLibrary, pages } from "../lib/store"
|
||||
import Pagebuilder from "../lib/components/pagebuilder/Pagebuilder.svelte"
|
||||
import { apiBaseURL, baseURL } from "../config"
|
||||
import { onMount } from "svelte"
|
||||
import NotFound from "./NotFound.svelte"
|
||||
export let path: string = "/"
|
||||
export let isHomepage = true
|
||||
let page: Content = $pages[path]
|
||||
$: page = $pages[path]
|
||||
let personPage = false
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
{#key page}
|
||||
<!-- Title -->
|
||||
{#if page?.pageTitle}
|
||||
<title>{page.pageTitle}</title>
|
||||
{:else if page?.meta?.title}
|
||||
<title>{page.meta.title}</title>
|
||||
{/if}
|
||||
|
||||
<!-- Description -->
|
||||
{#if page?.meta?.description}
|
||||
<meta name="description" content="{page.meta.description}" />
|
||||
{/if}
|
||||
|
||||
<!-- Keywords -->
|
||||
{#if page?.meta?.keywords}
|
||||
<meta name="keywords" content="{page.meta.keywords}" />
|
||||
{/if}
|
||||
|
||||
{#if page?.active === false}
|
||||
<meta name="robots" content="noindex" />
|
||||
{/if}
|
||||
<link rel="canonical" href="{baseURL + page?.path}" />
|
||||
{/key}
|
||||
</svelte:head>
|
||||
<div class="rows" class:HP="{path == '/'}">
|
||||
{#if page}
|
||||
{#each page.rows as row, i}
|
||||
<div class="row" id="row-{i}">
|
||||
{#if row.backgroundImage && $mediaLibrary[row.backgroundImage]}
|
||||
<div class="background-image">
|
||||
<img
|
||||
src="{`${apiBaseURL}medialib/${row?.backgroundImage}/${
|
||||
$mediaLibrary?.[row?.backgroundImage]?.file?.src
|
||||
}`}"
|
||||
alt="img"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="content" class:bright="{row.backgroundImage}">
|
||||
<Pagebuilder
|
||||
isHP="{path == '/'}"
|
||||
{i}
|
||||
{row}
|
||||
{page}
|
||||
pageId="{page.id}"
|
||||
bright="{!!row.backgroundImage}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{:else}
|
||||
<NotFound />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="less">
|
||||
@import "../lib/assets/css/variables.less";
|
||||
|
||||
.rows {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
max-width: 100vw;
|
||||
overflow: hidden;
|
||||
gap: 10px;
|
||||
|
||||
& > .row {
|
||||
margin: 15px 0px;
|
||||
padding: 15px 10px;
|
||||
@media @tablet {
|
||||
margin: 40px 0px;
|
||||
padding: 30px 10px;
|
||||
}
|
||||
width: 100%;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.background-image {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
img {
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
& > .content {
|
||||
width: 100%;
|
||||
max-width: 1400px;
|
||||
margin: 0px 2.5vw;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user