fork refine

This commit is contained in:
Stefan Pejcic
2024-02-05 10:23:04 +01:00
parent 3fffde9a8f
commit 8496a83edb
3634 changed files with 715528 additions and 2 deletions

View File

@@ -0,0 +1,357 @@
import React, { HTMLAttributes, useState } from "react";
import snarkdown from "snarkdown";
import useBaseUrl from "@docusaurus/useBaseUrl";
// @ts-expect-error no types
import { useDoc } from "@docusaurus/theme-common/internal";
import { useHistory, useLocation } from "@docusaurus/router";
import clsx from "clsx";
import { useCurrentTutorial } from "../../hooks/use-current-tutorial";
import { UnitCircle } from "../unit-circle";
import { TutorialCircle } from "../tutorial-circle";
import { useTutorialUIPackage } from "../../hooks/use-tutorial-ui-package";
import { PreferredUIPackage } from "../../context/TutorialUIPackageContext";
const uiNames: Record<PreferredUIPackage, string> = {
headless: "Headless",
antd: "Ant Design",
mui: "Material UI",
mantine: "Mantine",
"chakra-ui": "Chakra UI",
};
const baseIconUrl =
"https://refine.ams3.cdn.digitaloceanspaces.com/website/static/icons/colored/ui-framework-";
type LinkWithIdProps = HTMLAttributes<HTMLAnchorElement> & {
id: string;
isCurrent?: boolean;
dangerouslySetInnerHTML?: { __html: string };
};
const LinkWithId = ({
id,
isCurrent,
className,
dangerouslySetInnerHTML,
...rest
}: LinkWithIdProps) => {
const toUrl = useBaseUrl(`/docs/${id}`, { forcePrependBaseUrl: true });
return (
<a
{...rest}
href={toUrl}
className={clsx(
className,
isCurrent
? "text-gray-800 dark:text-gray-0"
: " hover:!text-refine-link-light active:!text-refine-link-light dark:hover:!text-refine-link-dark dark:active:!text-refine-link-dark",
)}
dangerouslySetInnerHTML={dangerouslySetInnerHTML}
/>
);
};
const markdownConverter = (text: string) => {
const numericStartRegexp = /^\d+\.\s?/g;
// use this to get the numeric start
const _numericStart = text.match(numericStartRegexp)?.[0] || "";
const numericStartIgnore = text.replace(numericStartRegexp, "");
const marked = snarkdown(numericStartIgnore);
return `${marked}`;
};
const TutorialUIStatus = () => {
const { preferred: preferredUIPackage } = useTutorialUIPackage();
return (
<div className="mt-4">
<div
className={clsx(
"rounded",
" bg-gray-50 dark:bg-gray-800",
"p-4",
)}
>
<div className={clsx("font-semibold text-sm leading-6")}>
Current Framework
</div>
<div
className={clsx(
"flex items-center justify-between",
"pt-3",
)}
>
<div className="flex items-center gap-2">
<img
src={`${baseIconUrl}${preferredUIPackage}.svg`}
className="h-auto w-6"
/>
<span className="text-sm leading-6">
{uiNames[preferredUIPackage]}
</span>
</div>
<div>
<LinkWithId
id="tutorial/introduction/select-framework"
className={clsx(
"underline",
"text-sm",
"text-refine-link-light",
"dark:text-refine-link-dark",
)}
>
Change
</LinkWithId>
</div>
</div>
</div>
</div>
);
};
type TocLinkProps = {
item: any;
activeId?: string;
setActiveId?: React.Dispatch<React.SetStateAction<string>>;
};
const TocLink: React.FC<TocLinkProps> = ({ item, activeId, setActiveId }) => {
const location = useLocation();
const history = useHistory();
const { hash: locationHash } = location;
React.useEffect(() => {
const targetElement = document.getElementById(item.id);
if (targetElement) {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const hash = `#${item.id}`;
if (hash !== locationHash) {
setActiveId(item.id);
window.history.replaceState({}, "", hash);
}
}
});
},
{
rootMargin: "0px 0px -80% 0px",
},
);
observer.observe(targetElement);
return () => {
observer.unobserve(targetElement);
};
}
}, [item.id]);
return (
<a
href={`#${item.id}`}
dangerouslySetInnerHTML={{ __html: item.value }}
className={clsx(
"text-gray-800 dark:text-gray-0 hover:!text-refine-link active:!text-refine-link dark:hover:!text-refine-link-dark dark:active:!text-refine-link-dark",
activeId === item.id &&
"!text-refine-link-light dark:!text-refine-link-dark",
)}
></a>
);
};
export const TutorialTOC = () => {
const {
toc,
metadata: { id: currentDocId },
} = useDoc();
const currentTutorial = useCurrentTutorial();
const { hash } = useLocation();
const baseActiveId = `${hash}`.replace("#", "");
const [selectedUnit, setSelectedUnit] = useState(currentTutorial.unit);
const [activeId, setActiveId] = React.useState<string | undefined>(
baseActiveId,
);
React.useEffect(() => {
setActiveId(baseActiveId);
}, [baseActiveId]);
const renderTocItem = (item: (typeof toc)[number]) => {
return (
<li
key={item.id}
style={{
marginLeft: (item.level - 2) * 12,
}}
className="mb-1"
>
<TocLink
item={item}
activeId={activeId}
setActiveId={setActiveId}
/>
</li>
);
};
const renderTOC = () => {
if (toc.length === 0) return null;
return (
<ul className="mt-1 list-none pl-0">{toc.map(renderTocItem)}</ul>
);
};
const renderUnitItem = (
doc: NonNullable<
typeof currentTutorial
>["units"][number]["docs"][number],
) => {
const formattedTitle = markdownConverter(doc.title);
const unitNo = doc.title.split(".")[0];
return (
<li
key={doc.id}
className={clsx("flex flex-row items-start gap-2", "pb-4")}
>
<div className={clsx("mt-0.5", "h-8 w-8", "flex-shrink-0")}>
<TutorialCircle
id={doc.id}
width="100%"
height="100%"
unitNo={isNaN(Number(unitNo)) ? undefined : unitNo}
isCurrent={doc.current}
/>
</div>
<div className="flex flex-col gap-2 pt-[6px]">
<LinkWithId
id={doc.id}
isCurrent={doc.current}
className={clsx("leading-[22px]", "no-underline", {
"text-gray-900 dark:text-white":
currentDocId === doc.id,
"hover:cursor-default hover:no-underline hover:text-gray-900":
doc.current,
})}
dangerouslySetInnerHTML={{ __html: formattedTitle }}
/>
{doc.current && renderTOC()}
</div>
</li>
);
};
const renderUnitDocs = (
unit?: NonNullable<typeof currentTutorial>["units"][number],
) => {
return (
<div>
<ul className={clsx("list-none", "pl-0")}>
{unit?.docs
.sort((a, b) =>
`${a.title}`?.localeCompare(`${b.title}`),
)
.map(renderUnitItem)}
</ul>
</div>
);
};
const renderUnitTab = (unit: (typeof currentTutorial)["units"][number]) => {
return (
<button
key={unit.no}
type="button"
data-unit-title={unit.title}
data-unit-no={unit.no}
data-unit-current={
unit.unit === selectedUnit ? "true" : undefined
}
onClick={() => setSelectedUnit(unit.unit)}
className={clsx(
"tutorial-unit",
unit.unit === selectedUnit &&
"bg-gradient-to-t from-gray-50 dark:from-gray-700 from-40% to-gray-200 dark:to-gray-600",
"-mb-1",
"flex items-center justify-center",
"w-[40px] h-[44px]",
"cursor-pointer",
"rounded-tl-[24px] rounded-tr-[24px]",
"border-none",
"font-semibold",
)}
>
<UnitCircle
unit={unit.unit}
width="32px"
height="32px"
isSelected={unit.unit === selectedUnit}
/>
</button>
);
};
const currentUnit:
| NonNullable<typeof currentTutorial>["units"][number]
| undefined = currentTutorial?.units.find(
(unit) => unit.unit === selectedUnit,
);
const isFirstUnit =
currentTutorial?.units?.find((el) => el?.unit === currentTutorial?.unit)
?.no === 1;
return (
<div className={clsx("tutorial-tracker", "max-h-[calc(100vh-6rem]")}>
<div
className={clsx(
"tutorial-units",
"mb-1 px-2",
"flex items-center gap-0.5",
)}
>
{currentTutorial?.units.map(renderUnitTab)}
</div>
<div
className={clsx(
"rounded",
"pt-6 px-3 pb-2",
"bg-gray-50 dark:bg-gray-700",
)}
>
{/* <div className={clsx("mb-2", "text-sm font-bold")}>
{currentUnit?.title ?? currentUnit?.unit ?? "-"}
</div> */}
<div className={clsx("text-sm")}>
{renderUnitDocs(currentUnit)}
</div>
</div>
{!isFirstUnit &&
currentDocId !== "tutorial/introduction/select-framework" && (
<TutorialUIStatus />
)}
</div>
);
};
export const useDocTOCwithTutorial = () => {
const tutorial = useCurrentTutorial();
const { frontMatter, toc } = useDoc();
const hidden = frontMatter.hide_table_of_contents;
const canRender = (!hidden && toc.length > 0) || tutorial?.isTutorial;
const tutorialTOC = canRender ? <TutorialTOC /> : undefined;
return {
hidden,
tutorialTOC,
};
};