feat: Documentation and API reference (#348)

Co-authored-by: Vadim Smirnov <smirnou.vadzim@gmail.com>
Co-authored-by: zakariasaad <zakaria.elas@gmail.com>
Co-authored-by: Vilfred Sikker <vilfredsikker@gmail.com>
Co-authored-by: Kasper <kasper@medusa-commerce.com>
Co-authored-by: Sebastian Rindom <skrindom@gmail.com>
Co-authored-by: Kasper Fabricius Kristensen <45367945+kasperkristensen@users.noreply.github.com>
This commit is contained in:
Oliver Windall Juhl
2021-08-24 18:16:42 +02:00
committed by GitHub
co-authored by Vadim Smirnov zakariasaad Vilfred Sikker Kasper Sebastian Rindom Kasper Fabricius Kristensen
parent 5d63b0c8d2
commit f76aa816a5
71 changed files with 16482 additions and 1156 deletions
+55
View File
@@ -0,0 +1,55 @@
import React, { useEffect, useState } from "react"
import CloseIcon from "../close-icon"
import styles from "./banner.module.css"
import clsx from "clsx"
import useThemeContext from "@theme/hooks/useThemeContext"
import ConfLogo from "../../../static/img/logo.svg"
const Banner = (props) => {
const [isBannerVisible, setIsBannerVisible] = useState(true)
const { isDarkTheme } = useThemeContext()
const handleDismissBanner = () => {
setIsBannerVisible(false)
if (localStorage) {
localStorage.setItem("mc::banner", false)
}
}
useEffect(() => {
if (localStorage) {
const shouldShow = localStorage.getItem("mc::banner")
if (!shouldShow) {
setIsBannerVisible(true)
}
if (shouldShow === "false") {
setIsBannerVisible(false)
}
}
}, [])
return (
isBannerVisible && (
<div className={clsx(styles.bannerContainer, "margin-top--lg")}>
<div
className={clsx(styles.banner, "padding--sm", "padding-right--md")}
>
<div className="padding" sx={{ marginLeft: "10px" }}>
<ConfLogo />
<p className="padding-left--md">
We are still working on building out our documentation and guides,
but if you are interested in learning more please reach out - we
would love to jump on a call with you and help you get set up!
</p>
</div>
{/* <div style={{ cursor: "pointer" }} onClick={handleDismissBanner}>
<CloseIcon fill={isDarkTheme ? "white" : "black"} />
</div> */}
</div>
</div>
)
)
}
export default Banner
@@ -0,0 +1,18 @@
.bannerContainer {
height: 100%;
padding: 10px 0px;
margin-bottom: 20px;
background-color: var(--ifm-medusa-gray);
border-radius: 5px;
}
.bannerContainer svg {
max-width: 100px;
margin-left: 15px;
}
.banner {
display: flex;
flex-direction: row;
justify-content: space-between;
}
+1
View File
@@ -0,0 +1 @@
export { default as Banner } from "./Banner"
+50
View File
@@ -0,0 +1,50 @@
import React from "react"
import Link from "@docusaurus/Link"
import CodeBlock from "@theme/CodeBlock"
import styles from "./intro.module.css"
const Intro = ({ title, desc }) => {
return (
<div>
<h1 className="margin-bottom--xs margin-top--lg">Documentation</h1>
<p className={styles.title}>{title}</p>
<h2 className="margin-top--lg margin-bottom--sm">Quickstart</h2>
<p className={styles.description}>{desc}</p>
<CodeBlock>
{`yarn global add @medusajs/medusa-cli
medusa new my-awesome-store`}
</CodeBlock>
<div>
<Link
to="quickstart/quick-start"
className="margin-top--md margin-bottom--sm button button--lg button--primary"
>
Quickstart Guide
</Link>
</div>
<div className={` margin-top--md`}>
<Link
to="tutorial/set-up-your-development-environment"
className={`${styles.docsLink}`}
>
Set up your own local environment
</Link>
</div>
<div className={`margin-top--md`}>
<Link
to="tutorial/creating-your-medusa-server"
className={`${styles.docsLink}`}
>
Create your very own Medusa server
</Link>
</div>
<div className={`margin-top--md margin-bottom--xl`}>
<Link to="how-to/plugins" className={`${styles.docsLink}`}>
Explore plugins
</Link>
</div>
</div>
)
}
export default Intro
+1
View File
@@ -0,0 +1 @@
export { default as Intro } from "./Intro"
@@ -0,0 +1,17 @@
.title {
font-size: 24px;
}
.description {
font-size: 20px;
}
.docsLink {
font-size: 20px;
}
@media screen and (max-width: 966px) {
.docsLink {
font-size: 16px;
}
}
+12
View File
@@ -0,0 +1,12 @@
import React from "react"
import RootLayout from "@theme/Layout"
const Layout = ({ children, ...props }) => {
return (
<RootLayout {...props}>
<div className="container">{children}</div>
</RootLayout>
)
}
export default Layout
+1
View File
@@ -0,0 +1 @@
export { default as Layout } from "./Layout"
@@ -0,0 +1,19 @@
import React from "react"
import styles from "./tab.module.css"
const TabItem = ({ title, isOverviewCard, items }) => {
const overviewModeList = () =>
items.map((item) => <p className="margin-bottom--xs">{item.title}</p>)
return (
<div className="col col--4 padding-right--md padding-bottom--md">
<div className={styles.card}>
<div className={styles.cardContent}>
<h3>{title}</h3>
<div>{isOverviewCard && overviewModeList()}</div>
</div>
</div>
</div>
)
}
export default TabItem
@@ -0,0 +1 @@
export { default as TabItem } from "./TabItem"
@@ -0,0 +1,18 @@
.card {
background-color: var(--ifm-medusa-gray);
min-height: 257px;
border-radius: 5px;
padding: 20px 13px;
height: 100%;
}
.cardContent {
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.cardContent p {
font-size: 18px;
}
+77
View File
@@ -0,0 +1,77 @@
import clsx from "clsx"
import React, { useState } from "react"
import { TabItem } from "./TabItem/"
const BUTTONS_DATA = [
{ buttonTitle: "Overview", value: "overview" },
{ buttonTitle: "Tutorial", value: "tutorial" },
{ buttonTitle: "Guides", value: "guide" },
{ buttonTitle: "Reference", value: "reference" },
]
const OVERVIEW_DATA = ["Tutorial", "Guides", "Reference"]
const TabsPanel = ({ items }) => {
const [sort, setSort] = useState("overview")
const buttons = BUTTONS_DATA.map((item) => (
// <button
// className={clsx(
// { [styles.buttonActive]: sort === item.value },
// styles.button
// )}
// onClick={() => setSort(item.value)}
// >
// {item.buttonTitle}
// </button>
<li
className={clsx("tabs__item", {
"tabs__item--active": sort === item.value,
})}
onClick={() => setSort(item.value)}
>
{item.buttonTitle}
</li>
))
const getOverviewCardItemsSet = (currentCard) =>
items.filter((item) => item.key === currentCard.toLowerCase())
const overviewCardsSet = () =>
OVERVIEW_DATA.map((item) => (
<TabItem
isOverviewCard
title={item}
items={getOverviewCardItemsSet(item)}
/>
))
const getSortedArray = () => {
return items.filter((entry) => entry.type === sort)
}
const renderTabItems = () => {
if (sort === "overview") return overviewCardsSet()
return getSortedArray().length > 0 ? (
getSortedArray().map((item) => {
return <TabItem title={item.title} />
})
) : (
<p>hold tight! we are building these things</p>
)
}
return (
<div className="padding-bottom--xl">
{/* <Box sx={{ borderBottom: "1px solid black", marginBottom: "50px" }}>
{buttons}
</Box> */}
<ul class="tabs margin-bottom--lg">{buttons}</ul>
<div className="container padding--none">
<div className="row row--no-gutters">{renderTabItems()}</div>
</div>
</div>
)
}
export default TabsPanel
+1
View File
@@ -0,0 +1 @@
export { default as TabsPanel } from "./TabsPanel"
@@ -0,0 +1,15 @@
.button {
background-color: transparent;
border: none;
cursor: pointer;
height: 30px;
font-size: 16px;
margin-right: 10px;
}
.buttonActive {
color: var(--ifm-color-primary);
border-bottom: 3px solid var(--ifm-color-primary) !important;
font-weight: 500;
transition: ;
}
+18
View File
@@ -0,0 +1,18 @@
import React from "react"
const CloseIcon = ({ fill = "black" }) => (
<svg
width="15"
height="15"
viewBox="0 0 9 8"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M1.05 7.76L0.51 7.22L3.79 3.92L0.51 0.62L1.05 0.0799997L4.33 3.38L7.59 0.0799997L8.13 0.62L4.85 3.92L8.13 7.22L7.59 7.76L4.33 4.48L1.05 7.76Z"
fill={fill}
/>
</svg>
)
export default CloseIcon
+48
View File
@@ -0,0 +1,48 @@
import React from "react"
const ConfLogo = ({ fill }) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 371.2226 96">
<title>medusa-logo-one-colour-rgb</title>
<g id="black-rgb">
<path
id="logoMark"
d="M77.8271,15.6225,56.08,3.0664a22.8877,22.8877,0,0,0-22.8877,0L11.4438,15.6225A22.8877,22.8877,0,0,0,0,35.4438V60.5562A22.8877,22.8877,0,0,0,11.4438,80.3775L33.1919,92.9336a22.8877,22.8877,0,0,0,22.8877,0L77.8271,80.3775A22.8876,22.8876,0,0,0,89.271,60.5562V35.4438A22.8876,22.8876,0,0,0,77.8271,15.6225ZM44.6357,70.3178A22.3178,22.3178,0,1,1,66.9531,48,22.3176,22.3176,0,0,1,44.6357,70.3178Z"
style="fill: #0a3149"
/>
<g id="type">
<path
id="type_CompoundPathItem_"
d="M163.5361,22.6571h14.1416V71.662H169.206V30.078L155.625,71.662h-8.8907l-13.581-41.0948V71.662h-8.4707V22.6571h14.2109l12.3906,39.2734Z"
style="fill: #0a3149"
/>
<path
id="type_CompoundPathItem_2"
d="M220.4453,60.6c-2.03,7.3516-8.2608,12.042-17.0118,12.042-10.9209,0-17.8525-7.3515-17.8525-18.4824s7.001-18.4814,18.0625-18.4814c12.3213,0,18.6914,9.24,15.332,20.7216H194.0517c.7012,5.8106,3.9209,9.2413,9.5215,9.2413,3.92,0,7.001-1.82,8.1914-5.0411Zm-26.253-9.871H211.414c.49-5.04-2.38-8.33-7.9111-8.33C198.2529,42.3993,195.0322,45.34,194.1923,50.7294Z"
style="fill: #0a3149"
/>
<path
id="type_CompoundPathItem_3"
d="M252.5742,22.6571h7.9814V71.662h-7.9814V66.4813a13.8489,13.8489,0,0,1-11.9014,6.1611c-9.24,0-15.752-7.3515-15.752-18.4824,0-11.06,6.4415-18.4814,15.752-18.4814a13.85,13.85,0,0,1,11.9014,6.16Zm.3506,31.5029c0-6.9306-3.9209-11.4814-9.8018-11.4814-5.95,0-9.87,4.5508-9.87,11.4814s3.92,11.4815,9.87,11.4815C249.0039,65.6415,252.9248,61.0907,252.9248,54.16Z"
style="fill: #0a3149"
/>
<path
id="type_CompoundPathItem_4"
d="M290.2333,36.6581h7.9805V71.662h-7.9805V65.3612c-2.4511,4.69-6.4414,7.1406-11.3417,7.1406-9.7305,0-11.2715-9.6611-11.2715-15.8916V36.6581h7.98V56.5409c0,4.62,1.0508,9.03,6.0918,9.03,5.18,0,8.541-4.62,8.541-11.0605Z"
style="fill: #0a3149"
/>
<path
id="type_CompoundPathItem_5"
d="M304.0888,59.1307H311.79c-.49,4.3408,2.7305,7.07,8.1914,7.07,4.2695,0,7.1406-1.68,7.1406-4.2,0-7.0009-22.5429-1.96-22.5429-15.0517,0-7,6.5107-11.27,14.9824-11.27,9.3808,0,16.1709,5.25,14.7011,13.1611h-7.7714c.98-3.99-2.17-6.7207-7-6.7207-4.0606,0-6.7207,1.82-6.7207,4.34,0,7.0713,22.6123,1.6807,22.6123,14.9825,0,7.07-6.3711,11.2011-15.4717,11.2011C310.04,72.6424,303.5292,67.6014,304.0888,59.1307Z"
style="fill: #0a3149"
/>
<path
id="type_CompoundPathItem_6"
d="M371.2226,51.78V71.662H363.872V65.5711a13.7856,13.7856,0,0,1-12.6015,7.0713c-7.42,0-12.0411-4.3408-12.0411-10.4316,0-7.07,6.16-11.3408,16.0313-11.3408a36.6377,36.6377,0,0,1,7.9814.91c0-5.3213-1.8906-9.1709-7.4912-9.1709-4.2,0-6.791,2.1-5.95,6.02H341.749c-1.89-7.77,4.9707-12.9511,14.0722-12.9511C366.0419,35.6786,371.2226,42.0487,371.2226,51.78Zm-7.7,6.0909a25.2,25.2,0,0,0-7.6308-1.19c-5.7413,0-8.5411,2.1006-8.5411,5.32,0,2.8008,2.1,4.27,5.53,4.27A10.8339,10.8339,0,0,0,363.5224,57.871Z"
style="fill: #0a3149"
/>
</g>
</g>
</svg>
)
export default ConfLogo
+190
View File
@@ -0,0 +1,190 @@
/* stylelint-disable docusaurus/copyright-header */
/**
* Any CSS included here will be global. The classic template
* bundles Infima by default. Infima is a CSS framework designed to
* work well for content-centric websites.
*/
/* You can override the default Infima variables here. */
@import url("https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;700&display=swap");
:root {
/* Colors */
--ifm-color-primary: #0a3149;
--ifm-color-primary-dark: #0a3149;
--ifm-color-primary-darker: #0a3149;
--ifm-color-primary-darkest: #0a3149;
--ifm-color-primary-light: #0a3149;
--ifm-color-primary-lighter: #0a3149;
--ifm-color-primary-lightest: #0a3149;
--ifm-medusa-gray: #f0f0f0;
/* Fonts */
--ifm-code-font-size: 90%;
--ifm-font-family-base: "Open Sans";
/* Sidebar */
--doc-sidebar-width: 350px !important;
/* Docs Page */
--ifm-docs-page-max-width: 700px;
/* Toc */
--ifm-toc-border-color: #f0f0f0;
--ifm-toc-link-color: #a6acb5;
--ifm-toc-padding-horizontal: 9px;
/* Navbar */
--ifm-navbar-shadow: 0px 1px 0px 0px #a6acb5;
}
html[data-theme="dark"] {
--ifm-color-primary-dark: #ff4848;
--ifm-color-primary: #ff4242;
--ifm-background-color: #1f1f1f;
--ifm-background-surface-color: #1f1f1f;
--ifm-medusa-gray: #292929;
--ifm-menu-color: #fff;
--ifm-toc-border-color: #333;
}
/* DocSearch */
html[data-theme="light"] .DocSearch-Button {
--docsearch-searchbox-background: #fff;
}
html[data-theme="dark"] .DocSearch-Button {
--docsearch-searchbox-background: #1f1f1f;
}
span.DocSearch-Button-Key {
display: none;
}
/* Navbar */
html[data-theme="dark"] .navbar-github-link:before {
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='white' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E")
no-repeat;
}
html[data-theme="dark"] .docusaurus-highlight-code-line {
background-color: rgba(0, 0, 0, 0.3);
}
.navbar-github-link:before {
content: "";
width: 24px;
height: 24px;
display: flex;
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E")
no-repeat;
}
.navbar__link svg {
display: none;
}
/* DocsPage */
article {
max-width: var(--ifm-docs-page-max-width);
margin-left: auto;
margin-right: auto;
}
.docusaurus-highlight-code-line {
background-color: rgba(0, 0, 0, 0.1);
display: block;
margin: 0 calc(-1 * var(--ifm-pre-padding));
padding: 0 var(--ifm-pre-padding);
}
/* TOC */
.table-of-contents {
font-size: 0.85rem;
}
.table-of-contents::before {
content: "ON THIS PAGE";
display: inline-block;
font-size: 0.85rem;
padding-bottom: 4px;
}
.table-of-contents .table-of-contents__link--active {
border-left: 3px solid var(--ifm-link-color);
color: var(--ifm-color-primary);
font-weight: 500;
margin-left: -14px;
padding-left: 10px;
}
/* Sidebar */
.main-wrapper aside {
z-index: var(--ifm-z-index-fixed);
}
.sidebar-bg {
background: var(--ifm-medusa-gray);
height: 100%;
}
.padding-top--md {
padding-top: 2rem !important;
}
a.menu__link.menu__link--active.active {
border-left: 4px solid var(--ifm-color-primary);
padding-left: 12px;
}
/* Pagination */
.pagination-nav {
margin-left: auto;
margin-right: auto;
max-width: var(--ifm-docs-page-max-width);
}
.navbar {
padding-left: 32px;
padding-right: 32px;
}
/* Footer */
footer .footer__items svg {
display: none;
}
footer .footer__item a:hover {
color: white;
}
@media screen and (min-width: 966px) {
footer .footer__col {
display: flex;
flex-direction: column;
align-items: center;
}
}
footer .footer__items li {
width: 150px;
}
footer .footer__title {
width: 150px;
}
.footer--dark {
--ifm-footer-background-color: #333;
}
.react-toggle {
display: none;
}
+20
View File
@@ -0,0 +1,20 @@
import React from "react"
import { Banner } from "../components/Banner/"
import { Intro } from "../components/Intro/"
import { Layout } from "../components/Layout/"
import styles from "./index.module.css"
export default function Home() {
return (
<Layout title={`Docs`} description="some description...">
<div className={styles.container}>
<Banner />
<Intro
title="Explore and learn how to use Medusa."
desc="Get up and running within 5 minutes."
/>
{/* <TabsPanel items={CARDS_DATA} /> */}
</div>
</Layout>
)
}
+37
View File
@@ -0,0 +1,37 @@
/* stylelint-disable docusaurus/copyright-header */
/**
* CSS files with the .module.css suffix will be treated as CSS modules
* and scoped locally.
*/
.heroBanner {
padding: 4rem 0;
text-align: center;
position: relative;
overflow: hidden;
}
@media screen and (max-width: 966px) {
.heroBanner {
padding: 2rem;
}
}
.buttons {
display: flex;
align-items: center;
justify-content: center;
}
.container {
display: flex;
flex-direction: column;
}
@media screen and (min-width: 966px) {
.container {
max-width: 850px;
margin: 0 auto;
}
}
+12
View File
@@ -0,0 +1,12 @@
import OriginalDocSidebar from "@theme-original/DocSidebar"
import React from "react"
const DocSidebar = (props) => {
return (
<div className="sidebar-bg">
<OriginalDocSidebar {...props} />
</div>
)
}
export default DocSidebar
@@ -0,0 +1,124 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
:root {
--collapse-button-bg-color-dark: #2e333a;
}
@media (min-width: 997px) {
.sidebar {
display: flex;
flex-direction: column;
max-height: 100vh;
height: 100%;
position: sticky;
top: 0;
padding-top: var(--ifm-navbar-height);
width: var(--doc-sidebar-width);
transition: opacity 50ms ease;
}
.sidebarWithHideableNavbar {
padding-top: 0;
}
.sidebarHidden {
opacity: 0;
height: 0;
overflow: hidden;
visibility: hidden;
}
.sidebarLogo {
display: flex !important;
align-items: center;
margin: 0 var(--ifm-navbar-padding-horizontal);
min-height: var(--ifm-navbar-height);
max-height: var(--ifm-navbar-height);
color: inherit !important;
text-decoration: none !important;
}
.sidebarLogo img {
margin-right: 0.5rem;
height: 2rem;
}
.menu {
flex-grow: 1;
padding: 0.5rem;
}
.menuLinkText {
cursor: initial;
}
.menuLinkText:hover {
background: none;
}
.menuWithAnnouncementBar {
margin-bottom: var(--docusaurus-announcement-bar-height);
}
.collapseSidebarButton {
display: block !important;
background-color: var(--ifm-button-background-color);
height: 40px;
position: sticky;
bottom: 0;
border-radius: 0;
border: 1px solid var(--ifm-toc-border-color);
}
.collapseSidebarButtonIcon {
transform: rotate(180deg);
margin-top: 4px;
}
html[dir="rtl"] .collapseSidebarButtonIcon {
transform: rotate(0);
}
html[data-theme="dark"] .collapseSidebarButton {
background-color: var(--collapse-button-bg-color-dark);
}
html[data-theme="dark"] .collapseSidebarButton:hover,
html[data-theme="dark"] .collapseSidebarButton:focus {
background-color: var(--ifm-color-emphasis-200);
}
}
.sidebarLogo,
.collapseSidebarButton {
display: none;
}
.sidebarMenuIcon {
vertical-align: middle;
}
.sidebarMenuCloseIcon {
display: inline-flex;
justify-content: center;
align-items: center;
height: 24px;
font-size: 1.5rem;
font-weight: var(--ifm-font-weight-bold);
line-height: 0.9;
width: 24px;
}
:global(.menu__list) :global(.menu__list) {
overflow-y: hidden;
will-change: height;
transition: height var(--ifm-transition-fast) linear;
}
:global(.menu__list-item--collapsed) :global(.menu__list) {
height: 0 !important;
}
+204
View File
@@ -0,0 +1,204 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import React, { useState, useRef, useCallback, useMemo } from "react"
import { createPortal } from "react-dom"
import useDocusaurusContext from "@docusaurus/useDocusaurusContext"
import { useHistory } from "@docusaurus/router"
import { useBaseUrlUtils } from "@docusaurus/useBaseUrl"
import Link from "@docusaurus/Link"
import Head from "@docusaurus/Head"
import useSearchQuery from "@theme/hooks/useSearchQuery"
import { DocSearchButton, useDocSearchKeyboardEvents } from "@docsearch/react"
import useAlgoliaContextualFacetFilters from "@theme/hooks/useAlgoliaContextualFacetFilters"
import { translate } from "@docusaurus/Translate"
import styles from "./styles.module.css"
let DocSearchModal = null
function Hit({ hit, children }) {
return <Link to={hit.url}>{children}</Link>
}
function ResultsFooter({ state, onClose }) {
const { generateSearchPageLink } = useSearchQuery()
return (
<Link to={generateSearchPageLink(state.query)} onClick={onClose}>
See all {state.context.nbHits} results
</Link>
)
}
function DocSearch({ contextualSearch, ...props }) {
const { siteMetadata } = useDocusaurusContext()
const contextualSearchFacetFilters = useAlgoliaContextualFacetFilters()
const configFacetFilters = props.searchParameters?.facetFilters ?? []
const facetFilters = contextualSearch
? // Merge contextual search filters with config filters
[...contextualSearchFacetFilters, ...configFacetFilters]
: // ... or use config facetFilters
configFacetFilters
// we let user override default searchParameters if he wants to
const searchParameters = {
...props.searchParameters,
facetFilters,
}
const { withBaseUrl } = useBaseUrlUtils()
const history = useHistory()
const searchContainer = useRef(null)
const searchButtonRef = useRef(null)
const [isOpen, setIsOpen] = useState(false)
const [initialQuery, setInitialQuery] = useState(null)
const importDocSearchModalIfNeeded = useCallback(() => {
if (DocSearchModal) {
return Promise.resolve()
}
return Promise.all([
import("@docsearch/react/modal"),
import("@docsearch/react/style"),
import("./styles.css"),
]).then(([{ DocSearchModal: Modal }]) => {
DocSearchModal = Modal
})
}, [])
const onOpen = useCallback(() => {
importDocSearchModalIfNeeded().then(() => {
searchContainer.current = document.createElement("div")
document.body.insertBefore(
searchContainer.current,
document.body.firstChild
)
setIsOpen(true)
})
}, [importDocSearchModalIfNeeded, setIsOpen])
const onClose = useCallback(() => {
setIsOpen(false)
searchContainer.current.remove()
}, [setIsOpen])
const onInput = useCallback(
(event) => {
importDocSearchModalIfNeeded().then(() => {
setIsOpen(true)
setInitialQuery(event.key)
})
},
[importDocSearchModalIfNeeded, setIsOpen, setInitialQuery]
)
const navigator = useRef({
navigate({ itemUrl }) {
history.push(itemUrl)
},
}).current
const transformItems = useRef((items) => {
return items.map((item) => {
// We transform the absolute URL into a relative URL.
// Alternatively, we can use `new URL(item.url)` but it's not
// supported in IE.
const a = document.createElement("a")
a.href = item.url
return {
...item,
url: withBaseUrl(`${a.pathname}${a.hash}`),
}
})
}).current
const resultsFooterComponent = useMemo(
() => (footerProps) => <ResultsFooter {...footerProps} onClose={onClose} />,
[onClose]
)
const transformSearchClient = useCallback(
(searchClient) => {
searchClient.addAlgoliaAgent("docusaurus", siteMetadata.docusaurusVersion)
return searchClient
},
[siteMetadata.docusaurusVersion]
)
useDocSearchKeyboardEvents({
isOpen,
onOpen,
onClose,
onInput,
searchButtonRef,
})
const translatedSearchLabel = translate({
id: "theme.SearchBar.label",
message: "Search",
description: "The ARIA label and placeholder for search button",
})
return (
<>
<Head>
{/* This hints the browser that the website will load data from Algolia,
and allows it to preconnect to the DocSearch cluster. It makes the first
query faster, especially on mobile. */}
<link
rel="preconnect"
href={`https://${props.appId}-dsn.algolia.net`}
crossOrigin="anonymous"
/>
</Head>
<div className={styles.searchBox}>
<DocSearchButton
onTouchStart={importDocSearchModalIfNeeded}
onFocus={importDocSearchModalIfNeeded}
onMouseOver={importDocSearchModalIfNeeded}
onClick={onOpen}
ref={searchButtonRef}
translations={{
buttonText: translatedSearchLabel,
buttonAriaLabel: translatedSearchLabel,
}}
/>
</div>
{isOpen &&
createPortal(
<DocSearchModal
onClose={onClose}
initialScrollY={window.scrollY}
initialQuery={initialQuery}
navigator={navigator}
transformItems={transformItems}
hitComponent={Hit}
resultsFooterComponent={resultsFooterComponent}
transformSearchClient={transformSearchClient}
{...props}
searchParameters={searchParameters}
/>,
searchContainer.current
)}
</>
)
}
function SearchBar() {
const { siteConfig } = useDocusaurusContext()
return <DocSearch {...siteConfig.themeConfig.algolia} />
}
export default SearchBar
+22
View File
@@ -0,0 +1,22 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
:root {
--docsearch-primary-color: var(--ifm-color-primary);
--docsearch-text-color: var(--ifm-font-color-base);
}
.DocSearch-Button {
margin: 0;
transition: all var(--ifm-transition-fast)
var(--ifm-transition-timing-default);
width: 100%;
}
.DocSearch-Container {
z-index: calc(var(--ifm-z-index-fixed) + 1);
}
@@ -0,0 +1,28 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
@media (max-width: 996px) {
.searchBox {
position: absolute;
right: var(--ifm-navbar-padding-horizontal);
}
}
@media (min-width: 997px) {
.searchBox {
padding: var(--ifm-navbar-item-padding-vertical)
var(--ifm-navbar-item-padding-horizontal);
flex: 1;
margin-left: 200px;
padding-right: 100px;
}
.searchBox button {
border: 1px solid var(--ifm-medusa-gray);
width: 250px;
}
}