[New Feature] Add web folder to support web documentation

This commit is contained in:
Rudy Haryanto
2022-10-04 20:17:17 +07:00
parent c27f624ca8
commit a54aa04c9b
251 changed files with 11079 additions and 0 deletions

View File

@@ -0,0 +1,72 @@
%link-style {
display: initial;
color: var(--ifm-font-color-base);
background-color: rgba(187, 239, 253, 0.3);
line-height: calc(var(--ifm-font-size-base) + 4px);
border-bottom: 1px solid var(--ifm-hr-border-color);
&:hover {
background-color: rgba(187, 239, 253, 0.6);
}
}
%link-style-dark {
background-color: rgba(97, 218, 251, 0.12);
border-bottom-color: rgba(97, 218, 251, 0.3);
&:hover {
background-color: rgba(97, 218, 251, 0.4);
border-bottom-color: var(--brand);
}
}
%hash-link-style {
background-color: transparent;
border-bottom: 0;
color: var(--subtle);
&:hover {
background-color: transparent;
color: var(--brand);
}
}
%button-link-style {
display: inline-block;
padding: 8px 16px;
border: 1px solid var(--ifm-color-emphasis-300);
border-radius: var(--ifm-global-radius);
color: var(--ifm-color-content-secondary);
&:hover {
background: var(--ifm-menu-color-background-hover);
color: var(--ifm-link-color);
}
}
%scrollbar-style {
&::-webkit-scrollbar {
width: 7px;
height: 7px;
}
&::-webkit-scrollbar-thumb {
background: #888;
border-radius: 10px;
}
&::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 10px;
}
}
%scrollbar-style-dark {
&::-webkit-scrollbar-track {
background: #141414;
}
&::-webkit-scrollbar-thumb {
background: var(--ifm-color-emphasis-200);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,802 @@
@import "shared";
.homepage {
width: 100%;
max-width: 100%;
}
/* ActionButton */
.ActionButton {
padding: 0.75rem 1.5rem;
text-align: center;
font-size: 1.25rem;
font-weight: 400;
text-decoration: none !important;
border-bottom: none;
transition: all 0.2s ease-out;
max-width: 50%;
&.primary {
color: var(--dark);
background-color: var(--brand);
&:hover {
color: black;
background-color: white;
}
}
&.secondary {
background: none;
color: var(--brand);
&::after {
content: "";
font-size: 24px;
margin-left: 5px;
}
&:hover {
color: white;
}
}
}
@media only screen and (max-width: 480px) {
.ActionButton {
max-width: 100%;
width: 100%;
display: block;
white-space: nowrap;
}
}
/* AcknowledgementList */
.AcknowledgementList {
display: grid;
padding: 0;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
grid-gap: 16px;
.item {
list-style: none;
a img {
border-radius: 20%;
overflow: hidden;
}
}
}
/* Community */
.Community .content {
max-width: 900px;
margin: 0 auto;
display: flex;
flex-direction: column;
.firstP img {
float: left;
width: 56px;
height: 56px;
margin-right: 20px;
}
}
@media only screen and (max-width: 480px) {
.Community .Heading {
width: 100%;
padding: 0 1rem;
margin-bottom: 1.5rem;
}
}
@media only screen and (min-width: 481px) and (max-width: 960px) {
.Community .Heading {
width: 100%;
padding: 0 4rem;
margin-bottom: 1.5rem;
}
.Community .AcknowledgementList {
width: 100%;
max-width: 500px;
margin: 2rem auto;
}
}
@media only screen and (min-width: 961px) {
.Community .column.first {
border-right: 1px solid var(--ifm-table-border-color);
}
}
/* Cross Platform */
.CrossPlatform {
svg {
max-width: 400px;
margin: -20px 0;
text {
fill: var(--ifm-color-content-secondary);
}
}
}
@media only screen and (max-width: 960px) {
.CrossPlatform .TwoColumns {
grid-gap: 2rem;
}
.CrossPlatform svg {
max-width: 100%;
margin: 0 auto;
}
}
@media only screen and (min-width: 481px) and (max-width: 960px) {
.CrossPlatform .column.last {
width: 86%;
margin: 0 auto;
text-align: center;
}
}
/* Fast Refresh */
/* Make video flush with the bottom */
.FastRefresh {
margin-bottom: -50px;
}
/* Get rid of extra padding at the bottom of the video */
.FastRefresh .column.last {
margin-bottom: -6px;
}
@media only screen and (max-width: 480px) {
.FastRefresh .column.last {
padding: 0;
}
.FastRefresh video {
width: 100%;
}
}
@media only screen and (min-width: 481px) and (max-width: 960px) {
.FastRefresh .TwoColumns {
grid-gap: 2rem;
}
.FastRefresh .column.last {
width: 100%;
padding: 0;
}
.FastRefresh video {
width: 100%;
}
}
@media only screen and (min-width: 961px) {
/* Give video more space than text */
.FastRefresh .TwoColumns {
grid-template-columns: 2fr 1fr;
}
/* Make video flush with top of section */
.FastRefresh .last {
margin-top: -50px;
}
/* Need to set video height so it'll fit */
.FastRefresh video {
height: 340px;
/* width: 100%; */
}
}
/* Get Started */
.GetStarted,
.GetStarted p {
color: white;
}
.GetStarted .Heading {
color: var(--brand);
text-align: center;
}
.GetStarted .content {
max-width: 900px;
margin: 0 auto;
display: flex;
flex-direction: column;
}
.GetStarted .steps {
align-self: center;
}
.GetStarted .steps li {
font-size: 28px;
margin-bottom: 8px;
}
.GetStarted .steps li p {
font-size: 17px;
}
.GetStarted .terminal {
display: flex;
flex-direction: column;
border-left: 1px solid gray;
border-right: 1px solid gray;
border-top: 1px solid gray;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
padding: 30px 30px 0px 30px;
width: 600px;
position: relative;
}
.GetStarted .terminal::before {
content: "○ ○ ○";
color: gray;
font-size: 16px;
position: absolute;
left: 15px;
top: 5px;
}
.GetStarted code {
color: white;
font-size: 18px;
position: relative;
background: none;
border: 0;
}
.GetStarted code:first-child::before {
content: ">";
position: absolute;
left: -13px;
color: gray;
}
@media screen and (max-width: 760px) {
.GetStarted .content {
width: 80%;
}
.GetStarted .steps li {
margin-left: -1rem;
}
.GetStarted .terminal {
width: 100%;
}
}
/* Header Hero */
.HeaderHero {
padding-top: 20px;
}
.HeaderHero .TwoColumns .column {
max-width: initial;
}
.HeaderHero .socialLinks {
display: flex;
justify-content: flex-end;
max-width: 1200px;
margin: -10px auto 0;
.twitter-follow-button,
.github-button {
margin-right: 1rem;
}
}
.HeaderHero .TwoColumns {
align-items: center;
}
.HeaderHero .title {
font-size: 84px;
color: var(--brand);
line-height: 1;
margin-top: 0;
margin-bottom: 20px;
font-weight: 500;
}
.HeaderHero .tagline {
font-size: 36px;
line-height: 1.3;
color: white;
font-weight: 500;
}
.HeaderHero .buttons {
margin-top: 40px;
}
.HeaderHero .image {
display: flex;
align-items: center;
justify-content: center;
}
@media only screen and (min-width: 961px) {
.HeaderHero .TwoColumns {
grid-template-columns: 3fr 1fr;
}
.HeaderHero .TwoColumns .column.left {
padding-right: 0;
}
.HeaderHero .TwoColumns .column.right {
padding-left: 0;
}
}
@media only screen and (min-width: 481px) and (max-width: 960px) {
.HeaderHero .column.first {
display: flex;
justify-content: center;
}
.HeaderHero .column.last {
text-align: center;
}
}
@media only screen and (max-width: 760px) {
.HeaderHero .title {
font-size: 60px;
}
.HeaderHero .tagline {
font-size: 30px;
}
.HeaderHero .socialLinks {
margin-top: -2rem;
}
}
/* Heading */
.Heading {
font-size: 25px;
color: var(--ifm-font-color-base);
line-height: 1.2;
margin-top: 0;
margin-bottom: 20px;
font-weight: 700;
}
/* Home page */
.HomePage {
width: 100%;
overflow-x: hidden;
}
/* Logo Animation */
.LogoAnimation {
width: 350px;
}
@media only screen and (max-width: 760px) {
.LogoAnimation {
width: 100%;
}
}
.LogoAnimation .screen {
transition: all 850ms ease-in-out;
stroke-opacity: 0;
transform: scale(2.25, 1.33) rotate(0);
stroke-width: 5px;
}
.LogoAnimation .background {
fill: var(--dark);
}
.LogoAnimation .logoInner {
transform: scale(1);
transition: all 850ms ease-in-out;
transition-delay: 50ms;
}
.LogoAnimation.mobile .logoInner,
.LogoAnimation.mobile2 .logoInner {
transform: scale(0.4);
}
.LogoAnimation.desktop .logoInner {
transform: scale(0.5);
}
.LogoAnimation.laptop .logoInner {
transform: scale(0.35);
}
.LogoAnimation.full .screen {
stroke-opacity: 0;
transform: scale(2.25, 1.33) rotate(0);
opacity: 1;
transition: none;
}
.LogoAnimation.mobile .screen {
stroke-opacity: 1;
transform: scale(1) rotate(0);
opacity: 1;
stroke-width: 5px;
}
.LogoAnimation.desktop .screen {
stroke-opacity: 1;
transform: scale(1.125, 1.1) rotate(-90deg);
opacity: 1;
stroke-width: 8px;
}
.LogoAnimation.laptop .screen {
stroke-opacity: 1;
transform: scale(0.83) rotate(-90deg);
opacity: 1;
stroke-width: 5px;
}
.LogoAnimation.mobile2 .screen {
stroke-opacity: 1;
opacity: 1;
stroke-width: 5px;
transform: scale(1) rotate(-180deg);
}
.LogoAnimation.full2 .screen {
stroke-opacity: 0;
transform: scale(2.25, 1.33) rotate(-180deg);
}
.LogoAnimation:not(.mobile):not(.mobile2) .speaker {
opacity: 0;
transform: scaleX(0);
}
.LogoAnimation:not(.desktop) .stand {
transform: scaleX(0);
}
.LogoAnimation:not(.laptop) .base {
transform: scaleX(0);
}
.LogoAnimation .speaker,
.LogoAnimation .stand,
.LogoAnimation .base {
transition: all 850ms ease-in-out;
}
/* Native Apps */
.AdvancedBioMaker {
overflow: hidden;
}
@media only screen and (max-width: 960px) {
.AdvancedBioMaker .column.last {
max-height: 300px;
}
}
@media only screen and (min-width: 481px) and (max-width: 960px) {
.AdvancedBioMaker .column.last {
width: 66.7%;
margin: 0 auto;
}
}
@media only screen and (min-width: 961px) {
.AdvancedBioMaker {
max-height: 400px;
}
/* Correct for whitespace in the image of phones */
.AdvancedBioMaker .column.left {
margin-top: -25px;
}
}
/* Native Code */
.WrittenPython .column.last {
margin-bottom: -50px;
}
.WrittenPython pre {
margin: 0;
}
.WrittenPython .prism-code {
border-radius: 0;
font-size: 80%;
background-color: #282c34;
}
@media only screen and (max-width: 480px) {
.WrittenPython .column.last {
width: 100%;
padding: 0;
overflow-x: hidden;
}
.WrittenPython .prism-code {
font-size: 10px;
padding: 1.25rem 1.25rem;
}
}
@media screen and (min-width: 481px) and (max-width: 960px) {
.WrittenPython .TwoColumns {
grid-gap: 2rem;
}
.WrittenPython .column.last {
width: 100%;
padding: 0;
background-color: var(--dark);
height: 28rem;
overflow-y: scroll;
}
.WrittenPython .prism-code {
width: 30rem;
margin: 0 auto;
padding: 1.25rem 0rem;
}
}
@media only screen and (min-width: 961px) {
.WrittenPython .TwoColumns .column.right {
/* Make flush with top and bottom */
margin-top: -50px;
/* Get rid of default left padding */
padding-left: 0;
}
.WrittenPython .column.right .prism-code {
/* Bleed background into the right */
margin-right: -9999px;
padding: 16px 1.5rem;
height: 460px;
}
}
/* Native Development */
.RichFeatures {
overflow-y: hidden;
}
.RichFeatures .dissection {
position: relative;
}
.RichFeatures .dissection img {
display: block;
margin-left: auto;
margin-right: auto;
}
@media only screen and (max-width: 960px) {
.RichFeatures .TwoColumns {
grid-gap: 2rem;
}
}
@media only screen and (max-width: 480px) {
.RichFeatures .dissection {
height: 350px;
}
}
@media only screen and (min-width: 481px) and (max-width: 960px) {
.RichFeatures .dissection {
height: 450px;
}
}
@media only screen and (min-width: 961px) {
.RichFeatures .dissection {
height: 300px;
}
}
/* Section */
.Section {
width: 100%;
padding-top: 50px;
padding-bottom: 50px;
overflow-x: hidden;
}
.Section + .Section {
border-top: 1px solid var(--ifm-table-border-color);
}
.Section.tint {
background-color: var(--ifm-menu-color-background-active);
}
.Section.dark {
background-color: var(--dark);
}
.Section p a {
@extend %link-style;
}
html[data-theme="dark"] .Section p a {
@extend %link-style-dark;
}
/* VideoContent */
.VideoContent {
.twitter-follow-button {
margin-top: 1.5rem;
}
}
@media only screen and (max-width: 960px) {
.VideoContent .TwoColumns {
grid-gap: 2rem;
}
.VideoContent .column.last {
width: 100%;
display: flex;
justify-content: center;
}
/*
* If the full-width video won't fit, make it full-width.
* https://jameshfisher.com/2017/08/30/how-do-i-make-a-full-width-iframe/
*/
.VideoContent .vidWrapper {
position: relative;
width: 100%;
padding-top: 56.25%;
}
.VideoContent iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
}
@media only screen and (min-width: 961px) {
/* Give more width for the video */
.VideoContent .TwoColumns {
grid-template-columns: 1fr 2fr;
}
.VideoContent iframe {
width: 560px;
height: 315px;
}
}
/* Two columns */
.TwoColumns {
display: grid;
}
.TwoColumns .column {
width: 100%;
}
.TwoColumns .column.first {
grid-area: first;
}
.TwoColumns .column.last {
grid-area: last;
}
@media only screen and (min-width: 961px) {
.TwoColumns {
max-width: 900px;
margin: 0 auto;
grid-template-columns: repeat(2, 1fr);
grid-template-areas: "first last";
}
.TwoColumns.reverse {
grid-template-areas: "last first";
}
.TwoColumns .column {
max-width: 450px;
}
.TwoColumns .column.left {
padding-right: 50px;
}
.TwoColumns .column.right {
padding-left: 50px;
}
}
@media only screen and (max-width: 960px) {
.TwoColumns,
.TwoColumns.reverse {
grid-template-columns: 1fr;
grid-template-areas: "first" "last";
}
.TwoColumns .column {
padding: 0 4rem;
}
}
@media only screen and (max-width: 480px) {
.TwoColumns .column {
padding: 0 1.25rem;
}
}
/* Twitter Follow Button */
.twitter-follow-button {
display: inline-block;
position: relative;
height: 28px;
box-sizing: border-box;
padding: 1px 10px 1px 9px;
background-color: #1b95e0;
color: #fff;
border-radius: 4px;
font-weight: 400;
cursor: pointer;
font-size: 13px;
line-height: 26px;
&:hover {
color: #fff;
background-color: #0c7abf;
}
.icon {
position: relative;
display: inline-block;
top: 4px;
height: 18px;
width: 18px;
margin-right: 4px;
background: transparent 0 0 no-repeat;
background-image: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2072%2072%22%3E%3Cpath%20fill%3D%22none%22%20d%3D%22M0%200h72v72H0z%22%2F%3E%3Cpath%20class%3D%22icon%22%20fill%3D%22%23fff%22%20d%3D%22M68.812%2015.14c-2.348%201.04-4.87%201.744-7.52%202.06%202.704-1.62%204.78-4.186%205.757-7.243-2.53%201.5-5.33%202.592-8.314%203.176C56.35%2010.59%2052.948%209%2049.182%209c-7.23%200-13.092%205.86-13.092%2013.093%200%201.026.118%202.02.338%202.98C25.543%2024.527%2015.9%2019.318%209.44%2011.396c-1.125%201.936-1.77%204.184-1.77%206.58%200%204.543%202.312%208.552%205.824%2010.9-2.146-.07-4.165-.658-5.93-1.64-.002.056-.002.11-.002.163%200%206.345%204.513%2011.638%2010.504%2012.84-1.1.298-2.256.457-3.45.457-.845%200-1.666-.078-2.464-.23%201.667%205.2%206.5%208.985%2012.23%209.09-4.482%203.51-10.13%205.605-16.26%205.605-1.055%200-2.096-.06-3.122-.184%205.794%203.717%2012.676%205.882%2020.067%205.882%2024.083%200%2037.25-19.95%2037.25-37.25%200-.565-.013-1.133-.038-1.693%202.558-1.847%204.778-4.15%206.532-6.774z%22%2F%3E%3C%2Fsvg%3E);
}
}

View File

@@ -0,0 +1,139 @@
@import "shared";
.showcaseSection {
max-width: 800px;
margin: 64px auto;
text-align: center;
padding: 0 20px;
.showcase img {
height: 100px;
border-radius: 20px;
}
.logos img {
padding: 0;
}
.info {
font-size: 15px;
}
p a {
@extend %link-style;
}
.form-button {
@extend %button-link-style;
margin-bottom: 36px;
border-width: 2px;
font-weight: 500;
&:hover {
border-color: var(--ifm-color-primary);
color: var(--ifm-font-color-base) !important;
}
}
}
html[data-theme="dark"] .showcaseSection {
p a {
@extend %link-style-dark;
}
.form-button {
color: var(--ifm-color-secondary-dark);
}
}
.home-showcase-section {
max-width: 800px;
margin: 20px auto 20px auto;
text-align: center;
padding-bottom: 40px;
p {
max-width: 560px;
margin: 0 auto;
}
}
.pinned img {
width: 150px;
border-radius: 20px;
}
.footnote {
font-size: 12px;
color: rgba(0, 0, 0, 0.4);
}
.home-showcase-section {
h2 {
font-size: 31px;
line-height: 40px;
margin: 10px 0;
}
.showcase img {
width: 100px;
height: 100px;
border-radius: 20px;
}
}
.showcaseHeader {
padding-bottom: 15px;
padding-top: 15px;
text-align: center;
}
.showcase {
margin: 30px auto 30px auto;
width: 100%;
display: inline-block;
text-align: center;
vertical-align: top;
h3 {
margin-bottom: 0;
line-height: 21px;
padding: 5px 5px 2px;
font-size: 21px;
}
p {
margin-top: 5px;
padding-top: 0 !important;
}
h3,
p {
color: var(--ifm-color-emphasis-800);
}
}
@media only screen and (max-device-width: 840px) {
.showcaseSection {
width: 100%;
}
}
@media only screen and (min-width: 600px) {
.showcase {
width: 50%;
}
}
@media only screen and (min-width: 960px) {
.showcase {
width: 25%;
}
}
@media only screen and (min-device-width: 736px) {
.showcaseSection .showcase img {
width: 100px;
max-height: 100px;
}
}

View File

@@ -0,0 +1,47 @@
@import "shared";
.versions-page {
max-width: 960px;
padding: 28px;
h1 {
font-size: 3rem;
}
h2 {
font-size: 2rem;
}
code {
white-space: pre;
border: 0;
}
p a,
td a {
@extend %link-style;
code {
background: none;
white-space: nowrap;
}
}
table th,
table td {
min-width: 100px;
font-size: 15px;
padding: 8px 20px;
}
.versions {
margin-bottom: 32px;
}
}
html[data-theme="dark"] .versions-page {
p a,
td a {
@extend %link-style-dark;
}
}

View File

@@ -0,0 +1,45 @@
export function setupDissectionAnimation() {
const section = document.querySelector('.RichFeatures');
const dissection = document.querySelector('.RichFeatures .dissection');
const images = dissection.children;
const numImages = images.length;
const fadeDistance = 40;
const navbarHeight = 60;
function clamp(val, min, max) {
return Math.min(max, Math.max(min, val));
}
// Scale the percent so that `min` goes to 0% and `max` goes to 100%
function scalePercent(percent, min, max) {
const scale = max - min;
return clamp((percent - min) / scale, 0, 1);
}
// Get the percentage that the image should be on the screen given
// how much the entire container is scrolled
// so we can fine-tune at what screen % the animation starts and stops
function getImagePercent(index, scrollPercent) {
const start = index / numImages;
return clamp((scrollPercent - start) * numImages, 0, 1);
}
function onScroll() {
const elPos = section.getBoundingClientRect().top - navbarHeight;
const height = window.innerHeight;
const screenPercent = 1 - clamp(elPos / height, 0, 1);
const scaledPercent = scalePercent(screenPercent, 0.2, 0.9);
for (let i = 0; i < numImages; i++) {
const imgPercent = getImagePercent(i, scaledPercent);
images[i].style.opacity = imgPercent;
const translation = fadeDistance * (1 - imgPercent);
images[i].style.left = `${translation}px`;
}
}
window.addEventListener('scroll', onScroll);
return () => window.removeEventListener('scroll', onScroll);
}

View File

@@ -0,0 +1,49 @@
export function setupHeaderAnimations() {
const steps = ['full', 'mobile', 'desktop', 'laptop', 'mobile2', 'full2'];
const intervals = [1250, 1500, 1500, 1500, 1500, 1250];
let i = 0;
const timeouts = [];
const logo = document.querySelector('.LogoAnimation');
function animateStep() {
const prev = steps[i];
logo.classList.remove(prev);
i = (i + 1) % steps.length;
const current = steps[i];
const timeout = intervals[i];
logo.classList.add(current);
timeouts.push(setTimeout(animateStep, timeout));
}
// only start the animation if the document is visible on load
if (!document.hidden) {
timeouts.push(
setTimeout(() => {
logo.classList.remove('init');
animateStep();
}, 2000)
);
}
function onVisibilityChange() {
if (document.hidden) {
timeouts.forEach(timeout => {
clearTimeout(timeout);
});
// clear the timeouts array
timeouts.length = 0;
} else {
// restart the animation when visible
animateStep();
}
}
// https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API
document.addEventListener('visibilitychange', onVisibilityChange, false);
return () =>
document.removeEventListener('visibilitychange', onVisibilityChange);
}

View File

@@ -0,0 +1,36 @@
---
title: Community
description: The OpenDBM Community
wrapperClassName: 'community-page'
---
# The OpenDBM Community
There are some developers / researchers around the world using OpenDBM. This is a brief overview of where you can find them.
## Where To Get Help
If you need help with your OpenDBM library, the right place to go depends on the type of help that you need.
### Repository
The core of **OpenDBM** is worked on full-time by AiCure's OpenDBM team. But there are far more people in the community who make key contributions and fix things. If the issue you are facing is code related, you should consider checking the open issues in the [main repository](https://github.com/AiCure/open_dbm/issues). If you cannot find an existing issue, please [use the Bug Report template](https://github.com/AiCure/open_dbm/issues/new?assignees=&labels=&template=bug_report.yaml) to create an issue with a minimal example.
### Stack Overflow
Many members of the community use Stack Overflow to ask questions. Read through the [existing questions](http://stackoverflow.com/questions/tagged/opendbm?sort=frequent) tagged with **opendbm** or [ask your own](http://stackoverflow.com/questions/ask?tags=opendbm)!
## Staying up to date
### Discussion
OpenDBM is still a young library, and its rapid release cycle leaves the door open for discussing how it can evolve at every step of the way. If you want to know the discussions that are talking about, you can read through the [Discussions](https://github.com/AiCure/open_dbm/discussions).
### Conferences
You can check the [OpenDBM Meetups](https://aicure.com/opendbm/) that happen around the world.
## Communities
### Forum-like groups
We want to ensure that OpenDBM feels like a community of like-minded researchers and clinicians. Hence, there are a few ways we encourage users to stay involvedand why we encourage you [to join DiME, too!](https://www.dimesociety.org/)

View File

@@ -0,0 +1,495 @@
import React, {useEffect} from 'react';
import GitHubButton from 'react-github-btn';
import Head from '@docusaurus/Head';
import useBaseUrl from '@docusaurus/useBaseUrl';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import Layout from '@theme/Layout';
import CodeBlock from '@theme/CodeBlock';
import CrossPlatformSVG from '../../static/img/homepage/cross-platform.svg';
import {setupDissectionAnimation} from './animations/_dissectionAnimation';
import {setupHeaderAnimations} from './animations/_headerAnimation';
const textContent = {
intro: `
OpenDBM is a software package that allows for calculation of digital biomarkers from video/audio of a person's
behavior by combining tools to measure behavioral characteristics like facial activity, voice, and movement into a single package for measurement of overall behavior.
<br/><br/>
<strong>Ease of use</strong>. OpenDBM is designed for ease of use, expanding the availability of such tools to the wider scientific community.
`,
writtenPython: `
It using Python language so you can integrate with other biomaker or ML libraries
<br/><br/>
<strong>Many OS platforms</strong>, one code. You can focus on building researching medical purpose
and the single codebase can share code across platforms. With OpenDBM,
one team can maintain multiple OS platforms and share a common researching application.
`,
codeExample: `
from opendbm.facial import FacialActivity
model = FacialActivity()
m.fit()
landmark = model.get_landmark()
landmark.mean()
landmark.std()
`,
forResearchers: `
Through OpenDBM, a user can objectively and sensitively measure behavioral characteristics such as facial activity, vocal acoustics,
characteristics of speech, patterns of movement, and oculomotion. <br/><br/>
From those behavioral characteristics, they can measure clinically meaningful symptomatology such as emotional expressivity, prosody
of voice, valence of speech, and severity of tremoramong many others.
`,
talks: `
Weve recorded some instructional videos (listed and linked to within the documentation) that should help the user get through
common steps such as installation, usage, etc.
`,
};
function Heading({text}) {
return <h2 className="Heading">{text}</h2>;
}
function ActionButton({href, type = 'primary', target, children}) {
return (
<a className={`ActionButton ${type}`} href={href} target={target}>
{children}
</a>
);
}
function TextColumn({title, text, moreContent}) {
return (
<>
<Heading text={title} />
<div dangerouslySetInnerHTML={{__html: text}} />
{moreContent}
</>
);
}
function HomeCallToAction() {
return (
<>
<ActionButton
type="primary"
href={useBaseUrl('docs/getting-started')}
target="_self">
Get started
</ActionButton>
<ActionButton
type="secondary"
href={useBaseUrl('docs/tutorial')}
target="_self">
Learn basics
</ActionButton>
</>
);
}
function GitHubStarButton() {
return (
<div className="github-button">
<GitHubButton
href="https://github.com/AiCure/open_dbm"
data-icon="octicon-star"
data-size="large"
aria-label="Star AiCure/open_dbm on GitHub">
Star
</GitHubButton>
</div>
);
}
function Section({
element = 'section',
children,
className,
background = 'light',
}) {
const El = element;
return <El className={`Section ${className} ${background}`}>{children}</El>;
}
function TwoColumns({columnOne, columnTwo, reverse}) {
return (
<div className={`TwoColumns ${reverse ? 'reverse' : ''}`}>
<div className={`column first ${reverse ? 'right' : 'left'}`}>
{columnOne}
</div>
<div className={`column last ${reverse ? 'left' : 'right'}`}>
{columnTwo}
</div>
</div>
);
}
function ScreenRect({className, fill, stroke}) {
return (
<rect
className={`screen ${className || ''}`}
rx="3%"
width="180"
height="300"
x="-90"
y="-150"
fill={fill}
stroke={stroke}
/>
);
}
function LogoAnimation() {
return (
<svg
className="LogoAnimation init"
width={350}
height={350}
xmlns="http://www.w3.org/2000/svg"
viewBox="-200 -200 400 400">
<title>OpenDBM logo</title>
<clipPath id="screen">
<ScreenRect fill="none" stroke="gray" />
</clipPath>
<rect
x="-25"
y="120"
width="50"
height="25"
rx="2"
fill="white"
stroke="none"
className="stand"
/>
<polygon
points="-125,90 125,90 160,145 -160,145"
fill="white"
stroke="white"
strokeWidth="5"
strokeLinejoin="round"
className="base"
/>
<ScreenRect className="background" stroke="none" />
<g clipPath="url(#screen)" className="logo">
<g className="logoInner">
<circle cx="0" cy="0" r="30" fill="#61dafb" />
<g stroke="#61dafb" strokeWidth="15" fill="none" id="logo">
<ellipse rx="165" ry="64" />
<ellipse rx="165" ry="64" transform="rotate(60)" />
<ellipse rx="165" ry="64" transform="rotate(120)" />
</g>
</g>
<line
x1="-30"
x2="30"
y1="130"
y2="130"
stroke="white"
strokeWidth="8"
strokeLinecap="round"
className="speaker"
/>
</g>
<ScreenRect fill="none" stroke="white" />
</svg>
);
}
function HeaderHero() {
return (
<Section background="dark" className="HeaderHero">
<div className="socialLinks">
<GitHubStarButton />
</div>
<TwoColumns
reverse
columnOne={
<img alt="" src={useBaseUrl('img/homepage/biomaker_logo2.gif')} />
}
columnTwo={
<>
<h1 className="title">OpenDBM</h1>
<p className="tagline">Digital Biomaker&nbsp;Library.</p>
<div className="buttons">
<HomeCallToAction />
</div>
</>
}
/>
</Section>
);
}
function AdvancedBioMaker() {
return (
<Section className="AdvancedBioMaker" background="light">
<TwoColumns
reverse
columnOne={
<TextColumn
title="Advanced Digital Biomaker"
text={textContent.intro}
/>
}
columnTwo={<img alt="" src={useBaseUrl('img/homepage/biomaker.png')} />}
/>
</Section>
);
}
function WrittenPython() {
return (
<Section className="WrittenPython" background="tint">
<TwoColumns
columnOne={
<TextColumn
title="Written in Python—support all OS platforms"
text={textContent.writtenPython}
/>
}
columnTwo={
<CodeBlock language="jsx">{textContent.codeExample}</CodeBlock>
}
/>
</Section>
);
}
function RichFeatures() {
return (
<Section className="RichFeatures" background="light">
<TwoColumns
reverse
columnOne={
<TextColumn
title="Rich Features for Researchers"
text={textContent.forResearchers}
/>
}
columnTwo={
<div className="dissection">
<img alt="" src={useBaseUrl('img/homepage/features.png')} />
</div>
}
/>
</Section>
);
}
function VideoContent() {
return (
<div>
<Section className="VideoContent" background="tint">
<br />
<TwoColumns
columnOne={
<TextColumn title="Talks and Videos" text={textContent.talks} />
}
columnTwo={
<div className="vidWrapper">
<iframe
src="https://www.youtube.com/embed/PNS-TQX5CFc"
title="OpenDBM Virtual Training"
frameBorder="0"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
/>
</div>
}
/>
<br />
<TwoColumns
columnOne={
<>
<p>
The{' '}
<a href="https://github.com/AiCure/open_dbm/graphs/contributors">
OpenDBM Contributor Team
</a>{' '}
has put together a short video where they explained about the
installation in some platforms.
</p>
</>
}
columnTwo={
<div className="vidWrapper">
<iframe
src="https://www.youtube.com/embed/CfNFBcf41u0"
title="OpenDBM Installation on Mac"
frameBorder="0"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
/>
<br></br>
<iframe
src="https://www.youtube.com/embed/TKON5UcxrwQ"
title="OpenDBM How to use on Mac"
frameBorder="0"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
/>
</div>
}
/>
</Section>
</div>
);
}
/* Community */
function AcknowledgementList() {
const {siteConfig} = useDocusaurusContext();
const apps = siteConfig.customFields.users.filter(app => app.pinned);
return (
<ul className="AcknowledgementList">
{apps.map((app, i) => {
const imgSource = !app.icon.startsWith('http')
? useBaseUrl('img/showcase/' + app.icon)
: app.icon;
return (
<li key={i} className="item">
{app.infoLink ? (
<a href={app.infoLink}>
<img src={imgSource} alt={app.name} />
</a>
) : (
<img src={imgSource} alt={app.name} />
)}
</li>
);
})}
</ul>
);
}
function Community() {
return (
<Section className="Community" background="light">
<div className="content">
<Heading text="Community Driven" />
<TwoColumns
columnOne={
<>
<p className="firstP">
<img src={useBaseUrl(`img/header_logo.png`)} alt="" />
<span>
AiCure released OpenDBM in 2020 and has been maintaining it
ever since.
</span>
</p>
<p>
We want to ensure that OpenDBM feels like a community of
like-minded researchers and clinicians. Hence, there are a few
ways we encourage users to stay involvedand why we encourage
you to join DiME, too! Most importantly, if youre interested in
OpenDBM, star the repo and sign up for our listserv for all
updates.
</p>
<p>
If youre thinking about contributing to OpenDBMto which we
say kudosplease{' '}
<span>
<a href="opendbm@aicure.com">reach out to us.</a>
</span>{' '}
Weve written{' '}
<span>
<a href="https://github.com/AiCure/open_dbm/blob/master/CODE_OF_CONDUCT.md">
code of conduct
</a>
</span>{' '}
and{' '}
<span>
<a href="https://github.com/AiCure/open_dbm/blob/master/CONTRIBUTING.md">
contribution guidelines
</a>
</span>{' '}
but also want to do whatever we can to help.
</p>
</>
}
columnTwo={
<>
<p>
<strong>Acknowledgements</strong> <br></br>A point that was
mentioned earlier and cannot be emphasized enough is that
OpenDBM is simply a compilation of existing but disparate
open-source software tools that weve built on top of. All these
tools are of course listed in the OpenDBM dependencies but we
want to acknowledge just a few here: OpenFace, built on OpenCV,
is at the heart of all facial measures and even some of the
movement ones. Parselmouth and its reliance on the Praat
software library lies behind most of the vocal acoustic
measures. DeepSpeech was used for all speech transcription and
NLTK is utilized for a lot of language metrics. OpenDBM would
not be possible without theseand several otheropen source
software packages.
</p>
<AcknowledgementList />
</>
}
/>
</div>
</Section>
);
}
function GetStarted() {
return (
<Section className="GetStarted" background="dark">
<div className="content">
<Heading text="Give it a try" />
<ol className="steps">
<li>
<p>Run this</p>
<div className="terminal">
<code>pip install opendbm</code>
</div>
</li>
<li>
<p>Read these</p>
<HomeCallToAction />
</li>
</ol>
</div>
</Section>
);
}
const useHomePageAnimations = () => {
useEffect(() => setupHeaderAnimations(), []);
useEffect(() => setupDissectionAnimation(), []);
};
const Index = () => {
useHomePageAnimations();
return (
<Layout
description="A digital biomaker reader based on Python"
wrapperClassName="homepage">
<Head>
<title>OpenDBM · Digital Biomaker Library</title>
<meta
property="og:title"
content="OpenDBM · Digital Biomaker Library"
/>
<meta
property="twitter:title"
content="OpenDBM · Digital Biomaker Library"
/>
</Head>
<HeaderHero />
<AdvancedBioMaker />
<WrittenPython />
<RichFeatures />
<VideoContent />
<Community />
<GetStarted />
</Layout>
);
};
export default Index;

View File

@@ -0,0 +1,119 @@
/**
* Copyright (c) AiCure, 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 useBaseUrl from '@docusaurus/useBaseUrl';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
const renderApp = (app, i) => {
return (
<div className="showcase" key={i}>
<div>
<a href={app.infoLink}>{renderAppIcon(app)}</a>
<h3>{app.name}</h3>
{renderLinks(app)}
{renderInfo(app.infoTitle, app.infoLink)}
</div>
</div>
);
};
const renderAppIcon = app => {
let imgSource = app.icon;
if (!app.icon.startsWith('http')) {
imgSource = useBaseUrl('img/showcase/' + app.icon);
}
return <img src={imgSource} alt={app.name} />;
};
const renderInfo = (title, uri) => {
return uri ? (
<p className="info">
<a href={uri} target="_blank">
{title}
</a>
</p>
) : null;
};
const renderLinks = app => {
if (!app.linkAppStore && !app.linkPlayStore) {
return;
}
const linkAppStore = app.linkAppStore ? (
<a href={app.linkAppStore} target="_blank">
iOS
</a>
) : null;
const linkPlayStore = app.linkPlayStore ? (
<a href={app.linkPlayStore} target="_blank">
Android
</a>
) : null;
return (
<p>
{linkPlayStore}
{linkPlayStore && linkAppStore ? ' • ' : ''}
{linkAppStore}
</p>
);
};
const Showcase = () => {
const {siteConfig} = useDocusaurusContext();
const showcaseApps = siteConfig.customFields.users;
const pinnedApps = showcaseApps.filter(app => app.pinned);
const featuredApps = showcaseApps
.filter(app => !app.pinned)
.sort((a, b) => a.name.localeCompare(b.name));
const apps = pinnedApps.concat(featuredApps);
return (
<Layout
title="Who's using OpenDBM?"
description="Some already using openDBM. You can check these below">
<div className="showcaseSection">
<div className="prose">
<h1>Who's using OpenDBM?</h1>
<p>
This section attempts to list all peer-reviewed scientific articlces
that have used OpenDBM for measurement of digital biomarkers.
</p>
</div>
<div>
<ul>
<li>
Galatzer-Levy, I., Abbas, A., Koesmahargyo, V., Yadav, V.,
Perez-Rodriguez, M. M., Rosenfield, P., ... & Hansen, B. J.
(2020). Facial and vocal markers of schizophrenia measured using
remote smartphone assessments. medRxiv.
</li>
<li>
Galatzer-Levy, I. R., Abbas, A., Ries, A., Homan, S.,
Koesmahargyo, V., Yadav, V., ... & Scholz, U. (2020). Validation
of Visual and Auditory Digital Markers of Suicidality in Acutely
Suicidal Psychiatric In-Patients. medRxiv.
</li>
</ul>
</div>
<a
className="form-button"
href="https://forms.gle/Hb6bDL1GJvG1ByUX7"
target="_blank">
To add to this list, please simply fill out this form.
</a>
</div>
</Layout>
);
};
export default Showcase;

View File

@@ -0,0 +1,119 @@
/**
* Copyright (c) AiCure, 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 useBaseUrl from '@docusaurus/useBaseUrl';
const versions = require('../../versions.json');
const VersionItem = ({version, currentVersion}) => {
const versionName = version === 'next' ? 'Master' : version;
const isCurrentVersion = currentVersion === version;
const isNext = version === 'next';
const isRC = version.toUpperCase().indexOf('-RC') !== -1;
const latestMajorVersion = versions[0].toUpperCase().replace('-RC', '');
const documentationLink = (
<a
href={useBaseUrl(
'docs/' + (isCurrentVersion ? '' : version + '/') + 'getting-started'
)}>
Documentation
</a>
);
let releaseNotesURL = 'https://github.com/AiCure/open_dbm/releases';
let releaseNotesTitle = 'Changelog';
if (isNext) {
releaseNotesURL = `https://github.com/AiCure/open_dbm/compare/${latestMajorVersion}-stable...main`;
releaseNotesTitle = 'Commits since ' + latestMajorVersion;
} else if (!isRC) {
releaseNotesURL = `https://github.com/AiCure/open_dbm/releases/tag/v${version}.0`;
}
const releaseNotesLink = <a href={releaseNotesURL}>{releaseNotesTitle}</a>;
return (
<tr>
<th>{versionName}</th>
<td>{documentationLink}</td>
<td>{releaseNotesLink}</td>
</tr>
);
};
const Versions = () => {
const currentVersion = versions.length > 0 ? versions[0] : null;
const latestVersions = ['next'].concat(
versions.filter(version => version.indexOf('-RC') !== -1)
);
const stableVersions = versions.filter(
version => version.indexOf('-RC') === -1 && version !== currentVersion
);
return (
<Layout title="Versions" wrapperClassName="versions-page">
<h1>OpenDBM versions</h1>
<p>
When we want to plan for a release, a new release candidate is created
off the <code>master</code> branch of{' '}
<a href={'https://github.com/AiCure/open_dbm'}>
<code>AiCure/open_dbm</code>
</a>
. The release candidate will be online for some time to allow
contributors like yourself to{' '}
<a href={useBaseUrl('docs/upgrading')}>verify the changes</a> and to
identify any issues by{' '}
<a href="https://github.com/AiCure/open_dbm/issues">
writing clear, actionable bug reports
</a>
. Eventually, the release candidate will be promoted to{' '}
<code>master</code> .
</p>
<h2>Latest version</h2>
<p>
The most recent stable version will be publish automatically whenever a
new release tag is created using the{' '}
<code>github git tag [new version]</code> command.
</p>
<table className="versions">
<tbody>
<VersionItem
key={'version_' + currentVersion}
version={currentVersion}
currentVersion={currentVersion}
/>
</tbody>
</table>
<h2>Previous versions</h2>
<table className="versions">
<tbody>
{stableVersions.map(version => (
<VersionItem
key={'version_' + version}
version={version}
currentVersion={currentVersion}
/>
))}
</tbody>
</table>
<h2>Archived versions</h2>
<p>
The documentation for versions below <code>0.2.0</code> can be found on
the separate docs word{' '}
<a href="https://docs.google.com/document/d/1O6OUSY9FHFCZfpIWu3Kgg0Q_dyp073xjjQ2K3viEffU/edit#heading=h.ttfc0bhy0aif">
in here
</a>
.
</p>
</Layout>
);
};
export default Versions;

View File

@@ -0,0 +1,3 @@
/// <reference types="react" />
import type { Props } from "@theme/BlogSidebar/Desktop";
export default function BlogSidebarDesktop({ sidebar }: Props): JSX.Element;

View File

@@ -0,0 +1,47 @@
import React from 'react';
import clsx from 'clsx';
import Link from '@docusaurus/Link';
import {translate} from '@docusaurus/Translate';
import styles from './styles.module.css';
export default function BlogSidebarDesktop({sidebar}) {
let cachedYear = null;
return (
<aside className="col col--3">
<nav
className={clsx(styles.sidebar, 'thin-scrollbar')}
aria-label={translate({
id: 'theme.blog.sidebar.navAriaLabel',
message: 'Blog recent posts navigation',
description: 'The ARIA label for recent posts in the blog sidebar',
})}>
<div className={clsx(styles.sidebarHeader, 'margin-bottom--md')}>
{sidebar.title}
</div>
<ul className={clsx(styles.sidebarItemList, 'clean-list')}>
{sidebar.items.map(item => {
const postYear = item.permalink.split('/')[2];
const yearHeader = cachedYear !== postYear && (
<h5 className={styles.sidebarItemTitle}>{postYear}</h5>
);
cachedYear = postYear;
return (
<>
{yearHeader}
<li key={item.permalink} className={styles.sidebarItem}>
<Link
isNavLink
to={item.permalink}
className={styles.sidebarItemLink}
activeClassName={styles.sidebarItemLinkActive}>
{item.title}
</Link>
</li>
</>
);
})}
</ul>
</nav>
</aside>
);
}

View File

@@ -0,0 +1,60 @@
.sidebar {
max-height: calc(100vh - (var(--ifm-navbar-height) + 2rem));
overflow-y: auto;
position: sticky;
top: var(--ifm-navbar-height);
padding: 20px 12px 0 0;
margin-left: -20px;
}
.sidebarHeader {
font-size: var(--ifm-h4-font-size);
font-weight: var(--ifm-font-weight-bold);
padding-left: 12px;
display: block;
}
.sidebarItemTitle {
margin: 0.75rem 0 0.5rem;
color: var(--subtle);
padding-left: 12px;
border-bottom: 0.01rem solid var(--ifm-table-border-color);
padding-bottom: 4px;
}
.sidebarItemList {
font-size: 13px;
}
.sidebarItem {
margin-top: 0.1rem;
line-height: 18px;
}
.sidebarItemLink {
color: var(--ifm-font-color-base);
padding: 4px 8px;
display: block;
border-left: 4px solid transparent;
border-radius: 0.25rem;
line-height: 18px;
}
.sidebarItemLink:hover {
background: var(--ifm-menu-color-background-active);
color: var(--ifm-font-color-base);
text-decoration: none;
}
.sidebarItemLinkActive {
color: var(--ifm-font-color-base);
background: var(--ifm-menu-color-background-active);
border-left-color: var(--ifm-menu-color-active);
font-weight: 700;
}
@media (max-width: 996px) {
.sidebar {
display: none;
}
}

View File

@@ -0,0 +1,3 @@
/// <reference types="react" />
import type { Props } from "@theme/BlogSidebar/Mobile";
export default function BlogSidebarMobile(props: Props): JSX.Element;

View File

@@ -0,0 +1,41 @@
import React from 'react';
import Link from '@docusaurus/Link';
import {NavbarSecondaryMenuFiller} from '@docusaurus/theme-common';
import styles from './styles.module.css';
function BlogSidebarMobileSecondaryMenu({sidebar}) {
let cachedYear = null;
return (
<ul className="menu__list blog-menu__list">
{sidebar.items.map(item => {
const postYear = item.permalink.split('/')[2];
const yearHeader = cachedYear !== postYear && (
<h5 className={styles.sidebarItemTitle}>{postYear}</h5>
);
cachedYear = postYear;
return (
<>
{yearHeader}
<li key={item.permalink} className="menu__list-item">
<Link
isNavLink
to={item.permalink}
className="menu__link"
activeClassName="menu__link--active">
{item.title}
</Link>
</li>
</>
);
})}
</ul>
);
}
export default function BlogSidebarMobile(props) {
return (
<NavbarSecondaryMenuFiller
component={BlogSidebarMobileSecondaryMenu}
props={props}
/>
);
}

View File

@@ -0,0 +1,7 @@
.sidebarItemTitle {
margin: 0.75rem 0 0.5rem;
color: var(--subtle);
padding-left: 12px;
border-bottom: 0.01rem solid var(--ifm-table-border-color);
padding-bottom: 4px;
}

View File

@@ -0,0 +1,18 @@
/**
* Copyright (c) AiCure, 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 DocItemFooterOriginal from '@theme-original/DocItemFooter';
import DocsRating from '../../../core/DocsRating';
export default function DocItemFooter(props) {
return (
<>
<DocsRating label={props.content.metadata.unversionedId} />
<DocItemFooterOriginal {...props} />
</>
);
}

View File

@@ -0,0 +1,103 @@
/**
* Copyright (c) AiCure, 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 DefaultNavbarItem from '@theme/NavbarItem/DefaultNavbarItem';
import DropdownNavbarItem from '@theme/NavbarItem/DropdownNavbarItem';
import {
useVersions,
useLatestVersion,
useActiveDocContext,
} from '@docusaurus/plugin-content-docs/client';
import {useDocsPreferredVersion} from '@docusaurus/theme-common';
import {translate} from '@docusaurus/Translate';
const getVersionMainDoc = version =>
version.docs.find(doc => doc.id === version.mainDocId);
export default function DocsVersionDropdownNavbarItem({
mobile,
docsPluginId,
dropdownActiveClassDisabled,
dropdownItemsBefore,
dropdownItemsAfter,
...props
}) {
const activeDocContext = useActiveDocContext(docsPluginId);
const versions = useVersions(docsPluginId);
const latestVersion = useLatestVersion(docsPluginId);
const {preferredVersion, savePreferredVersionName} =
useDocsPreferredVersion(docsPluginId);
// (CUSTOM) Hide version dropdown on non-versioned pages
if (!activeDocContext.activeDoc) {
return null;
}
// (CUSTOM) Show only `next` and last 5 versions in the dropdown
const reducedVersions = versions.slice(0, 6);
function getItems() {
const versionLinks = reducedVersions.map(version => {
// We try to link to the same doc, in another version
// When not possible, fallback to the "main doc" of the version
const versionDoc =
activeDocContext?.alternateDocVersions[version.name] ||
getVersionMainDoc(version);
return {
isNavLink: true,
label: version.label,
to: versionDoc.path,
isActive: () => version === activeDocContext?.activeVersion,
onClick: () => {
savePreferredVersionName(version.name);
},
};
});
return [...dropdownItemsBefore, ...versionLinks, ...dropdownItemsAfter];
}
const items = getItems();
const dropdownVersion =
activeDocContext.activeVersion ?? preferredVersion ?? latestVersion; // Mobile dropdown is handled a bit differently
const dropdownLabel =
mobile && items
? translate({
id: 'theme.navbar.mobileVersionsDropdown.label',
message: 'Versions',
description:
'The label for the navbar versions dropdown on mobile view',
})
: dropdownVersion.label;
const dropdownTo =
mobile && items ? undefined : getVersionMainDoc(dropdownVersion).path; // We don't want to render a version dropdown with 0 or 1 item
// If we build the site with a single docs version (onlyIncludeVersions: ['1.0.0'])
// We'd rather render a button instead of a dropdown
if (items.length <= 1) {
return (
<DefaultNavbarItem
{...props}
mobile={mobile}
label={dropdownLabel}
to={dropdownTo}
isActive={dropdownActiveClassDisabled ? () => false : undefined}
/>
);
}
return (
<DropdownNavbarItem
{...props}
mobile={mobile}
label={dropdownLabel}
to={dropdownTo}
items={items}
isActive={dropdownActiveClassDisabled ? () => false : undefined}
/>
);
}