diff --git a/inlang/source-code/doc-layout-component/src/mock/manifest.ts b/inlang/source-code/doc-layout-component/src/mock/manifest.ts index 3b9709d320..a4a0bce458 100644 --- a/inlang/source-code/doc-layout-component/src/mock/manifest.ts +++ b/inlang/source-code/doc-layout-component/src/mock/manifest.ts @@ -118,26 +118,30 @@ export const manifestWithoutNamespace: MarketplaceManifest & { uniqueID: string license: "Apache-2.0", } -export const html = ` -

#Why Paraglide?

-

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.

-

#Use it with your Favorite Framework

-

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.

- - - - - - - - -

#People Love It

-

A few recent comments.

- - - - - - -

#Getting started

-

To use Paraglide standalone without a framework, run the following command:

` +export const html = `

HTML Ipsum Presents

+ +

Pellentesque habitant morbi tristique 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. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

+ +

Header Level 2

+ +
    +
  1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  2. +
  3. Aliquam tincidunt mauris eu risus.
  4. +
+ +

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.

+ +

Header Level 3

+ + + +

+#header h1 a {
+  display: block;
+  width: 300px;
+  height: 80px;
+}
+
` diff --git a/inlang/source-code/doc-layout-component/src/stories/inlang-doc-in-page-navigation.ts b/inlang/source-code/doc-layout-component/src/stories/inlang-doc-in-page-navigation.ts new file mode 100644 index 0000000000..54fd032dfa --- /dev/null +++ b/inlang/source-code/doc-layout-component/src/stories/inlang-doc-in-page-navigation.ts @@ -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`
+ On this page +
+ ${this._headlines.map((headline) => { + if (headline.level === "H1") { + return html`${headline.element.textContent}` + } else if (headline.level === "H2") { + return html`${headline.element.textContent}` + } else { + return html`${headline.element.textContent}` + } + })} +
+
+ ${this._headlines[0] && + html``} +
` + } +} + +// add types +declare global { + interface HTMLElementTagNameMap { + "inlang-doc-in-page-navigation": InlangDocInPageNavigation + } +} diff --git a/inlang/source-code/doc-layout-component/src/stories/inlang-doc-layout.ts b/inlang/source-code/doc-layout-component/src/stories/inlang-doc-layout.ts index 14654b7728..0c324b8413 100644 --- a/inlang/source-code/doc-layout-component/src/stories/inlang-doc-layout.ts +++ b/inlang/source-code/doc-layout-component/src/stories/inlang-doc-layout.ts @@ -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" @@ -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 { @@ -97,7 +103,11 @@ export default class InlangDocLayout extends LitElement { -
+
+ +
{ diff --git a/inlang/source-code/doc-layout-component/src/stories/inlang-doc-navigation.ts b/inlang/source-code/doc-layout-component/src/stories/inlang-doc-navigation.ts index 6b1da33c66..d7e92c7ab2 100644 --- a/inlang/source-code/doc-layout-component/src/stories/inlang-doc-navigation.ts +++ b/inlang/source-code/doc-layout-component/src/stories/inlang-doc-navigation.ts @@ -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 {