Skip to content

Commit

Permalink
Merge pull request #2676 from opral/nilsjacobsen/inlmc-78-hierarchy-i…
Browse files Browse the repository at this point in the history
…n-navigation

Nilsjacobsen/inlmc 78 hierarchy in navigation
  • Loading branch information
NilsJacobsen authored Apr 29, 2024
2 parents 567c413 + 9f368b8 commit 5fc3b43
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 27 deletions.
50 changes: 27 additions & 23 deletions inlang/source-code/doc-layout-component/src/mock/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,26 +118,30 @@ export const manifestWithoutNamespace: MarketplaceManifest & { uniqueID: string
license: "Apache-2.0",
}

export const html = `<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/github-dark-dimmed.min.css">
<h1 id="why-paraglide" class="doc-font-semibold doc-leading-relaxed doc-relative doc-my-6 doc-cursor-pointer doc-group/heading doc-no-underline doc-text-3xl doc-pb-3 doc-mb-2 doc-mt-12"><span class="doc-font-medium doc-hidden md:doc-block doc-mr-2 text-primary doc-opacity-0 group-hover/heading:doc-opacity-100 transition-opacity doc-absolute -doc-left-6">#</span><a onclick="event.preventDefault(); window.scrollTo({top: document.getElementById(event.target.hash.substring(1)).offsetTop - 96, behavior: &#x22;smooth&#x22;}); window.history.pushState(null, null, event.target.hash);" href="#why-paraglide">Why Paraglide?</a></h1>
<p class="doc-text-base text-surface-600 doc-my-4 doc-leading-relaxed">With Paraglide's treeshakeable messages, each page only loads the messages it actually uses. Incremental loading like this would usually take forever to get right, with Paraglide you get it for free.</p>
<h1 id="use-it-with-your-favorite-framework" class="doc-font-semibold doc-leading-relaxed doc-relative doc-my-6 doc-cursor-pointer doc-group/heading doc-no-underline doc-text-3xl doc-pb-3 doc-mb-2 doc-mt-12"><span class="doc-font-medium doc-hidden md:doc-block doc-mr-2 text-primary doc-opacity-0 group-hover/heading:doc-opacity-100 transition-opacity doc-absolute -doc-left-6">#</span><a onclick="event.preventDefault(); window.scrollTo({top: document.getElementById(event.target.hash.substring(1)).offsetTop - 96, behavior: &#x22;smooth&#x22;}); window.history.pushState(null, null, event.target.hash);" href="#use-it-with-your-favorite-framework">Use it with your Favorite Framework</a></h1>
<p class="doc-text-base text-surface-600 doc-my-4 doc-leading-relaxed">Paraglide is framework agnostic, but there are framework-specific libraries available. If there is one for your framework you will want to follow its documentation instead. If there isn't, read on.</p>
<doc-links>
<doc-link title="Paraglide-Next" icon="tabler:brand-nextjs" href="/m/osslbuzt/paraglide-next-i18n" description="Go to Library"></doc-link>
<doc-link title="Paraglide-SvelteKit" icon="simple-icons:svelte" href="/m/dxnzrydw/paraglide-sveltekit-i18n" description="Go to Library"></doc-link>
<doc-link title="Paraglide-Astro" icon="devicon-plain:astro" href="/m/iljlwzfs/paraglide-astro-i18n" description="Go to Library"></doc-link>
<doc-link title="Paraglide-SolidStart" icon="tabler:brand-solidjs" href="/m/n860p17j/paraglide-solidstart-i18n" description="Go to Library"></doc-link>
<doc-link title="Paraglide-Remix" icon="simple-icons:remix" href="/m/fnhuwzrx/paraglide-remix-i18n" description="Go to Library"></doc-link>
<doc-link title="Or write your own" icon="ph:sparkle-fill" href="#writing-a-framework-library" description="Learn How"></doc-link>
</doc-links>
<h1 id="people-love-it" class="doc-font-semibold doc-leading-relaxed doc-relative doc-my-6 doc-cursor-pointer doc-group/heading doc-no-underline doc-text-3xl doc-pb-3 doc-mb-2 doc-mt-12"><span class="doc-font-medium doc-hidden md:doc-block doc-mr-2 text-primary doc-opacity-0 group-hover/heading:doc-opacity-100 transition-opacity doc-absolute -doc-left-6">#</span><a onclick="event.preventDefault(); window.scrollTo({top: document.getElementById(event.target.hash.substring(1)).offsetTop - 96, behavior: &#x22;smooth&#x22;}); window.history.pushState(null, null, event.target.hash);" href="#people-love-it">People Love It</a></h1>
<p class="doc-text-base text-surface-600 doc-my-4 doc-leading-relaxed">A few recent comments.</p>
<doc-comments>
<doc-comment text="Just tried Paraglide JS from @inlangHQ. This is how i18n should be done! Totally new level of DX for both implementation and managing translations! Superb support for SvelteKit as well ⭐" author="Patrik Engborg" icon="mdi:twitter"></doc-comment>
<doc-comment text="I was messing with various i18n frameworks and tools in combination with Astro, and must say that Paraglide was the smoothest experience. I have migrated my website from i18next and it was a breeze. SSG and SSR worked out of the box (which was the first one for me), and overall DX is great. Thanks for your work!" author="Dalibor Hon" icon="mdi:discord"></doc-comment>
<doc-comment text="Awesome library 🙂 Thanks so much! 1) The docs were simple and straight forward 2) Everything just worked.. no headaches" author="Dimitry" icon="mdi:discord"></doc-comment>
<doc-comment text="Thank you for that huge work you have done and still doing!" author="ZerdoX-x" icon="mdi:github"></doc-comment>
</doc-comments>
<h1 id="getting-started" class="doc-font-semibold doc-leading-relaxed doc-relative doc-my-6 doc-cursor-pointer doc-group/heading doc-no-underline doc-text-3xl doc-pb-3 doc-mb-2 doc-mt-12"><span class="doc-font-medium doc-hidden md:doc-block doc-mr-2 text-primary doc-opacity-0 group-hover/heading:doc-opacity-100 transition-opacity doc-absolute -doc-left-6">#</span><a onclick="event.preventDefault(); window.scrollTo({top: document.getElementById(event.target.hash.substring(1)).offsetTop - 96, behavior: &#x22;smooth&#x22;}); window.history.pushState(null, null, event.target.hash);" href="#getting-started">Getting started</a></h1>
<p class="doc-text-base text-surface-600 doc-my-4 doc-leading-relaxed">To use Paraglide standalone without a framework, run the following command:</p>`
export const html = `<h1>HTML Ipsum Presents</h1>
<p><strong>Pellentesque habitant morbi tristique</strong> senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. <em>Aenean ultricies mi vitae est.</em> Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, <code>commodo vitae</code>, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. <a href="#">Donec non enim</a> in turpis pulvinar facilisis. Ut felis.</p>
<h2>Header Level 2</h2>
<ol>
<li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li>
<li>Aliquam tincidunt mauris eu risus.</li>
</ol>
<blockquote><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.</p></blockquote>
<h3>Header Level 3</h3>
<ul>
<li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li>
<li>Aliquam tincidunt mauris eu risus.</li>
</ul>
<pre><code>
#header h1 a {
display: block;
width: 300px;
height: 80px;
}
</code></pre>`
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import { html, LitElement, css } from "lit"
import { customElement, property, state } from "lit/decorators.js"
import { baseStyling } from "../styling/base.js"

import SlMenu from "@shoelace-style/shoelace/dist/components/menu/menu.component.js"
import SlMenuItem from "@shoelace-style/shoelace/dist/components/menu-item/menu-item.component.js"
import SlAvatar from "@shoelace-style/shoelace/dist/components/avatar/avatar.component.js"

import type { MarketplaceManifest } from "@inlang/marketplace-manifest"

// in case an app defines it's own set of shoelace components, prevent double registering
if (!customElements.get("sl-menu")) customElements.define("sl-menu", SlMenu)
if (!customElements.get("sl-menu-item")) customElements.define("sl-menu-item", SlMenuItem)
if (!customElements.get("sl-avatar")) customElements.define("sl-avatar", SlAvatar)

type Headlines = { level: "H1" | "H2" | "H3"; anchor: string; element: Element }[]

@customElement("inlang-doc-in-page-navigation")
export default class InlangDocInPageNavigation extends LitElement {
static override styles = [
baseStyling,
css`
.container {
font-size: 14px;
font-weight: 600;
padding-top: 20px;
}
.list {
display: flex;
flex-direction: column;
margin-top: 8px;
}
.link {
text-decoration: none;
color: var(--sl-color-neutral-600);
font-size: 14px;
padding: 5px 0;
font-weight: 400;
}
.link-container {
display: flex;
align-items: center;
gap: 6px;
color: var(--sl-color-neutral-600);
}
.link:hover {
color: var(--sl-color-primary-600);
}
.separator {
height: 1px;
width: 100%;
background-color: var(--sl-color-neutral-200);
margin: 16px 0;
}
.h1 {
margin-left: 0;
}
.h2 {
margin-left: 16px;
}
.h3 {
margin-left: 32px;
}
`,
]

@property({ type: Object })
manifest: MarketplaceManifest & { uniqueID: string } = {} as MarketplaceManifest & {
uniqueID: string
}

@property({ type: Array })
contentInHtml: HTMLCollection | undefined

@state()
private _headlines: Headlines = []

private _replaceChars = (str: string) => {
return str
.replaceAll(" ", "-")
.replaceAll("/", "")
.replace("#", "")
.replaceAll("(", "")
.replaceAll(")", "")
.replaceAll("?", "")
.replaceAll(".", "")
.replaceAll("@", "")
.replaceAll(/([\uE000-\uF8FF]|\uD83C[\uDF00-\uDFFF]|\uD83D[\uDC00-\uDDFF])/g, "")
.replaceAll("✂", "")
.replaceAll(":", "")
}

private _findHeadlineElements = (elements: HTMLCollection) => {
const headers: Headlines = []

// eslint-disable-next-line unicorn/no-for-loop
for (let i = 0; i < elements.length; i++) {
const element = elements[i]
// Check if the element is an h1 or h2
if (
element &&
element.textContent &&
(element.tagName === "H1" || element.tagName === "H2" || element.tagName === "H3")
) {
// Add the element to the headers array
const id = this._replaceChars(element.textContent.toLowerCase())
headers.push({ level: element.tagName, anchor: id, element: element })
}
}

// Return the array of h1 and h2 elements
return headers
}

_doesH1Exist = (headlines: Headlines) => {
return headlines.some((headline) => headline.level === "H1")
}

override async firstUpdated() {
if (this.contentInHtml) {
this._headlines = this._findHeadlineElements(this.contentInHtml)
}
}

override render() {
return html`<div class="container" part="base">
On this page
<div class="list">
${this._headlines.map((headline) => {
if (headline.level === "H1") {
return html`<a class=${`link h1`} href="#${headline.anchor}"
>${headline.element.textContent}</a
>`
} else if (headline.level === "H2") {
return html`<a
class=${`link ${this._doesH1Exist(this._headlines) ? "h2" : "h1"}`}
styles=${"margin-left: 10px"}
href="#${headline.anchor}"
>${headline.element.textContent}</a
>`
} else {
return html`<a
class=${`link ${this._doesH1Exist(this._headlines) ? "h3" : "h2"}`}
styles=${"margin-left: 20px"}
href="#${headline.anchor}"
>${headline.element.textContent}</a
>`
}
})}
</div>
<div class="separator"></div>
${this._headlines[0] &&
html`<div class="link-container">
<a class="link" href="#${this._headlines[0].anchor}">Scroll to top</a
><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24">
<path
fill="currentColor"
d="M11 16h2v-4.2l1.6 1.6L16 12l-4-4l-4 4l1.4 1.4l1.6-1.6zm1 6q-2.075 0-3.9-.788t-3.175-2.137T2.788 15.9T2 12t.788-3.9t2.137-3.175T8.1 2.788T12 2t3.9.788t3.175 2.137T21.213 8.1T22 12t-.788 3.9t-2.137 3.175t-3.175 2.138T12 22m0-2q3.35 0 5.675-2.325T20 12t-2.325-5.675T12 4T6.325 6.325T4 12t2.325 5.675T12 20m0-8"
/>
</svg>
</div>`}
</div>`
}
}

// add types
declare global {
interface HTMLElementTagNameMap {
"inlang-doc-in-page-navigation": InlangDocInPageNavigation
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { MarketplaceManifest } from "@inlang/marketplace-manifest"
import overridePrimitiveColors from "./../helper/overridePrimitiveColors.js"

import "./inlang-doc-navigation.ts"
import "./inlang-doc-in-page-navigation.ts"

import SlDrawer from "@shoelace-style/shoelace/dist/components/drawer/drawer.component.js"
import SlButton from "@shoelace-style/shoelace/dist/components/button/button.component.js"
Expand Down Expand Up @@ -32,7 +33,12 @@ export default class InlangDocLayout extends LitElement {
width: min-content;
position: relative;
height: 100%;
padding: 0 32px;
padding: 0 40px;
}
@media (max-width: 1280px) {
.main-column {
padding: 0 20px;
}
}
@media (max-width: 768px) {
.main-column {
Expand Down Expand Up @@ -97,7 +103,11 @@ export default class InlangDocLayout extends LitElement {
</div>
<slot></slot>
</div>
<div class="right-column"></div>
<div class="right-column">
<inlang-doc-in-page-navigation
.contentInHtml=${this.children}
></inlang-doc-in-page-navigation>
</div>
<sl-drawer
.open=${this._drawerIsOpen}
@sl-after-hide=${() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ export default class InlangDocNavigation extends LitElement {
font-weight: 600;
}
.link-indicator {
width: 16px;
height: 16px;
width: 14px;
height: 14px;
color: var(--sl-color-neutral-400);
}
sl-avatar {
Expand Down

0 comments on commit 5fc3b43

Please sign in to comment.