zwischenstand
This commit is contained in:
@@ -138,82 +138,6 @@ meta:
|
|||||||
filter:
|
filter:
|
||||||
type: block
|
type: block
|
||||||
|
|
||||||
- name: helpcenterQuestion
|
|
||||||
label:
|
|
||||||
de: HC Fragen
|
|
||||||
en: HC Questions
|
|
||||||
muiIcon: book-open-page-variant
|
|
||||||
defaultSort:
|
|
||||||
field: insertTime
|
|
||||||
order: DESC
|
|
||||||
setDefault:
|
|
||||||
field: type
|
|
||||||
value: page
|
|
||||||
views:
|
|
||||||
- type: table
|
|
||||||
columns:
|
|
||||||
- source: active
|
|
||||||
name: Aktiv
|
|
||||||
filter: true
|
|
||||||
- source: name
|
|
||||||
name: Name
|
|
||||||
filter: true
|
|
||||||
- source: path
|
|
||||||
name: Pfad
|
|
||||||
filter: true
|
|
||||||
filter:
|
|
||||||
type: helpcenterQuestion
|
|
||||||
- name: Blog
|
|
||||||
label:
|
|
||||||
de: Blog
|
|
||||||
en: Blog
|
|
||||||
muiIcon: book-open-page-variant
|
|
||||||
defaultSort:
|
|
||||||
field: insertTime
|
|
||||||
order: DESC
|
|
||||||
setDefault:
|
|
||||||
field: type
|
|
||||||
value: page
|
|
||||||
views:
|
|
||||||
- type: table
|
|
||||||
columns:
|
|
||||||
- source: active
|
|
||||||
name: Aktiv
|
|
||||||
filter: true
|
|
||||||
- source: name
|
|
||||||
name: Name
|
|
||||||
filter: true
|
|
||||||
- source: path
|
|
||||||
name: Pfad
|
|
||||||
filter: true
|
|
||||||
filter:
|
|
||||||
type: blog
|
|
||||||
|
|
||||||
- name: product
|
|
||||||
label:
|
|
||||||
de: Produkte
|
|
||||||
en: Products
|
|
||||||
muiIcon: book-open-page-variant
|
|
||||||
defaultSort:
|
|
||||||
field: insertTime
|
|
||||||
order: DESC
|
|
||||||
setDefault:
|
|
||||||
field: type
|
|
||||||
value: product
|
|
||||||
views:
|
|
||||||
- type: table
|
|
||||||
columns:
|
|
||||||
- source: active
|
|
||||||
name: Aktiv
|
|
||||||
filter: true
|
|
||||||
- source: name
|
|
||||||
name: Name
|
|
||||||
filter: true
|
|
||||||
- source: path
|
|
||||||
name: Pfad
|
|
||||||
filter: true
|
|
||||||
filter:
|
|
||||||
type: product
|
|
||||||
permissions:
|
permissions:
|
||||||
public:
|
public:
|
||||||
methods:
|
methods:
|
||||||
@@ -279,21 +203,7 @@ fields:
|
|||||||
default: "col-6"
|
default: "col-6"
|
||||||
small: "col-12"
|
small: "col-12"
|
||||||
large: "col-6"
|
large: "col-6"
|
||||||
- type: string
|
|
||||||
name: question
|
|
||||||
index: [single]
|
|
||||||
meta:
|
|
||||||
label:
|
|
||||||
de: Frage
|
|
||||||
en: Question
|
|
||||||
dependsOn:
|
|
||||||
eval: $.type == "helpcenterQuestion"
|
|
||||||
containerProps:
|
|
||||||
layout:
|
|
||||||
size:
|
|
||||||
default: "col-6"
|
|
||||||
small: "col-12"
|
|
||||||
large: "col-6"
|
|
||||||
- type: string
|
- type: string
|
||||||
name: path
|
name: path
|
||||||
index: [single]
|
index: [single]
|
||||||
@@ -305,7 +215,7 @@ fields:
|
|||||||
de: "Ein Pfad sollte mit einem / starten und ohne enden."
|
de: "Ein Pfad sollte mit einem / starten und ohne enden."
|
||||||
en: "A path should start with a / and end without one."
|
en: "A path should start with a / and end without one."
|
||||||
dependsOn:
|
dependsOn:
|
||||||
eval: $.type == "page" || $.type == "helpcenterQuestion"
|
eval: $.type == "page"
|
||||||
containerProps:
|
containerProps:
|
||||||
layout:
|
layout:
|
||||||
size:
|
size:
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
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 Content from "./routes/Content.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 SSRSkip from "./lib/components/SSRSkip.svelte"
|
||||||
import { baseURL } from "./config"
|
import { baseURL } from "./config"
|
||||||
import { isMobile, location, openModal } from "./lib/store"
|
import { isMobile, location, openModal } from "./lib/store"
|
||||||
|
import StaticHomepage from "./routes/StaticHomepage.svelte"
|
||||||
|
|
||||||
export let url = ""
|
export let url = ""
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@
|
|||||||
<div class="app-shell">
|
<div class="app-shell">
|
||||||
<Header />
|
<Header />
|
||||||
<main>
|
<main>
|
||||||
<Content location={$location} />
|
<StaticHomepage />
|
||||||
</main>
|
</main>
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export const baseURL = `${protocol}//${baseDomain}`
|
|||||||
export const apiBaseURL = "/api/"
|
export const apiBaseURL = "/api/"
|
||||||
|
|
||||||
export const websiteName = "Kontextwerk"
|
export const websiteName = "Kontextwerk"
|
||||||
export const companyName = "Kontextwerk GmbH"
|
export const companyName = "Kontextwerk"
|
||||||
export const email = "hello@kontextwerk.de"
|
export const email = "hello@kontextwerk.de"
|
||||||
export const streetAddress = "Gertrudenstraße 3"
|
export const streetAddress = "Gertrudenstraße 3"
|
||||||
export const localityAddress = "Hamburg"
|
export const localityAddress = "Hamburg"
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<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}
|
||||||
@@ -0,0 +1,301 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import OpenGraph from "./OpenGraph.svelte"
|
||||||
|
import Product from "./Product.svelte"
|
||||||
|
import SchemaOrg from "./SchemaORG.svelte"
|
||||||
|
import { websiteName } from "../../../../config"
|
||||||
|
|
||||||
|
export let article: boolean = false,
|
||||||
|
createdAt: Date = null,
|
||||||
|
updatedAt: Date = null,
|
||||||
|
keywords: string = "",
|
||||||
|
metaDescription: string = "",
|
||||||
|
title: string = "",
|
||||||
|
product: BKDFProduct = null,
|
||||||
|
noIndex = false,
|
||||||
|
active = true,
|
||||||
|
FAQ = false,
|
||||||
|
FAQDetails: {
|
||||||
|
question: string
|
||||||
|
answer: string
|
||||||
|
}[] = []
|
||||||
|
|
||||||
|
let pageTitle: string
|
||||||
|
keywords += ", BinKrassDuFass, BKDF, BinKrassDuFass.de"
|
||||||
|
|
||||||
|
if (title) pageTitle = `${title} - ${websiteName}`
|
||||||
|
else pageTitle = `${websiteName}`
|
||||||
|
|
||||||
|
const openGraphProps = {
|
||||||
|
article,
|
||||||
|
datePublished: createdAt,
|
||||||
|
lastUpdated: updatedAt,
|
||||||
|
metaDescription,
|
||||||
|
pageTitle,
|
||||||
|
product,
|
||||||
|
}
|
||||||
|
const schemaOrgProps = {
|
||||||
|
createdAt,
|
||||||
|
updatedAt,
|
||||||
|
article,
|
||||||
|
description: metaDescription,
|
||||||
|
title: pageTitle,
|
||||||
|
product,
|
||||||
|
FAQ,
|
||||||
|
FAQDetails,
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>{pageTitle}</title>
|
||||||
|
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="{metaDescription}"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<meta
|
||||||
|
name="keywords"
|
||||||
|
content="{keywords}"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<meta
|
||||||
|
property="og:site_name"
|
||||||
|
content="{websiteName}"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{#if noIndex || active === false}
|
||||||
|
<meta
|
||||||
|
name="robots"
|
||||||
|
content="noindex, nofollow"
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
<meta
|
||||||
|
name="robots"
|
||||||
|
content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1"
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
{#if product}
|
||||||
|
<Product product="{product}" />
|
||||||
|
{/if}
|
||||||
|
<OpenGraph {...openGraphProps} />
|
||||||
|
|
||||||
|
<SchemaOrg {...schemaOrgProps} />
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { baseURL, companyName } from "../../../../config"
|
||||||
|
import { location } from "../../../store"
|
||||||
|
export let article: boolean = false
|
||||||
|
export let datePublished: Date | null
|
||||||
|
export let lastUpdated: Date | null
|
||||||
|
export let metaDescription: string | null
|
||||||
|
export let pageTitle: string | null
|
||||||
|
export let product: BKDFProduct
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<meta
|
||||||
|
property="og:locale"
|
||||||
|
content="{'de_DE'}"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
property="og:url"
|
||||||
|
content="{$location.url}"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
property="og:type"
|
||||||
|
content="{article ? 'article' : 'website'}"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
property="og:title"
|
||||||
|
content="{pageTitle}"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
property="og:description"
|
||||||
|
content="{metaDescription}"
|
||||||
|
/>
|
||||||
|
{#if product}
|
||||||
|
<meta
|
||||||
|
property="og:image"
|
||||||
|
content="{product.featuredImage?.url}"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
property="og:image:width"
|
||||||
|
content="{String(product.featuredImage?.width)}"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
property="og:image:height"
|
||||||
|
content="{String(product.featuredImage?.height)}"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
property="og:image:alt"
|
||||||
|
content="{product.title}"
|
||||||
|
/>
|
||||||
|
<!-- Twitter Card Tags for Product -->
|
||||||
|
<meta
|
||||||
|
name="twitter:card"
|
||||||
|
content="summary_large_image"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="twitter:title"
|
||||||
|
content="{pageTitle}"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="twitter:description"
|
||||||
|
content="{metaDescription}"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="twitter:image"
|
||||||
|
content="{product.featuredImage?.url}"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="twitter:image:alt"
|
||||||
|
content="{product.title}"
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
<meta
|
||||||
|
property="og:image"
|
||||||
|
content="{baseURL}/api/_/assets/logo/logo-blue.svg"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
property="og:image:width"
|
||||||
|
content="576"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
property="og:image:height"
|
||||||
|
content="158"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
property="og:image:alt"
|
||||||
|
content="BinKrassDuFass Logo"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Twitter Card Tags for Default -->
|
||||||
|
<meta
|
||||||
|
name="twitter:card"
|
||||||
|
content="summary_large_image"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="twitter:title"
|
||||||
|
content="{pageTitle}"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="twitter:description"
|
||||||
|
content="{metaDescription}"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="twitter:image"
|
||||||
|
content="{baseURL}/api/_/assets/logo/logo-blue.svg"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="twitter:image:alt"
|
||||||
|
content="BinKrassDuFass Logo"
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if article}
|
||||||
|
<meta
|
||||||
|
property="article:publisher"
|
||||||
|
content="{companyName}"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
property="article:author"
|
||||||
|
content="{companyName}"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
property="article:published_time"
|
||||||
|
content="{datePublished?.toISOString()}"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
property="article:modified_time"
|
||||||
|
content="{lastUpdated?.toISOString()}"
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</svelte:head>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { location } from "../../../store"
|
||||||
|
export let product: BKDFProduct
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<link rel="canonical" href="{$location.url}" />
|
||||||
|
|
||||||
|
<meta property="product:brand" content="BinKrassDuFass" />
|
||||||
|
|
||||||
|
<meta property="product:name" content="{product.title}" />
|
||||||
|
|
||||||
|
<meta property="product:price:amount" content="{String(product.priceRange.minVariantPrice.amount)}" />
|
||||||
|
|
||||||
|
<meta property="product:price:currency" content="{product.priceRange.minVariantPrice.currencyCode}" />
|
||||||
|
</svelte:head>
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {
|
||||||
|
baseURL,
|
||||||
|
socialIcons,
|
||||||
|
websiteName,
|
||||||
|
streetAddress,
|
||||||
|
regionAddress,
|
||||||
|
zipCode,
|
||||||
|
countryAddress,
|
||||||
|
localityAddress,
|
||||||
|
email,
|
||||||
|
} from "../../../../config"
|
||||||
|
import { location } from "../../../store"
|
||||||
|
export let createdAt: Date | string = new Date(),
|
||||||
|
updatedAt: Date | string = new Date(),
|
||||||
|
article = false,
|
||||||
|
blog = false,
|
||||||
|
FAQ = false,
|
||||||
|
FAQDetails: {
|
||||||
|
question: string
|
||||||
|
answer: string
|
||||||
|
}[] = [],
|
||||||
|
metaDescription = "",
|
||||||
|
title = "",
|
||||||
|
product: BKDFProduct
|
||||||
|
|
||||||
|
createdAt = new Date(createdAt).toLocaleDateString("de-DE", {
|
||||||
|
year: "numeric",
|
||||||
|
month: "2-digit",
|
||||||
|
day: "2-digit",
|
||||||
|
})
|
||||||
|
updatedAt = new Date(updatedAt).toLocaleDateString("de-DE", {
|
||||||
|
year: "numeric",
|
||||||
|
month: "2-digit",
|
||||||
|
day: "2-digit",
|
||||||
|
})
|
||||||
|
|
||||||
|
const defaultProps = {
|
||||||
|
author: {
|
||||||
|
"@id": `${baseURL}/#company`,
|
||||||
|
},
|
||||||
|
publisher: {
|
||||||
|
"@id": `${baseURL}/#company`,
|
||||||
|
},
|
||||||
|
inLanguage: {
|
||||||
|
"@type": "Language",
|
||||||
|
name: "German",
|
||||||
|
},
|
||||||
|
datePublished: createdAt,
|
||||||
|
dateModified: updatedAt,
|
||||||
|
isPartOf: {
|
||||||
|
"@id": `${baseURL}/#website`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const schemaOrgEntity = {
|
||||||
|
"@type": ["Store", "Organization"],
|
||||||
|
"@id": `${baseURL}/#company`,
|
||||||
|
name: websiteName,
|
||||||
|
url: baseURL,
|
||||||
|
email,
|
||||||
|
address: {
|
||||||
|
"@type": "PostalAddress",
|
||||||
|
streetAddress,
|
||||||
|
addressLocality: localityAddress,
|
||||||
|
addressRegion: regionAddress,
|
||||||
|
postalCode: zipCode,
|
||||||
|
addressCountry: countryAddress,
|
||||||
|
},
|
||||||
|
image: {
|
||||||
|
"@type": "ImageObject",
|
||||||
|
"@id": `${baseURL}/#image`,
|
||||||
|
inLanguage: {
|
||||||
|
"@type": "Language",
|
||||||
|
name: "German",
|
||||||
|
},
|
||||||
|
contentUrl: `${baseURL}/api/_/assets/logo/logo-blue.svg`,
|
||||||
|
width: 576,
|
||||||
|
height: 158,
|
||||||
|
caption: "BinKrassDuFass Logo",
|
||||||
|
},
|
||||||
|
logo: {
|
||||||
|
"@type": "ImageObject",
|
||||||
|
"@id": `${baseURL}/#logo`,
|
||||||
|
url: baseURL,
|
||||||
|
contentUrl: `${baseURL}/api/_/assets/logo/logo-blue.svg`,
|
||||||
|
caption: "BinKrassDuFass Logo",
|
||||||
|
inLanguage: {
|
||||||
|
"@type": "Language",
|
||||||
|
name: "German",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
priceRange: "$$",
|
||||||
|
location: {
|
||||||
|
"@id": location,
|
||||||
|
},
|
||||||
|
sameAs: [Object.values(socialIcons)],
|
||||||
|
}
|
||||||
|
|
||||||
|
const schemaOrgWebsite = {
|
||||||
|
"@type": "WebSite",
|
||||||
|
"@id": `${baseURL}/#website`,
|
||||||
|
url: baseURL,
|
||||||
|
name: "BKDF",
|
||||||
|
description: "BinKrassDuFass Online Store",
|
||||||
|
...defaultProps,
|
||||||
|
}
|
||||||
|
|
||||||
|
const schemaOrgWebPage = {
|
||||||
|
"@type": "WebPage",
|
||||||
|
"@id": `${$location.url}#webpage`,
|
||||||
|
url: $location.url,
|
||||||
|
name: title,
|
||||||
|
...defaultProps,
|
||||||
|
description: metaDescription,
|
||||||
|
|
||||||
|
potentialAction: [
|
||||||
|
{
|
||||||
|
"@type": "ReadAction",
|
||||||
|
target: [$location.url],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
let schemaOrgArticle = null
|
||||||
|
if (article || blog) {
|
||||||
|
schemaOrgArticle = {
|
||||||
|
"@type": "Article",
|
||||||
|
"@id": `${$location.url}#article`,
|
||||||
|
headline: title,
|
||||||
|
description: metaDescription,
|
||||||
|
...defaultProps,
|
||||||
|
articleSection: ["blog"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let schemaOrgBlog = null
|
||||||
|
if (blog) {
|
||||||
|
schemaOrgBlog = {
|
||||||
|
"@type": "BlogPosting",
|
||||||
|
"@id": `${$location.url}#blog`,
|
||||||
|
headline: title,
|
||||||
|
description: metaDescription,
|
||||||
|
...defaultProps,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let schemaOrgFAQ = null
|
||||||
|
if (FAQ) {
|
||||||
|
schemaOrgFAQ = {
|
||||||
|
"@type": "FAQPage",
|
||||||
|
"@id": `${$location.url}#faq`,
|
||||||
|
...defaultProps,
|
||||||
|
mainEntity: FAQDetails.map((faq) => ({
|
||||||
|
"@type": "Question",
|
||||||
|
name: faq.question,
|
||||||
|
acceptedAnswer: {
|
||||||
|
"@type": "Answer",
|
||||||
|
text: faq.answer,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let schemaOrgProduct = null
|
||||||
|
if (product)
|
||||||
|
schemaOrgProduct = {
|
||||||
|
"@context": "http://schema.org/",
|
||||||
|
"@type": "Product",
|
||||||
|
"@id": `${$location.url}/#product`,
|
||||||
|
...defaultProps,
|
||||||
|
image: {
|
||||||
|
url: product.images[0]?.url,
|
||||||
|
caption: product.images[0]?.altText,
|
||||||
|
},
|
||||||
|
name: product.title,
|
||||||
|
description: product.description,
|
||||||
|
sku: product.sku,
|
||||||
|
brand: "BinKrassDuFass",
|
||||||
|
category: product.categories[0]?.name,
|
||||||
|
mainEntityOfPage: {
|
||||||
|
"@id": `${$location.url}#webpage`,
|
||||||
|
},
|
||||||
|
releaseDate: createdAt,
|
||||||
|
dateModified: updatedAt,
|
||||||
|
url: $location.url,
|
||||||
|
offers: {
|
||||||
|
"@type": "Offer",
|
||||||
|
availability: "http://schema.org/InStock",
|
||||||
|
url: $location.url,
|
||||||
|
price: product.priceRange.minVariantPrice.amount,
|
||||||
|
priceCurrency: product.priceRange.minVariantPrice.currencyCode,
|
||||||
|
itemCondition: "NewCondition",
|
||||||
|
seller: {
|
||||||
|
"@type": "Organization",
|
||||||
|
"@id": `${baseURL}/#company`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const schemaOrgArray = [
|
||||||
|
schemaOrgEntity,
|
||||||
|
schemaOrgWebsite,
|
||||||
|
schemaOrgWebPage,
|
||||||
|
schemaOrgProduct,
|
||||||
|
schemaOrgFAQ,
|
||||||
|
schemaOrgArticle,
|
||||||
|
schemaOrgBlog,
|
||||||
|
].filter(Boolean)
|
||||||
|
const schemaOrgObject = {
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@graph": schemaOrgArray,
|
||||||
|
}
|
||||||
|
let jsonLdString = JSON.stringify(schemaOrgObject)
|
||||||
|
let jsonLdScript = `
|
||||||
|
<script type="application/ld+json">
|
||||||
|
${jsonLdString}
|
||||||
|
${"<"}/script>
|
||||||
|
`
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
{@html jsonLdScript}
|
||||||
|
</svelte:head>
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,350 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
<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
@@ -0,0 +1,12 @@
|
|||||||
|
<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}
|
||||||
@@ -0,0 +1,261 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<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}
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,342 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
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
|
||||||
@@ -36,31 +36,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if loading}
|
{#if loading}
|
||||||
<section class="content-loading">
|
|
||||||
<p>Inhalt wird geladen …</p>
|
|
||||||
</section>
|
|
||||||
{:else if errorMessage}
|
|
||||||
<section class="content-error">
|
|
||||||
<p>{errorMessage}</p>
|
|
||||||
</section>
|
|
||||||
{:else if contentEntry}
|
|
||||||
<article class="content-article">
|
|
||||||
<header>
|
|
||||||
<h1>{contentEntry.meta?.title ?? contentEntry.name}</h1>
|
|
||||||
{#if contentEntry.meta?.description}
|
|
||||||
<p class="lead">{contentEntry.meta.description}</p>
|
|
||||||
{/if}
|
|
||||||
</header>
|
|
||||||
|
|
||||||
{#if contentEntry.blocks?.length}
|
|
||||||
<section class="content-debug">
|
|
||||||
<h2>Struktur</h2>
|
|
||||||
<pre>{JSON.stringify(contentEntry.blocks, null, 2)}</pre>
|
|
||||||
</section>
|
|
||||||
{:else}
|
|
||||||
<p>Für diesen Inhalt sind noch keine Bausteine definiert.</p>
|
|
||||||
{/if}
|
|
||||||
</article>
|
|
||||||
{:else}
|
{:else}
|
||||||
<NotFound />
|
<NotFound />
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
Reference in New Issue
Block a user