forked from cms/tibi-svelte-starter
Artikel rendern slug für eventuelle Anker Links. Navigation vereinfacht und Item in eigene Komponente ausgelagert, um später schöner eine Multi-Level Navigation erstellen zu können. Label in Navigation-Collection umbenannt. Home-Page Komponente vereinfacht. Content-Komponente für eventuelles Animated-Ancher-Scrolling erweitert.
This commit is contained in:
parent
345ecb6177
commit
1bfa0d8b1b
@ -144,7 +144,7 @@ x-url: &url
|
||||
defaultValue: "default"
|
||||
choices:
|
||||
- { id: "default", name: "Gleicher Tab oder Seite (ohne Refresh)" }
|
||||
- { id: "_self", name: "Gleicher Tab oder Seite (Standardwert)" }
|
||||
- { id: "_self", name: "Gleicher Tab oder Seite (mit Refresh)" }
|
||||
- { id: "_blank", name: "Neuer Tab oder Fenster" }
|
||||
- { id: "_parent", name: "Elternfenster" }
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
import { _ } from "svelte-i18n"
|
||||
import { Router, Route } from "svelte-routing"
|
||||
import { scrollToTop } from "svelte-scrollto"
|
||||
import { location } from "../store"
|
||||
import { location, generalInfo } from "../store"
|
||||
|
||||
import Home from "./routes/Home.svelte"
|
||||
import Content from "./routes/Content.svelte"
|
||||
@ -31,6 +31,33 @@
|
||||
if (typeof window !== "undefined") console.log("App initialized")
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
{#if $generalInfo?.meta?.metaTitle}
|
||||
<title>{$generalInfo?.meta?.metaTitle}</title>
|
||||
{/if}
|
||||
{#if $generalInfo?.meta?.metaDescription}
|
||||
<meta name="description" content="{$generalInfo?.meta?.metaDescription}" />
|
||||
{/if}
|
||||
{#if $generalInfo?.meta?.metaKeywords}
|
||||
<meta name="keywords" content="{$generalInfo?.meta?.metaKeywords.replaceAll(' ', '')}" />
|
||||
{/if}
|
||||
{#if $generalInfo?.person?.firstname || $generalInfo?.person?.lastname}
|
||||
<meta
|
||||
name="author"
|
||||
content="{$generalInfo?.person?.firstname ? $generalInfo?.person?.firstname : ''} {$generalInfo?.person
|
||||
?.lastname
|
||||
? $generalInfo?.person?.lastname
|
||||
: ''}"
|
||||
/>
|
||||
{/if}
|
||||
{#if $generalInfo?.meta?.metaTagRobots && $generalInfo?.meta?.metaTagRobots.length}
|
||||
<meta name="robots" content="{$generalInfo?.meta?.metaTagRobots}" />
|
||||
{/if}
|
||||
{#if $generalInfo?.media?.favicon}
|
||||
<link rel="shortcut icon" type="image/x-icon" href="{$generalInfo?.media?.favicon.src}" />
|
||||
{/if}
|
||||
</svelte:head>
|
||||
|
||||
<Header />
|
||||
|
||||
<Router url="{url}">
|
||||
|
@ -1,4 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { afterUpdate } from "svelte"
|
||||
import * as animateScroll from "svelte-scrollto"
|
||||
import { _ } from "svelte-i18n"
|
||||
import { getContent, getArticles } from "../../api"
|
||||
import { generalInfo, currentLang, location } from "../../store"
|
||||
@ -67,6 +69,7 @@
|
||||
})
|
||||
.finally(() => {
|
||||
loading = false
|
||||
scrollToAnchor()
|
||||
})
|
||||
}
|
||||
|
||||
@ -103,6 +106,7 @@
|
||||
})
|
||||
.finally(() => {
|
||||
loading = false
|
||||
scrollToAnchor()
|
||||
})
|
||||
}
|
||||
|
||||
@ -130,6 +134,23 @@
|
||||
loadArticle()
|
||||
}
|
||||
}
|
||||
|
||||
afterUpdate(() => {
|
||||
scrollToAnchor()
|
||||
})
|
||||
|
||||
const scrollToAnchor = () => {
|
||||
if (content && window.location.hash) {
|
||||
window.setTimeout(() => {
|
||||
animateScroll.scrollTo({
|
||||
delay: 100,
|
||||
element: window.location.hash,
|
||||
offset: -200,
|
||||
onDone: () => {},
|
||||
})
|
||||
}, 500)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
|
@ -1,44 +1,14 @@
|
||||
<script lang="ts">
|
||||
import * as animateScroll from "svelte-scrollto"
|
||||
|
||||
import { generalInfo } from "../../store"
|
||||
// import { generalInfo } from "../../store"
|
||||
|
||||
// import ContactForm from "../widgets/ContactForm.svelte"
|
||||
// import GoogleMaps from "../widgets/GoogleMaps.svelte"
|
||||
import ScrollTo from "../widgets/ScrollTo.svelte"
|
||||
import GeneralMediaImage from "../widgets/GeneralMediaImage.svelte"
|
||||
// import GeneralMediaImage from "../widgets/GeneralMediaImage.svelte"
|
||||
import ArticlesList from "../widgets/ArticlesList.svelte"
|
||||
|
||||
let expandedForm: string = "recipe"
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
{#if $generalInfo?.meta?.metaTitle}
|
||||
<title>{$generalInfo?.meta?.metaTitle}</title>
|
||||
{/if}
|
||||
{#if $generalInfo?.meta?.metaDescription}
|
||||
<meta name="description" content="{$generalInfo?.meta?.metaDescription}" />
|
||||
{/if}
|
||||
{#if $generalInfo?.meta?.metaKeywords}
|
||||
<meta name="keywords" content="{$generalInfo?.meta?.metaKeywords.replaceAll(' ', '')}" />
|
||||
{/if}
|
||||
{#if $generalInfo?.person?.firstname || $generalInfo?.person?.lastname}
|
||||
<meta
|
||||
name="author"
|
||||
content="{$generalInfo?.person?.firstname ? $generalInfo?.person?.firstname : ''} {$generalInfo?.person
|
||||
?.lastname
|
||||
? $generalInfo?.person?.lastname
|
||||
: ''}"
|
||||
/>
|
||||
{/if}
|
||||
{#if $generalInfo?.meta?.metaTagRobots && $generalInfo?.meta?.metaTagRobots.length}
|
||||
<meta name="robots" content="{$generalInfo?.meta?.metaTagRobots}" />
|
||||
{/if}
|
||||
{#if $generalInfo?.media?.favicon}
|
||||
<link rel="shortcut icon" type="image/x-icon" href="{$generalInfo?.media?.favicon.src}" />
|
||||
{/if}
|
||||
</svelte:head>
|
||||
|
||||
<ArticlesList pages="{['/']}" />
|
||||
|
||||
<!-- <GeneralMediaImage id="test1" /> -->
|
||||
@ -46,17 +16,3 @@
|
||||
<!-- <ContactForm type="recipe" collapsed="{expandedForm !== 'recipe'}" /> -->
|
||||
<!-- <ContactForm type="contact" collapsed="{expandedForm !== 'contact'}" /> -->
|
||||
<!-- <GoogleMaps /> -->
|
||||
|
||||
<ScrollTo
|
||||
on:scrollTo="{(e) => {
|
||||
expandedForm = null
|
||||
animateScroll.scrollTo({
|
||||
delay: 100,
|
||||
element: '#' + e.detail.element,
|
||||
offset: -200,
|
||||
onDone: () => {
|
||||
expandedForm = e.detail.element
|
||||
},
|
||||
})
|
||||
}}"
|
||||
/>
|
||||
|
@ -66,7 +66,10 @@
|
||||
</script>
|
||||
|
||||
{#if article && published}
|
||||
<article class="{cssClass} {article?.layout?.variant} {marginClasses} {paddingClasses}">
|
||||
<article
|
||||
class="{cssClass} {article?.layout?.variant} {marginClasses} {paddingClasses}"
|
||||
id="{article?.content?.slug}"
|
||||
>
|
||||
{#if article?.layout?.variant === "top"}
|
||||
{#if article?.content?.types?.media?.files?.length}
|
||||
<TibiArticleMediaFile
|
||||
|
@ -1,12 +1,11 @@
|
||||
<script lang="ts">
|
||||
import * as animateScroll from "svelte-scrollto"
|
||||
import Icon from "mdi-svelte"
|
||||
import { mdiMenu } from "@mdi/js"
|
||||
|
||||
import { links, link } from "svelte-routing"
|
||||
import { navigations, currentLang, location } from "../../store"
|
||||
import { navigations, currentLang } from "../../store"
|
||||
|
||||
import LanguageChooser from "./LanguageChooser.svelte"
|
||||
import NavigationItem from "./NavigationItem.svelte"
|
||||
|
||||
export let ident = "main"
|
||||
|
||||
@ -26,36 +25,9 @@
|
||||
<LanguageChooser />
|
||||
|
||||
{#if navigation}
|
||||
<nav class="{ident}" use:links>
|
||||
<nav class="{ident}">
|
||||
{#each navigation?.items || [] as item}
|
||||
{#if !item.settings.url.hidden}
|
||||
{#if item.settings.url.url}
|
||||
{#if item.settings.url.target === "default"}
|
||||
<a
|
||||
use:link
|
||||
href="{item.settings.url.url}"
|
||||
class:active="{'/' + item.settings.page === $location.path}"
|
||||
>
|
||||
{item.settings.title}
|
||||
</a>
|
||||
{:else}
|
||||
<a
|
||||
href="{item.settings.url.url}"
|
||||
target="{item.settings.url.target}"
|
||||
on:click="{() => {
|
||||
animateScroll.scrollTo({ element: item.settings.url.url, offset: -200 })
|
||||
showMobileNav = false
|
||||
}}"
|
||||
>
|
||||
{item.settings.title}
|
||||
</a>
|
||||
{/if}
|
||||
{:else}
|
||||
<a href="{item.settings.page + '/'}" class:active="{'/' + item.settings.page === $location.path}">
|
||||
{item.settings.title}
|
||||
</a>
|
||||
{/if}
|
||||
{/if}
|
||||
<NavigationItem item="{item}" />
|
||||
{/each}
|
||||
</nav>
|
||||
|
||||
@ -64,44 +36,9 @@
|
||||
<Icon path="{mdiMenu}" size="2" />
|
||||
</div>
|
||||
|
||||
<nav class="{ident}-mobile" class:show="{showMobileNav}" use:links>
|
||||
<nav class="{ident}-mobile" class:show="{showMobileNav}">
|
||||
{#each navigation?.items || [] as item}
|
||||
{#if !item.settings.url.hidden}
|
||||
{#if item.settings.url.url}
|
||||
{#if item.settings.url.target === "default"}
|
||||
<a
|
||||
use:link
|
||||
href="{item.settings.url.url}"
|
||||
class:active="{'/' + item.settings.page === $location.path}"
|
||||
>
|
||||
{item.settings.title}
|
||||
</a>
|
||||
{:else}
|
||||
<div class="nav-item">
|
||||
<a
|
||||
href="{item.settings.url.url}"
|
||||
target="{item.settings.url.target}"
|
||||
on:click="{() => {
|
||||
animateScroll.scrollTo({ element: item.settings.url.url, offset: -200 })
|
||||
showMobileNav = false
|
||||
}}"
|
||||
>
|
||||
{item.settings.title}
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="nav-item">
|
||||
<a
|
||||
href="{item.settings.page + '/'}"
|
||||
on:click="{() => (showMobileNav = false)}"
|
||||
class:active="{'/' + item.settings.page === $location.path}"
|
||||
>
|
||||
{item.settings.title}
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
<NavigationItem item="{item}" />
|
||||
{/each}
|
||||
</nav>
|
||||
{/if}
|
||||
|
63
src/components/widgets/NavigationItem.svelte
Normal file
63
src/components/widgets/NavigationItem.svelte
Normal file
@ -0,0 +1,63 @@
|
||||
<script lang="ts">
|
||||
// import * as animateScroll from "svelte-scrollto"
|
||||
import { link, navigate } from "svelte-routing"
|
||||
import { location } from "../../store"
|
||||
|
||||
export let item: NavigationItem
|
||||
|
||||
const isActive = (itemPath: string, locationPath: string): boolean | any => {
|
||||
itemPath = "/" + itemPath + (locationPath.endsWith("/") ? "/" : "")
|
||||
locationPath = (locationPath.startsWith("/") ? "" : "/") + locationPath
|
||||
|
||||
if (!locationPath.endsWith("/")) {
|
||||
return locationPath.startsWith(itemPath)
|
||||
}
|
||||
|
||||
return itemPath === locationPath
|
||||
}
|
||||
|
||||
const onClickInternalLink = (item: NavigationItem) => {
|
||||
let url = ""
|
||||
if (item.settings?.page) {
|
||||
url += item.settings?.page
|
||||
}
|
||||
if (item.settings?.url?.url) {
|
||||
url += (url.endsWith("/") ? "" : "/") + item.settings.url.url
|
||||
}
|
||||
|
||||
if (item.settings?.page) {
|
||||
navigate(url, { replace: true })
|
||||
}
|
||||
}
|
||||
|
||||
const getInternalUrl = (item): string => {
|
||||
if (item.settings.page === "/") {
|
||||
return item.settings.page
|
||||
}
|
||||
|
||||
return item.settings.page + "/"
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if !item.settings.url.hidden}
|
||||
{#if item.settings.url.url}
|
||||
{#if item.settings.url.target === "default"}
|
||||
<a
|
||||
use:link
|
||||
href="{getInternalUrl(item)}"
|
||||
class:active="{isActive(item.settings.page, $location.path)}"
|
||||
on:click|stopPropagation="{() => onClickInternalLink(item)}"
|
||||
>
|
||||
{item.settings.title}
|
||||
</a>
|
||||
{:else}
|
||||
<a href="{item.settings.url.url}" target="{item.settings.url.target}">
|
||||
{item.settings.title}
|
||||
</a>
|
||||
{/if}
|
||||
{:else}
|
||||
<a use:link href="{getInternalUrl(item)}" class:active="{isActive(item.settings.page, $location.path)}">
|
||||
{item.settings.title}
|
||||
</a>
|
||||
{/if}
|
||||
{/if}
|
@ -29,11 +29,8 @@ article,
|
||||
|
||||
.article-details {
|
||||
&.default {
|
||||
background: #000;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&.news {
|
||||
background: #fc0;
|
||||
}
|
||||
}
|
||||
|
@ -56,6 +56,8 @@ header {
|
||||
text-decoration: none;
|
||||
transition: @transition-default;
|
||||
font-weight: 500;
|
||||
border: 1px solid @primary;
|
||||
padding: 0 @space-xs;
|
||||
|
||||
&:hover {
|
||||
color: @primary;
|
||||
|
Loading…
Reference in New Issue
Block a user