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 = ` -
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.
-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.
-A few recent comments.
-To use Paraglide standalone without a framework, run the following command:
` +export const html = `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.
+ +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 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`