mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-06 19:36:41 +00:00
feat(documentation): svelte app as document generator (#566)
* check in full site application * rm unused assets components * change base css * i18n * new doc workflow * fix crosslink to doc in github * nav, console link, assets, console brand * edit configjs server * rm go struct references * cleanup input output bindings * working dir * export path * always trigger * rel paths * cname * rev workdir * fix fallbacklanguage, home on large screens * remove mit from site
This commit is contained in:
121
site/src/components/CodeTable.svelte
Normal file
121
site/src/components/CodeTable.svelte
Normal file
@@ -0,0 +1,121 @@
|
||||
<script>
|
||||
let tabs = [
|
||||
{lang: 'yaml', content:"main: halllo"},
|
||||
{lang: 'js', content:"console.log();"},
|
||||
{lang: 'html', content:"<p>hello</p>"},
|
||||
]
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.pc-tab > input,
|
||||
.pc-tab section > div {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#tab1:checked ~ section .tab1,
|
||||
#tab2:checked ~ section .tab2,
|
||||
#tab3:checked ~ section .tab3 {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#tab1:checked ~ nav .tab1,
|
||||
#tab2:checked ~ nav .tab2,
|
||||
#tab3:checked ~ nav .tab3 {
|
||||
color: red;
|
||||
}
|
||||
|
||||
/* Visual Styles */
|
||||
*, *:after, *:before {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.pc-tab {
|
||||
width: 100%;
|
||||
max-width: 700px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.pc-tab ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.pc-tab ul li label {
|
||||
float: left;
|
||||
padding: 10px 25px;
|
||||
border: 1px solid #8795a1;
|
||||
border-bottom: 0;
|
||||
background: #212224;
|
||||
}
|
||||
.pc-tab ul li:first-child label {
|
||||
border-top-left-radius: 8px;
|
||||
}
|
||||
|
||||
.pc-tab ul li:last-child label {
|
||||
border-top-right-radius: 8px;
|
||||
}
|
||||
.pc-tab ul li label:hover {
|
||||
background: #ffffff20;
|
||||
}
|
||||
.pc-tab ul li label:active {
|
||||
background: #ffffff30;
|
||||
}
|
||||
.pc-tab ul li:not(:last-child) label {
|
||||
border-right-width: 0;
|
||||
}
|
||||
.pc-tab section {
|
||||
clear: both;
|
||||
}
|
||||
.pc-tab section div {
|
||||
padding: 20px;
|
||||
width: 100%;
|
||||
border: 1px solid #8795a1;
|
||||
background: #1e1e1e;
|
||||
line-height: 1.5em;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
#tab1:checked ~ nav .tab1 label,
|
||||
#tab2:checked ~ nav .tab2 label,
|
||||
#tab3:checked ~ nav .tab3 label {
|
||||
background: #1e1e1e;
|
||||
position: relative;
|
||||
color: white;
|
||||
}
|
||||
#tab1:checked ~ nav .tab1 label:after,
|
||||
#tab2:checked ~ nav .tab2 label:after,
|
||||
#tab3:checked ~ nav .tab3 label:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 2px;
|
||||
width: 100%;
|
||||
background: #1e1e1e;
|
||||
left: 0;
|
||||
bottom: -1px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div class="pc-tab">
|
||||
<input checked id="tab1" type="radio" name="pct" />
|
||||
<input id="tab2" type="radio" name="pct" />
|
||||
<input id="tab3" type="radio" name="pct" />
|
||||
<nav>
|
||||
<ul>
|
||||
{#each tabs as { lang }, i}
|
||||
<li class="tab{i+1}">
|
||||
<label for="tab{i+1}">{lang}</label>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</nav>
|
||||
<section>
|
||||
{#each tabs as { lang, content }, i}
|
||||
<div class="tab{i+1}">
|
||||
{content}
|
||||
</div>
|
||||
{/each}
|
||||
</section>
|
||||
</div>
|
||||
428
site/src/components/Docs.svelte
Normal file
428
site/src/components/Docs.svelte
Normal file
@@ -0,0 +1,428 @@
|
||||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import GuideContents from "./GuideContents.svelte"; // TODO rename
|
||||
import Icon from "./Icon.svelte";
|
||||
import manifest from '../../static/manifest.json';
|
||||
export let owner = "caos";
|
||||
export let project = "zitadel/site";
|
||||
export let path = "/docs";
|
||||
export let dir = "";
|
||||
export let edit_title = "edit this section";
|
||||
export let sections;
|
||||
let active_section;
|
||||
|
||||
let container;
|
||||
let aside;
|
||||
let show_contents = false;
|
||||
|
||||
onMount(() => {
|
||||
// don't update `active_section` for headings above level 4, see _sections.js
|
||||
const anchors = container.querySelectorAll("[id]:not([data-scrollignore])");
|
||||
|
||||
let positions;
|
||||
|
||||
const onresize = () => {
|
||||
const { top } = container.getBoundingClientRect();
|
||||
positions = [].map.call(anchors, anchor => {
|
||||
return anchor.getBoundingClientRect().top - top;
|
||||
});
|
||||
};
|
||||
|
||||
let last_id = window.location.hash.slice(1);
|
||||
|
||||
const onscroll = () => {
|
||||
const top = -window.scrollY;
|
||||
|
||||
let i = anchors.length;
|
||||
while (i--) {
|
||||
if (positions[i] + top < 40) {
|
||||
const anchor = anchors[i];
|
||||
const { id } = anchor;
|
||||
|
||||
if (id !== last_id) {
|
||||
active_section = id;
|
||||
last_id = id;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("scroll", onscroll, true);
|
||||
window.addEventListener("resize", onresize, true);
|
||||
|
||||
// wait for fonts to load...
|
||||
const timeouts = [setTimeout(onresize, 1000), setTimeout(onscroll, 5000)];
|
||||
|
||||
onresize();
|
||||
onscroll();
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("scroll", onscroll, true);
|
||||
window.removeEventListener("resize", onresize, true);
|
||||
|
||||
timeouts.forEach(timeout => clearTimeout(timeout));
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
aside {
|
||||
position: fixed;
|
||||
background-color: var(--side-nav-back);
|
||||
left: 0.8rem;
|
||||
bottom: 0.8rem;
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
border-radius: .5rem;
|
||||
overflow: hidden;
|
||||
border: 1px solid #8795a1;
|
||||
box-shadow: 1px 1px 6px rgba(0, 0, 0, 0.1);
|
||||
transition: width 0.2s, height 0.2s;
|
||||
}
|
||||
|
||||
aside button {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 3.4rem;
|
||||
height: 3.4rem;
|
||||
}
|
||||
|
||||
aside.open {
|
||||
width: calc(100vw - 3rem);
|
||||
height: calc(100vh - var(--nav-h) - 3rem);
|
||||
}
|
||||
|
||||
aside.open::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: calc(100% - 2rem);
|
||||
height: 2em;
|
||||
pointer-events: none;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
aside::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 1.9em;
|
||||
width: calc(100% - 2rem);
|
||||
height: 2em;
|
||||
/* background: linear-gradient(
|
||||
to bottom,
|
||||
rgba(255, 255, 255, 0) 0%,
|
||||
rgba(255, 255, 255, 0.7) 50%,
|
||||
rgba(255, 255, 255, 1) 100%
|
||||
); */
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: absolute;
|
||||
font-family: var(--font);
|
||||
overflow-y: auto;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 4em 1.6rem 2em 3.2rem;
|
||||
bottom: 2em;
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: var(--top-offset) var(--side-nav);
|
||||
tab-size: 2;
|
||||
-moz-tab-size: 2;
|
||||
}
|
||||
|
||||
@media (min-width: 832px) {
|
||||
/* can't use vars in @media :( */
|
||||
aside {
|
||||
display: block;
|
||||
width: var(--sidebar-w);
|
||||
height: 100vh;
|
||||
top: 0;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
overflow: hidden;
|
||||
background-color: var(--side-nav-back);
|
||||
color: white;
|
||||
}
|
||||
|
||||
aside.open::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
aside::after {
|
||||
content: "";
|
||||
bottom: 0;
|
||||
height: var(--top-offset);
|
||||
/* background: linear-gradient(
|
||||
to bottom,
|
||||
rgba(103, 103, 120, 0) 0%,
|
||||
rgba(103, 103, 120, 0.7) 50%,
|
||||
rgba(103, 103, 120, 1) 100%
|
||||
); */
|
||||
}
|
||||
|
||||
aside button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
padding: var(--top-offset) 0 6.4rem 3.2rem;
|
||||
font-family: var(--font);
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
bottom: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding-left: calc(var(--sidebar-w) + var(--side-nav));
|
||||
}
|
||||
|
||||
.content :global(.side-by-side) {
|
||||
display: grid;
|
||||
grid-template-columns: calc(50% - 0.5em) calc(50% - 0.5em);
|
||||
grid-gap: 1em;
|
||||
}
|
||||
|
||||
.content :global(.side-by-side) :global(.code) {
|
||||
padding: 1em 0;
|
||||
}
|
||||
}
|
||||
|
||||
.content h2 {
|
||||
margin-top: 8rem;
|
||||
padding: 2rem 1.6rem 4rem 0.2rem;
|
||||
border-top: var(--border-w) solid #6767785b; /* based on --second */
|
||||
color: var(--text);
|
||||
line-height: 1;
|
||||
font-size: var(--h3);
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.content section:first-of-type > h2 {
|
||||
margin-top: -8rem;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.content :global(h4) {
|
||||
margin: 2em 0 1em 0;
|
||||
}
|
||||
|
||||
.content :global(.offset-anchor) {
|
||||
position: relative;
|
||||
display: block;
|
||||
top: calc(-1 * (var(--nav-h) + var(--top-offset) - 1rem));
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.content :global(.anchor) {
|
||||
position: absolute;
|
||||
display: block;
|
||||
/* TODO replace link icon */
|
||||
/* background: url(../icons/link.svg) 0 50% no-repeat; */
|
||||
background-size: 1em 1em;
|
||||
width: 1.4em;
|
||||
height: 1em;
|
||||
left: -1.3em;
|
||||
opacity: 0;
|
||||
color: white;
|
||||
transition: opacity 0.2s;
|
||||
border: none !important; /* TODO get rid of linkify */
|
||||
}
|
||||
|
||||
.content :global(.anchor) :global(i) {
|
||||
color: #8795a1;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.content :global(h2):hover :global(.anchor),
|
||||
.content :global(h3):hover :global(.anchor),
|
||||
.content :global(h4):hover :global(.anchor),
|
||||
.content :global(h5):hover :global(.anchor),
|
||||
.content :global(h6):hover :global(.anchor) {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.content :global(h5) :global(.anchor),
|
||||
.content :global(h6) :global(.anchor) {
|
||||
top: 0.2em;
|
||||
}
|
||||
}
|
||||
|
||||
.content :global(h3),
|
||||
.content :global(h3 > code) {
|
||||
margin: 6.4rem 0 0 0;
|
||||
padding: 2rem 1.6rem 5.6rem 0.2rem;
|
||||
color: var(--text);
|
||||
border-top: var(--border-w) solid #6767781f; /* based on --second */
|
||||
background: transparent;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.content :global(h3):first-of-type {
|
||||
border: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* avoid doubled border-top */
|
||||
.content :global(h3 > code) {
|
||||
border-radius: 0 0 0 0;
|
||||
border: none;
|
||||
font-size: inherit;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.content :global(h4),
|
||||
.content :global(h4 > code) {
|
||||
font-family: inherit;
|
||||
font-weight: 500;
|
||||
font-size: 2.4rem;
|
||||
color: var(--text);
|
||||
margin: 6.4rem 0 1.6rem 0;
|
||||
padding-left: 0;
|
||||
background: transparent;
|
||||
line-height: 1;
|
||||
padding: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.content :global(h4 > em) {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.content :global(h5) {
|
||||
font-size: 2.4rem;
|
||||
margin: 2em 0 0.5em 0;
|
||||
}
|
||||
|
||||
.content :global(code) {
|
||||
padding: 0.3rem 0.8rem 0.3rem;
|
||||
margin: 0 0.2rem;
|
||||
top: -0.1rem;
|
||||
background: var(--back-api);
|
||||
}
|
||||
|
||||
.content :global(pre) :global(code) {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
top: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.content :global(pre) {
|
||||
margin: 0 0 2em 0;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.content :global(.icon) {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
stroke: currentColor;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.content :global(table) {
|
||||
margin: 0 0 2em 0;
|
||||
}
|
||||
|
||||
section > :global(.code-block) > :global(pre) {
|
||||
display: inline-block;
|
||||
/* background: var(--back-api); */
|
||||
color: white;
|
||||
padding: 0.3rem 0.8rem;
|
||||
margin: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
section > :global(.code-block) > :global(pre.language-markup) {
|
||||
padding: 0.3rem 0.8rem 0.2rem;
|
||||
background: var(--back-api);
|
||||
}
|
||||
|
||||
section > :global(p) {
|
||||
max-width: var(--linemax);
|
||||
}
|
||||
|
||||
section :global(p) {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: var(--h5);
|
||||
float: right;
|
||||
pointer-events: all;
|
||||
color: var(--prime);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* no linkify on these */
|
||||
small a {
|
||||
all: unset;
|
||||
}
|
||||
|
||||
small a:before {
|
||||
all: unset;
|
||||
}
|
||||
|
||||
section :global(blockquote) {
|
||||
color: #e91e63;
|
||||
border: 2px solid var(--flash);
|
||||
}
|
||||
|
||||
section :global(blockquote) :global(code) {
|
||||
background: hsl(204, 100%, 95%) !important;
|
||||
color: #e91e63;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div bind:this={container} class="content listify">
|
||||
{#each sections as section}
|
||||
<section data-id={section.slug}>
|
||||
<h2>
|
||||
<span class="offset-anchor" id={section.slug} />
|
||||
<!-- svelte-ignore a11y-missing-content -->
|
||||
<a href="{dir}#{section.slug}" class="anchor" aria-hidden />
|
||||
|
||||
{@html section.metadata.title}
|
||||
<small>
|
||||
<a
|
||||
href="https://github.com/{owner}/{project}/edit/master{path}/{dir}/{section.file}"
|
||||
title={edit_title}>
|
||||
<Icon name="las la-external-link-alt" size="24px" />
|
||||
</a>
|
||||
</small>
|
||||
</h2>
|
||||
|
||||
{@html section.html}
|
||||
</section>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<aside bind:this={aside} class="sidebar-container" class:open={show_contents}>
|
||||
<div class="sidebar" on:click={() => (show_contents = false)}>
|
||||
<!-- scroll container -->
|
||||
<GuideContents {dir} {sections} {active_section} {show_contents} />
|
||||
</div>
|
||||
|
||||
<button on:click={() => (show_contents = !show_contents)}>
|
||||
<Icon name={show_contents ? 'las la-window-close' : 'las la-bars'} />
|
||||
</button>
|
||||
</aside>
|
||||
142
site/src/components/GuideContents.svelte
Normal file
142
site/src/components/GuideContents.svelte
Normal file
@@ -0,0 +1,142 @@
|
||||
<script>
|
||||
import { afterUpdate } from 'svelte';
|
||||
import Icon from './Icon.svelte';
|
||||
import CodeTable from './CodeTable.svelte';
|
||||
export let dir = '';
|
||||
export let sections = [];
|
||||
export let active_section = null;
|
||||
export let show_contents;
|
||||
export let prevent_sidebar_scroll = false;
|
||||
|
||||
let ul;
|
||||
|
||||
afterUpdate(() => {
|
||||
// bit of a hack — prevent sidebar scrolling if
|
||||
// TOC is open on mobile, or scroll came from within sidebar
|
||||
if (prevent_sidebar_scroll || show_contents && window.innerWidth < 832) return;
|
||||
|
||||
const active = ul.querySelector('.active');
|
||||
|
||||
if (active) {
|
||||
const { top, bottom } = active.getBoundingClientRect();
|
||||
|
||||
const min = 200;
|
||||
const max = window.innerHeight - 200;
|
||||
|
||||
if (top > max) {
|
||||
ul.parentNode.scrollBy({
|
||||
top: top - max,
|
||||
left: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
} else if (bottom < min) {
|
||||
ul.parentNode.scrollBy({
|
||||
top: bottom - min,
|
||||
left: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.reference-toc li {
|
||||
display: block;
|
||||
line-height: 1.2;
|
||||
margin: 0 0 4rem 0;
|
||||
}
|
||||
|
||||
a {
|
||||
position: relative;
|
||||
transition: color 0.2s;
|
||||
border-bottom: none;
|
||||
padding: 0;
|
||||
color: var(--dark-text);
|
||||
}
|
||||
|
||||
.section {
|
||||
display: block;
|
||||
padding: 0 0 .8rem 0;
|
||||
font-size: var(--h6);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.subsection {
|
||||
display: block;
|
||||
font-size: 1.6rem;
|
||||
font-family: var(--font);
|
||||
padding: 0 0 0.6em 0;
|
||||
}
|
||||
|
||||
.section:hover,
|
||||
.subsection:hover,
|
||||
.active {
|
||||
color: var(--flash);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.subsection[data-level="4"] {
|
||||
padding-left: 1.2rem;
|
||||
}
|
||||
|
||||
.icon-container {
|
||||
position: absolute;
|
||||
top: -.2rem;
|
||||
right: 2.4rem;
|
||||
}
|
||||
|
||||
@media (min-width: 832px) {
|
||||
a {
|
||||
color: var(--sidebar-text);
|
||||
}
|
||||
|
||||
a:hover,
|
||||
.section:hover,
|
||||
.subsection:hover,
|
||||
.active {
|
||||
color: #5282c1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<ul
|
||||
bind:this={ul}
|
||||
class="reference-toc"
|
||||
on:mouseenter="{() => prevent_sidebar_scroll = true}"
|
||||
on:mouseleave="{() => prevent_sidebar_scroll = false}"
|
||||
>
|
||||
{#each sections as section}
|
||||
<li>
|
||||
<a class="section" class:active="{section.slug === active_section}" href="{dir}#{section.slug}">
|
||||
{@html section.metadata.title}
|
||||
|
||||
{#if section.slug === active_section}
|
||||
<div class="icon-container">
|
||||
<Icon name="las la-arrow-right" />
|
||||
</div>
|
||||
{/if}
|
||||
</a>
|
||||
|
||||
{#each section.subsections as subsection}
|
||||
<!-- see <script> below: on:click='scrollTo(event, subsection.slug)' -->
|
||||
<a
|
||||
class="subsection"
|
||||
class:active="{subsection.slug === active_section}"
|
||||
href="{dir}#{subsection.slug}"
|
||||
data-level="{subsection.level}"
|
||||
>
|
||||
{@html subsection.title}
|
||||
|
||||
{#if subsection.slug === active_section}
|
||||
<div class="icon-container">
|
||||
<Icon name="las la-arrow-right" />
|
||||
</div>
|
||||
{/if}
|
||||
</a>
|
||||
{/each}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
20
site/src/components/Icon.svelte
Normal file
20
site/src/components/Icon.svelte
Normal file
@@ -0,0 +1,20 @@
|
||||
<script>
|
||||
export let name;
|
||||
export let size = '20px';
|
||||
</script>
|
||||
|
||||
<style>
|
||||
:global(i) {
|
||||
font-size: 20px;
|
||||
height: 20px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
vertical-align: middle;
|
||||
-o-object-fit: contain;
|
||||
object-fit: contain;
|
||||
-webkit-transform-origin: center center;
|
||||
transform-origin: center center;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i class={name} style="font-size: {size}; height: {size};"><use xlink:href="#{name}" /></i>
|
||||
65
site/src/components/LanguageSwitcher.svelte
Normal file
65
site/src/components/LanguageSwitcher.svelte
Normal file
@@ -0,0 +1,65 @@
|
||||
<script context="module">
|
||||
import { setCookie } from '../modules/cookie.js';
|
||||
import { docLanguages } from '../modules/language-store.js'
|
||||
import {LANGUAGES} from '../../config.js';
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import { locale } from 'svelte-i18n';
|
||||
import { startClient } from '../i18n.js';
|
||||
|
||||
let group= $locale;
|
||||
|
||||
$:setLocale(group);
|
||||
function setLocale(language) {
|
||||
if (typeof window !== 'undefined') {
|
||||
locale.set(language);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--deep-blue: #1e3470;
|
||||
--speed3: cubic-bezier(0.26, 0.48, 0.08, 0.9);
|
||||
--height: 30px;
|
||||
}
|
||||
|
||||
.language-switcher {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.language-switcher input {
|
||||
appearance: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.language-switcher .select {
|
||||
height: var(--height);
|
||||
width: var(--height);
|
||||
border-radius: 50vw;
|
||||
font-size: calc(var(--height) / 2.5);
|
||||
color: #fff;
|
||||
mix-blend-mode: difference;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.language-switcher .current {
|
||||
background-color: white;
|
||||
color: black;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="language-switcher">
|
||||
{#each LANGUAGES as lang}
|
||||
<label class="select {lang == group ? 'current' : 'notcurrent'}">
|
||||
<input type=radio bind:group value={lang}>
|
||||
{lang.toUpperCase()}
|
||||
</label>
|
||||
{/each}
|
||||
</div>
|
||||
168
site/src/components/Nav.svelte
Normal file
168
site/src/components/Nav.svelte
Normal file
@@ -0,0 +1,168 @@
|
||||
<script>
|
||||
import LanguageSwitcher from './LanguageSwitcher.svelte'
|
||||
import NavItem from './NavItem.svelte'
|
||||
import { onMount, setContext } from 'svelte';
|
||||
import { writable } from 'svelte/store';
|
||||
import Icon from './Icon.svelte';
|
||||
export let segment;
|
||||
export let logo;
|
||||
export let title;
|
||||
import { _ } from 'svelte-i18n';
|
||||
const current = writable(null);
|
||||
setContext('nav', current);
|
||||
let visible = true;
|
||||
|
||||
let hash_changed = false;
|
||||
function handle_hashchange() {
|
||||
hash_changed = true;
|
||||
}
|
||||
let last_scroll = 0;
|
||||
function handle_scroll() {
|
||||
const scroll = window.pageYOffset;
|
||||
if (!hash_changed) {
|
||||
visible = (scroll < 50 || scroll < last_scroll);
|
||||
}
|
||||
last_scroll = scroll;
|
||||
hash_changed = false;
|
||||
}
|
||||
$: $current = segment;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
header {
|
||||
box-sizing: border-box;
|
||||
position: fixed;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100vw;
|
||||
height: var(--nav-h);
|
||||
padding: 0 3rem;
|
||||
margin: 0 auto;
|
||||
box-shadow: 0 -0.4rem 0.9rem 0.2rem rgba(0,0,0,.5);
|
||||
z-index: 100;
|
||||
user-select: none;
|
||||
transform: translate(0,calc(-100% - 1rem));
|
||||
transition: transform 0.2s;
|
||||
backdrop-filter: saturate(100%) blur(10px);
|
||||
}
|
||||
header.visible {
|
||||
transform: none;
|
||||
}
|
||||
nav {
|
||||
box-sizing: border-box;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: var(--nav-h);
|
||||
padding: 0 3rem 0 3rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: transparent;
|
||||
transform: none;
|
||||
transition: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.home {
|
||||
width: 200px;
|
||||
line-height: 22px;
|
||||
font-size: 22px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.home:hover {
|
||||
color: inherit;
|
||||
text-decoration:none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.home img {
|
||||
display: block;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
border-bottom: none;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
@media (min-width: 400px) {
|
||||
.home {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
a img {
|
||||
max-height: 40px;
|
||||
}
|
||||
|
||||
.switcher-wrapper {
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 8px;
|
||||
border: 1px solid hsla(0,0%,100%,.12);
|
||||
box-shadow: 0 0 0 0 rgba(0,0,0,.2), 0 0 0 0 rgba(0,0,0,.14), 0 0 0 0 rgba(0,0,0,.12);
|
||||
padding: 0 15px;
|
||||
height: 36px;
|
||||
color: var(--prime);
|
||||
transition: background-color .2 ease;
|
||||
margin: 0 1rem;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #5282c110;
|
||||
}
|
||||
button:active {
|
||||
background-color: #5282c120;
|
||||
}
|
||||
|
||||
button span {
|
||||
font-size: 14px;
|
||||
line-height: 14px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<svelte:window on:hashchange={handle_hashchange} on:scroll={handle_scroll} />
|
||||
|
||||
<header class:visible="{visible}">
|
||||
<nav>
|
||||
<a
|
||||
rel="prefetch"
|
||||
href="."
|
||||
class="home"
|
||||
title="{title}"
|
||||
>
|
||||
{#if logo}
|
||||
<img src={logo} alt={title} />
|
||||
{:else if title}
|
||||
{title}
|
||||
{/if}
|
||||
</a>
|
||||
|
||||
<span class="fill-space"></span>
|
||||
|
||||
<a href='https://console.zitadel.ch'><button>
|
||||
<span>{$_('toconsole')}</span>
|
||||
</button>
|
||||
</a>
|
||||
|
||||
<NavItem external="https://github.com/caos" title="GitHub Repo">
|
||||
<Icon name="lab la-github" size="24px"></Icon>
|
||||
</NavItem>
|
||||
|
||||
<div class="switcher-wrapper">
|
||||
<LanguageSwitcher></LanguageSwitcher>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
30
site/src/components/NavItem.svelte
Normal file
30
site/src/components/NavItem.svelte
Normal file
@@ -0,0 +1,30 @@
|
||||
<style>
|
||||
a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--text);
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { getContext } from 'svelte';
|
||||
export let segment = null;
|
||||
export let external = null;
|
||||
export let title = '';
|
||||
const current = getContext('nav');
|
||||
</script>
|
||||
|
||||
{#if external}
|
||||
<a href={external}><slot></slot></a>
|
||||
{:else}
|
||||
<a rel="prefetch" alt="{title}" href={segment}><slot></slot></a>
|
||||
{/if}
|
||||
15
site/src/components/Section.svelte
Normal file
15
site/src/components/Section.svelte
Normal file
@@ -0,0 +1,15 @@
|
||||
<style>
|
||||
section {
|
||||
position: relative;
|
||||
margin: 10rem auto;
|
||||
padding: 0 var(--side-nav);
|
||||
max-width: 120rem;
|
||||
}
|
||||
section :global(h3) {
|
||||
color: var(--text);
|
||||
}
|
||||
</style>
|
||||
|
||||
<section>
|
||||
<slot></slot>
|
||||
</section>
|
||||
82
site/src/components/Split.svelte
Normal file
82
site/src/components/Split.svelte
Normal file
@@ -0,0 +1,82 @@
|
||||
<style>
|
||||
.split {
|
||||
display: grid;
|
||||
grid-row-gap: 1em;
|
||||
grid-template-areas:
|
||||
"one"
|
||||
"two"
|
||||
"three"
|
||||
"what"
|
||||
"how";
|
||||
}
|
||||
|
||||
.split :global(p) {
|
||||
font-size: var(--h5);
|
||||
}
|
||||
|
||||
.how {
|
||||
/* needed to prevent the <pre> from
|
||||
breaking the grid layout */
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.how :global(.cta) :global(a) {
|
||||
display: inline-block;
|
||||
text-align: right;
|
||||
background-color: var(--prime);
|
||||
padding: 0.5em 1.8em 0.5em 1em;
|
||||
border-radius: 16px;
|
||||
color: white;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.how :global(.cta) :global(a)::after {
|
||||
right: 0.5em;
|
||||
top: 0.75em;
|
||||
}
|
||||
|
||||
.what {
|
||||
margin: 2em 0 0 0;
|
||||
}
|
||||
|
||||
.how :global(.cta) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@media (min-width: 900px) {
|
||||
.split {
|
||||
grid-column-gap: 1em;
|
||||
grid-row-gap: 1em;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
grid-template-areas:
|
||||
"one two"
|
||||
"three how"
|
||||
"what what";
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.split {
|
||||
grid-column-gap: 1em;
|
||||
grid-row-gap: 5em;
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
grid-template-areas:
|
||||
"one one two two three three"
|
||||
"what what what how how how";
|
||||
}
|
||||
|
||||
.what {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="split">
|
||||
<div class="what" style="grid-area: what;">
|
||||
<slot name="what"></slot>
|
||||
</div>
|
||||
|
||||
<div class="how" style="grid-area: how;">
|
||||
<slot name="how"></slot>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user