base
This commit is contained in:
@@ -3,7 +3,6 @@
|
|||||||
import Header from "./lib/components/header/Header.svelte"
|
import Header from "./lib/components/header/Header.svelte"
|
||||||
import Footer from "./lib/components/Footer.svelte"
|
import Footer from "./lib/components/Footer.svelte"
|
||||||
import Notifications from "./lib/components/widgets/Notifications.svelte"
|
import Notifications from "./lib/components/widgets/Notifications.svelte"
|
||||||
import SSRSkip from "./lib/components/SSRSkip.svelte"
|
|
||||||
import { isMobile, location, openModal } from "./lib/store"
|
import { isMobile, location, openModal } from "./lib/store"
|
||||||
import StaticHomepage from "./routes/StaticHomepage.svelte"
|
import StaticHomepage from "./routes/StaticHomepage.svelte"
|
||||||
|
|
||||||
@@ -81,6 +80,16 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
|
<div class="app-shell">
|
||||||
|
<Header />
|
||||||
|
<main>
|
||||||
|
<StaticHomepage />
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Notifications />
|
||||||
|
|
||||||
<style
|
<style
|
||||||
lang="less"
|
lang="less"
|
||||||
global
|
global
|
||||||
@@ -93,7 +102,7 @@
|
|||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
font-family: "Outfit", sans-serif;
|
font-family: "Outfit", sans-serif;
|
||||||
background-color: #ffffff;
|
background-color: var(--bg-100);
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
|
|
||||||
button {
|
button {
|
||||||
@@ -111,7 +120,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background-color: #ffffff;
|
background-color: var(--bg-100);
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
import TopRightCrinkle from "./widgets/TopRightCrinkle.svelte"
|
import TopRightCrinkle from "./widgets/TopRightCrinkle.svelte"
|
||||||
|
|
||||||
let {
|
let {
|
||||||
brightBackground,
|
brightBackground = true,
|
||||||
border,
|
border = true,
|
||||||
activated,
|
activated = true,
|
||||||
icon,
|
icon,
|
||||||
bigVersion,
|
bigVersion,
|
||||||
contentSnippet,
|
contentSnippet,
|
||||||
@@ -70,7 +70,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.bright {
|
&.bright {
|
||||||
background: var(--neutral-white);
|
|
||||||
.bar {
|
.bar {
|
||||||
background: var(--neutral-white);
|
background: var(--neutral-white);
|
||||||
&.border {
|
&.border {
|
||||||
@@ -79,7 +78,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.dark {
|
&.dark {
|
||||||
background: var(--bg-100);
|
|
||||||
.bar {
|
.bar {
|
||||||
background: var(--bg-100);
|
background: var(--bg-100);
|
||||||
&.border {
|
&.border {
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { location } from "../store"
|
|
||||||
|
|
||||||
let oldPath: string
|
|
||||||
$: if (typeof window !== "undefined" && oldPath !== $location.path) {
|
|
||||||
const ref = oldPath ? document.location.protocol + "//" + document.location.host + oldPath : document.referrer
|
|
||||||
oldPath = $location.path
|
|
||||||
fetch(oldPath, {
|
|
||||||
headers: {
|
|
||||||
"x-ssr-skip": "204",
|
|
||||||
"x-ssr-ref": ref,
|
|
||||||
"x-ssr-res": `${window.innerWidth}x${window.innerHeight}`,
|
|
||||||
// no cache
|
|
||||||
"cache-control": "no-cache, no-store, must-revalidate",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
30
frontend/src/lib/components/chatbotDemo/Chatbot.svelte
Normal file
30
frontend/src/lib/components/chatbotDemo/Chatbot.svelte
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Chat } from "./chat"
|
||||||
|
import InputRow from "./InputRow.svelte"
|
||||||
|
import Messages from "./Messages.svelte"
|
||||||
|
function generateUniqueId() {
|
||||||
|
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15)
|
||||||
|
}
|
||||||
|
let chat = $derived(
|
||||||
|
new Chat(generateUniqueId(), {
|
||||||
|
url: "https://2schat-server.robins-spielwiese.de/api/v1/chatbot/stream",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="chat-wrapper">
|
||||||
|
<Messages {chat} />
|
||||||
|
<InputRow {chat} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.chat-wrapper {
|
||||||
|
min-height: 400px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
84
frontend/src/lib/components/chatbotDemo/InputRow.svelte
Normal file
84
frontend/src/lib/components/chatbotDemo/InputRow.svelte
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { mdiSendVariantOutline } from "@mdi/js"
|
||||||
|
import Icon from "../widgets/Icon.svelte"
|
||||||
|
import type { Chat } from "./chat"
|
||||||
|
let { chat }: { chat: Chat } = $props()
|
||||||
|
let value = $state("")
|
||||||
|
let textareaEl: HTMLTextAreaElement = $state(null)
|
||||||
|
function autoResize() {
|
||||||
|
if (textareaEl) {
|
||||||
|
textareaEl.style.height = "auto"
|
||||||
|
textareaEl.style.height = textareaEl.scrollHeight + "px"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="input-row">
|
||||||
|
<textarea
|
||||||
|
placeholder="Schreibe eine Nachricht..."
|
||||||
|
bind:value
|
||||||
|
rows="1"
|
||||||
|
bind:this={textareaEl}
|
||||||
|
oninput={autoResize}
|
||||||
|
onkeydown={(e) => {
|
||||||
|
if (e.key === "Enter" && !e.shiftKey) {
|
||||||
|
e.preventDefault()
|
||||||
|
if (value.trim()) {
|
||||||
|
chat.generateResponse(value.trim())
|
||||||
|
value = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
></textarea>
|
||||||
|
<button
|
||||||
|
class="btn"
|
||||||
|
onclick={() => {
|
||||||
|
if (value.trim()) {
|
||||||
|
chat.generateResponse(value.trim())
|
||||||
|
value = ""
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
aria-label="Nachricht senden"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
path={mdiSendVariantOutline}
|
||||||
|
size="1.4rem"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.input-row {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
position: relative;
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 3.6rem;
|
||||||
|
padding: 0.6rem;
|
||||||
|
border-radius: 1.2rem;
|
||||||
|
padding-bottom: 2.4rem;
|
||||||
|
max-height: 12rem;
|
||||||
|
line-height: 1.4rem;
|
||||||
|
resize: none;
|
||||||
|
overflow: hidden;
|
||||||
|
color: white;
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
border-top: 1px solid var(--primary-100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.btn {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0.6rem;
|
||||||
|
right: 0.6rem;
|
||||||
|
height: 2rem;
|
||||||
|
width: 2.4rem;
|
||||||
|
min-width: unset;
|
||||||
|
background-color: var(--primary-100);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
23
frontend/src/lib/components/chatbotDemo/Messages.svelte
Normal file
23
frontend/src/lib/components/chatbotDemo/Messages.svelte
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { Chat } from "./chat"
|
||||||
|
let {
|
||||||
|
chat,
|
||||||
|
}: {
|
||||||
|
chat: Chat
|
||||||
|
} = $props()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{#each chat.messages as message, idx (idx)}
|
||||||
|
<div
|
||||||
|
class="message"
|
||||||
|
class:assistant={message.role === "assistant"}
|
||||||
|
class:user={message.role === "user"}
|
||||||
|
>
|
||||||
|
{@html message.content}
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
</style>
|
||||||
175
frontend/src/lib/components/chatbotDemo/chat.ts
Normal file
175
frontend/src/lib/components/chatbotDemo/chat.ts
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
import { fetchEventSource } from "@microsoft/fetch-event-source"
|
||||||
|
|
||||||
|
export type Role = "user" | "assistant" | "system"
|
||||||
|
|
||||||
|
export interface ChatMessage {
|
||||||
|
role: Role
|
||||||
|
content: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ChatRequestPayload {
|
||||||
|
user_id: string
|
||||||
|
message: string
|
||||||
|
session_id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerEvent =
|
||||||
|
| { type: "token"; delta: string }
|
||||||
|
| { type: "heartbeat" }
|
||||||
|
| { type: "error"; message: string }
|
||||||
|
| { type: "final"; state: unknown }
|
||||||
|
|
||||||
|
// Optionale Konfiguration für Headers, URL usw.
|
||||||
|
export interface ChatOptions {
|
||||||
|
/** Vollständige URL, z. B. "https://api.example.com/stream" */
|
||||||
|
url: string
|
||||||
|
/** Zusätzliche Request-Header (z. B. Auth) */
|
||||||
|
headers?: Record<string, string>
|
||||||
|
/** Fallback-Assistant-Fehlermeldung für die UI */
|
||||||
|
fallbackErrorText?: string
|
||||||
|
/** Offen lassen, wenn Tab im Hintergrund – sinnvoll für Safari */
|
||||||
|
openWhenHidden?: boolean
|
||||||
|
/** User-ID, wenn du sie nicht pro Aufruf übergibst */
|
||||||
|
userId?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Chat {
|
||||||
|
public messages: ChatMessage[] = []
|
||||||
|
public isStreaming = false
|
||||||
|
public lastFinalState: unknown = null
|
||||||
|
|
||||||
|
private sessionId: string
|
||||||
|
private opts: ChatOptions
|
||||||
|
private controller: AbortController | null = null
|
||||||
|
|
||||||
|
constructor(sessionId: string, opts: ChatOptions) {
|
||||||
|
if (!sessionId) throw new Error("session_id ist erforderlich")
|
||||||
|
if (!opts?.url) throw new Error("ChatOptions.url ist erforderlich")
|
||||||
|
this.sessionId = sessionId
|
||||||
|
this.opts = {
|
||||||
|
fallbackErrorText: "Es ist ein Fehler aufgetreten.",
|
||||||
|
openWhenHidden: true,
|
||||||
|
...opts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abortActiveStream() {
|
||||||
|
this.controller?.abort()
|
||||||
|
this.controller = null
|
||||||
|
this.isStreaming = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sendet eine Benutzer-Nachricht und streamt die Assistent-Antwort in `messages`.
|
||||||
|
* @param message Text der Benutzer-Nachricht
|
||||||
|
* @param userId optionaler Override der User-ID (falls nicht in opts.userId)
|
||||||
|
*/
|
||||||
|
public async generateResponse(message: string, userId?: string): Promise<void> {
|
||||||
|
const text = (message ?? "").trim()
|
||||||
|
if (!text) return
|
||||||
|
if (this.isStreaming) this.abortActiveStream()
|
||||||
|
this.messages = [...this.messages, { role: "user", content: text }]
|
||||||
|
const assistantIndex = this.messages.length
|
||||||
|
this.messages = [...this.messages, { role: "assistant", content: "" }]
|
||||||
|
|
||||||
|
const payload: ChatRequestPayload = {
|
||||||
|
user_id: userId ?? this.opts.userId ?? "anonymous",
|
||||||
|
message: text,
|
||||||
|
session_id: this.sessionId,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.controller = new AbortController()
|
||||||
|
this.isStreaming = true
|
||||||
|
this.lastFinalState = null
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fetchEventSource(this.opts.url, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...(this.opts.headers ?? {}),
|
||||||
|
},
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
signal: this.controller.signal,
|
||||||
|
openWhenHidden: this.opts.openWhenHidden,
|
||||||
|
|
||||||
|
onopen: async (res) => {
|
||||||
|
if (!res.ok) throw new Error(`HTTP ${res.status}`)
|
||||||
|
},
|
||||||
|
|
||||||
|
onmessage: (ev) => {
|
||||||
|
if (!ev.data) return
|
||||||
|
let parsed: ServerEvent | null = null
|
||||||
|
try {
|
||||||
|
parsed = JSON.parse(ev.data) as ServerEvent
|
||||||
|
} catch {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (parsed.type) {
|
||||||
|
case "token": {
|
||||||
|
const delta = parsed.delta ?? ""
|
||||||
|
const updated = {
|
||||||
|
...this.messages[assistantIndex],
|
||||||
|
content: (this.messages[assistantIndex]?.content ?? "") + delta,
|
||||||
|
}
|
||||||
|
this.messages = [
|
||||||
|
...this.messages.slice(0, assistantIndex),
|
||||||
|
updated,
|
||||||
|
...this.messages.slice(assistantIndex + 1),
|
||||||
|
]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case "heartbeat": {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case "error": {
|
||||||
|
const msg = parsed.message || this.opts.fallbackErrorText!
|
||||||
|
const updated = {
|
||||||
|
...this.messages[assistantIndex],
|
||||||
|
content: (this.messages[assistantIndex]?.content || "") + `\n\n⚠️ ${msg}`,
|
||||||
|
}
|
||||||
|
this.messages = [
|
||||||
|
...this.messages.slice(0, assistantIndex),
|
||||||
|
updated,
|
||||||
|
...this.messages.slice(assistantIndex + 1),
|
||||||
|
]
|
||||||
|
this.abortActiveStream()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case "final": {
|
||||||
|
this.lastFinalState = parsed.state
|
||||||
|
this.abortActiveStream()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onerror: (err) => {
|
||||||
|
const updated = {
|
||||||
|
...this.messages[assistantIndex],
|
||||||
|
content:
|
||||||
|
(this.messages[assistantIndex]?.content || "") +
|
||||||
|
`\n\n⚠️ ${this.opts.fallbackErrorText} (${String(err)})`,
|
||||||
|
}
|
||||||
|
this.messages = [
|
||||||
|
...this.messages.slice(0, assistantIndex),
|
||||||
|
updated,
|
||||||
|
...this.messages.slice(assistantIndex + 1),
|
||||||
|
]
|
||||||
|
throw err
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
this.isStreaming = false
|
||||||
|
this.controller = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { spaLink } from "../../actions"
|
|
||||||
import { navigationCache } from "../../store"
|
|
||||||
|
|
||||||
export let location: LocationStore = undefined
|
|
||||||
|
|
||||||
let paths: string[] = []
|
|
||||||
|
|
||||||
$: if (location?.path?.match(/\/[^\/]+\//)) {
|
|
||||||
let _paths: string[] = []
|
|
||||||
let _p = location.path
|
|
||||||
while (_p || _p.includes("/")) {
|
|
||||||
_paths.push(_p)
|
|
||||||
// remove last part
|
|
||||||
_p = _p.replace(/\/[^\/]+\/?$/, "")
|
|
||||||
}
|
|
||||||
paths = _paths.reverse()
|
|
||||||
} else {
|
|
||||||
paths = []
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- only for paths with more levels -->
|
|
||||||
{#if paths.length}
|
|
||||||
<nav aria-label="Breadcrumbs" class="breadcrumbs">
|
|
||||||
<div class="container vp-m">
|
|
||||||
<ol>
|
|
||||||
{#each paths as path}
|
|
||||||
{#if $navigationCache[path]}
|
|
||||||
<li><a use:spaLink href="{path}">{$navigationCache[path].name}</a></li>
|
|
||||||
{/if}
|
|
||||||
{/each}
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
{/if}
|
|
||||||
@@ -1,301 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { apiBaseOverride, location } from "../../store"
|
|
||||||
import blocks from "./blocks"
|
|
||||||
import CrinkledSection from "../CrinkledSection.svelte"
|
|
||||||
import MedialibImage from "../widgets/MedialibImage.svelte"
|
|
||||||
import Button from "../widgets/Button.svelte"
|
|
||||||
|
|
||||||
export let block: ContentBlock,
|
|
||||||
apiBase: string = null,
|
|
||||||
verticalPadding = true,
|
|
||||||
noHorizontalMargin = false
|
|
||||||
|
|
||||||
if (apiBase) $apiBaseOverride = apiBase
|
|
||||||
$: blockComponent = blocks[block.type] || blocks.default
|
|
||||||
let scrollTargetContainer: HTMLDivElement
|
|
||||||
let lastAnchor: string
|
|
||||||
$: if (block.anchorId && typeof window !== "undefined") {
|
|
||||||
const targetAnchor = $location.hash
|
|
||||||
if (targetAnchor !== lastAnchor && scrollTargetContainer) {
|
|
||||||
lastAnchor = targetAnchor
|
|
||||||
if (targetAnchor == "#" + block.anchorId) {
|
|
||||||
setTimeout(() => {
|
|
||||||
scrollTargetContainer.scrollIntoView({ behavior: "instant", block: "start", inline: "nearest" })
|
|
||||||
setTimeout(() => {
|
|
||||||
scrollTargetContainer.scrollIntoView({ behavior: "smooth", block: "start", inline: "nearest" })
|
|
||||||
}, 600)
|
|
||||||
}, 10)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<CrinkledSection activated="{block.crinkledSection || false}">
|
|
||||||
{#if block.anchorId}
|
|
||||||
<!-- position 100px above -->
|
|
||||||
<div style="position:relative;">
|
|
||||||
<div
|
|
||||||
bind:this="{scrollTargetContainer}"
|
|
||||||
style="position: absolute; top: -100px"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
<section
|
|
||||||
class:additionalHeightAtBottom="{block.additionalHeightBottom}"
|
|
||||||
class:headerHeightUp="{block?.background?.headerHeightUp}"
|
|
||||||
data-type="{block.type}"
|
|
||||||
class="content-section minheight-{block?.background?.minHeight} {blockComponent.sectionClass} {block.background
|
|
||||||
?.color
|
|
||||||
? block?.background?.color + '-bg'
|
|
||||||
: ''}"
|
|
||||||
>
|
|
||||||
{#if block.background?.image}
|
|
||||||
<div class="background-image">
|
|
||||||
<figure>
|
|
||||||
<MedialibImage id="{block?.background?.image}" />
|
|
||||||
{#if block?.background?.overlay}
|
|
||||||
<div class="overlay"></div>
|
|
||||||
{/if}
|
|
||||||
</figure>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
<section
|
|
||||||
class="content"
|
|
||||||
class:narrowWidth="{block.contentWidth == 1}"
|
|
||||||
class:normalWidth="{block.contentWidth == 2}"
|
|
||||||
class:fullWidth="{block.contentWidth == 0}"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="wrapper"
|
|
||||||
class:noHorizontalMargin="{noHorizontalMargin}"
|
|
||||||
class:noMobileHorizontalMargin="{block?.background?.noHorizontalMargin}"
|
|
||||||
class:verticalPadding="{verticalPadding && !block?.background?.noVerticalPadding}"
|
|
||||||
>
|
|
||||||
{#if block.headline || block.subline}
|
|
||||||
<div class="headline-row">
|
|
||||||
<div
|
|
||||||
class="container bp-xl"
|
|
||||||
class:darkColor="{!block?.background?.image && block?.background?.color === 'white'}"
|
|
||||||
>
|
|
||||||
<!-- Überschrift Element -->
|
|
||||||
{#if block.headline}
|
|
||||||
{#if block.headlineH1}
|
|
||||||
<h1 class="h2">{block.headline}</h1>
|
|
||||||
{:else}
|
|
||||||
<h2 class:doublyLined="{block.doublyLined}">{block.headline}</h2>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
{#if block.subline}
|
|
||||||
<h3>{block.subline}</h3>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{#if block?.headlineLink}
|
|
||||||
<button class="">
|
|
||||||
<a
|
|
||||||
href="{block.headlineLink}"
|
|
||||||
class="headline-link"
|
|
||||||
>
|
|
||||||
{block.headlineLinkText}
|
|
||||||
</a>
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<svelte:component
|
|
||||||
this="{blockComponent.component}"
|
|
||||||
block="{block}"
|
|
||||||
/>
|
|
||||||
<div class="buttons">
|
|
||||||
{#each block.callToActionButtons || [] as button}
|
|
||||||
<Button button="{button}" />
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</section>
|
|
||||||
</CrinkledSection>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
@import "../../../lib/assets/css/variables.less";
|
|
||||||
.content-section {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
&.additionalHeightAtBottom {
|
|
||||||
padding-bottom: 3rem;
|
|
||||||
}
|
|
||||||
&.headerHeightUp {
|
|
||||||
margin-top: -86px;
|
|
||||||
}
|
|
||||||
&.minheight-normal {
|
|
||||||
height: 38rem;
|
|
||||||
min-height: 75vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.minheight-extended {
|
|
||||||
padding-top: 145px;
|
|
||||||
height: calc(38rem + 145px);
|
|
||||||
min-height: 75vh;
|
|
||||||
}
|
|
||||||
&.homepageRow {
|
|
||||||
height: calc(38rem + 145px + 96px + 96px);
|
|
||||||
max-height: 97vh;
|
|
||||||
}
|
|
||||||
&.chapterPreview {
|
|
||||||
.content {
|
|
||||||
& > .wrapper {
|
|
||||||
margin: 0px !important;
|
|
||||||
max-width: 100% !important;
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.ImproveYourselfDescription {
|
|
||||||
height: unset;
|
|
||||||
.content {
|
|
||||||
.wrapper {
|
|
||||||
display: unset !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.buttons {
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
justify-content: flex-start;
|
|
||||||
padding-top: 2.4rem;
|
|
||||||
}
|
|
||||||
& .content {
|
|
||||||
position: relative;
|
|
||||||
z-index: 2;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
.headline-row {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
& > button {
|
|
||||||
width: fit-content;
|
|
||||||
white-space: nowrap;
|
|
||||||
a {
|
|
||||||
color: var(--white-100);
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.container {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1.8rem;
|
|
||||||
|
|
||||||
h2,
|
|
||||||
h1 {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
h2.doublyLined,
|
|
||||||
h3.doublyLined {
|
|
||||||
border-top: 1px solid white;
|
|
||||||
border-bottom: 1px solid white;
|
|
||||||
padding: 1.2rem 3.6rem;
|
|
||||||
margin: 1.2rem -3.6rem;
|
|
||||||
margin-top: 0px;
|
|
||||||
width: calc(100% + 7.2rem);
|
|
||||||
font-size: 1.6rem;
|
|
||||||
}
|
|
||||||
&.darkColor {
|
|
||||||
h2,
|
|
||||||
h1,
|
|
||||||
h3 {
|
|
||||||
color: var(--text-invers-100);
|
|
||||||
}
|
|
||||||
h2.doublyLined,
|
|
||||||
h3.doublyLined {
|
|
||||||
border-top: 1px solid var(--bg-100);
|
|
||||||
border-bottom: 1px solid var(--bg-100);
|
|
||||||
padding: 1.2rem 3.6rem;
|
|
||||||
margin: 1.2rem -3.6rem;
|
|
||||||
margin-top: 0px;
|
|
||||||
width: calc(100% + 7.2rem);
|
|
||||||
font-size: 1.6rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
margin-bottom: 2.4rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.wrapper {
|
|
||||||
margin: 0px var(--horizontal-default-margin);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
width: calc(100% - var(--horizontal-default-margin));
|
|
||||||
max-width: calc(100% - var(--horizontal-default-margin));
|
|
||||||
height: 100%;
|
|
||||||
&.verticalPadding {
|
|
||||||
padding: var(--vertical-default-margin) 0px;
|
|
||||||
}
|
|
||||||
&.noHorizontalMargin {
|
|
||||||
margin: 0px;
|
|
||||||
max-width: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
&.noMobileHorizontalMargin {
|
|
||||||
@media @mobile {
|
|
||||||
margin: 0px;
|
|
||||||
max-width: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.normalWidth {
|
|
||||||
.wrapper {
|
|
||||||
max-width: var(--normal-max-width);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.narrowWidth {
|
|
||||||
.wrapper {
|
|
||||||
max-width: var(--small-max-width);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.background-image {
|
|
||||||
z-index: 1;
|
|
||||||
top: 0px;
|
|
||||||
overflow: hidden;
|
|
||||||
left: 0px;
|
|
||||||
right: 0px;
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
figure {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
position: relative;
|
|
||||||
.overlay {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 100;
|
|
||||||
background-color: rgba(32, 28, 28, 0.3);
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
top: 0px;
|
|
||||||
left: 0px;
|
|
||||||
right: 0px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
:global .background-image > figure > img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global .black-bg {
|
|
||||||
background-color: var(--bg-100);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { onMount } from "svelte"
|
|
||||||
import { register } from "swiper/element/bundle"
|
|
||||||
import MedialibImage from "../widgets/MedialibImage.svelte"
|
|
||||||
export let images: string[]
|
|
||||||
export let imageHoverEffect: boolean
|
|
||||||
export let forceFullHeight: boolean
|
|
||||||
register(false)
|
|
||||||
let swiper: any
|
|
||||||
onMount(async () => {
|
|
||||||
if (swiper !== undefined) {
|
|
||||||
const response = await fetch("/dist/index.css"),
|
|
||||||
cssText = await response.text(),
|
|
||||||
params = {
|
|
||||||
injectStyles: [cssText],
|
|
||||||
}
|
|
||||||
Object.assign(swiper, params)
|
|
||||||
swiper.initialize()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#if images.length > 1}
|
|
||||||
<div class="default-swiper">
|
|
||||||
<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 (image)}
|
|
||||||
<swiper-slide class="relative">
|
|
||||||
<div
|
|
||||||
class:imageHoverEffect="{imageHoverEffect}"
|
|
||||||
class="image-container"
|
|
||||||
>
|
|
||||||
<MedialibImage
|
|
||||||
id="{image}"
|
|
||||||
filter="{typeof window !== 'undefined' && window.innerWidth > 500 ? 'xl' : 'm'}"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</swiper-slide>
|
|
||||||
{/each}
|
|
||||||
</swiper-container>
|
|
||||||
</div>
|
|
||||||
{:else if images[0]}
|
|
||||||
<div
|
|
||||||
class="image-container single flex"
|
|
||||||
class:forceFullHeight="{forceFullHeight}"
|
|
||||||
class:imageHoverEffect="{imageHoverEffect}"
|
|
||||||
>
|
|
||||||
<MedialibImage
|
|
||||||
id="{images[0]}"
|
|
||||||
filter="{typeof window !== 'undefined' && window.innerWidth > 500 ? 'xl' : 'm'}"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
@import "../../assets/css/variables.less";
|
|
||||||
|
|
||||||
:global .default-swiper {
|
|
||||||
flex: 2 !important;
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
swiper-container {
|
|
||||||
width: 100%;
|
|
||||||
swiper-slide {
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-container {
|
|
||||||
max-width: 100%;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
overflow: visible;
|
|
||||||
max-width: @body-maxwidth;
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-end;
|
|
||||||
:global &.imageHoverEffect:hover img {
|
|
||||||
transform: scale(1.05);
|
|
||||||
transform-origin: center;
|
|
||||||
}
|
|
||||||
:global & > img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
:global &.forceFullHeight img {
|
|
||||||
width: unset;
|
|
||||||
}
|
|
||||||
@media @mobile {
|
|
||||||
:global & > img {
|
|
||||||
height: 90% !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { BarLoader, Circle } from "svelte-loading-spinners"
|
|
||||||
export let size: string,
|
|
||||||
type: "bar" | "circle" = "circle"
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="{type}">
|
|
||||||
{#if type == "bar"}
|
|
||||||
<BarLoader
|
|
||||||
size="{size}"
|
|
||||||
color="#741e20"
|
|
||||||
unit="rem"
|
|
||||||
/>
|
|
||||||
{:else}
|
|
||||||
<Circle
|
|
||||||
size="{size}"
|
|
||||||
color="#741e20"
|
|
||||||
unit="rem"
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
div {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
padding: 2.4rem 0px;
|
|
||||||
&.circle {
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { onMount } from "svelte"
|
|
||||||
import Item from "./Item.svelte"
|
|
||||||
import { selfImprovementChapters } from "../../../../store"
|
|
||||||
|
|
||||||
export let block: ContentBlock<"selfImprovementChapterPreview">
|
|
||||||
const chapters = block.selfImprovementChapterPreview
|
|
||||||
let interval: NodeJS.Timeout
|
|
||||||
|
|
||||||
function startInterval() {
|
|
||||||
interval = setInterval(() => {
|
|
||||||
selectedChapter = (selectedChapter + 1) % $selfImprovementChapters.length
|
|
||||||
}, 3000)
|
|
||||||
}
|
|
||||||
|
|
||||||
function stopInterval() {
|
|
||||||
clearInterval(interval)
|
|
||||||
}
|
|
||||||
onMount(() => {
|
|
||||||
startInterval()
|
|
||||||
return () => clearInterval(interval)
|
|
||||||
})
|
|
||||||
let selectedChapter = 0
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<ul>
|
|
||||||
{#each $selfImprovementChapters as chapter, i}
|
|
||||||
<li
|
|
||||||
on:mouseenter="{() => {
|
|
||||||
stopInterval()
|
|
||||||
selectedChapter = i
|
|
||||||
}}"
|
|
||||||
on:mouseleave="{startInterval}"
|
|
||||||
class:active="{selectedChapter === i}"
|
|
||||||
>
|
|
||||||
<Item
|
|
||||||
chapter="{chapter}"
|
|
||||||
bind:selectedChapter="{selectedChapter}"
|
|
||||||
index="{i}"
|
|
||||||
previewImage="{chapters.find((p) => p.chapter === chapter.id)?.previewImage}"
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
{/each}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
* {
|
|
||||||
transition-duration: 0s;
|
|
||||||
}
|
|
||||||
.container {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
list-style: none;
|
|
||||||
width: 100%;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
width: 100%;
|
|
||||||
aspect-ratio: 4 / 3;
|
|
||||||
@media (max-width: 1500px) {
|
|
||||||
aspect-ratio: unset;
|
|
||||||
&::before {
|
|
||||||
float: left;
|
|
||||||
padding-top: 125%;
|
|
||||||
content: "";
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
display: block;
|
|
||||||
content: "";
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 1500px) {
|
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
width: 33.33%;
|
|
||||||
}
|
|
||||||
|
|
||||||
li.active {
|
|
||||||
order: -1;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,350 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { onMount, afterUpdate } from "svelte"
|
|
||||||
import MedialibImage from "../../../widgets/MedialibImage.svelte"
|
|
||||||
import { spaLink } from "../../../../actions"
|
|
||||||
import { getVariableNameForChapter } from "../../../../utils"
|
|
||||||
|
|
||||||
export let selectedChapter: number, index: number, chapter: SelfImprovementChapter, previewImage: string
|
|
||||||
const color = chapter.color
|
|
||||||
$: active = selectedChapter === index
|
|
||||||
|
|
||||||
let upperPart: HTMLElement
|
|
||||||
let lowerPart: HTMLElement
|
|
||||||
let topOverlay: HTMLElement
|
|
||||||
let bottomOverlay: HTMLElement
|
|
||||||
|
|
||||||
function updateOverlayHeights() {
|
|
||||||
if (topOverlay && bottomOverlay && upperPart && lowerPart) {
|
|
||||||
topOverlay.style.height = `${upperPart.offsetHeight + 24}px`
|
|
||||||
bottomOverlay.style.height = `${lowerPart.offsetHeight + 24}px`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
window.addEventListener("resize", updateOverlayHeights)
|
|
||||||
})
|
|
||||||
|
|
||||||
afterUpdate(() => {
|
|
||||||
updateOverlayHeights()
|
|
||||||
})
|
|
||||||
|
|
||||||
$: if (active) {
|
|
||||||
updateOverlayHeights()
|
|
||||||
} else {
|
|
||||||
if (topOverlay && bottomOverlay) {
|
|
||||||
topOverlay.style.height = "0px"
|
|
||||||
bottomOverlay.style.height = "0px"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function getSlug(type: number) {
|
|
||||||
if (type == 1) return "krasskraft"
|
|
||||||
return "unknown"
|
|
||||||
}
|
|
||||||
|
|
||||||
let colorName = getVariableNameForChapter(chapter.type)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="chapter-preview-block"
|
|
||||||
class:active="{active}"
|
|
||||||
>
|
|
||||||
{#if chapter.locked}
|
|
||||||
<div
|
|
||||||
class="locked"
|
|
||||||
class:active="{active}"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 72 72"
|
|
||||||
fill="none"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
d="M15.75 30.165V24C15.75 18.6294 17.8835 13.4787 21.6811 9.68109C25.4787 5.88348 30.6294 3.75 36 3.75C41.3706 3.75 46.5213 5.88348 50.3189 9.68109C54.1165 13.4787 56.25 18.6294 56.25 24V30.165C59.595 30.414 61.77 31.044 63.363 32.637C66 35.271 66 39.516 66 48C66 56.484 66 60.729 63.363 63.363C60.729 66 56.484 66 48 66H24C15.516 66 11.271 66 8.637 63.363C6 60.729 6 56.484 6 48C6 39.516 6 35.271 8.637 32.637C10.227 31.044 12.405 30.414 15.75 30.165ZM20.25 24C20.25 19.8228 21.9094 15.8168 24.8631 12.8631C27.8168 9.90937 31.8228 8.25 36 8.25C40.1772 8.25 44.1832 9.90937 47.1369 12.8631C50.0906 15.8168 51.75 19.8228 51.75 24V30.012C50.601 30 49.353 30 48 30H24C22.644 30 21.399 30 20.25 30.012V24ZM42 48C42 49.5913 41.3679 51.1174 40.2426 52.2426C39.1174 53.3679 37.5913 54 36 54C34.4087 54 32.8826 53.3679 31.7574 52.2426C30.6321 51.1174 30 49.5913 30 48C30 46.4087 30.6321 44.8826 31.7574 43.7574C32.8826 42.6321 34.4087 42 36 42C37.5913 42 39.1174 42.6321 40.2426 43.7574C41.3679 44.8826 42 46.4087 42 48Z"
|
|
||||||
fill="white"></path>
|
|
||||||
</svg>
|
|
||||||
<p>Coming Soon</p>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
<div class="background-image">
|
|
||||||
<MedialibImage id="{previewImage}" />
|
|
||||||
|
|
||||||
<div
|
|
||||||
bind:this="{topOverlay}"
|
|
||||||
class="overlay top"
|
|
||||||
style="background-color: var({colorName})"
|
|
||||||
></div>
|
|
||||||
<div
|
|
||||||
bind:this="{bottomOverlay}"
|
|
||||||
class="overlay bottom"
|
|
||||||
style="background-color: var({colorName})"
|
|
||||||
></div>
|
|
||||||
<div
|
|
||||||
class="overlay left"
|
|
||||||
style="background-color: var({colorName})"
|
|
||||||
></div>
|
|
||||||
<div
|
|
||||||
class="overlay right"
|
|
||||||
style="background-color: var({colorName})"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="content"
|
|
||||||
class:active="{active}"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
bind:this="{upperPart}"
|
|
||||||
class:active="{active}"
|
|
||||||
class="upper-part"
|
|
||||||
>
|
|
||||||
<h4
|
|
||||||
class:active="{active}"
|
|
||||||
style="{!active ? 'color: ' + color : 'color: var(--bg-100)'}"
|
|
||||||
>
|
|
||||||
{chapter.alias}
|
|
||||||
</h4>
|
|
||||||
<h3
|
|
||||||
class:active="{active}"
|
|
||||||
style="{!active ? 'color: ' + color : 'color: var(--bg-100)'}"
|
|
||||||
>
|
|
||||||
{chapter.title}
|
|
||||||
</h3>
|
|
||||||
<p
|
|
||||||
class:active="{active}"
|
|
||||||
style="{!active ? 'color: ' + 'transparent' : 'color: var(--bg-100)'}"
|
|
||||||
>
|
|
||||||
{chapter.shortDescription}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
bind:this="{lowerPart}"
|
|
||||||
class="lower-part"
|
|
||||||
class:active="{active}"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
style="background-color: var({colorName})"
|
|
||||||
disabled="{chapter.locked}"
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
href="/selfimprovement/{getSlug(chapter.type)}"
|
|
||||||
use:spaLink
|
|
||||||
aria-disabled="{chapter.locked ? 'true' : 'false'}"
|
|
||||||
>
|
|
||||||
Weiter
|
|
||||||
</a>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style
|
|
||||||
lang="less"
|
|
||||||
global
|
|
||||||
>
|
|
||||||
@import "../../../../assets/css/variables.less";
|
|
||||||
.chapter-preview-block {
|
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
.locked {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 100;
|
|
||||||
pointer-events: none;
|
|
||||||
background: rgba(13, 12, 12, 0.5);
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
p {
|
|
||||||
color: white;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
svg {
|
|
||||||
width: 72px;
|
|
||||||
height: 72px;
|
|
||||||
}
|
|
||||||
@media (max-width: 1500px) {
|
|
||||||
&:not(.active) {
|
|
||||||
svg {
|
|
||||||
height: 24px;
|
|
||||||
width: 24px;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
font-size: 0.6rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.background-image {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
position: absolute;
|
|
||||||
z-index: 0;
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
.overlay {
|
|
||||||
position: absolute;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
&.top {
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
&.bottom {
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
&.left {
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 0;
|
|
||||||
}
|
|
||||||
&.right {
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
width: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.active .background-image .overlay.top {
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
&.active .background-image .overlay.bottom {
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
&.active .background-image .overlay.left {
|
|
||||||
width: 3.6rem;
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
width: 0.6rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.active .background-image .overlay.right {
|
|
||||||
width: 3.6rem;
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
width: 0.6rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.content {
|
|
||||||
transition: border-color 0.3s ease, color 0.3s ease;
|
|
||||||
border-top: 2.4rem solid transparent;
|
|
||||||
border-bottom: 2.4rem solid transparent;
|
|
||||||
border-left: 3.6rem solid transparent;
|
|
||||||
border-right: 3.6rem solid transparent;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
position: relative;
|
|
||||||
z-index: 10;
|
|
||||||
.upper-part {
|
|
||||||
transition: border-color 0.3s ease, color 0.3s ease, background-color 0.3s ease;
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1.2rem;
|
|
||||||
h4 {
|
|
||||||
margin: 0px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
color: var(--bg-100);
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
font-size: 3rem;
|
|
||||||
font-weight: 700;
|
|
||||||
margin-bottom: 1.2rem;
|
|
||||||
color: var(--bg-100);
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
font-size: 1.2rem;
|
|
||||||
color: var(--bg-100);
|
|
||||||
}
|
|
||||||
@media (max-width: 1500px) {
|
|
||||||
h3:not(.active) {
|
|
||||||
font-size: 1.6rem;
|
|
||||||
}
|
|
||||||
p:not(.active) {
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
h4:not(.active) {
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
gap: 0.6rem;
|
|
||||||
h3 {
|
|
||||||
font-size: 1.6rem;
|
|
||||||
margin-bottom: 0px;
|
|
||||||
}
|
|
||||||
h3:not(.active) {
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
margin-top: 0px;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
}
|
|
||||||
p:not(.active) {
|
|
||||||
font-size: 0.5rem;
|
|
||||||
}
|
|
||||||
h4 {
|
|
||||||
font-size: 0.8rem;
|
|
||||||
}
|
|
||||||
h4:not(.active) {
|
|
||||||
font-size: 0.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
border-bottom: 2.4rem solid transparent;
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
border-width: 0.6rem !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
border-width: 0.6rem !important;
|
|
||||||
}
|
|
||||||
.lower-part {
|
|
||||||
border-top: 2.4rem solid transparent;
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
border-width: 0rem !important;
|
|
||||||
}
|
|
||||||
transition: border-color 0.3s ease, color 0.3s ease, background-color 0.3s ease;
|
|
||||||
button {
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
text-transform: uppercase;
|
|
||||||
padding: 6px 12px;
|
|
||||||
@media (max-width: 1500px) {
|
|
||||||
padding-left: 0px;
|
|
||||||
}
|
|
||||||
color: var(--bg-100);
|
|
||||||
&[disabled] {
|
|
||||||
cursor: not-allowed;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:not(.active) {
|
|
||||||
@media (max-width: 1500px) {
|
|
||||||
.lower-part {
|
|
||||||
button {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import ColumnsColumn from "./ColumnsColumn.svelte"
|
|
||||||
export let block: ContentBlock<"columns">
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
{#each block.columns || [] as column}
|
|
||||||
<ColumnsColumn column="{column}" />
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
@import "../../../../lib/assets/css/variables.less";
|
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
gap: 1.1rem;
|
|
||||||
max-width: var(--normal-max-width);
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
@media @mobile {
|
|
||||||
flex-direction: column-reverse;
|
|
||||||
:global & > section {
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { apiBaseOverride } from "../../../store"
|
|
||||||
import DefaultImage from "../DefaultImage.svelte"
|
|
||||||
import ChapterDescription from "./columns/ChapterDescription.svelte"
|
|
||||||
import Cta from "./columns/CTA.svelte"
|
|
||||||
import Text from "./columns/Text.svelte"
|
|
||||||
|
|
||||||
export let column: BlockColumn
|
|
||||||
|
|
||||||
export let apiBase: string = null
|
|
||||||
if (apiBase) $apiBaseOverride = apiBase
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<section
|
|
||||||
class:imageMobileBackground="{column.imageMobileBackground}"
|
|
||||||
class="{column.colWidth > 0 ? 'col-md-' + column.colWidth + ' col-12' : 'col-md-auto col-12'}"
|
|
||||||
class:align-self-start="{column.verticalAlign == 'top'}"
|
|
||||||
class:align-self-center="{column.verticalAlign == 'middle'}"
|
|
||||||
class:align-self-end="{column.verticalAlign == 'bottom'}"
|
|
||||||
>
|
|
||||||
{#if column.type == "text"}
|
|
||||||
<Text column="{column}" />
|
|
||||||
{:else if column.type == "image"}
|
|
||||||
<DefaultImage
|
|
||||||
images="{column.images}"
|
|
||||||
imageHoverEffect="{column.imageHoverEffect}"
|
|
||||||
forceFullHeight="{column.forceFullHeight}"
|
|
||||||
/>
|
|
||||||
{:else if column.type == "cta"}
|
|
||||||
<Cta column="{column}" />
|
|
||||||
{:else if column.type == "chapterDescription"}
|
|
||||||
<ChapterDescription
|
|
||||||
title="{column.chapterDescription.title}"
|
|
||||||
type="{column.chapterDescription.type}"
|
|
||||||
description="{column.chapterDescription.description}"
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
@import "../../../../lib/assets/css/variables.less";
|
|
||||||
section {
|
|
||||||
width: 0px;
|
|
||||||
flex-grow: 1;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: flex-start;
|
|
||||||
position: relative;
|
|
||||||
z-index: 2;
|
|
||||||
@media @mobile {
|
|
||||||
&.imageMobileBackground {
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
bottom: 0px;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.align-self-start {
|
|
||||||
justify-content: flex-start;
|
|
||||||
}
|
|
||||||
&.align-self-center {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
&.align-self-end {
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import CookieSet from "../../widgets/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>
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import MedialibImage from "../../widgets/MedialibImage.svelte"
|
|
||||||
import Cta from "./columns/CTA.svelte"
|
|
||||||
|
|
||||||
export let block: ContentBlock<"homepage">
|
|
||||||
const hp = block.mainHomepage
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<section class="homepage">
|
|
||||||
<div class="placeholder"></div>
|
|
||||||
<div class="left">
|
|
||||||
<Cta
|
|
||||||
column="{{
|
|
||||||
type: 'cta',
|
|
||||||
cta: hp.cta,
|
|
||||||
}}"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="hp-media">
|
|
||||||
<MedialibImage
|
|
||||||
id="{hp.image}"
|
|
||||||
filter="{typeof window !== 'undefined' && window.innerWidth > 500 ? 'xl' : 'm'}"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<style
|
|
||||||
lang="less"
|
|
||||||
global
|
|
||||||
>
|
|
||||||
@import "../../../assets/css/variables.less";
|
|
||||||
|
|
||||||
.homepage {
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
transition: --color 1s ease;
|
|
||||||
margin: -96px 0;
|
|
||||||
|
|
||||||
.placeholder {
|
|
||||||
}
|
|
||||||
.left {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1.5rem;
|
|
||||||
position: absolute;
|
|
||||||
z-index: 10;
|
|
||||||
transform: translateY(96px);
|
|
||||||
width: 100%;
|
|
||||||
@media @mobile {
|
|
||||||
padding: 0px var(--horizontal-default-margin);
|
|
||||||
}
|
|
||||||
@media @mobile {
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.hp-media {
|
|
||||||
max-width: 600px;
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
height: 100%;
|
|
||||||
align-items: flex-end;
|
|
||||||
flex-grow: 1;
|
|
||||||
@media @mobile {
|
|
||||||
&::before {
|
|
||||||
//shadow
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.9) 100%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
height: calc(100%);
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
@media @mobile {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media @mobile {
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import MedialibImage from "../../widgets/MedialibImage.svelte"
|
|
||||||
|
|
||||||
export let block: ContentBlock<"improveYourselfDescription">
|
|
||||||
const des = block.improveYourselfDescription
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<section class="improveYourselfDescription">
|
|
||||||
<div class="img">
|
|
||||||
<MedialibImage
|
|
||||||
id="{des.image}"
|
|
||||||
filter="xxl"
|
|
||||||
/>
|
|
||||||
<div class="shadow"></div>
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<h2 class="h1">Improve Yourself</h2>
|
|
||||||
<p>{@html des.upperDescription}</p>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<span
|
|
||||||
><svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="44"
|
|
||||||
height="32"
|
|
||||||
viewBox="0 0 44 32"
|
|
||||||
fill="none"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M42.5934 9.61759L21.9008 31.2762L1.20832 9.61759L10.829 0.5H32.9727L42.5934 9.61759Z"
|
|
||||||
stroke="#EB5757"></path>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
<span style="color: #EB5757;">Fitness</span>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<span
|
|
||||||
><svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="44"
|
|
||||||
height="32"
|
|
||||||
viewBox="0 0 44 32"
|
|
||||||
fill="none"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M23.0956 25.0301C25.4532 25.0301 27.6755 24.4466 29.625 23.4161C26.9937 28.2334 21.8812 31.5 16.0069 31.5C7.44266 31.5 0.5 24.5574 0.5 15.9931C0.5 8.2062 6.23975 1.75968 13.719 0.653784C10.8856 3.21373 9.10412 6.9181 9.10412 11.0386C9.10412 18.7659 15.3684 25.0301 23.0956 25.0301Z"
|
|
||||||
stroke="#56F2B0"></path>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
<span style="color: #56F2B0;">Entspannung</span>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<span
|
|
||||||
><svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="44"
|
|
||||||
height="32"
|
|
||||||
viewBox="0 0 44 32"
|
|
||||||
fill="none"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M18.4213 6.89342L18.8989 8.42622L19.3761 6.89329C20.5237 3.20667 23.8663 0.5 27.7914 0.5C32.6671 0.5 36.6326 4.44843 36.6326 9.48563C36.6326 11.9912 35.6486 13.7553 33.9735 15.7311L18.9071 31.2817L3.82337 15.7311C2.15062 13.7562 1.16675 11.9768 1.16675 9.4684C1.16675 4.43715 5.12025 0.599827 10.0049 0.599827C13.9382 0.599827 17.2753 3.21536 18.4213 6.89342Z"
|
|
||||||
stroke="#C0F256"></path>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
<span style="color: #C0F256;">Ernährung</span>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<span
|
|
||||||
><svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="44"
|
|
||||||
height="32"
|
|
||||||
viewBox="0 0 44 32"
|
|
||||||
fill="none"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M16 1.16101L20.157 9.87264L20.2738 10.1176L20.5429 10.153L30.1127 11.4145L23.112 18.0601L22.9152 18.2469L22.9646 18.5137L24.7221 28.005L16.2385 23.4006L16 23.2711L15.7615 23.4006L7.27786 28.005L9.03536 18.5137L9.08477 18.2469L8.88795 18.0601L1.88728 11.4145L11.4571 10.153L11.7262 10.1176L11.843 9.87264L16 1.16101Z"
|
|
||||||
stroke="#56F2F2"></path>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
<span style="color: #56F2F2;">Weiterbildung</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<p>{@html des.lowerDescription}</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<style
|
|
||||||
lang="less"
|
|
||||||
global
|
|
||||||
>
|
|
||||||
@import "../../../assets/css/variables.less";
|
|
||||||
.improveYourselfDescription {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
gap: 2.4rem;
|
|
||||||
max-width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
.img {
|
|
||||||
width: 400px;
|
|
||||||
min-width: 400px;
|
|
||||||
max-height: 100%;
|
|
||||||
height: 100%;
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
.shadow {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.content {
|
|
||||||
height: 100%;
|
|
||||||
flex-grow: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: flex-start;
|
|
||||||
gap: 2.4rem;
|
|
||||||
z-index: 99;
|
|
||||||
p {
|
|
||||||
color: var(--text-100);
|
|
||||||
}
|
|
||||||
ul {
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
justify-content: space-between;
|
|
||||||
li {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.6rem;
|
|
||||||
span {
|
|
||||||
font-size: 0.8rem;
|
|
||||||
font-weight: 700;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media @mobile {
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 0px;
|
|
||||||
.content {
|
|
||||||
ul {
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1.2rem;
|
|
||||||
li {
|
|
||||||
flex-direction: row;
|
|
||||||
span:first-child {
|
|
||||||
width: 100px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.img {
|
|
||||||
max-width: 100%;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0px;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: 1;
|
|
||||||
.shadow {
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0px;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: rgba(13, 12, 12, 0.5);
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
h2 {
|
|
||||||
color: var(--primary-200);
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,12 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import Content from "../../../../routes/Content.svelte"
|
|
||||||
|
|
||||||
export let block: ContentBlock<"predefinedBlock">
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#if block.predefinedBlock?.id}
|
|
||||||
<Content
|
|
||||||
id="{block.predefinedBlock.id}"
|
|
||||||
allwaysInView
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
@@ -1,261 +0,0 @@
|
|||||||
<script
|
|
||||||
lang="ts"
|
|
||||||
context="module"
|
|
||||||
>
|
|
||||||
import "simplebar"
|
|
||||||
import "simplebar/dist/simplebar.css"
|
|
||||||
import ResizeObserver from "resize-observer-polyfill"
|
|
||||||
if (typeof window !== "undefined") window.ResizeObserver = ResizeObserver
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { getCachedEntries } from "../../../../../api"
|
|
||||||
import { getBCGraphProductsByIds } from "../../../../functions/CommerceAPIs/bigCommerce/product"
|
|
||||||
import { backgroundImages } from "../../../../store"
|
|
||||||
import MedialibImage from "../../../widgets/MedialibImage.svelte"
|
|
||||||
|
|
||||||
export let block: ContentBlock<"ratingPreview">
|
|
||||||
let ratings: ProductRating[] = []
|
|
||||||
let productsMap: Record<string, BKDFProduct> = {}
|
|
||||||
getCachedEntries("rating", {
|
|
||||||
_id: {
|
|
||||||
$in: block?.ratingsPreview?.ratings?.map((rating) => rating.rating),
|
|
||||||
},
|
|
||||||
}).then((entries) => {
|
|
||||||
getBCGraphProductsByIds(entries.map((entry) => String(entry.bigCommerceProductId))).then((products) => {
|
|
||||||
productsMap = products.reduce((acc, product) => {
|
|
||||||
acc[product.id] = product
|
|
||||||
return acc
|
|
||||||
}, {})
|
|
||||||
ratings = entries
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
function formatDate(date: string) {
|
|
||||||
return new Date(date).toLocaleDateString("de-DE", {
|
|
||||||
year: "numeric",
|
|
||||||
month: "long",
|
|
||||||
day: "numeric",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function returnWidthForRating(rating: ProductRating) {
|
|
||||||
const ratingP =
|
|
||||||
(rating.rating.quality + rating.rating.priceQualityRatio + rating.rating.comfort + rating.rating.overall) *
|
|
||||||
5
|
|
||||||
return ratingP
|
|
||||||
}
|
|
||||||
|
|
||||||
function returnAvgForRating(rating: ProductRating) {
|
|
||||||
const ratingP =
|
|
||||||
(rating.rating.quality + rating.rating.priceQualityRatio + rating.rating.comfort + rating.rating.overall) /
|
|
||||||
4
|
|
||||||
return ratingP.toFixed(1)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#if ratings.length}
|
|
||||||
<div
|
|
||||||
data-simplebar
|
|
||||||
class="review-list-container horizontalScrollbar"
|
|
||||||
>
|
|
||||||
<ul class="review-list">
|
|
||||||
{#each ratings as rating}
|
|
||||||
<li class="review-item">
|
|
||||||
<div class="upper-box">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="48"
|
|
||||||
height="49"
|
|
||||||
viewBox="0 0 48 49"
|
|
||||||
fill="none"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M48 0.25V47.5833L0 0.25H48Z"
|
|
||||||
fill="white"></path>
|
|
||||||
</svg>
|
|
||||||
<div class="background-image">
|
|
||||||
<img
|
|
||||||
src="{productsMap[rating.bigCommerceProductId].featuredImage.url}"
|
|
||||||
alt="{rating.title}"
|
|
||||||
/>
|
|
||||||
<MedialibImage
|
|
||||||
id="{$backgroundImages?.standard}"
|
|
||||||
filter="l"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="overlay">
|
|
||||||
<h3>
|
|
||||||
{rating.title}
|
|
||||||
</h3>
|
|
||||||
<p>{rating.comment}</p>
|
|
||||||
<div class="date">{formatDate(rating.review_date)}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="rating-bar">
|
|
||||||
<div
|
|
||||||
class="rating-filled"
|
|
||||||
style="width: {returnWidthForRating(rating)}%; "
|
|
||||||
>
|
|
||||||
<div class="star-wrapper">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="25"
|
|
||||||
height="25"
|
|
||||||
viewBox="0 0 25 25"
|
|
||||||
fill="none"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M16.7692 23.1373L16.7717 23.1388C17.1926 23.3925 17.6791 23.5165 18.1701 23.4952C18.6612 23.474 19.1351 23.3085 19.5326 23.0193C19.9301 22.7302 20.2335 22.3303 20.405 21.8697C20.5762 21.4098 20.6083 20.9097 20.4972 20.4318C20.497 20.431 20.4968 20.4302 20.4967 20.4294L19.3653 15.5223L22.8594 12.4729H22.8607L23.1422 12.2302C23.515 11.9087 23.7846 11.4842 23.9171 11.0101C24.0496 10.536 24.0392 10.0333 23.8872 9.56506C23.7352 9.09682 23.4483 8.68389 23.0626 8.37804C22.6772 8.0725 22.2103 7.88743 21.7202 7.84595C21.7197 7.84591 21.7192 7.84587 21.7187 7.84583L16.7503 7.41534L14.8029 2.78442C14.8027 2.78387 14.8024 2.78331 14.8022 2.78276C14.6125 2.32902 14.2929 1.94145 13.8836 1.66874C13.4738 1.39569 12.9924 1.25 12.5 1.25C12.0076 1.25 11.5262 1.3957 11.1164 1.66874C10.7069 1.94157 10.3872 2.32937 10.1976 2.78337C10.1974 2.78372 10.1973 2.78407 10.1971 2.78442L8.25556 7.41539L3.28596 7.84583C3.28549 7.84586 3.28502 7.8459 3.28455 7.84594C2.7945 7.8874 2.32754 8.07248 1.94214 8.37804C1.55638 8.68388 1.2695 9.09682 1.11748 9.56506C0.965459 10.0333 0.955069 10.536 1.08761 11.0101L2.05069 10.7409L1.08761 11.0101C1.21983 11.4831 1.48842 11.9066 1.85981 12.2279L5.63577 15.5275L4.50617 20.4294C4.50605 20.43 4.50592 20.4305 4.50579 20.4311C4.39454 20.9092 4.42654 21.4096 4.59781 21.8697C4.76928 22.3303 5.07273 22.7302 5.47023 23.0193C5.86773 23.3085 6.34163 23.474 6.8327 23.4952C7.32376 23.5165 7.81019 23.3925 8.23118 23.1388L8.23442 23.1368L12.4968 20.546L16.7692 23.1373Z"
|
|
||||||
fill="#2F4858"
|
|
||||||
stroke="white"
|
|
||||||
stroke-width="2"></path>
|
|
||||||
</svg>
|
|
||||||
<span>{returnAvgForRating(rating)}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
{/each}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<style
|
|
||||||
lang="less"
|
|
||||||
global
|
|
||||||
>
|
|
||||||
.review-list-container {
|
|
||||||
width: 100%;
|
|
||||||
max-width: var(--normal-max-width);
|
|
||||||
.simplebar-wrapper {
|
|
||||||
padding-bottom: 1.2rem;
|
|
||||||
.simplebar-content {
|
|
||||||
max-width: var(--normal-max-width);
|
|
||||||
& > .inner-wrapper {
|
|
||||||
display: flex;
|
|
||||||
gap: 2.2rem;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.simplebar-track {
|
|
||||||
background-color: rgba(13, 12, 12, 0.25);
|
|
||||||
height: 7px;
|
|
||||||
overflow: visible;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
.simplebar-scrollbar {
|
|
||||||
transition-duration: 0ms !important;
|
|
||||||
cursor: pointer;
|
|
||||||
&::before {
|
|
||||||
background-color: var(--bg-100);
|
|
||||||
top: -2px;
|
|
||||||
opacity: 1;
|
|
||||||
border-radius: 0;
|
|
||||||
height: 11px;
|
|
||||||
left: 0px;
|
|
||||||
|
|
||||||
transition-delay: 0s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.review-list {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
gap: 1.2rem;
|
|
||||||
height: 400px;
|
|
||||||
align-items: flex-start;
|
|
||||||
.review-item {
|
|
||||||
overflow: visible;
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1px;
|
|
||||||
.upper-box {
|
|
||||||
width: 320px;
|
|
||||||
height: 320px;
|
|
||||||
position: relative;
|
|
||||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, #000100);
|
|
||||||
svg {
|
|
||||||
position: absolute;
|
|
||||||
top: 0px;
|
|
||||||
right: 0px;
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
position: absolute;
|
|
||||||
&:first-of-type {
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
&:last-of-type {
|
|
||||||
z-index: -2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.overlay {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
padding: 1.2rem;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 0.6rem;
|
|
||||||
h3 {
|
|
||||||
color: var(--neutral-white);
|
|
||||||
font-family: Outfit;
|
|
||||||
font-size: 1rem;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 700;
|
|
||||||
line-height: normal;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
color: var(--neutral-white);
|
|
||||||
font-family: Poly;
|
|
||||||
font-size: 1rem;
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: normal;
|
|
||||||
}
|
|
||||||
.date {
|
|
||||||
color: var(--neutral-white);
|
|
||||||
border-top: 1px solid var(--neutral-white);
|
|
||||||
font-size: 0.7rem;
|
|
||||||
padding-top: 6px;
|
|
||||||
width: fit-content;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.rating-bar {
|
|
||||||
width: 100%;
|
|
||||||
height: 9px;
|
|
||||||
background: rgba(47, 72, 88, 0.2);
|
|
||||||
.rating-filled {
|
|
||||||
background: var(--text-invers-100);
|
|
||||||
height: 100%;
|
|
||||||
left: 0px;
|
|
||||||
position: relative;
|
|
||||||
.star-wrapper {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 0.6rem;
|
|
||||||
padding: 0spx;
|
|
||||||
position: absolute;
|
|
||||||
top: 100%;
|
|
||||||
right: 0%;
|
|
||||||
transform: translate(50%, -35%);
|
|
||||||
span {
|
|
||||||
font-family: Outfit;
|
|
||||||
font-size: 20px;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 700;
|
|
||||||
line-height: 14px;
|
|
||||||
margin-top: -4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,203 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { onMount } from "svelte"
|
|
||||||
import { getDBEntries } from "../../../../api"
|
|
||||||
import { getVariableNameForChapter } from "../../../utils"
|
|
||||||
|
|
||||||
export let block: ContentBlock<"splittedHomepage">
|
|
||||||
let selfImprovementChapters: SelfImprovementChapter[] = []
|
|
||||||
let interval: NodeJS.Timeout
|
|
||||||
let selectedChapter = -1
|
|
||||||
let currentColor = "#741e20" // Initial color
|
|
||||||
|
|
||||||
getDBEntries("selfImprovementChapter").then((res) => {
|
|
||||||
selfImprovementChapters = res
|
|
||||||
if (selfImprovementChapters.length > 0) {
|
|
||||||
selectedChapter = -1
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
// set width and height of placeholder to elements width and height
|
|
||||||
const placeholder = document.querySelector(".placeholder")
|
|
||||||
const elements = document.querySelector(".elements")
|
|
||||||
if (placeholder && elements) {
|
|
||||||
const { width, height } = elements.getBoundingClientRect()
|
|
||||||
placeholder.style.width = `${width}px`
|
|
||||||
placeholder.style.height = `${height}px`
|
|
||||||
}
|
|
||||||
}, 10)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function startInterval() {
|
|
||||||
interval = setInterval(() => {
|
|
||||||
selectedChapter = (selectedChapter + 1) % selfImprovementChapters.length
|
|
||||||
updateShadowColor(selfImprovementChapters[selectedChapter]?.color || "#000")
|
|
||||||
}, 3000)
|
|
||||||
}
|
|
||||||
|
|
||||||
function stopInterval() {
|
|
||||||
clearInterval(interval)
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateShadowColor(newColor: string) {
|
|
||||||
const filter = document.getElementById("redShadow")
|
|
||||||
const feDropShadow = filter?.querySelector("feDropShadow")
|
|
||||||
if (feDropShadow) {
|
|
||||||
feDropShadow.style.transition = "flood-color 1s ease"
|
|
||||||
feDropShadow.setAttribute("flood-color", newColor)
|
|
||||||
currentColor = newColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
startInterval()
|
|
||||||
return () => clearInterval(interval)
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<section
|
|
||||||
class="splittedHomepage"
|
|
||||||
style="--color: {currentColor}"
|
|
||||||
>
|
|
||||||
<div class="placeholder"></div>
|
|
||||||
<ul class="elements">
|
|
||||||
{#each selfImprovementChapters as chapter, i}
|
|
||||||
<li
|
|
||||||
class:selected="{i == selectedChapter}"
|
|
||||||
on:mouseenter="{() => {
|
|
||||||
stopInterval()
|
|
||||||
selectedChapter = i
|
|
||||||
updateShadowColor(chapter.color)
|
|
||||||
}}"
|
|
||||||
on:mouseleave="{startInterval}"
|
|
||||||
>
|
|
||||||
{#if i == selectedChapter}
|
|
||||||
<h2
|
|
||||||
class="{i % 2 ? '' : 'transparent-heading'}"
|
|
||||||
style="{i % 2
|
|
||||||
? `color: var(${getVariableNameForChapter(chapter.type)})`
|
|
||||||
: `-webkit-text-stroke: 1px var(${getVariableNameForChapter(chapter.type)})`}"
|
|
||||||
>
|
|
||||||
{chapter.title}
|
|
||||||
</h2>
|
|
||||||
{:else}
|
|
||||||
<h2 class="{i % 2 ? 'white-heading' : 'transparent-heading'}">{chapter.alias}</h2>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<p style="color: var({getVariableNameForChapter(chapter.type)} !important;">
|
|
||||||
{@html chapter.shortDescription}
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
{/each}
|
|
||||||
</ul>
|
|
||||||
<div class="media">
|
|
||||||
<svg
|
|
||||||
viewBox="-100 -100 1200 1275"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<defs>
|
|
||||||
<filter
|
|
||||||
id="redShadow"
|
|
||||||
x="-50%"
|
|
||||||
y="-50%"
|
|
||||||
width="200%"
|
|
||||||
height="200%"
|
|
||||||
>
|
|
||||||
<feDropShadow
|
|
||||||
dx="-10"
|
|
||||||
dy="0"
|
|
||||||
stdDeviation="20"
|
|
||||||
flood-color="{currentColor}"></feDropShadow>
|
|
||||||
</filter>
|
|
||||||
</defs>
|
|
||||||
<path
|
|
||||||
d="M877.847 697.627H631.968L582.45 612.133H1000L904.042 446.139H481.362L605.704 231.311L680.46 360.44H854.319L685.658 69.1471L565.762 0L0 977.156L168.593 1074.55H738.253L794.132 977.772L728.062 863.552H973.941L877.847 697.627ZM204.637 924.082L396.895 592.025L589.153 924.082H204.637Z"
|
|
||||||
fill="black"
|
|
||||||
filter="url(#redShadow)"></path>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
@import "../../../assets/css/variables.less";
|
|
||||||
|
|
||||||
.splittedHomepage {
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
transition: --color 1s ease;
|
|
||||||
.placeholder {
|
|
||||||
}
|
|
||||||
.elements {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1.5rem;
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
@media @mobile {
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
li {
|
|
||||||
transition: max-height 1.5s ease;
|
|
||||||
|
|
||||||
max-height: 5rem;
|
|
||||||
max-width: 900px;
|
|
||||||
h2 {
|
|
||||||
font-size: 4.5rem;
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-weight: 700;
|
|
||||||
line-height: 4.5rem;
|
|
||||||
font-family: sans-serif;
|
|
||||||
@media @mobile {
|
|
||||||
font-size: 3rem;
|
|
||||||
line-height: 3rem;
|
|
||||||
}
|
|
||||||
&.transparent-heading {
|
|
||||||
@media @mobile {
|
|
||||||
font-size: 3rem;
|
|
||||||
}
|
|
||||||
font-weight: 700;
|
|
||||||
color: transparent;
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
-webkit-text-stroke: 1px white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
height: fit-content;
|
|
||||||
max-height: 10rem;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
p {
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 1s;
|
|
||||||
}
|
|
||||||
&.selected {
|
|
||||||
p {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.media {
|
|
||||||
max-width: 600px;
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
height: 100%;
|
|
||||||
align-items: center;
|
|
||||||
flex-grow: 1;
|
|
||||||
@media @mobile {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
svg {
|
|
||||||
width: 100%;
|
|
||||||
transition: fill 1s ease;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media @mobile {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import MedialibImage from "../../widgets/MedialibImage.svelte"
|
|
||||||
|
|
||||||
export let item: {
|
|
||||||
image: string
|
|
||||||
title: string
|
|
||||||
descriptions: string[]
|
|
||||||
}
|
|
||||||
export let i: number
|
|
||||||
export let isMobile: boolean
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<div class="image-wrapper">
|
|
||||||
<MedialibImage id="{item.image}" />
|
|
||||||
<h3>
|
|
||||||
{i + 1}
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<div class="title-row">
|
|
||||||
<h4>
|
|
||||||
{item.title}
|
|
||||||
</h4>
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="{isMobile ? 58 : 72}"
|
|
||||||
height="{isMobile ? 58 : 72}"
|
|
||||||
viewBox="0 0 {isMobile ? 58 : 72} {isMobile ? 58 : 72}"
|
|
||||||
fill="none"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M0 {isMobile ? 58 : 72}V0L{isMobile ? 58 : 72} {isMobile ? 58 : 72}H0Z"
|
|
||||||
fill="#EB5757"></path>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div class="descriptions">
|
|
||||||
<ul>
|
|
||||||
{#each item.descriptions as description}
|
|
||||||
<li>
|
|
||||||
{description}
|
|
||||||
</li>
|
|
||||||
{/each}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
@@ -1,172 +0,0 @@
|
|||||||
<script
|
|
||||||
lang="ts"
|
|
||||||
context="module"
|
|
||||||
>
|
|
||||||
import "simplebar"
|
|
||||||
import "simplebar/dist/simplebar.css"
|
|
||||||
import ResizeObserver from "resize-observer-polyfill"
|
|
||||||
if (typeof window !== "undefined") window.ResizeObserver = ResizeObserver
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import MedialibImage from "../../widgets/MedialibImage.svelte"
|
|
||||||
import Step from "./Step.svelte"
|
|
||||||
|
|
||||||
export let block: ContentBlock<"steps">
|
|
||||||
console.log("block", block)
|
|
||||||
let innerWidth = 0
|
|
||||||
$: isMobile = innerWidth < 968
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<svelte:window bind:innerWidth="{innerWidth}" />
|
|
||||||
|
|
||||||
{#if block.steps?.horizontal}
|
|
||||||
<div
|
|
||||||
class=" product-preview-list verticalScrollbar"
|
|
||||||
data-simplebar
|
|
||||||
>
|
|
||||||
<ul class="step-blocks horizontal">
|
|
||||||
{#each block.steps.items as item, i}
|
|
||||||
<Step
|
|
||||||
item="{item}"
|
|
||||||
i="{i}"
|
|
||||||
isMobile="{isMobile}"
|
|
||||||
/>
|
|
||||||
{/each}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<ul class="step-blocks">
|
|
||||||
{#each block.steps.items as item, i}
|
|
||||||
<Step
|
|
||||||
item="{item}"
|
|
||||||
i="{i}"
|
|
||||||
isMobile="{isMobile}"
|
|
||||||
/>
|
|
||||||
{/each}
|
|
||||||
</ul>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<style
|
|
||||||
lang="less"
|
|
||||||
global
|
|
||||||
>
|
|
||||||
@import "../../../assets/css/variables.less";
|
|
||||||
.step-blocks {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(560px, 1fr));
|
|
||||||
@media @mobile {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
&.horizontal {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
overflow-x: auto;
|
|
||||||
gap: 2.4rem;
|
|
||||||
padding-bottom: 2.4rem;
|
|
||||||
|
|
||||||
& > li {
|
|
||||||
min-width: 500px;
|
|
||||||
flex-grow: 1;
|
|
||||||
height: unset !important;
|
|
||||||
@media @mobile {
|
|
||||||
min-width: 300px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media @mobile {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gap: 2.4rem;
|
|
||||||
& > li {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100%;
|
|
||||||
.image-wrapper {
|
|
||||||
width: 100%;
|
|
||||||
position: relative;
|
|
||||||
aspect-ratio: 1 / 1;
|
|
||||||
h3 {
|
|
||||||
position: absolute;
|
|
||||||
right: 3.6rem;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 5.5rem;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 700;
|
|
||||||
line-height: 100%; /* 128px */
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-family: sans-serif;
|
|
||||||
z-index: 4;
|
|
||||||
bottom: 2.4rem;
|
|
||||||
|
|
||||||
@media @mobile {
|
|
||||||
font-size: 3rem;
|
|
||||||
bottom: 2rem;
|
|
||||||
right: 2.4rem;
|
|
||||||
}
|
|
||||||
font-weight: 700;
|
|
||||||
color: transparent;
|
|
||||||
|
|
||||||
display: inline-block;
|
|
||||||
-webkit-text-stroke: 2px #eb5757;
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.content {
|
|
||||||
margin-top: -3.6rem;
|
|
||||||
width: 100%;
|
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
z-index: 3;
|
|
||||||
.title-row {
|
|
||||||
height: 3.6rem;
|
|
||||||
max-height: 3.6rem;
|
|
||||||
z-index: 3;
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: center;
|
|
||||||
h4 {
|
|
||||||
background-color: #eb5757;
|
|
||||||
padding: 1.2rem 2.4rem;
|
|
||||||
font-size: 1.6rem;
|
|
||||||
max-height: 100%;
|
|
||||||
width: calc(100% - 3 * 3.6rem - 2rem);
|
|
||||||
color: var(--bg-100);
|
|
||||||
@media @mobile {
|
|
||||||
font-size: 1.2rem;
|
|
||||||
width: calc(100% - 3 * 2.4rem - 2rem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.descriptions {
|
|
||||||
background-color: #eb5757;
|
|
||||||
padding: 0px;
|
|
||||||
flex-grow: 1;
|
|
||||||
ul {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
li {
|
|
||||||
padding: 2.4rem;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
color: var(--bg-100);
|
|
||||||
border-bottom: 2px solid var(--bg-100);
|
|
||||||
&:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import Button from "../../../widgets/Button.svelte"
|
|
||||||
|
|
||||||
export let column: BlockColumn<"cta">
|
|
||||||
const cta = column.cta
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="cta"
|
|
||||||
id="cta"
|
|
||||||
>
|
|
||||||
{#if cta.upperHeadline}
|
|
||||||
<small>
|
|
||||||
{cta.upperHeadline}
|
|
||||||
</small>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<h1>
|
|
||||||
{cta.whiteHeadline}
|
|
||||||
{#if cta.headlineArrangement !== "row"} <br />{/if} <em class="red">{cta.redHeadline}</em>
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<p>{cta.description}</p>
|
|
||||||
<div class="buttons">
|
|
||||||
{#each cta.callToActionButtons as button}
|
|
||||||
<Button button="{button}" />
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
#cta {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1.6rem;
|
|
||||||
|
|
||||||
small {
|
|
||||||
font-weight: 700;
|
|
||||||
font-family: Outfit-Bold, sans-serif;
|
|
||||||
color: var(--text-100);
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
font-size: 1.2rem;
|
|
||||||
color: var(--text-100);
|
|
||||||
}
|
|
||||||
.buttons {
|
|
||||||
text-transform: uppercase;
|
|
||||||
display: flex;
|
|
||||||
gap: 0.3rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { getVariableNameForChapter } from "../../../../utils"
|
|
||||||
|
|
||||||
export let title: string, type: number, description: string
|
|
||||||
|
|
||||||
//
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h1 style="color: var({getVariableNameForChapter(type)});">
|
|
||||||
{title}
|
|
||||||
</h1>
|
|
||||||
<p style="color: var({getVariableNameForChapter(type)});">
|
|
||||||
{@html description}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
h1 {
|
|
||||||
font-size: 4.8rem;
|
|
||||||
text-transform: uppercase;
|
|
||||||
margin-bottom: 2.4rem;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
font-size: 1.2rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import LinkList from "../../../widgets/LinkList.svelte"
|
|
||||||
|
|
||||||
export let column: BlockColumn<"text">
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{@html column.text}
|
|
||||||
{#if column.links?.length}
|
|
||||||
<div class="button-wrap gap-m">
|
|
||||||
<!-- Buttons sitzen im Wrapper, sollten vermutlich auch flexibel / repeatable sein, mal nur einen, mal zwei, mal keiner -->
|
|
||||||
<LinkList links="{column.links}" />
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
@@ -1,193 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { onMount } from "svelte"
|
|
||||||
import Icon from "../../../widgets/Icon.svelte"
|
|
||||||
|
|
||||||
import { mdiCalendarAccount, mdiCalendarRemove } from "@mdi/js"
|
|
||||||
|
|
||||||
export let dateValue: Date,
|
|
||||||
placeholder: string,
|
|
||||||
id: string,
|
|
||||||
editMode = true
|
|
||||||
let Datepicker: any
|
|
||||||
onMount(() => {
|
|
||||||
if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
||||||
import("vanillajs-datepicker")
|
|
||||||
.then((module) => {
|
|
||||||
Datepicker = module.Datepicker
|
|
||||||
})
|
|
||||||
.catch((err) => console.error("Failed to load datepicker:", err))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
let datepicker: any
|
|
||||||
let wrapper: HTMLLabelElement
|
|
||||||
function getElementPosition(
|
|
||||||
element: HTMLElement,
|
|
||||||
options: {
|
|
||||||
minSpace: number
|
|
||||||
offsetAbove: number
|
|
||||||
} = { minSpace: 150, offsetAbove: 75 }
|
|
||||||
): {
|
|
||||||
x: string
|
|
||||||
y: string
|
|
||||||
} {
|
|
||||||
const rect = element.getBoundingClientRect()
|
|
||||||
const scrollTop = window.scrollY || window.pageYOffset
|
|
||||||
const windowHeight = window.innerHeight
|
|
||||||
const topBody = Number(document.body.style.top.split("px")[0])
|
|
||||||
let top = rect.bottom + scrollTop + 5 - topBody
|
|
||||||
|
|
||||||
// Check if there's the specified minimum visible space below the element
|
|
||||||
if (windowHeight - rect.bottom < options.minSpace) {
|
|
||||||
// Place the element above the current element with the specified offset
|
|
||||||
top = rect.top + scrollTop - element.offsetHeight - options.offsetAbove - topBody + 100
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
y: top + "px",
|
|
||||||
x: window.innerWidth - rect.right - 20 + "px",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function respawnElement(element: HTMLElement, deepCopy: boolean = true) {
|
|
||||||
var newElement = element.cloneNode(deepCopy)
|
|
||||||
element.parentNode.replaceChild(newElement, element)
|
|
||||||
return newElement
|
|
||||||
}
|
|
||||||
|
|
||||||
function hideAllModals() {
|
|
||||||
const dateModal = document.getElementById("dateModal")
|
|
||||||
dateModal.style.display = "none"
|
|
||||||
respawnElement(dateModal, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
function outSideDateModalClickListener(e: MouseEvent) {
|
|
||||||
const dateModal = document.getElementById("dateModal")
|
|
||||||
if (
|
|
||||||
!dateModal.contains(e.target as Node) &&
|
|
||||||
e.target !== document &&
|
|
||||||
!(e.target as Element).classList.contains("datepicker-cell")
|
|
||||||
) {
|
|
||||||
hideDatepicker()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function dateChangeListener(e: any) {
|
|
||||||
dateValue = new Date(e.detail.date)
|
|
||||||
// element has data-for="error-message-birthday" attribute, get it by that
|
|
||||||
const el = document.querySelector(`[data-for="error-message-${id}"]`)
|
|
||||||
if (el) {
|
|
||||||
el.remove()
|
|
||||||
}
|
|
||||||
hideDatepicker()
|
|
||||||
}
|
|
||||||
|
|
||||||
function hideDatepicker() {
|
|
||||||
const dateModal = document.getElementById("dateModal")
|
|
||||||
dateModal.style.display = "none"
|
|
||||||
datepicker?.destroy()
|
|
||||||
|
|
||||||
dateModal.removeEventListener("changeDate", dateChangeListener)
|
|
||||||
document.removeEventListener("click", outSideDateModalClickListener)
|
|
||||||
}
|
|
||||||
|
|
||||||
function showDatepicker() {
|
|
||||||
let dateModal = document.getElementById("dateModal")
|
|
||||||
hideAllModals()
|
|
||||||
if (datepicker) hideDatepicker()
|
|
||||||
dateModal = document.getElementById("dateModal")
|
|
||||||
dateModal.style.display = "block"
|
|
||||||
dateModal.style.top = getElementPosition(wrapper, { minSpace: 300, offsetAbove: 300 }).y
|
|
||||||
dateModal.style.right = getElementPosition(wrapper, { minSpace: 300, offsetAbove: 300 }).x
|
|
||||||
datepicker = new Datepicker(dateModal, {})
|
|
||||||
if (dateValue) datepicker.setDate(dateValue)
|
|
||||||
dateModal.addEventListener("changeDate", dateChangeListener)
|
|
||||||
document.addEventListener("click", outSideDateModalClickListener)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<label
|
|
||||||
bind:this="{wrapper}"
|
|
||||||
id="{id}"
|
|
||||||
class="custom-date-picker"
|
|
||||||
>
|
|
||||||
<span class:hasValue="{!!dateValue || !editMode}">{placeholder}</span>
|
|
||||||
{#if editMode}
|
|
||||||
<div class="wrapper">
|
|
||||||
{#if dateValue}
|
|
||||||
<button
|
|
||||||
data-cy="custom-date-picker-text"
|
|
||||||
class="btn text transparent"
|
|
||||||
on:click|preventDefault|stopPropagation="{showDatepicker}"
|
|
||||||
>
|
|
||||||
{new Date(dateValue).toLocaleDateString("de-DE")}
|
|
||||||
</button>
|
|
||||||
{:else}
|
|
||||||
<div style="opacity: 0.75;">{placeholder}</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<button
|
|
||||||
data-cy="custom-date-picker-icon"
|
|
||||||
aria-label="Toggle datepicker"
|
|
||||||
class="btn transparent icon"
|
|
||||||
on:click|preventDefault|stopPropagation="{() => {
|
|
||||||
hideAllModals()
|
|
||||||
if (dateValue) dateValue = null
|
|
||||||
else showDatepicker()
|
|
||||||
}}"
|
|
||||||
>
|
|
||||||
{#if !dateValue}
|
|
||||||
<Icon
|
|
||||||
path="{mdiCalendarAccount}"
|
|
||||||
width="24px"
|
|
||||||
height="24px"
|
|
||||||
/>
|
|
||||||
{:else}
|
|
||||||
<Icon
|
|
||||||
path="{mdiCalendarRemove}"
|
|
||||||
width="24px"
|
|
||||||
height="24px"
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
</button>
|
|
||||||
</div>{:else if !editMode}
|
|
||||||
{#if dateValue}
|
|
||||||
<div class="no-input">
|
|
||||||
{new Date(dateValue).toLocaleDateString("de-DE")}
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<div class="no-input">-</div>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
@import "vanillajs-datepicker/css/datepicker.css";
|
|
||||||
:global .hiddenscript {
|
|
||||||
opacity: 0;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
.custom-date-picker:global {
|
|
||||||
min-height: 60px;
|
|
||||||
flex-grow: 1;
|
|
||||||
height: 24px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
justify-content: space-between;
|
|
||||||
.wrapper {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
.btn.icon,
|
|
||||||
.btn.text {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
color: var(--text-invers-100);
|
|
||||||
|
|
||||||
&.text {
|
|
||||||
color: var(--text-invers-100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.icon {
|
|
||||||
opacity: 0.75;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,342 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { mdiCameraOutline, mdiInformationOutline, mdiMovieOpenOutline } from "@mdi/js"
|
|
||||||
import { apiBaseURL } from "../../../../../config"
|
|
||||||
import Icon from "../../../widgets/Icon.svelte"
|
|
||||||
import { createEventDispatcher } from "svelte"
|
|
||||||
import { newNotification } from "../../../../store"
|
|
||||||
import MedialibImage from "../../../widgets/MedialibImage.svelte"
|
|
||||||
import MedialibFile from "../../../widgets/MedialibFile.svelte"
|
|
||||||
import Modal from "../../../Modal.svelte"
|
|
||||||
import { tooltip } from "../../../../functions/utils"
|
|
||||||
const dispatch = createEventDispatcher()
|
|
||||||
export let id,
|
|
||||||
classList: string = "",
|
|
||||||
placeholder: string = "",
|
|
||||||
disabled: boolean = false,
|
|
||||||
helperText: string = "",
|
|
||||||
value: FileField = null,
|
|
||||||
collectionName: string,
|
|
||||||
entryId: string,
|
|
||||||
type: "image" | "video" = "image",
|
|
||||||
noDelete: boolean = false,
|
|
||||||
imgIsData = false
|
|
||||||
let file: File, fileInput: HTMLInputElement
|
|
||||||
let showApproveModal = false
|
|
||||||
function onChange(forceUpload = false) {
|
|
||||||
file = fileInput?.files[0]
|
|
||||||
const maxFileSize = 150 * 1024 * 1024 // 150 MB in Bytes
|
|
||||||
const supportedMimeTypes = [
|
|
||||||
// Images
|
|
||||||
"image/jpeg",
|
|
||||||
"image/png",
|
|
||||||
"image/gif",
|
|
||||||
"image/webp",
|
|
||||||
"image/bmp",
|
|
||||||
"image/tiff",
|
|
||||||
"image/svg+xml",
|
|
||||||
"video/mp4",
|
|
||||||
"video/webm",
|
|
||||||
"video/ogg",
|
|
||||||
"video/quicktime",
|
|
||||||
]
|
|
||||||
|
|
||||||
const supportedFormats = `
|
|
||||||
Unterstützte Bildformate: JPEG, PNG, GIF, WebP, BMP, TIFF, SVG.
|
|
||||||
Unterstützte Videoformate: MP4, WebM, Ogg.
|
|
||||||
`
|
|
||||||
|
|
||||||
const excludedMimeTypes = ["video/quicktime"]
|
|
||||||
const excludedExtensions = ["mov"]
|
|
||||||
|
|
||||||
if (file) {
|
|
||||||
const fileExtension = file?.name?.split(".")?.pop()?.toLowerCase()
|
|
||||||
|
|
||||||
if (
|
|
||||||
(excludedMimeTypes.includes(file.type) || excludedExtensions.includes(fileExtension)) &&
|
|
||||||
forceUpload !== true
|
|
||||||
) {
|
|
||||||
showApproveModal = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!supportedMimeTypes.includes(file.type)) {
|
|
||||||
newNotification({
|
|
||||||
class: "error",
|
|
||||||
html: `Der Dateityp - "${file.type}" wird nicht unterstützt. Bitte wähle eine unterstützte Datei aus. ${supportedFormats}`,
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const isSupported = supportedMimeTypes.includes(file.type)
|
|
||||||
|
|
||||||
if (!isSupported) {
|
|
||||||
newNotification({
|
|
||||||
class: "error",
|
|
||||||
html: `Der Dateityp "${file.type}" wird nicht unterstützt. Bitte wähle eine unterstützte Datei aus. ${supportedFormats}`,
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const reader = new FileReader()
|
|
||||||
if (file.size > maxFileSize) {
|
|
||||||
newNotification({
|
|
||||||
class: "error",
|
|
||||||
html: "Die Datei ist zu groß. Bitte wähle eine Datei unter 150 MB aus bzw. komprimiere die ausgewählte Datei.",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
reader.addEventListener("load", function () {
|
|
||||||
imgIsData = true
|
|
||||||
const objectURL = URL.createObjectURL(file)
|
|
||||||
value = {
|
|
||||||
path: file.name,
|
|
||||||
type: file.type,
|
|
||||||
size: file.size,
|
|
||||||
src: objectURL,
|
|
||||||
}
|
|
||||||
rerender = !rerender
|
|
||||||
dispatch("change")
|
|
||||||
})
|
|
||||||
// @ts-ignore
|
|
||||||
reader.readAsDataURL(file)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let highlight = false
|
|
||||||
let focused = false
|
|
||||||
let rerender = false
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<label class="file file-input-label">
|
|
||||||
<div class="headline">
|
|
||||||
<span class:hasValue="{true}">
|
|
||||||
{placeholder}
|
|
||||||
|
|
||||||
{#if helperText}
|
|
||||||
<div
|
|
||||||
use:tooltip="{{
|
|
||||||
content: helperText,
|
|
||||||
}}"
|
|
||||||
class="helperText"
|
|
||||||
>
|
|
||||||
<Icon path="{mdiInformationOutline}" />
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<Icon
|
|
||||||
path="{type == 'image' ? mdiCameraOutline : mdiMovieOpenOutline}"
|
|
||||||
size="24px"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="file-input"
|
|
||||||
class:focused="{focused}"
|
|
||||||
class:highlight-container="{highlight}"
|
|
||||||
role="button"
|
|
||||||
aria-label="File upload"
|
|
||||||
tabindex="{0}"
|
|
||||||
on:dragenter|preventDefault="{() => {
|
|
||||||
highlight = true
|
|
||||||
}}"
|
|
||||||
on:dragover|preventDefault="{() => {
|
|
||||||
highlight = true
|
|
||||||
}}"
|
|
||||||
on:dragleave|preventDefault="{() => {
|
|
||||||
highlight = false
|
|
||||||
}}"
|
|
||||||
on:drop|preventDefault="{(e) => {
|
|
||||||
highlight = false
|
|
||||||
fileInput = e.dataTransfer
|
|
||||||
onChange()
|
|
||||||
}}"
|
|
||||||
>
|
|
||||||
<div class="fileContainer">
|
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
bind:this="{fileInput}"
|
|
||||||
on:change|stopPropagation="{onChange}"
|
|
||||||
readonly="{disabled}"
|
|
||||||
id="{id}"
|
|
||||||
accept="{type === 'image' ? 'image/*' : 'video/*'}"
|
|
||||||
on:click|stopPropagation
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="filePreview"
|
|
||||||
class:hasChildElement="{!!value}"
|
|
||||||
>
|
|
||||||
{#key rerender}
|
|
||||||
{#if value}
|
|
||||||
{#if type == "image"}
|
|
||||||
{#if typeof value === "string"}
|
|
||||||
<MedialibImage
|
|
||||||
id="{value}"
|
|
||||||
filter="l"
|
|
||||||
/>
|
|
||||||
{:else if imgIsData}
|
|
||||||
<img
|
|
||||||
src="{value.src}"
|
|
||||||
alt="{value.path}"
|
|
||||||
/>
|
|
||||||
{:else if typeof value.src === "string" && value.src.includes(";base64,")}
|
|
||||||
<img
|
|
||||||
src="{value.src}"
|
|
||||||
alt="{value.path}"
|
|
||||||
/>
|
|
||||||
{:else if typeof value.src === "string" && !value.src.match(/^https?:\/\//)}
|
|
||||||
<img
|
|
||||||
src="{`${apiBaseURL}${collectionName}/${entryId}/${value.src}`}"
|
|
||||||
alt="{value.path}"
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
{:else if type == "video"}
|
|
||||||
{#if typeof value == "string"}
|
|
||||||
<MedialibFile
|
|
||||||
id="{value}"
|
|
||||||
let:entry
|
|
||||||
let:src
|
|
||||||
>
|
|
||||||
<video>
|
|
||||||
<track kind="captions" />
|
|
||||||
<source
|
|
||||||
src="{src}#t=0.1"
|
|
||||||
type="video/mp4"
|
|
||||||
/>
|
|
||||||
|
|
||||||
Your browser does not support the video tag.
|
|
||||||
</video>
|
|
||||||
</MedialibFile>
|
|
||||||
{:else}
|
|
||||||
<video
|
|
||||||
controls
|
|
||||||
preload="metadata"
|
|
||||||
>
|
|
||||||
<track kind="captions" />
|
|
||||||
<source
|
|
||||||
src="{value.src}#t=0.1"
|
|
||||||
type="video/mp4"
|
|
||||||
/>
|
|
||||||
</video>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
<div>
|
|
||||||
{#if noDelete}
|
|
||||||
<button
|
|
||||||
aria-label="Datei hochladen"
|
|
||||||
class="delete cta primary"
|
|
||||||
on:click|stopPropagation|preventDefault="{() => {
|
|
||||||
// change file by clicking input
|
|
||||||
fileInput.click()
|
|
||||||
}}"
|
|
||||||
>
|
|
||||||
Austauschen
|
|
||||||
</button>
|
|
||||||
{:else}
|
|
||||||
<button
|
|
||||||
aria-label="Datei löschen"
|
|
||||||
class="delete cta primary"
|
|
||||||
on:click|stopPropagation|preventDefault="{() => {
|
|
||||||
value = null
|
|
||||||
dispatch('change')
|
|
||||||
}}"
|
|
||||||
>
|
|
||||||
Löschen
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{/key}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
{#if showApproveModal}
|
|
||||||
<Modal
|
|
||||||
show="{true}"
|
|
||||||
on:close="{() => {
|
|
||||||
showApproveModal = false
|
|
||||||
}}"
|
|
||||||
>
|
|
||||||
<svelte:fragment slot="title">Dateiformat Warnung</svelte:fragment>
|
|
||||||
<p>
|
|
||||||
Deine Datei wird von einigen Browsern, insbesondere von Google Chrome, nur eingeschränkt unterstützt. Wenn
|
|
||||||
du die Datei dennoch hochladen möchtest, klicke auf "Fortfahren". Andernfalls empfehlen wir, das Video
|
|
||||||
zunächst in eine MP4-Datei zu konvertieren. Dafür stehen zahlreiche kostenlose Online-Dienste zur Verfügung.
|
|
||||||
</p>
|
|
||||||
<div
|
|
||||||
slot="footer"
|
|
||||||
class="file-warning-footer"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
class="btn cta primary"
|
|
||||||
on:click="{() => {
|
|
||||||
showApproveModal = false
|
|
||||||
}}"
|
|
||||||
>
|
|
||||||
Abbrechen
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn cta secondary"
|
|
||||||
on:click="{() => {
|
|
||||||
showApproveModal = false
|
|
||||||
onChange(true)
|
|
||||||
}}">Fortfahren</button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<style
|
|
||||||
lang="less"
|
|
||||||
global
|
|
||||||
>
|
|
||||||
.file-input-label {
|
|
||||||
.headline {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
width: 100%;
|
|
||||||
span {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
padding: 0px !important;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
.helperText {
|
|
||||||
color: var(--bg-100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.fileContainer {
|
|
||||||
height: fit-content;
|
|
||||||
padding: 0.6rem 0px !important;
|
|
||||||
|
|
||||||
input {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.filePreview {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 0.6rem;
|
|
||||||
min-height: 15px;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
&.hasChildElement {
|
|
||||||
overflow: hidden;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
img,
|
|
||||||
video {
|
|
||||||
height: 180px;
|
|
||||||
max-height: 180px;
|
|
||||||
width: 100% !important;
|
|
||||||
background-color: var(--bg-100);
|
|
||||||
object-fit: contain !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { mdiInformationOutline } from "@mdi/js"
|
|
||||||
import { tooltip } from "../../../../functions/utils"
|
|
||||||
import Icon from "../../../widgets/Icon.svelte"
|
|
||||||
|
|
||||||
export let value: any,
|
|
||||||
id,
|
|
||||||
classList: string = "",
|
|
||||||
onChange: (e: Event) => void,
|
|
||||||
type: "password" | "text" | "number" | "checkbox" | "noInput" | "textarea" | "select" = "text",
|
|
||||||
placeholder: string = "",
|
|
||||||
disabled: boolean = false,
|
|
||||||
name = "",
|
|
||||||
options: { name: string; value: string }[] = [],
|
|
||||||
selectedOptionIndex = 0,
|
|
||||||
helperText = ""
|
|
||||||
|
|
||||||
$: hasValue = Boolean(value)
|
|
||||||
|
|
||||||
const attributes = {
|
|
||||||
id,
|
|
||||||
class: classList,
|
|
||||||
placeholder,
|
|
||||||
name: name || id,
|
|
||||||
disabled: !!disabled,
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<label
|
|
||||||
style=""
|
|
||||||
class:textarea="{type == 'textarea'}"
|
|
||||||
class:checkbox="{type == 'checkbox'}"
|
|
||||||
>
|
|
||||||
{#if type !== "checkbox"}
|
|
||||||
<span class:hasValue="{hasValue || type === 'noInput'}">{placeholder}</span>
|
|
||||||
{/if}
|
|
||||||
{#if type == "checkbox"}
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
{...attributes}
|
|
||||||
on:change="{onChange}"
|
|
||||||
bind:checked="{value}"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="checkit-span"
|
|
||||||
aria-label="Toggle checkbox"
|
|
||||||
tabindex="{0}"
|
|
||||||
on:click="{() => {
|
|
||||||
value = !value
|
|
||||||
setTimeout(() => {
|
|
||||||
const event = new Event('change', { bubbles: true })
|
|
||||||
document.getElementById(id)?.dispatchEvent(event)
|
|
||||||
}, 10)
|
|
||||||
}}"
|
|
||||||
on:keydown="{(e) => {}}"></button>
|
|
||||||
{/if}
|
|
||||||
{#if type == "password"}
|
|
||||||
<input
|
|
||||||
{...attributes}
|
|
||||||
on:blur="{onChange}"
|
|
||||||
bind:value="{value}"
|
|
||||||
on:change="{onChange}"
|
|
||||||
type="password"
|
|
||||||
class="sentry-mask"
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
{#if type == "text"}
|
|
||||||
<input
|
|
||||||
on:blur="{onChange}"
|
|
||||||
{...attributes}
|
|
||||||
bind:value="{value}"
|
|
||||||
on:change="{onChange}"
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
{#if type == "textarea"}
|
|
||||||
<textarea
|
|
||||||
on:blur="{onChange}"
|
|
||||||
{...attributes}
|
|
||||||
bind:value="{value}"
|
|
||||||
on:change="{onChange}"></textarea>
|
|
||||||
{/if}
|
|
||||||
{#if type == "number"}
|
|
||||||
<input
|
|
||||||
on:blur="{onChange}"
|
|
||||||
type="number"
|
|
||||||
{...attributes}
|
|
||||||
bind:value="{value}"
|
|
||||||
on:change="{onChange}"
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
{#if type == "noInput"}
|
|
||||||
<div class="no-input">
|
|
||||||
{#if id.includes("pass")}
|
|
||||||
************
|
|
||||||
{:else}
|
|
||||||
{Boolean(value) ? value : "-"}
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{#if type == "select"}
|
|
||||||
<select
|
|
||||||
on:change="{onChange}"
|
|
||||||
{...attributes}
|
|
||||||
bind:value="{value}"
|
|
||||||
>
|
|
||||||
{#each options as option, index}
|
|
||||||
<option
|
|
||||||
value="{option.value}"
|
|
||||||
selected="{index === selectedOptionIndex}"
|
|
||||||
>
|
|
||||||
{option.name}
|
|
||||||
</option>
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
{/if}
|
|
||||||
{#if type !== "checkbox"}
|
|
||||||
<span class="underline"></span>
|
|
||||||
{/if}
|
|
||||||
{#if helperText}
|
|
||||||
<div
|
|
||||||
use:tooltip="{{
|
|
||||||
content: helperText,
|
|
||||||
}}"
|
|
||||||
class="helperText"
|
|
||||||
>
|
|
||||||
<Icon path="{mdiInformationOutline}" />
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
.checkbox {
|
|
||||||
width: 1.2rem !important;
|
|
||||||
}
|
|
||||||
.helperText {
|
|
||||||
position: absolute;
|
|
||||||
right: 0px;
|
|
||||||
color: var(--bg-100);
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import Select from "svelte-select/Select.svelte"
|
|
||||||
export let options: { label: string; value: string }[] = [],
|
|
||||||
selectedOption: { label: string; value: string } = null,
|
|
||||||
value = "",
|
|
||||||
placeholder: string,
|
|
||||||
clearable = false,
|
|
||||||
editMode = true
|
|
||||||
|
|
||||||
$: value = selectedOption?.value
|
|
||||||
$: hasValue = Boolean(value)
|
|
||||||
let focused = false
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
|
||||||
<!-- svelte-ignore a11y-no-noninteractive-element-to-interactive-role -->
|
|
||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
|
||||||
<!-- svelte-ignore a11y-label-has-associated-control -->
|
|
||||||
<label
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
on:click|stopPropagation="{() => {
|
|
||||||
focused = true
|
|
||||||
}}"
|
|
||||||
>
|
|
||||||
<span class:hasValue="{hasValue || !editMode}">{placeholder}</span>
|
|
||||||
{#if editMode}
|
|
||||||
<Select
|
|
||||||
bind:items="{options}"
|
|
||||||
inputAttributes="{{ autocomplete: 'on' }}"
|
|
||||||
placeholder="{placeholder}"
|
|
||||||
showChevron="{true}"
|
|
||||||
bind:focused="{focused}"
|
|
||||||
clearable="{clearable}"
|
|
||||||
hideEmptyState="{true}"
|
|
||||||
searchable="{true}"
|
|
||||||
bind:value="{selectedOption}"
|
|
||||||
on:clear="{() => (selectedOption = null)}"
|
|
||||||
/>
|
|
||||||
{:else if !editMode}
|
|
||||||
<div class="no-input">{selectedOption?.label || "-"}</div>
|
|
||||||
{/if}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
:global .svelte-select {
|
|
||||||
background-color: var(--neutral-white) !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: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
height: 100%;
|
|
||||||
font-size: 1rem !important;
|
|
||||||
border: 0px solid black !important;
|
|
||||||
}
|
|
||||||
* {
|
|
||||||
border-left: 0px solid black !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: var(--primary-200) !important;
|
|
||||||
font-weight: 700 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
import type { SvelteComponent } from "svelte"
|
|
||||||
import PredefinedBlock from "./PredefinedBlock.svelte"
|
|
||||||
import Columns from "./Columns.svelte"
|
|
||||||
import ProductListPreviewWidget from "../product/ProductListPreviewWidget.svelte"
|
|
||||||
import NewsletterRow from "./NewsletterRow.svelte"
|
|
||||||
import SplittedHomepage from "./SplittedHomepage.svelte"
|
|
||||||
import ImproveYourselfDescription from "./ImproveYourselfDescription.svelte"
|
|
||||||
import HomepageRow from "./HomepageRow.svelte"
|
|
||||||
import ChapterPreview from "./ChapterPreview/ChapterPreview.svelte"
|
|
||||||
import PreviewCard from "./RatingPreview/PreviewCard.svelte"
|
|
||||||
import Steps from "./Steps.svelte"
|
|
||||||
|
|
||||||
const blocks: {
|
|
||||||
[key: string]: {
|
|
||||||
sectionClass: string
|
|
||||||
component: typeof SvelteComponent<{ block?: ContentBlock }>
|
|
||||||
}
|
|
||||||
} = {
|
|
||||||
default: {
|
|
||||||
sectionClass: "",
|
|
||||||
component: Columns,
|
|
||||||
},
|
|
||||||
columns: {
|
|
||||||
sectionClass: "",
|
|
||||||
component: Columns,
|
|
||||||
},
|
|
||||||
|
|
||||||
predefinedBlock: {
|
|
||||||
sectionClass: "predefined-block",
|
|
||||||
component: PredefinedBlock,
|
|
||||||
},
|
|
||||||
productSlider: {
|
|
||||||
sectionClass: "",
|
|
||||||
component: ProductListPreviewWidget,
|
|
||||||
},
|
|
||||||
NewsletterRow: {
|
|
||||||
sectionClass: "newsletter-row",
|
|
||||||
component: NewsletterRow,
|
|
||||||
},
|
|
||||||
splittedHomepage: {
|
|
||||||
sectionClass: "",
|
|
||||||
component: SplittedHomepage,
|
|
||||||
},
|
|
||||||
improveYourselfDescription: {
|
|
||||||
sectionClass: "ImproveYourselfDescription",
|
|
||||||
component: ImproveYourselfDescription,
|
|
||||||
},
|
|
||||||
homepage: {
|
|
||||||
sectionClass: "homepageRow",
|
|
||||||
component: HomepageRow,
|
|
||||||
},
|
|
||||||
selfImprovementChapterPreview: {
|
|
||||||
sectionClass: "chapterPreview",
|
|
||||||
component: ChapterPreview,
|
|
||||||
},
|
|
||||||
ratingPreview: {
|
|
||||||
sectionClass: "ratingPreview",
|
|
||||||
component: PreviewCard,
|
|
||||||
},
|
|
||||||
stepNr: {
|
|
||||||
sectionClass: "stepNr",
|
|
||||||
component: Steps,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export default blocks
|
|
||||||
@@ -8,6 +8,9 @@
|
|||||||
mdiPoll,
|
mdiPoll,
|
||||||
} from "@mdi/js"
|
} from "@mdi/js"
|
||||||
import ProductCategoryFrame from "../widgets/ProductCategoryFrame.svelte"
|
import ProductCategoryFrame from "../widgets/ProductCategoryFrame.svelte"
|
||||||
|
import CrinkledSection from "../CrinkledSection.svelte"
|
||||||
|
import CardWrapper from "../widgets/CardWrapper.svelte"
|
||||||
|
import Chatbot from "../chatbotDemo/Chatbot.svelte"
|
||||||
|
|
||||||
const voiceProperties: Array<{ title: string; icon: string; color: string }> = [
|
const voiceProperties: Array<{ title: string; icon: string; color: string }> = [
|
||||||
{
|
{
|
||||||
@@ -33,17 +36,29 @@
|
|||||||
]
|
]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<section>
|
<CrinkledSection
|
||||||
<ProductCategoryFrame
|
brightBackground={false}
|
||||||
properties={voiceProperties}
|
border={true}
|
||||||
title="Chatbots"
|
activated={true}
|
||||||
reverse={true}
|
>
|
||||||
upperDescription="Unsere Voicebots sind rund um die Uhr für Ihre Kunden da und bieten maßgeschneiderte Lösungen, die perfekt auf Ihre Bedürfnisse abgestimmt sind."
|
{#snippet contentSnippet()}
|
||||||
lowerDescription="Durch den Einsatz modernster KI-Technologien gewährleisten wir eine intelligente und effiziente Kommunikation, die den höchsten Datenschutzstandards entspricht."
|
<ProductCategoryFrame
|
||||||
>
|
properties={voiceProperties}
|
||||||
{#snippet primaryContent()}{/snippet}
|
title="Chatbot Demo"
|
||||||
</ProductCategoryFrame>
|
reverse={true}
|
||||||
</section>
|
upperDescription="Unsere Voicebots sind rund um die Uhr für Ihre Kunden da und bieten maßgeschneiderte Lösungen, die perfekt auf Ihre Bedürfnisse abgestimmt sind."
|
||||||
|
lowerDescription="Durch den Einsatz modernster KI-Technologien gewährleisten wir eine intelligente und effiziente Kommunikation, die den höchsten Datenschutzstandards entspricht."
|
||||||
|
>
|
||||||
|
{#snippet primaryContent()}
|
||||||
|
<CardWrapper>
|
||||||
|
{#snippet contentSnippet()}
|
||||||
|
<Chatbot />
|
||||||
|
{/snippet}
|
||||||
|
</CardWrapper>
|
||||||
|
{/snippet}
|
||||||
|
</ProductCategoryFrame>
|
||||||
|
{/snippet}
|
||||||
|
</CrinkledSection>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
section {
|
section {
|
||||||
|
|||||||
@@ -77,84 +77,86 @@
|
|||||||
class="splittedHomepage"
|
class="splittedHomepage"
|
||||||
style="--color: {currentColor}"
|
style="--color: {currentColor}"
|
||||||
>
|
>
|
||||||
<div class="placeholder"></div>
|
<div class="wrapper">
|
||||||
<ul class="elements">
|
<div class="placeholder"></div>
|
||||||
{#each chapters as chapter, i}
|
<ul class="elements">
|
||||||
<li
|
{#each chapters as chapter, i}
|
||||||
class:selected={i == selectedChapter}
|
<li
|
||||||
on:mouseenter={() => {
|
class:selected={i == selectedChapter}
|
||||||
stopInterval()
|
on:mouseenter={() => {
|
||||||
selectedChapter = i
|
stopInterval()
|
||||||
updateShadowColor(chapter.color)
|
selectedChapter = i
|
||||||
}}
|
updateShadowColor(chapter.color)
|
||||||
on:mouseleave={startInterval}
|
}}
|
||||||
>
|
on:mouseleave={startInterval}
|
||||||
{#if i == selectedChapter}
|
>
|
||||||
<h2
|
{#if i == selectedChapter}
|
||||||
class={i % 2 ? "" : "transparent-heading"}
|
<h2
|
||||||
style={i % 2 ? `color: ${chapter.color}` : `-webkit-text-stroke: 1px ${chapter.color}`}
|
class={i % 2 ? "" : "transparent-heading"}
|
||||||
>
|
style={i % 2 ? `color: ${chapter.color}` : `-webkit-text-stroke: 1px ${chapter.color}`}
|
||||||
{chapter.title}
|
>
|
||||||
</h2>
|
{chapter.title}
|
||||||
{:else}
|
</h2>
|
||||||
<h2 class={i % 2 ? "white-heading" : "transparent-heading"}>{chapter.alias}</h2>
|
{:else}
|
||||||
{/if}
|
<h2 class={i % 2 ? "white-heading" : "transparent-heading"}>{chapter.alias}</h2>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<p style="color: {chapter.color} !important;">
|
<p style="color: {chapter.color} !important;">
|
||||||
{@html chapter.shortDescription}
|
{@html chapter.shortDescription}
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
<div class="media">
|
<div class="media">
|
||||||
<svg
|
<svg
|
||||||
viewBox="0 0 51 57"
|
viewBox="0 0 51 57"
|
||||||
fill="none"
|
fill="none"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<defs>
|
<defs>
|
||||||
<filter
|
<filter
|
||||||
id="redShadow"
|
id="redShadow"
|
||||||
x="-20%"
|
x="-20%"
|
||||||
y="-20%"
|
y="-20%"
|
||||||
width="200%"
|
width="200%"
|
||||||
height="200%"
|
height="200%"
|
||||||
primitiveUnits="objectBoundingBox"
|
primitiveUnits="objectBoundingBox"
|
||||||
>
|
>
|
||||||
<feDropShadow
|
<feDropShadow
|
||||||
dx="-0.01"
|
dx="-0.01"
|
||||||
dy="0"
|
dy="0"
|
||||||
stdDeviation="0.01"
|
stdDeviation="0.01"
|
||||||
flood-color="currentColor"
|
flood-color="currentColor"
|
||||||
></feDropShadow>
|
></feDropShadow>
|
||||||
</filter>
|
</filter>
|
||||||
<clipPath id="bd9e6d0ed5"
|
<clipPath id="bd9e6d0ed5"
|
||||||
><path
|
><path
|
||||||
d="M 165 159.082031 L 215.691406 159.082031 L 215.691406 216 L 165 216 Z M 165 159.082031 "
|
d="M 165 159.082031 L 215.691406 159.082031 L 215.691406 216 L 165 216 Z M 165 159.082031 "
|
||||||
clip-rule="nonzero"
|
clip-rule="nonzero"
|
||||||
></path></clipPath
|
></path></clipPath
|
||||||
><clipPath id="8bf626c89e"
|
><clipPath id="8bf626c89e"
|
||||||
><path
|
><path
|
||||||
d="M 0.601562 0.0820312 L 50.519531 0.0820312 L 50.519531 56.761719 L 0.601562 56.761719 Z M 0.601562 0.0820312 "
|
d="M 0.601562 0.0820312 L 50.519531 0.0820312 L 50.519531 56.761719 L 0.601562 56.761719 Z M 0.601562 0.0820312 "
|
||||||
clip-rule="nonzero"
|
clip-rule="nonzero"
|
||||||
></path></clipPath
|
></path></clipPath
|
||||||
><clipPath id="e48e3f235c"
|
><clipPath id="e48e3f235c"
|
||||||
><rect
|
><rect
|
||||||
x="0"
|
x="0"
|
||||||
width="51"
|
width="51"
|
||||||
y="0"
|
y="0"
|
||||||
height="57"
|
height="57"
|
||||||
></rect></clipPath
|
></rect></clipPath
|
||||||
>
|
>
|
||||||
</defs>
|
</defs>
|
||||||
<path
|
<path
|
||||||
fill="black"
|
fill="black"
|
||||||
d="M 32.6875 31.042969 C 40.058594 28.636719 47.421875 35.292969 45.523438 42.96875 C 43.078125 52.433594 32.925781 56.882812 22.929688 56.535156 L 22.929688 49.703125 C 39.636719 49.835938 45.316406 32.035156 32.6875 31.042969 M 29.917969 36.039062 C 30.808594 36.039062 31.53125 36.761719 31.53125 37.652344 C 31.53125 38.542969 30.808594 39.265625 29.917969 39.265625 C 29.027344 39.265625 28.304688 38.542969 28.304688 37.652344 C 28.304688 36.761719 29.027344 36.039062 29.917969 36.039062 Z M 24.203125 36.039062 C 25.09375 36.039062 25.816406 36.761719 25.816406 37.652344 C 25.816406 38.542969 25.09375 39.265625 24.203125 39.265625 C 23.3125 39.265625 22.589844 38.542969 22.589844 37.652344 C 22.589844 36.761719 23.3125 36.039062 24.203125 36.039062 Z M 18.484375 36.039062 C 19.378906 36.039062 20.097656 36.761719 20.097656 37.652344 C 20.097656 38.542969 19.378906 39.265625 18.484375 39.265625 C 17.59375 39.265625 16.871094 38.542969 16.871094 37.652344 C 16.871094 36.761719 17.59375 36.039062 18.484375 36.039062 Z M 28.578125 16.488281 C 36.304688 14.222656 46.632812 9.421875 49.421875 1.210938 C 50.289062 3.949219 50.550781 6.75 50.269531 9.457031 C 50.066406 11.445312 49.726562 13.175781 49.273438 14.691406 C 47.90625 18.25 45.480469 21.414062 42.160156 23.6875 C 42.496094 21.765625 42.253906 19.792969 41.460938 18.011719 C 36.949219 23.855469 21.6875 25.710938 14.578125 30.480469 C 11.671875 32.429688 9.898438 35.710938 9.898438 39.257812 C 9.898438 45.679688 18.644531 51.957031 22.769531 56.53125 C 13.492188 56.164062 4.390625 51.664062 1.753906 43.210938 C -5.558594 19.773438 30.507812 16.988281 42.691406 0.109375 C 42.144531 8.601562 36.023438 13.347656 28.578125 16.488281 Z M 28.578125 16.488281 "
|
d="M 32.6875 31.042969 C 40.058594 28.636719 47.421875 35.292969 45.523438 42.96875 C 43.078125 52.433594 32.925781 56.882812 22.929688 56.535156 L 22.929688 49.703125 C 39.636719 49.835938 45.316406 32.035156 32.6875 31.042969 M 29.917969 36.039062 C 30.808594 36.039062 31.53125 36.761719 31.53125 37.652344 C 31.53125 38.542969 30.808594 39.265625 29.917969 39.265625 C 29.027344 39.265625 28.304688 38.542969 28.304688 37.652344 C 28.304688 36.761719 29.027344 36.039062 29.917969 36.039062 Z M 24.203125 36.039062 C 25.09375 36.039062 25.816406 36.761719 25.816406 37.652344 C 25.816406 38.542969 25.09375 39.265625 24.203125 39.265625 C 23.3125 39.265625 22.589844 38.542969 22.589844 37.652344 C 22.589844 36.761719 23.3125 36.039062 24.203125 36.039062 Z M 18.484375 36.039062 C 19.378906 36.039062 20.097656 36.761719 20.097656 37.652344 C 20.097656 38.542969 19.378906 39.265625 18.484375 39.265625 C 17.59375 39.265625 16.871094 38.542969 16.871094 37.652344 C 16.871094 36.761719 17.59375 36.039062 18.484375 36.039062 Z M 28.578125 16.488281 C 36.304688 14.222656 46.632812 9.421875 49.421875 1.210938 C 50.289062 3.949219 50.550781 6.75 50.269531 9.457031 C 50.066406 11.445312 49.726562 13.175781 49.273438 14.691406 C 47.90625 18.25 45.480469 21.414062 42.160156 23.6875 C 42.496094 21.765625 42.253906 19.792969 41.460938 18.011719 C 36.949219 23.855469 21.6875 25.710938 14.578125 30.480469 C 11.671875 32.429688 9.898438 35.710938 9.898438 39.257812 C 9.898438 45.679688 18.644531 51.957031 22.769531 56.53125 C 13.492188 56.164062 4.390625 51.664062 1.753906 43.210938 C -5.558594 19.773438 30.507812 16.988281 42.691406 0.109375 C 42.144531 8.601562 36.023438 13.347656 28.578125 16.488281 Z M 28.578125 16.488281 "
|
||||||
fill-opacity="1"
|
fill-opacity="1"
|
||||||
fill-rule="evenodd"
|
fill-rule="evenodd"
|
||||||
filter="url(#redShadow)"
|
filter="url(#redShadow)"
|
||||||
></path>
|
></path>
|
||||||
</svg>
|
</svg>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -167,9 +169,24 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: center;
|
||||||
transition: --color 1s ease;
|
transition: --color 1s ease;
|
||||||
max-width: var(--normal-max-width);
|
background-color: var(--bg-100);
|
||||||
|
margin-top: -4.8rem;
|
||||||
|
padding: 4.8rem 0;
|
||||||
|
|
||||||
|
z-index: 1000;
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
max-width: var(--normal-max-width);
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
transition: --color 1s ease;
|
||||||
|
}
|
||||||
.elements {
|
.elements {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
{
|
{
|
||||||
title: "24/7 Verfügbar",
|
title: "24/7 Verfügbar",
|
||||||
icon: mdiHours24,
|
icon: mdiHours24,
|
||||||
color: "#FFFFFF",
|
color: "var(--text-reverse-100)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Individuell",
|
title: "Individuell",
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
{
|
{
|
||||||
title: "DSGVO konform",
|
title: "DSGVO konform",
|
||||||
icon: mdiBookAccountOutline,
|
icon: mdiBookAccountOutline,
|
||||||
color: "#FFFFFF",
|
color: "var(--text-reverse-100)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Intelligent",
|
title: "Intelligent",
|
||||||
@@ -27,11 +27,15 @@
|
|||||||
]
|
]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<CrinkledSection brightBackground={false}>
|
<CrinkledSection
|
||||||
|
brightBackground={true}
|
||||||
|
border={true}
|
||||||
|
activated={true}
|
||||||
|
>
|
||||||
{#snippet contentSnippet()}
|
{#snippet contentSnippet()}
|
||||||
<ProductCategoryFrame
|
<ProductCategoryFrame
|
||||||
properties={voiceProperties}
|
properties={voiceProperties}
|
||||||
title="Voicebots"
|
title="Voicebot Demo"
|
||||||
upperDescription="Unsere Voicebots sind rund um die Uhr für Ihre Kunden da und bieten maßgeschneiderte Lösungen, die perfekt auf Ihre Bedürfnisse abgestimmt sind."
|
upperDescription="Unsere Voicebots sind rund um die Uhr für Ihre Kunden da und bieten maßgeschneiderte Lösungen, die perfekt auf Ihre Bedürfnisse abgestimmt sind."
|
||||||
lowerDescription="Durch den Einsatz modernster KI-Technologien gewährleisten wir eine intelligente und effiziente Kommunikation, die den höchsten Datenschutzstandards entspricht."
|
lowerDescription="Durch den Einsatz modernster KI-Technologien gewährleisten wir eine intelligente und effiziente Kommunikation, die den höchsten Datenschutzstandards entspricht."
|
||||||
>
|
>
|
||||||
@@ -57,7 +61,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
img {
|
img {
|
||||||
width: 60%;
|
width: 60% !important;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
export let tabs: string[] = [],
|
|
||||||
selectedTab = 0
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="tabs">
|
|
||||||
{#each tabs as tab, i}
|
|
||||||
<button
|
|
||||||
class={selectedTab === i ? 'selected' : ''}
|
|
||||||
on:click={() => (selectedTab = i)}
|
|
||||||
>
|
|
||||||
{tab}
|
|
||||||
</button>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
.tabs {
|
|
||||||
border-top: 2px solid var(--krass-kraft-primary);
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
max-width: var(--normal-max-width);
|
|
||||||
|
|
||||||
button {
|
|
||||||
font-size: 1.2rem;
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-weight: 700;
|
|
||||||
color: var(--krass-kraft-primary);
|
|
||||||
padding: 12px 24px;
|
|
||||||
&.selected {
|
|
||||||
color: white;
|
|
||||||
background-color: var(--krass-kraft-primary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
67
frontend/src/lib/components/widgets/CardWrapper.svelte
Normal file
67
frontend/src/lib/components/widgets/CardWrapper.svelte
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
let { contentSnippet, headerSnippet }: { contentSnippet: () => any; headerSnippet?: () => any } = $props()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="card-wrapper">
|
||||||
|
<div class="header">
|
||||||
|
{#if headerSnippet}
|
||||||
|
{@render headerSnippet()}
|
||||||
|
{/if}
|
||||||
|
<span class="bar"></span>
|
||||||
|
<span class="crinkle">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="48"
|
||||||
|
height="48"
|
||||||
|
viewBox="0 0 48 48"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M1.32395 0.999996L50 49"
|
||||||
|
stroke={"white"}
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="body">
|
||||||
|
{@render contentSnippet()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.card-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
height: 2.4rem;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.bar {
|
||||||
|
flex-grow: 1;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 4px 0px;
|
||||||
|
border-top: 1px solid white;
|
||||||
|
border-left: 1px solid white;
|
||||||
|
}
|
||||||
|
.crinkle {
|
||||||
|
width: 2.4rem;
|
||||||
|
height: 2.4rem;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-left: -2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.body {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
gap: 1.2rem;
|
||||||
|
border: 1px solid white;
|
||||||
|
border-top: 0px solid white;
|
||||||
|
border-radius: 0px 0px 4px 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
upperDescription,
|
upperDescription,
|
||||||
lowerDescription,
|
lowerDescription,
|
||||||
reverse = false,
|
reverse = false,
|
||||||
|
|
||||||
primaryContent,
|
primaryContent,
|
||||||
}: {
|
}: {
|
||||||
properties: Array<{ title: string; icon: string; color: string }>
|
properties: Array<{ title: string; icon: string; color: string }>
|
||||||
@@ -18,36 +19,41 @@
|
|||||||
} = $props()
|
} = $props()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<section class="improveYourselfDescription">
|
<section
|
||||||
{#if !reverse}
|
class="product-frame"
|
||||||
<div class="primary">
|
class:reverse
|
||||||
{@render primaryContent()}
|
>
|
||||||
</div>
|
<div class="wrapper">
|
||||||
{/if}
|
{#if !reverse}
|
||||||
|
<div class="primary">
|
||||||
|
{@render primaryContent()}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<h2 class="h1">{title}</h2>
|
<h2 class="h1">{title}</h2>
|
||||||
<p>{@html upperDescription}</p>
|
<p>{@html upperDescription}</p>
|
||||||
<ul>
|
<ul>
|
||||||
{#each properties as property}
|
{#each properties as property}
|
||||||
<li>
|
<li>
|
||||||
<span style="color: {property.color};"
|
<span style="color: {property.color};"
|
||||||
><Icon
|
><Icon
|
||||||
path={property.icon}
|
path={property.icon}
|
||||||
size="2.4rem"
|
size="2.4rem"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span style="color: {property.color};">{property.title}</span>
|
<span style="color: {property.color};">{property.title}</span>
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
<p>{@html lowerDescription}</p>
|
<p>{@html lowerDescription}</p>
|
||||||
</div>
|
|
||||||
{#if reverse}
|
|
||||||
<div class="primary">
|
|
||||||
{@render primaryContent()}
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{#if reverse}
|
||||||
|
<div class="primary">
|
||||||
|
{@render primaryContent()}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<style
|
<style
|
||||||
@@ -55,94 +61,113 @@
|
|||||||
global
|
global
|
||||||
>
|
>
|
||||||
@import "../../assets/css/variables.less";
|
@import "../../assets/css/variables.less";
|
||||||
.improveYourselfDescription {
|
.product-frame {
|
||||||
|
background-color: white;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 2.4rem;
|
gap: 2.4rem;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
padding-bottom: 4.8rem;
|
||||||
|
|
||||||
.primary {
|
.wrapper {
|
||||||
width: 400px;
|
width: var(--small-max-width);
|
||||||
min-width: 400px;
|
|
||||||
max-height: 100%;
|
|
||||||
height: 100%;
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
.shadow {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.content {
|
|
||||||
height: 100%;
|
|
||||||
flex-grow: 1;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
justify-content: center;
|
||||||
justify-content: flex-start;
|
align-items: center;
|
||||||
gap: 2.4rem;
|
gap: 2.4rem;
|
||||||
z-index: 99;
|
|
||||||
p {
|
.primary {
|
||||||
color: var(--text-100);
|
width: 400px;
|
||||||
}
|
min-width: 400px;
|
||||||
ul {
|
max-height: 100%;
|
||||||
display: flex;
|
height: 100%;
|
||||||
width: 100%;
|
img {
|
||||||
justify-content: space-between;
|
width: 100%;
|
||||||
li {
|
height: 100%;
|
||||||
display: flex;
|
object-fit: contain;
|
||||||
flex-direction: column;
|
}
|
||||||
align-items: center;
|
.shadow {
|
||||||
gap: 0.6rem;
|
display: none;
|
||||||
span {
|
|
||||||
font-size: 0.8rem;
|
|
||||||
font-weight: 700;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
@media @mobile {
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 0px;
|
|
||||||
.content {
|
.content {
|
||||||
|
height: 100%;
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 2.4rem;
|
||||||
|
z-index: 99;
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: var(--text-reverse-100);
|
||||||
|
}
|
||||||
ul {
|
ul {
|
||||||
flex-direction: column;
|
display: flex;
|
||||||
gap: 1.2rem;
|
width: 100%;
|
||||||
|
justify-content: space-between;
|
||||||
li {
|
li {
|
||||||
flex-direction: row;
|
display: flex;
|
||||||
span:first-child {
|
flex-direction: column;
|
||||||
width: 100px;
|
align-items: center;
|
||||||
display: flex;
|
gap: 0.6rem;
|
||||||
justify-content: center;
|
span {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.img {
|
@media @mobile {
|
||||||
max-width: 100%;
|
flex-direction: column;
|
||||||
position: absolute;
|
padding: 0px;
|
||||||
bottom: 0px;
|
.content {
|
||||||
width: 100%;
|
ul {
|
||||||
height: 100%;
|
flex-direction: column;
|
||||||
z-index: 1;
|
gap: 1.2rem;
|
||||||
.shadow {
|
li {
|
||||||
display: block;
|
flex-direction: row;
|
||||||
|
span:first-child {
|
||||||
|
width: 100px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.img {
|
||||||
|
max-width: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: rgba(13, 12, 12, 0.5);
|
z-index: 1;
|
||||||
z-index: 2;
|
.shadow {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(13, 12, 12, 0.5);
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
h2 {
|
||||||
|
color: var(--primary-200);
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
h2 {
|
&.reverse {
|
||||||
color: var(--primary-200);
|
background-color: var(--bg-100);
|
||||||
font-weight: 700;
|
p {
|
||||||
|
color: var(--text-100) !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mdi/js": "^7.4.47",
|
"@mdi/js": "^7.4.47",
|
||||||
|
"@microsoft/fetch-event-source": "^2.0.1",
|
||||||
"@okrad/svelte-progressbar": "^2.2.0",
|
"@okrad/svelte-progressbar": "^2.2.0",
|
||||||
"@paypal/paypal-js": "^8.2.0",
|
"@paypal/paypal-js": "^8.2.0",
|
||||||
"@sentry/cli": "^2.45.0",
|
"@sentry/cli": "^2.45.0",
|
||||||
|
|||||||
@@ -1786,6 +1786,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@microsoft/fetch-event-source@npm:^2.0.1":
|
||||||
|
version: 2.0.1
|
||||||
|
resolution: "@microsoft/fetch-event-source@npm:2.0.1"
|
||||||
|
checksum: a50e1c0f33220206967266d0a4bbba0703e2793b079d9f6e6bfd48f71b2115964a803e14cf6e902c6fab321edc084f26022334f5eaacc2cec87f174715d41852
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@nicolo-ribaudo/chokidar-2@npm:2.1.8-no-fsevents.3":
|
"@nicolo-ribaudo/chokidar-2@npm:2.1.8-no-fsevents.3":
|
||||||
version: 2.1.8-no-fsevents.3
|
version: 2.1.8-no-fsevents.3
|
||||||
resolution: "@nicolo-ribaudo/chokidar-2@npm:2.1.8-no-fsevents.3"
|
resolution: "@nicolo-ribaudo/chokidar-2@npm:2.1.8-no-fsevents.3"
|
||||||
@@ -6081,6 +6088,7 @@ __metadata:
|
|||||||
"@babel/plugin-transform-async-to-generator": ^7.27.1
|
"@babel/plugin-transform-async-to-generator": ^7.27.1
|
||||||
"@babel/preset-env": ^7.27.2
|
"@babel/preset-env": ^7.27.2
|
||||||
"@mdi/js": ^7.4.47
|
"@mdi/js": ^7.4.47
|
||||||
|
"@microsoft/fetch-event-source": ^2.0.1
|
||||||
"@okrad/svelte-progressbar": ^2.2.0
|
"@okrad/svelte-progressbar": ^2.2.0
|
||||||
"@paypal/paypal-js": ^8.2.0
|
"@paypal/paypal-js": ^8.2.0
|
||||||
"@sentry/cli": ^2.45.0
|
"@sentry/cli": ^2.45.0
|
||||||
|
|||||||
Reference in New Issue
Block a user