mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-24 22:46:57 +02:00
feat(v2): bootstrap theme, preset, template, CI previews (#2981)
* fix(v2): doc sidebar * chore(v2): prettier * fix(v2): docs navbar path * fix(v2): fix error about activepath * chore(v2): prettier * feat(v2): change active color * feat(v2): Add bootstrap doc * docs(v2): Update preset * doc(v2): finish bootstrap documentation * chore(v2): run lint * doc(v2): update hook * fix theme bootstrap layout (far from perfect) * Try to fix bootstrap theme and deploy it! * fix netlify error Co-authored-by: slorber <lorber.sebastien@gmail.com>
This commit is contained in:
parent
5fdf96c552
commit
8c05aa8e94
17 changed files with 561 additions and 67 deletions
|
@ -14,34 +14,45 @@ import Layout from '@theme/Layout';
|
|||
import {MDXProvider} from '@mdx-js/react';
|
||||
import {matchPath} from '@docusaurus/router';
|
||||
|
||||
function DocPage(props) {
|
||||
const {route: baseRoute, docsMetadata, location} = props;
|
||||
// case-sensitive route such as it is defined in the sidebar
|
||||
const currentRoute =
|
||||
baseRoute.routes.find((route) => {
|
||||
return matchPath(location.pathname, route);
|
||||
}) || {};
|
||||
const {permalinkToSidebar, docsSidebars} = docsMetadata;
|
||||
const sidebar = permalinkToSidebar[currentRoute.path];
|
||||
|
||||
if (Object.keys(currentRoute).length === 0) {
|
||||
return <NotFound {...props} />;
|
||||
}
|
||||
|
||||
function DocPageContent({currentDocRoute, versionMetadata, children}) {
|
||||
const {permalinkToSidebar, docsSidebars} = versionMetadata;
|
||||
const sidebarName = permalinkToSidebar[currentDocRoute.path];
|
||||
const sidebar = docsSidebars[sidebarName];
|
||||
return (
|
||||
<Layout title="Doc page" description="My Doc page">
|
||||
<DocSidebar
|
||||
docsSidebars={docsSidebars}
|
||||
path={currentRoute.path}
|
||||
sidebar={sidebar}
|
||||
/>
|
||||
<section className="offset-1 mr-4 mt-4 col-xl-6 offset-xl-4 p-0 justify-content-center align-self-center overflow-hidden">
|
||||
<MDXProvider components={MDXComponents}>
|
||||
{renderRoutes(baseRoute.routes)}
|
||||
</MDXProvider>
|
||||
</section>
|
||||
<div className="d-flex vh-100 overflow-hidden">
|
||||
{sidebar && (
|
||||
<div className="vh-100" role="complementary">
|
||||
<DocSidebar path={currentDocRoute.path} sidebar={sidebar} />
|
||||
</div>
|
||||
)}
|
||||
<main className="vh-100 w-100 d-flex flex-column align-items-center overflow-auto p-5">
|
||||
<MDXProvider components={MDXComponents}>{children}</MDXProvider>
|
||||
</main>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
function DocPage(props) {
|
||||
const {
|
||||
route: {routes: docRoutes},
|
||||
versionMetadata,
|
||||
location,
|
||||
} = props;
|
||||
const currentDocRoute = docRoutes.find((docRoute) =>
|
||||
matchPath(location.pathname, docRoute),
|
||||
);
|
||||
if (!currentDocRoute) {
|
||||
return <NotFound {...props} />;
|
||||
}
|
||||
return (
|
||||
<DocPageContent
|
||||
currentDocRoute={currentDocRoute}
|
||||
versionMetadata={versionMetadata}>
|
||||
{renderRoutes(docRoutes)}
|
||||
</DocPageContent>
|
||||
);
|
||||
}
|
||||
|
||||
export default DocPage;
|
||||
|
|
|
@ -61,9 +61,7 @@ const DocSidebarItem = ({item, onItemClick, ...props}) => {
|
|||
}
|
||||
};
|
||||
|
||||
const DocSidebar = (props) => {
|
||||
const {docsSidebars, sidebar: currentSidebar} = props;
|
||||
|
||||
const DocSidebar = ({sidebar, path}) => {
|
||||
const [sidebarShown, setSidebarShown] = useState(false);
|
||||
const handleSidebarToggle = useCallback(() => {
|
||||
setSidebarShown(!sidebarShown);
|
||||
|
@ -71,18 +69,6 @@ const DocSidebar = (props) => {
|
|||
|
||||
useLockBodyScroll(sidebarShown);
|
||||
|
||||
if (!currentSidebar) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const sidebarData = docsSidebars[currentSidebar];
|
||||
|
||||
if (!sidebarData) {
|
||||
throw new Error(
|
||||
`Cannot find the sidebar "${currentSidebar}" in the sidebar config!`,
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classNames('bg-info', styles.sidebar)}>
|
||||
<div
|
||||
|
@ -115,7 +101,7 @@ const DocSidebar = (props) => {
|
|||
</div>
|
||||
<div className={classNames(styles.sideMenu)}>
|
||||
<Nav vertical className="list-unstyled p-3 mr-auto">
|
||||
{sidebarData.map((item) => (
|
||||
{sidebar.map((item) => (
|
||||
<DocSidebarItem
|
||||
key={item.label}
|
||||
item={item}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* 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 from 'react';
|
||||
import Layout from '@theme/Layout';
|
||||
import {MDXProvider} from '@mdx-js/react';
|
||||
import MDXComponents from '@theme/MDXComponents';
|
||||
|
||||
function MDXPage(props) {
|
||||
const {content: MDXPageContent} = props;
|
||||
const {frontMatter, metadata} = MDXPageContent;
|
||||
const {title, description} = frontMatter;
|
||||
const {permalink} = metadata;
|
||||
|
||||
return (
|
||||
<Layout title={title} description={description} permalink={permalink}>
|
||||
<main>
|
||||
<div className="container margin-vert--lg padding-vert--lg">
|
||||
<MDXProvider components={MDXComponents}>
|
||||
<MDXPageContent />
|
||||
</MDXProvider>
|
||||
</div>
|
||||
</main>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
export default MDXPage;
|
|
@ -0,0 +1,14 @@
|
|||
/**
|
||||
* 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 from 'react';
|
||||
|
||||
function TabItem(props) {
|
||||
return <div>{props.children}</div>;
|
||||
}
|
||||
|
||||
export default TabItem;
|
154
packages/docusaurus-theme-bootstrap/src/theme/Tabs/index.js
Normal file
154
packages/docusaurus-theme-bootstrap/src/theme/Tabs/index.js
Normal file
|
@ -0,0 +1,154 @@
|
|||
/**
|
||||
* 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, Children, useEffect} from 'react';
|
||||
|
||||
import clsx from 'clsx';
|
||||
|
||||
import styles from './styles.module.css';
|
||||
|
||||
const keys = {
|
||||
left: 37,
|
||||
right: 39,
|
||||
tab: 9,
|
||||
};
|
||||
|
||||
// TODO quick fix to make bootstrap theme work
|
||||
function useUserPreferencesContext() {
|
||||
const [state, setState] = useState({});
|
||||
|
||||
return {
|
||||
tabGroupChoices: state,
|
||||
setTabGroupChoices: (id, value) => {
|
||||
setState((s) => ({...s, [id]: value}));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function Tabs(props) {
|
||||
const {block, children, defaultValue, values, groupId} = props;
|
||||
|
||||
const {tabGroupChoices, setTabGroupChoices} = useUserPreferencesContext();
|
||||
|
||||
const [selectedValue, setSelectedValue] = useState(defaultValue);
|
||||
const [keyboardPress, setKeyboardPress] = useState(false);
|
||||
|
||||
if (groupId != null) {
|
||||
const relevantTabGroupChoice = tabGroupChoices[groupId];
|
||||
if (
|
||||
relevantTabGroupChoice != null &&
|
||||
relevantTabGroupChoice !== selectedValue &&
|
||||
values.some((value) => value.value === relevantTabGroupChoice)
|
||||
) {
|
||||
setSelectedValue(relevantTabGroupChoice);
|
||||
}
|
||||
}
|
||||
|
||||
const changeSelectedValue = (newValue) => {
|
||||
setSelectedValue(newValue);
|
||||
if (groupId != null) {
|
||||
setTabGroupChoices(groupId, newValue);
|
||||
}
|
||||
};
|
||||
|
||||
const tabRefs = [];
|
||||
|
||||
const focusNextTab = (tabs, target) => {
|
||||
const next = tabs.indexOf(target) + 1;
|
||||
|
||||
if (!tabs[next]) {
|
||||
tabs[0].focus();
|
||||
} else {
|
||||
tabs[next].focus();
|
||||
}
|
||||
};
|
||||
|
||||
const focusPreviousTab = (tabs, target) => {
|
||||
const prev = tabs.indexOf(target) - 1;
|
||||
|
||||
if (!tabs[prev]) {
|
||||
tabs[tabs.length - 1].focus();
|
||||
} else {
|
||||
tabs[prev].focus();
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeydown = (tabs, target, event) => {
|
||||
switch (event.keyCode) {
|
||||
case keys.right:
|
||||
focusNextTab(tabs, target);
|
||||
break;
|
||||
case keys.left:
|
||||
focusPreviousTab(tabs, target);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyboardEvent = (event) => {
|
||||
if (event.metaKey || event.altKey || event.ctrlKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
setKeyboardPress(true);
|
||||
};
|
||||
|
||||
const handleMouseEvent = () => {
|
||||
setKeyboardPress(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('keydown', handleKeyboardEvent);
|
||||
window.addEventListener('mousedown', handleMouseEvent);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ul
|
||||
role="tablist"
|
||||
aria-orientation="horizontal"
|
||||
className={clsx('tabs', {
|
||||
'tabs--block': block,
|
||||
})}>
|
||||
{values.map(({value, label}) => (
|
||||
<li
|
||||
role="tab"
|
||||
tabIndex={0}
|
||||
aria-selected={selectedValue === value}
|
||||
className={clsx('tabs__item', styles.tabItem, {
|
||||
'tabs__item--active': selectedValue === value,
|
||||
})}
|
||||
style={keyboardPress ? {} : {outline: 'none'}}
|
||||
key={value}
|
||||
ref={(tabControl) => tabRefs.push(tabControl)}
|
||||
onKeyDown={(event) => {
|
||||
handleKeydown(tabRefs, event.target, event);
|
||||
handleKeyboardEvent(event);
|
||||
}}
|
||||
onFocus={() => changeSelectedValue(value)}
|
||||
onClick={() => {
|
||||
changeSelectedValue(value);
|
||||
setKeyboardPress(false);
|
||||
}}
|
||||
onPointerDown={() => setKeyboardPress(false)}>
|
||||
{label}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div role="tabpanel" className="margin-vert--md">
|
||||
{
|
||||
Children.toArray(children).filter(
|
||||
(child) => child.props.value === selectedValue,
|
||||
)[0]
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Tabs;
|
|
@ -0,0 +1,11 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.tabItem {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
/**
|
||||
* 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 defaultTheme from 'prism-react-renderer/themes/palenight';
|
||||
|
||||
const usePrismTheme = () => {
|
||||
return defaultTheme;
|
||||
};
|
||||
|
||||
export default usePrismTheme;
|
Loading…
Add table
Add a link
Reference in a new issue