docs: add anchor link to reference parameters (#6194)

- Added anchor links to items in parameter components
- Added sectionTitle prop in markdown theme

Note: Due to the second point, the change requires generating references to see the anchor links, which would result in a big diff in this PR. Instead, next time the references are generated for a release, this feature will available for use.
This commit is contained in:
Shahed Nasser
2024-01-24 10:42:55 +02:00
committed by GitHub
parent 75fd6b0c83
commit ddc0c9dfff
22 changed files with 229 additions and 63 deletions

View File

@@ -8,7 +8,10 @@ import { formatParameterComponent } from "../../utils/format-parameter-component
export default function (theme: MarkdownTheme) {
Handlebars.registerHelper(
"parameterComponent",
function (this: ReflectionParameterType[]) {
function (
this: ReflectionParameterType[],
options: Handlebars.HelperOptions
) {
const { parameterComponent, maxLevel, parameterComponentExtraProps } =
theme.getFormattingOptionsForLocation()
const parameters = this.reduce(
@@ -26,6 +29,7 @@ export default function (theme: MarkdownTheme) {
parameterComponent,
componentItems: parameters,
extraProps: parameterComponentExtraProps,
sectionTitle: options.hash.sectionTitle,
})
}
)

View File

@@ -5,13 +5,13 @@ import { ParameterReflection } from "typedoc"
export default function (theme: MarkdownTheme) {
Handlebars.registerHelper(
"parameter",
function (this: ParameterReflection[]) {
function (this: ParameterReflection[], options: Handlebars.HelperOptions) {
const { parameterStyle } = theme.getFormattingOptionsForLocation()
if (parameterStyle === "list") {
return Handlebars.helpers.parameterList.call(this)
} else if (parameterStyle === "component") {
return Handlebars.helpers.parameterComponent.call(this)
return Handlebars.helpers.parameterComponent.call(this, options)
} else {
return Handlebars.helpers.parameterTable.call(this)
}

View File

@@ -10,7 +10,11 @@ export default function () {
const hookParameters = getHookParams(this)
if (hookParameters?.length) {
parametersStr = Handlebars.helpers.parameter.call(hookParameters)
parametersStr = Handlebars.helpers.parameter.call(hookParameters, {
hash: {
sectionTitle: this.name,
},
})
}
return parametersStr

View File

@@ -31,6 +31,7 @@ export default function (theme: MarkdownTheme) {
parameterComponent,
componentItems: mutationParameters,
extraProps: parameterComponentExtraProps,
sectionTitle: this.name,
})
}
)

View File

@@ -31,6 +31,7 @@ export default function (theme: MarkdownTheme) {
parameterComponent,
componentItems: mutationParameters,
extraProps: parameterComponentExtraProps,
sectionTitle: this.name,
})
}
)

View File

@@ -31,6 +31,7 @@ export default function (theme: MarkdownTheme) {
parameterComponent,
componentItems: mutationParameters,
extraProps: parameterComponentExtraProps,
sectionTitle: this.name,
})
}
)

View File

@@ -13,7 +13,7 @@ export default function (theme: MarkdownTheme) {
if (reflection.variant === "signature" && "type" in reflection) {
return getReturnFromType(theme, reflection)
} else if (reflection.comment) {
return getReturnFromComment(theme, reflection.comment)
return getReturnFromComment(theme, reflection.comment, reflection.name)
} else {
return ""
}
@@ -49,6 +49,7 @@ function getReturnFromType(
parameterComponent,
componentItems,
extraProps: parameterComponentExtraProps,
sectionTitle: reflection.name,
})
} else {
return formatReturnAsList(componentItems)
@@ -77,7 +78,11 @@ function formatReturnAsList(componentItems: Parameter[], level = 1): string {
.join("\n")
}
function getReturnFromComment(theme: MarkdownTheme, comment: Comment) {
function getReturnFromComment(
theme: MarkdownTheme,
comment: Comment,
reflectionName: string
) {
const md: string[] = []
const {
parameterStyle,
@@ -113,6 +118,7 @@ function getReturnFromComment(theme: MarkdownTheme, comment: Comment) {
...parameterComponentExtraProps,
title: commentPart.target.name,
},
sectionTitle: reflectionName,
})}\n\n`
: `\n\n<details>\n<summary>\n${
commentPart.target.name

View File

@@ -10,7 +10,10 @@ import { formatParameterComponent } from "../../utils/format-parameter-component
export default function (theme: MarkdownTheme) {
Handlebars.registerHelper(
"typeDeclarationMembers",
function (this: DeclarationReflection[]) {
function (
this: DeclarationReflection[],
options: Handlebars.HelperOptions
) {
const { parameterComponent, maxLevel, parameterComponentExtraProps } =
theme.getFormattingOptionsForLocation()
const comments = this.map(
@@ -24,6 +27,10 @@ export default function (theme: MarkdownTheme) {
[]
) as DeclarationReflection[]
// if (typeof options.hash.sectionTitle !== "string") {
// console.log("here2")
// }
let result = ""
switch (theme.objectLiteralTypeDeclarationStyle) {
case "list": {
@@ -36,6 +43,7 @@ export default function (theme: MarkdownTheme) {
parameterComponent,
maxLevel,
parameterComponentExtraProps,
sectionTitle: options.hash.sectionTitle,
})
break
}
@@ -65,11 +73,13 @@ function getComponentMarkdownContent({
parameterComponent,
maxLevel,
parameterComponentExtraProps,
sectionTitle,
}: {
properties: DeclarationReflection[]
parameterComponent?: string
maxLevel?: number | undefined
parameterComponentExtraProps?: Record<string, unknown>
sectionTitle: string
}) {
const parameters = properties.map((property) =>
reflectionFormatter({
@@ -84,6 +94,7 @@ function getComponentMarkdownContent({
parameterComponent,
componentItems: parameters as Parameter[],
extraProps: parameterComponentExtraProps,
sectionTitle,
})
}

View File

@@ -7,7 +7,10 @@ import { formatParameterComponent } from "../../utils/format-parameter-component
export default function (theme: MarkdownTheme) {
Handlebars.registerHelper(
"typeParameterComponent",
function (this: TypeParameterReflection[]) {
function (
this: TypeParameterReflection[],
options: Handlebars.HelperOptions
) {
const { parameterComponent, maxLevel, parameterComponentExtraProps } =
theme.getFormattingOptionsForLocation()
const parameters = this.map((parameter) =>
@@ -18,10 +21,15 @@ export default function (theme: MarkdownTheme) {
})
)
// if (typeof options.hash.sectionTitle !== "string") {
// console.log("here3")
// }
return formatParameterComponent({
parameterComponent,
componentItems: parameters,
extraProps: parameterComponentExtraProps,
sectionTitle: options.hash.sectionTitle,
})
}
)

View File

@@ -5,13 +5,16 @@ import { TypeParameterReflection } from "typedoc"
export default function (theme: MarkdownTheme) {
Handlebars.registerHelper(
"typeParameter",
function (this: TypeParameterReflection[]) {
function (
this: TypeParameterReflection[],
options: Handlebars.HelperOptions
) {
const { parameterStyle } = theme.getFormattingOptionsForLocation()
if (parameterStyle === "list") {
return Handlebars.helpers.typeParameterList.call(this)
} else if (parameterStyle === "component") {
return Handlebars.helpers.typeParameterComponent.call(this)
return Handlebars.helpers.typeParameterComponent.call(this, options)
} else {
return Handlebars.helpers.typeParameterTable.call(this)
}

View File

@@ -24,7 +24,7 @@
{{#with typeParameters}}
{{{typeParameter}}}
{{{typeParameter sectionTitle=../name}}}
{{/with}}
@@ -92,7 +92,7 @@
{{#with type.declaration.children}}
{{{typeDeclarationMembers}}}
{{{typeDeclarationMembers sectionTitle=../name}}}
{{/with}}
@@ -118,7 +118,7 @@
{{{titleLevel}}} Properties
{{{typeDeclarationMembers}}}
{{{typeDeclarationMembers sectionTitle=../name}}}
{{/with}}

View File

@@ -38,7 +38,7 @@
{{#with typeParameters}}
{{{typeParameter}}}
{{{typeParameter sectionTitle=../name}}}
{{/with}}

View File

@@ -38,7 +38,7 @@
{{#with typeParameters}}
{{{typeParameter}}}
{{{typeParameter sectionTitle=../name}}}
{{/with}}
@@ -54,7 +54,7 @@
{{#with parameters}}
{{{parameter}}}
{{{parameter sectionTitle=../name}}}
{{/with}}
@@ -106,7 +106,7 @@
{{#with declaration.children}}
{{{typeDeclarationMembers}}}
{{{typeDeclarationMembers sectionTitle=../../name}}}
{{/with}}

View File

@@ -34,7 +34,7 @@ ___
{{#with children}}
{{{parameterComponent}}}
{{{parameterComponent sectionTitle=../owningReflection.name}}}
{{/with}}
@@ -86,7 +86,7 @@ ___
{{#with children}}
{{{parameterComponent}}}
{{{parameterComponent sectionTitle=../owningReflection.name}}}
{{/with}}

View File

@@ -32,7 +32,7 @@
{{#with model.typeParameters}}
{{{typeParameter}}}
{{{typeParameter sectionTitle=../model.name}}}
{{/with}}

View File

@@ -3,6 +3,7 @@ import { Parameter } from "../types"
type FormatParameterComponentProps = {
parameterComponent: string | undefined
componentItems: Parameter[]
sectionTitle: string
extraProps?: Record<string, unknown>
}
@@ -43,6 +44,7 @@ export function formatParameterComponent({
parameterComponent,
componentItems,
extraProps,
sectionTitle,
}: FormatParameterComponentProps): string {
let extraPropsArr: string[] = []
if (extraProps) {
@@ -54,5 +56,5 @@ export function formatParameterComponent({
componentItems = sortComponentItems(componentItems)
return `<${parameterComponent} parameters={${JSON.stringify(
componentItems
)}} ${extraPropsArr.join(" ")}/>`
)}} ${extraPropsArr.join(" ")} sectionTitle="${sectionTitle}"/>`
}

View File

@@ -1,34 +1,53 @@
import {
CopyButton,
DetailsSummary,
ExpandableNotice,
FeatureFlagNotice,
InlineCode,
MarkdownContent,
} from "docs-ui"
import React from "react"
import React, { useEffect, useMemo, useRef, useState } from "react"
import Details from "../../../theme/Details"
import clsx from "clsx"
import { Parameter } from ".."
import {
ArrowDownLeftMini,
ArrowsPointingOutMini,
Link,
TriangleRightMini,
} from "@medusajs/icons"
import IconFlagMini from "../../../theme/Icon/FlagMini"
import decodeStr from "../../../utils/decode-str"
import { useLocation } from "@docusaurus/router"
import useDocusaurusContext from "@docusaurus/useDocusaurusContext"
import isInView from "../../../utils/is-in-view"
type ParameterTypesItemsProps = {
parameters: Parameter[]
type CommonProps = {
level?: number
expandUrl?: string
sectionTitle?: string
}
const ParameterTypesItems = ({
parameters,
type ParameterTypesItemProps = {
parameter: Parameter
elementKey: number
} & CommonProps &
React.AllHTMLAttributes<HTMLDivElement>
const ParameterTypesItem = ({
parameter,
level = 1,
expandUrl,
}: ParameterTypesItemsProps) => {
function getGroupName() {
elementKey,
sectionTitle,
}: ParameterTypesItemProps) => {
const location = useLocation()
const {
siteConfig: { url },
} = useDocusaurusContext()
const groupName = useMemo(() => {
switch (level) {
case 1:
return "group/parameterOne"
@@ -39,8 +58,8 @@ const ParameterTypesItems = ({
case 4:
return "group/parameterFour"
}
}
function getBorderForGroupName() {
}, [level])
const borderForGroupName = useMemo(() => {
switch (level) {
case 1:
return "group-open/parameterOne:border-solid group-open/parameterOne:border-0 group-open/parameterOne:border-b"
@@ -51,8 +70,8 @@ const ParameterTypesItems = ({
case 4:
return "group-open/parameterFour:border-solid group-open/parameterFour:border-0 group-open/parameterFour:border-b"
}
}
function getRotateForGroupName() {
}, [level])
const rotateForGroupName = useMemo(() => {
switch (level) {
case 1:
return "group-open/parameterOne:rotate-90"
@@ -63,15 +82,52 @@ const ParameterTypesItems = ({
case 4:
return "group-open/parameterFour:rotate-90"
}
}
}, [level])
function getItemClassNames(details = true) {
return clsx(
"odd:[&:not(:first-child):not(:last-child)]:!border-y last:not(:first-child):!border-t",
"first:!border-t-0 first:not(:last-child):!border-b last:!border-b-0 even:!border-y-0",
details && getGroupName(),
!details && getBorderForGroupName()
details && groupName,
!details && borderForGroupName
)
}
const formatId = (str: string) => {
return str.replaceAll(" ", "_")
}
const parameterId = useMemo(() => {
return sectionTitle
? `#${formatId(sectionTitle)}-${formatId(
parameter.name
)}-${level}-${elementKey}`
: ""
}, [sectionTitle, parameter, elementKey])
const parameterPath = useMemo(
() => `${location.pathname}${parameterId}`,
[location, parameterId]
)
const parameterUrl = useMemo(
() => `${url}${parameterPath}`,
[url, parameterPath]
)
const ref = useRef<HTMLDivElement>()
const [isSelected, setIsSelected] = useState(false)
useEffect(() => {
if (!parameterId.length) {
return
}
const shouldScroll = location.hash === parameterId
if (shouldScroll && !isSelected && ref.current && !isInView(ref.current)) {
ref.current.scrollIntoView({
block: "center",
})
}
setIsSelected(shouldScroll)
}, [parameterId])
function getSummary(parameter: Parameter, nested = true) {
return (
<DetailsSummary
@@ -102,7 +158,8 @@ const ParameterTypesItems = ({
level === 2 && "pl-3",
level === 3 && "pl-[120px]",
level === 4 && "pl-[160px]",
!nested && "cursor-default"
!nested && "cursor-default",
isSelected && "animate-flash animate-bg-surface"
)}
onClick={(e) => {
const targetElm = e.target as HTMLElement
@@ -112,13 +169,15 @@ const ParameterTypesItems = ({
return
}
}}
summaryRef={!nested ? ref : undefined}
id={!nested && parameterId ? parameterId : ""}
>
<div className="flex gap-0.5">
{nested && (
<TriangleRightMini
className={clsx(
"text-medusa-fg-subtle transition-transform",
getRotateForGroupName()
rotateForGroupName
)}
/>
)}
@@ -127,6 +186,21 @@ const ParameterTypesItems = ({
className={clsx("text-medusa-fg-subtle flip-y")}
/>
)}
{level === 1 && parameterId.length > 0 && (
<CopyButton
text={parameterUrl}
onCopy={(e: React.MouseEvent<HTMLSpanElement, MouseEvent>) => {
e.preventDefault()
e.stopPropagation()
}}
>
<Link
className={clsx(
"text-medusa-fg-interactive hover:text-medusa-fg-interactive-hover"
)}
/>
</CopyButton>
)}
<div className="flex gap-0.75 flex-wrap">
<InlineCode>{decodeStr(parameter.name)}</InlineCode>
<span className="font-monospace text-compact-small-plus text-medusa-fg-subtle">
@@ -168,32 +242,48 @@ const ParameterTypesItems = ({
)
}
return (
<>
{parameter.children?.length > 0 && (
<Details
summary={getSummary(parameter)}
className={clsx(getItemClassNames())}
heightAnimation={true}
ref={ref}
id={parameterId ? parameterId : ""}
>
{parameter.children && (
<ParameterTypesItems
parameters={parameter.children}
level={level + 1}
expandUrl={expandUrl}
/>
)}
</Details>
)}
{(parameter.children?.length || 0) === 0 && getSummary(parameter, false)}
</>
)
}
type ParameterTypesItemsProps = {
parameters: Parameter[]
} & CommonProps
const ParameterTypesItems = ({
parameters,
...rest
}: ParameterTypesItemsProps) => {
return (
<div>
{parameters.map((parameter, key) => {
return (
<>
{parameter.children?.length > 0 && (
<Details
summary={getSummary(parameter)}
key={key}
className={clsx(getItemClassNames())}
heightAnimation={true}
>
{parameter.children && (
<ParameterTypesItems
parameters={parameter.children}
level={level + 1}
expandUrl={expandUrl}
/>
)}
</Details>
)}
{(parameter.children?.length || 0) === 0 &&
getSummary(parameter, false)}
</>
)
})}
{parameters.map((parameter, key) => (
<ParameterTypesItem
parameter={parameter}
key={key}
elementKey={key}
{...rest}
/>
))}
</div>
)
}

View File

@@ -16,6 +16,7 @@ export type Parameter = {
type ParameterTypesType = {
parameters: Parameter[]
expandUrl?: string
sectionTitle?: string
} & React.HTMLAttributes<HTMLDivElement>
const ParameterTypesItems = lazy(async () => import("./Items"))
@@ -34,6 +35,7 @@ const ParameterTypes = ({
<ParameterTypesItems
parameters={parameters}
expandUrl={props.expandUrl}
sectionTitle={props.sectionTitle}
/>
</Suspense>
</div>

View File

@@ -0,0 +1,10 @@
export default function isInView(element: Element): boolean {
const rect = element.getBoundingClientRect()
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <=
(window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
)
}

View File

@@ -10,7 +10,8 @@ export type CopyButtonProps = {
buttonClassName?: string
tooltipClassName?: string
tooltipText?: string
} & React.HTMLAttributes<HTMLDivElement>
onCopy?: (e: React.MouseEvent<HTMLSpanElement, MouseEvent>) => void
} & Omit<React.HTMLAttributes<HTMLDivElement>, "onCopy">
export const CopyButton = ({
text,
@@ -19,6 +20,7 @@ export const CopyButton = ({
tooltipText = "Copy to Clipboard",
children,
className,
onCopy,
}: CopyButtonProps) => {
const { isCopied, handleCopy } = useCopy(text)
@@ -30,7 +32,10 @@ export const CopyButton = ({
>
<span
className={clsx("cursor-pointer", buttonClassName)}
onClick={handleCopy}
onClick={(e) => {
onCopy?.(e)
handleCopy()
}}
>
{children}
</span>

View File

@@ -11,6 +11,7 @@ export type DetailsSummaryProps = {
className?: string
titleClassName?: string
hideExpandableIcon?: boolean
summaryRef?: React.LegacyRef<HTMLDivElement>
} & Omit<React.HTMLAttributes<HTMLElement>, "title">
export const DetailsSummary = ({
@@ -23,6 +24,7 @@ export const DetailsSummary = ({
className,
titleClassName,
hideExpandableIcon = false,
summaryRef,
...rest
}: DetailsSummaryProps) => {
return (
@@ -36,6 +38,7 @@ export const DetailsSummary = ({
"no-marker",
className
)}
ref={summaryRef}
{...rest}
>
<span className="gap-docs_0.25 flex flex-col">

View File

@@ -577,6 +577,17 @@ module.exports = {
transform: "scale(1)",
},
},
flash: {
"0%": {
backgroundColor: "transparent",
},
"50%": {
backgroundColor: "var(--animation-color)",
},
"100%": {
backgroundColor: "transparent",
},
},
}),
animation: {
fadeIn: "fadeIn 500ms",
@@ -595,6 +606,7 @@ module.exports = {
pulsingDots: "pulsingDots 1s alternate infinite",
minimize: "minimize 500ms",
maximize: "maximize 500ms",
flash: "flash 1500ms 1",
},
},
fontFamily: {
@@ -708,6 +720,9 @@ module.exports = {
".flip-y": {
transform: "rotateY(180deg)",
},
".animate-bg-surface": {
"--animation-color": "var(--docs-bg-subtle-pressed)",
},
})
addComponents({
".btn-secondary-icon": {