Initial commit

Co-Authored-By: kotte <14197736+mrtamagotchi@users.noreply.github.com>
Co-Authored-By: mikaeltellhed <2311083+mikaeltellhed@users.noreply.github.com>
Co-Authored-By: Tore Knudsen <18231882+torekndsn@users.noreply.github.com>
Co-Authored-By: Michael Cartner <32543275+michaelcartner@users.noreply.github.com>
This commit is contained in:
Eric Tuvesson
2023-09-05 12:08:55 +02:00
commit 53f0d6320e
2704 changed files with 76354 additions and 0 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,65 @@
import React from 'react'
import { getGuideListing } from '../../static/data/guides.js'
import featuredGuides from '../../static/data/featuredGuides.json'
import { Grid, GridLayout } from '../components/layout/Grid/Grid'
import { Section } from '../components/layout/Section/Section'
import { GuideCard } from '../components/cards/GuideCard/GuideCard'
interface GuideListingProps {
title: string
hasNoLink: boolean
isFeaturedOnly: boolean
}
export function GuideListing({
title,
hasNoLink,
isFeaturedOnly,
}: GuideListingProps) {
if (isFeaturedOnly) {
return (
<Section
title={title}
linkHref={!hasNoLink ? 'docs/learn' : null}
linkLabel={!hasNoLink ? 'View all' : null}
>
<Grid layout={GridLayout.Half} hasEqualHeightItems>
{featuredGuides.map((guide) => {
return (
<GuideCard
key={guide.title}
imageUrl={guide.imageUrl}
title={guide.title}
description={guide.description}
href={guide.href}
/>
)
})}
</Grid>
</Section>
)
} else {
return getGuideListing().map((category) => (
<Section
key={category.key}
title={category.title}
linkHref={null}
linkLabel={null}
>
<Grid layout={GridLayout.Half} hasEqualHeightItems>
{category.items.map((guide) => {
return (
<GuideCard
key={guide.key}
imageUrl={guide.imageUrl}
title={guide.title}
description={guide.description}
href={guide.href}
/>
)
})}
</Grid>
</Section>
))
}
}

68
src/blocks/HeroBlock.tsx Normal file
View File

@@ -0,0 +1,68 @@
import React from 'react'
import { Section } from '../components/layout/Section/Section'
import { Title, TitleSize } from '../components/typography/Title/Title'
import { Text } from '../components/typography/Text/Text'
import { Grid, GridLayout } from '../components/layout/Grid/Grid'
import {
YouTubeEmbed,
YouTubeEmbedProps,
} from '../components/common/YouTubeEmbed/YouTubeEmbed'
import { LinkCard, LinkCardProps } from '../components/cards/LinkCard/LinkCard'
interface IHeroBlockYoutube extends YouTubeEmbedProps {
type: 'youtube'
}
interface IHeroBlockLink extends LinkCardProps {
type: 'link'
}
interface HeroBlockProps {
title: string
text: string
gridItems: (IHeroBlockLink | IHeroBlockYoutube)[]
}
export function HeroBlock({ title, text, gridItems }: HeroBlockProps) {
return (
<Section hasNoHeader>
<Title size={TitleSize.Large} headingLevel={1}>
{title}
</Title>
<Text>{text}</Text>
<Grid layout={GridLayout.Grid_2_1_1} hasEqualHeightItems>
{gridItems.map((item, i) => {
switch (item.type) {
case 'youtube':
return (
<YouTubeEmbed
key={i + item.videoId}
videoId={item.videoId}
/>
)
case 'link':
return (
<LinkCard
key={i + item.label}
colorVariable={item.colorVariable}
label={item.label}
href={item.href}
backgroundImage={item.backgroundImage}
playIcon={item.playIcon}
/>
)
default:
return (
<Text key={i}>
Error: Wrong item type provided.
</Text>
)
}
})}
</Grid>
</Section>
)
}

View File

@@ -0,0 +1,29 @@
import React from 'react'
import {
ButtonCard,
ButtonCardProps,
} from '../components/cards/ButtonCard/ButtonCard'
import { Grid, GridLayout } from '../components/layout/Grid/Grid'
import { Section } from '../components/layout/Section/Section'
interface LinkButtonGridProps {
title?: string
links: ButtonCardProps[]
}
export function LinkButtonGrid({ title, links }: LinkButtonGridProps) {
return (
<Section title={title} linkLabel={null}>
<Grid layout={GridLayout.Thirds} hasEqualHeightItems>
{links.map((link) => (
<ButtonCard
key={link.href}
title={link.title}
href={link.href}
onClick={link.onClick}
/>
))}
</Grid>
</Section>
)
}

View File

@@ -0,0 +1,55 @@
import React from 'react';
import { Section } from '../components/layout/Section/Section';
import { Grid, GridLayout } from '../components/layout/Grid/Grid';
import modules from '../../static/library/modules/index.json';
import featuredModuleIds from '../../static/data/featuredModules.json';
import { ModuleCard } from '../components/cards/ModuleCard/ModuleCard';
const featuredModules = featuredModuleIds.map((moduleId) =>
modules.find((module) => module.label === moduleId)
);
interface ModuleListingProps {
title: string;
hasNoLink?: boolean;
isFeaturedOnly: boolean;
}
export function ModuleListing({
title,
hasNoLink,
isFeaturedOnly,
}: ModuleListingProps) {
const renderedModules = isFeaturedOnly ? featuredModules : modules;
return (
<Section
title={title}
linkHref={hasNoLink ? undefined : '/library/modules/overview'}
linkLabel={hasNoLink ? false : 'View all'}
>
<Grid layout={GridLayout.Thirds} hasEqualHeightItems>
{renderedModules.map((module) => {
if (!module) return null;
return (
<ModuleCard
key={module.label}
title={module.label}
description={module.desc}
readMoreUrl={module.docs}
thumbUrl={module.icon}
importArgs={{
path: module.project,
options: {
thumb: module.icon,
name: module.label,
},
}}
/>
);
})}
</Grid>
</Section>
);
}

View File

@@ -0,0 +1,55 @@
import React from 'react';
import { Section } from '../components/layout/Section/Section';
import { Grid, GridLayout } from '../components/layout/Grid/Grid';
import modules from '../../static/library/prefabs/index.json';
import featuredModuleIds from '../../static/data/featuredPrefabs.json';
import { ModuleCard } from '../components/cards/ModuleCard/ModuleCard';
const featuredModules = featuredModuleIds.map((moduleId) =>
modules.find((module) => module.label === moduleId)
);
interface ModuleListingProps {
title: string;
hasNoLink?: boolean;
isFeaturedOnly: boolean;
}
export function PrefabListing({
title,
hasNoLink,
isFeaturedOnly,
}: ModuleListingProps) {
const renderedModules = isFeaturedOnly ? featuredModules : modules;
return (
<Section
title={title}
linkHref={hasNoLink ? undefined : '/library/prefabs/overview'}
linkLabel={hasNoLink ? false : 'View all'}
>
<Grid layout={GridLayout.Thirds} hasEqualHeightItems>
{renderedModules.map((module) => {
if (!module) return null;
return (
<ModuleCard
key={module.label}
title={module.label}
description={module.desc}
readMoreUrl={module.docs}
thumbUrl={module.icon}
importArgs={{
path: module.project,
options: {
thumb: module.icon,
name: module.label,
},
}}
/>
);
})}
</Grid>
</Section>
);
}

View File

@@ -0,0 +1,37 @@
import React, { useMemo } from 'react'
import { Grid, GridLayout } from '../components/layout/Grid/Grid'
import { Section } from '../components/layout/Section/Section'
import featuredProjects from '../../static/data/featuredProjects.json'
import projects from '../../static/data/projects.json'
import { ProjectCard } from '../components/cards/ProjectCard/ProjectCard'
export function ProjectListing({ title, hasNoLink, isFeaturedOnly }) {
const renderedProjects = useMemo(
() => (isFeaturedOnly ? featuredProjects : projects),
[isFeaturedOnly]
)
return (
<Section
title={title}
linkHref={hasNoLink ? null : 'library/examples/overview'}
linkLabel={hasNoLink ? null : 'View all'}
>
<Grid layout={GridLayout.Thirds} hasEqualHeightItems>
{renderedProjects.map((project) => {
return (
<ProjectCard
key={project.title}
title={project.title}
description={project.description}
backend={project.backend}
href={project.href}
imageUrl={project.imageUrl}
project={project.project}
/>
)
})}
</Grid>
</Section>
)
}

View File

@@ -0,0 +1,49 @@
import React from 'react'
import { VideoLinkCard } from '../components/cards/VideoLinkCard/VideoLinkCard'
import { Grid, GridLayout } from '../components/layout/Grid/Grid'
import { Section } from '../components/layout/Section/Section'
import videos from '../../static/data/youtubeVideos.json'
import featuredVideoIds from '../../static/data/featuredVideos.json'
const featuredVideos = featuredVideoIds.map((id) =>
videos.find((video) => video.videoId === id)
)
interface VideoListingProps {
title: string
hasNoLink?: boolean
isFeaturedOnly: boolean
}
export function VideoListing({
title,
hasNoLink,
isFeaturedOnly,
}: VideoListingProps) {
const renderedVideos = isFeaturedOnly ? featuredVideos : videos
return (
<Section
title={title}
linkHref={
hasNoLink
? null
: 'https://www.youtube.com/channel/UCLkJ8XYV1J1RqrZKY-o1YWg'
}
linkLabel={hasNoLink ? false : 'View all'}
>
<Grid layout={GridLayout.Half} hasEqualHeightItems>
{renderedVideos.map((video) => {
return (
<VideoLinkCard
key={video.videoId}
title={video.title}
description={video.description}
videoLength={video.videoLength}
videoId={video.videoId}
/>
)
})}
</Grid>
</Section>
)
}

View File

@@ -0,0 +1,105 @@
@use '../../../css/utils/mixins.scss';
.Root {
display: flex;
&.is-horisontal {
flex-direction: row;
}
&.is-vertical {
flex-direction: column;
}
}
.ImageContainer {
flex-grow: 0;
flex-shrink: 0;
position: relative;
background-color: #ffffff;
.Root.is-horisontal & {
@include mixins.aspect-ratio(1, 1);
width: 156px;
}
.Root.is-vertical & {
@include mixins.aspect-ratio(354, 200);
}
}
.Image {
background-position: center;
position: absolute;
background-repeat: no-repeat;
background-size: cover;
top: 0;
bottom: 0;
left: 0;
right: 0;
.Root.is-vertical &:not(.has-no-padding) {
background-size: contain;
top: 12px;
bottom: 12px;
left: 12px;
right: 12px;
}
.Root.is-horisontal & {
background-size: cover;
}
}
.Content {
background-color: var(--doc-color-noodl-black-light);
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
.Root.is-horisontal & {
padding: 25px 32px;
}
.Root.is-vertical & {
padding: 16px 24px;
}
}
.PrimaryAction {
font-weight: 600;
cursor: pointer;
display: inline-block;
margin-right: 24px;
border-bottom: none !important;
font-family: var(--doc-font-title);
span {
color: var(--doc-color-noodl-orange);
transition: color var(--ifm-transition-fast)
var(--ifm-transition-timing-default);
&:hover {
color: var(--doc-color-noodl-orange-140);
}
}
}
.SecondaryAction {
font-weight: 600;
cursor: pointer;
display: inline-block;
border-bottom: none !important;
font-family: var(--doc-font-title);
span {
color: var(--doc-color-text);
transition: color var(--ifm-transition-fast)
var(--ifm-transition-timing-default);
&:hover {
color: var(--doc-color-noodl-orange);
}
}
}

View File

@@ -0,0 +1,105 @@
import useDocusaurusContext from '@docusaurus/useDocusaurusContext'
import useIsBrowser from '@docusaurus/useIsBrowser'
import clsx from 'clsx'
import React, { useMemo } from 'react'
import { TextSize, Text } from '../../typography/Text/Text'
import { Title, TitleSize } from '../../typography/Title/Title'
import css from './ArticleCard.module.scss'
export enum ArticleCardLayout {
Horisontal = 'is-horisontal',
Vertical = 'is-vertical',
}
interface IActionSlot {
label: string
onClick?: () => void
href?: string
}
export interface ArticleCardProps {
layout?: ArticleCardLayout
title: string
description: string
hasNoPaddingInThumb?: boolean
primaryAction: IActionSlot
secondaryAction?: IActionSlot
imageUrl?: string
}
export function ArticleCard({
layout = ArticleCardLayout.Vertical,
title,
description,
hasNoPaddingInThumb,
primaryAction,
secondaryAction,
imageUrl,
}: ArticleCardProps) {
const { siteConfig } = useDocusaurusContext()
const isBrowser = useIsBrowser()
const thumbUrl = useMemo(() => {
return !isBrowser
? null
: location.protocol +
'//' +
location.host +
siteConfig.baseUrl.slice(0, -1) +
(imageUrl[0]==='/'?imageUrl:('/'+imageUrl))
}, [isBrowser])
return (
<article className={clsx(css['Root'], css[layout])}>
<div className={css['ImageContainer']}>
{thumbUrl && (
<div
className={clsx(
css['Image'],
hasNoPaddingInThumb && css['has-no-padding']
)}
style={{ backgroundImage: `url(${thumbUrl})` }}
/>
)}
</div>
<div className={css['Content']}>
<div>
<header>
<Title size={TitleSize.Smaller} headingLevel={1}>
{title}
</Title>
</header>
<Text size={TextSize.Small}>{description}</Text>
</div>
<footer className={css['Footer']}>
{Boolean(primaryAction) && (
<a
className={css['PrimaryAction']}
href={primaryAction.href}
onClick={primaryAction.onClick}
>
<Text size={TextSize.Small} isSpan>
{primaryAction.label}
</Text>
</a>
)}
{Boolean(secondaryAction) && (
<a
className={css['SecondaryAction']}
href={secondaryAction.href}
onClick={secondaryAction.onClick}
>
<Text size={TextSize.Small} isSpan>
{secondaryAction.label}
</Text>
</a>
)}
</footer>
</div>
</article>
)
}

View File

@@ -0,0 +1,20 @@
@use '../../../css/utils/typography.scss';
.Root {
@include typography.h2;
font-size: 26px;
line-height: 1.1;
display: flex;
justify-content: center;
align-items: center;
padding: 32px 24px 24px;
height: 100%;
min-height: 180px;
border-bottom: none !important;
background-color: var(--doc-color-noodl-green);
text-align: center;
&:hover {
background-color: var(--doc-color-noodl-green-180);
}
}

View File

@@ -0,0 +1,19 @@
import Link from '@docusaurus/Link'
import React from 'react'
import css from './ButtonCard.module.scss'
export interface ButtonCardProps {
title: string
href?: string
onClick?: () => void
}
export function ButtonCard({ title, href, onClick }: ButtonCardProps) {
const Tag = href ? Link : 'button'
return (
<Tag className={css['Root']} to={href} onClick={onClick}>
{title}
</Tag>
)
}

View File

@@ -0,0 +1 @@
export { default } from './ButtonCard'

View File

@@ -0,0 +1,32 @@
import useDocusaurusContext from '@docusaurus/useDocusaurusContext'
import React from 'react'
import { ArticleCard, ArticleCardLayout } from '../ArticleCard/ArticleCard'
interface GuideCardProps {
title: string
description: string
href: string
imageUrl?: string
}
export function GuideCard({
title,
description,
href,
imageUrl,
}: GuideCardProps) {
const { siteConfig } = useDocusaurusContext()
return (
<ArticleCard
layout={ArticleCardLayout.Horisontal}
primaryAction={{
label: 'Read guide',
href: siteConfig.baseUrl.slice(0, -1) + href,
}}
title={title}
description={description}
imageUrl={imageUrl}
/>
)
}

View File

@@ -0,0 +1,42 @@
@use '../../../css/utils/typography.scss';
.Root {
display: flex;
flex-direction: column;
padding: 24px;
justify-content: space-between;
position: relative;
background-size: cover;
background-position: center;
&:before {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
pointer-events: none;
opacity: 0;
transition: opacity var(--ifm-transition-fast)
var(--ifm-transition-timing-default);
background-color: var(--doc-color-noodl-white);
}
&:hover:before {
opacity: 0.5;
}
}
.IconContainer {
position: relative;
}
.Label {
@include typography.h3;
color: var(--doc-color-noodl-black) !important;
max-width: 131px;
display: block;
position: relative;
padding-bottom: 20px;
}

View File

@@ -0,0 +1,52 @@
import Link from '@docusaurus/Link'
import React, { useMemo } from 'react'
import css from './LinkCard.module.scss'
import PlayIcon from '../../../../static/img/icons/play-black.svg'
import useDocusaurusContext from '@docusaurus/useDocusaurusContext'
import useIsBrowser from '@docusaurus/useIsBrowser'
export interface LinkCardProps {
href: string
colorVariable: string
backgroundImage: string
label: string
playIcon?: boolean
}
export function LinkCard({
href,
colorVariable,
backgroundImage,
label,
playIcon
}: LinkCardProps) {
const { siteConfig } = useDocusaurusContext()
const isBrowser = useIsBrowser()
const bgUrl = useMemo(() => {
return isBrowser && backgroundImage
? location.protocol +
'//' +
location.host +
siteConfig.baseUrl.slice(0, -1) +
backgroundImage
: null
}, [isBrowser])
return (
<Link
className={css['Root']}
style={{
backgroundColor: `var(${colorVariable})`,
backgroundImage: bgUrl ? `url(${bgUrl})` : null,
}}
to={href}
>
<span className={css['Label']}>{label}</span>
{(playIcon===undefined || playIcon === true) && (<div className={css['IconContainer']}>
<PlayIcon />
</div>)}
</Link>
)
}

View File

@@ -0,0 +1 @@
export { default } from './LinkCard'

View File

@@ -0,0 +1,41 @@
import useDocusaurusContext from '@docusaurus/useDocusaurusContext'
import useIsBrowser from '@docusaurus/useIsBrowser'
import React from 'react'
import { useMemo } from 'react'
import {
importIntoNoodl,
ImportIntoNoodlArgs,
} from '../../../utils/importIntoNoodl'
import { ArticleCard, ArticleCardLayout } from '../ArticleCard/ArticleCard'
interface ModuleCardProps {
thumbUrl: string
title: string
description: string
readMoreUrl: string
importArgs: ImportIntoNoodlArgs
}
export function ModuleCard({
thumbUrl,
title,
description,
readMoreUrl,
importArgs,
}: ModuleCardProps) {
const { siteConfig } = useDocusaurusContext()
return (
<ArticleCard
title={title}
description={description}
hasNoPaddingInThumb
imageUrl={thumbUrl}
layout={ArticleCardLayout.Vertical}
primaryAction={{
label: 'Read more',
href: siteConfig.baseUrl.slice(0, -1) + readMoreUrl,
}}
/>
)
}

View File

@@ -0,0 +1,51 @@
.Root {
display: block;
padding: 13px 18px;
font-weight: 600;
color: var(--doc-color-noodl-white) !important;
border: 2px solid transparent !important;
position: relative;
line-height: 1;
height: 100%;
font-size: 14px;
&:after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: var(--doc-color-noodl-white);
opacity: 0;
pointer-events: none;
}
&.has-hoverstate:hover {
border: 2px solid var(--doc-color-noodl-white) !important;
&:after {
opacity: 0.3;
}
}
&.is-visual {
background-color: var(--doc-color-visual-node);
}
&.is-data {
background-color: var(--doc-color-data-node);
}
&.is-custom {
background-color: var(--doc-color-custom-node);
}
&.is-logic {
background-color: var(--doc-color-logic-node);
}
&.is-connection {
background-color: var(--doc-color-connection-node);
}
}

View File

@@ -0,0 +1,35 @@
import Link from '@docusaurus/Link'
import clsx from 'clsx'
import React from 'react'
import css from './NodeCard.module.scss'
export enum NodeType {
Visual = 'is-visual',
Data = 'is-data',
Custom = 'is-custom',
Logic = 'is-logic',
Connection = 'is-connection',
}
export interface NodeCardProps {
nodeType: NodeType
label: string
docUrl?: string
}
export function NodeCard({ nodeType, label, docUrl }: NodeCardProps) {
const Tag = docUrl ? Link : 'div'
return (
<Tag
className={clsx(
css['Root'],
css[nodeType],
Boolean(docUrl) && css['has-hoverstate']
)}
to={docUrl}
>
{label}
</Tag>
)
}

View File

@@ -0,0 +1 @@
export { default } from './NodeCard'

View File

@@ -0,0 +1,46 @@
import useDocusaurusContext from '@docusaurus/useDocusaurusContext'
import React from 'react'
import { importIntoNoodl } from '../../../utils/importIntoNoodl'
import { ArticleCard, ArticleCardLayout } from '../ArticleCard/ArticleCard'
interface ProjectCardProps {
title: string
description: string
href: string
imageUrl: string
project: string
backend: string
}
export function ProjectCard({
title,
description,
href,
imageUrl,
project,
backend,
}: ProjectCardProps) {
const { siteConfig } = useDocusaurusContext()
return (
<ArticleCard
layout={ArticleCardLayout.Vertical}
title={title}
description={description}
imageUrl={imageUrl}
primaryAction={{
label: 'Download project',
onClick: () =>
importIntoNoodl(project, {
name: title,
cf: backend,
thumb: imageUrl,
}),
}}
secondaryAction={{
label: 'Read more',
href: siteConfig.baseUrl.slice(0, -1) + href,
}}
/>
)
}

View File

@@ -0,0 +1,75 @@
@use '../../../css/utils/mixins.scss';
@use '../../../css/utils/typography.scss';
$_hover-background: var(--doc-color-noodl-black-light);
.Root {
display: flex;
align-items: stretch;
position: relative;
outline: 10px solid transparent;
transition: outline var(--ifm-transition-fast)
var(--ifm-transition-timing-default);
&:hover {
outline: 10px solid $_hover-background;
}
}
.Thumb {
@include mixins.aspect-ratio(560, 315);
position: relative;
overflow: hidden;
width: calc(50% - 24px);
background-size: cover;
background-position: center;
}
.Details {
display: flex;
flex-direction: column;
justify-content: space-between;
width: calc(50% + 24px);
padding-left: 24px;
transition: background-color var(--ifm-transition-fast)
var(--ifm-transition-timing-default);
.Root:hover & {
background-color: $_hover-background;
}
}
.Title {
@include typography.h4;
padding-bottom: 10px;
}
.Description {
@include typography.body-small;
}
.VideoLength {
font-size: 12px;
padding-top: 10px;
display: flex;
align-items: center;
svg {
width: 18px;
height: 18px;
}
span {
margin-left: 5px;
line-height: 1;
}
}
.Link {
font-size: 0;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}

View File

@@ -0,0 +1,47 @@
import React from 'react'
import css from './VideoLinkCard.module.scss'
import PlayIcon from '../../../../static/img/icons/play-white.svg'
export interface VideoLinkCardProps {
videoId: string
title: string
description: string
videoLength: string
}
export function VideoLinkCard({
videoId,
title,
description,
videoLength,
}: VideoLinkCardProps) {
return (
<article className={css['Root']}>
<div
className={css['Thumb']}
style={{
backgroundImage: `url(http://img.youtube.com/vi/${videoId}/0.jpg)`,
}}
/>
<div className={css['Details']}>
<div>
<h1 className={css['Title']}>{title}</h1>
<p className={css['Description']}>{description}</p>
</div>
<p className={css['VideoLength']}>
<PlayIcon />
<span>{videoLength}</span>
</p>
</div>
<a
className={css['Link']}
href={`https://www.youtube.com/watch?v=${videoId}`}
target="_blank"
rel="noopener noreferrer"
>
Watch video
</a>
</article>
)
}

View File

@@ -0,0 +1 @@
export { default } from './VideoLinkCard'

View File

@@ -0,0 +1,15 @@
@use '../../../css/utils/mixins.scss';
.Root {
@include mixins.aspect-ratio(560, 315);
position: relative;
overflow: hidden;
iframe {
left: 0;
top: 0;
height: 100%;
width: 100%;
position: absolute;
}
}

View File

@@ -0,0 +1,22 @@
import React from 'react'
import css from './YouTubeEmbed.module.scss'
export interface YouTubeEmbedProps {
videoId: string
}
export function YouTubeEmbed({ videoId }: YouTubeEmbedProps) {
return (
<div className={css['Root']}>
<iframe
width="840"
height="472"
src={`https://www.youtube.com/embed/${videoId}`}
title="YouTube video player"
frameBorder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
)
}

View File

@@ -0,0 +1,49 @@
import React from 'react'
function fallbackCopyTextToClipboard(text) {
var textArea = document.createElement('textarea');
textArea.value = text;
// Avoid scrolling to bottom
textArea.style.top = '0';
textArea.style.left = '0';
textArea.style.position = 'fixed';
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
var successful = document.execCommand('copy');
var msg = successful ? 'successful' : 'unsuccessful';
console.log('Fallback: Copying text command was ' + msg);
} catch (err) {
console.error('Fallback: Oops, unable to copy', err);
}
document.body.removeChild(textArea);
}
function copyTextToClipboard(text) {
if (!navigator.clipboard) {
fallbackCopyTextToClipboard(text);
return;
}
navigator.clipboard.writeText(text).then(
function () {
console.log('Async: Copying to clipboard was successful!');
},
function (err) {
console.error('Async: Could not copy text: ', err);
}
);
}
function copyJsonToClipboard(json) {
copyTextToClipboard(JSON.stringify(json));
}
export default function CopyToClipboardButton(props) {
return (
<button className="ndl-copy-nodes-button" onClick={() => copyJsonToClipboard(props.json)}></button>
)
}

View File

@@ -0,0 +1,29 @@
import React from 'react'
import { importIntoNoodl } from '../utils/importIntoNoodl'
interface ImportButtonProps {
zip: string
name: string
thumb: string
cf: string
}
export default function ImportButton({
zip,
name,
thumb,
cf,
}: ImportButtonProps) {
return (
<button
className="ndl-import-button"
onClick={() =>
importIntoNoodl(zip, {
name,
thumb,
cf,
})
}
/>
)
}

View File

@@ -0,0 +1,6 @@
.Root {
max-width: var(--ifm-container-width-xl);
padding: var(--doc-size-page-top-spacing-l)
var(--ifm-navbar-padding-horizontal);
margin: 0 auto;
}

View File

@@ -0,0 +1,10 @@
import React, { ReactNode } from 'react'
import css from './Container.module.scss'
export interface ContainerProps {
children: ReactNode
}
export function Container({ children }: ContainerProps) {
return <div className={css['Root']}>{children}</div>
}

View File

@@ -0,0 +1,103 @@
$_gutter: 24px;
.Root {
display: flex;
box-sizing: border-box;
flex-wrap: wrap;
margin-top: $_gutter * -1;
margin-left: $_gutter * -1;
&.has-equal-height-items {
align-items: stretch;
}
}
.GridItem {
flex-grow: 0;
flex-shrink: 0;
padding-top: $_gutter;
padding-left: $_gutter;
pointer-events: none;
> * {
pointer-events: all;
}
.Root.has-equal-height-items & > * {
height: 100%;
}
.Root.is-grid-half > & {
width: 100%;
@media (min-width: 850px) {
width: 50%;
}
}
.Root.is-grid-thirds > & {
width: 100%;
@media (min-width: 600px) {
width: 50%;
}
@media (min-width: 900px) {
width: 33.333%;
}
}
.Root.is-grid-fifths > & {
width: 100%;
@media (min-width: 350px) {
width: 50%;
}
@media (min-width: 520px) {
width: 33.333%;
}
@media (min-width: 800px) {
width: 25%;
}
@media (min-width: 1250px) {
width: 20%;
}
}
.Root.is-grid-2-1-1 > & {
width: 100%;
@media (min-width: 400px) {
&:nth-child(3n-2) {
width: 100%;
}
&:not(:nth-child(3n-2)) {
width: 50%;
}
}
@media (min-width: 800px) {
&:nth-child(3n-2) {
width: 50%;
}
&:not(:nth-child(3n-2)) {
width: 25%;
}
}
}
.Root.is-grid-2-3 > & {
&:nth-child(odd) {
width: 40%;
}
&:nth-child(even) {
width: 60%;
}
}
}

View File

@@ -0,0 +1,46 @@
import clsx from 'clsx'
import React, { ReactNode, useMemo } from 'react'
import css from './Grid.module.scss'
export enum GridLayout {
Half = 'is-grid-half',
Thirds = 'is-grid-thirds',
Fifths = 'is-grid-fifths',
Grid_2_1_1 = 'is-grid-2-1-1',
Grid_2_3 = 'is-grid-2-3',
}
export interface GridProps {
layout: GridLayout
children: ReactNode
hasEqualHeightItems?: boolean
}
export function Grid({ layout, children, hasEqualHeightItems }: GridProps) {
const childArray = useMemo(
() => (Array.isArray(children) ? children : [children]),
[children]
)
return (
<div
className={clsx(
css['Root'],
css[layout],
hasEqualHeightItems && css['has-equal-height-items']
)}
>
{/* using i is not the best, but not all items will have ids.
if items orders or rendering get all weird or buggy this is the cause */}
{childArray.map((child, i) => {
if (child === null) return null
return (
<div key={i} className={css['GridItem']}>
{child}
</div>
)
})}
</div>
)
}

View File

@@ -0,0 +1,22 @@
@use '../../../css/utils/typography.scss';
.Root {
padding-bottom: 120px;
}
.Header {
display: flex;
justify-content: space-between;
align-items: baseline;
}
.Link {
@include typography.h3;
color: var(--doc-color-noodl-white-65) !important; // ajajaj
&:hover {
color: var(--doc-color-noodl-white) !important;
border-bottom-color: transparent !important;
}
}

View File

@@ -0,0 +1,42 @@
import Link from '@docusaurus/Link'
import React, { ReactNode } from 'react'
import { Title, TitleProps, TitleSize } from '../../typography/Title/Title'
import css from './Section.module.scss'
export interface SectionProps {
title?: TitleProps['children']
titleSize?: TitleProps['size']
linkLabel?: string | false
linkHref?: string
children: ReactNode
hasNoHeader?: boolean
}
export function Section({
title,
titleSize,
linkLabel = 'View all',
linkHref,
children,
hasNoHeader,
}: SectionProps) {
return (
<section className={css['Root']}>
{!hasNoHeader && (
<div className={css['Header']}>
<Title size={titleSize}>{title}</Title>
{Boolean(linkLabel) && (
<Link className={css['Link']} href={linkHref}>
{linkLabel}
</Link>
)}
</div>
)}
<div className={css['Content']}>{children}</div>
</section>
)
}

View File

@@ -0,0 +1 @@
export { default } from './Section'

View File

@@ -0,0 +1,36 @@
@use '../../../css/utils/typography.scss';
.Root {
font-family: var(--doc-font-text);
margin-block-start: 0;
margin-block-end: 0;
max-width: 860px;
[data-theme='dark'] & {
@extend %font-smoothing;
}
&.is-size-default {
@include typography.body;
&.has-bottom-spacing {
@include typography.body-bottom-spacing;
}
}
&.is-size-small {
@include typography.body-small;
&.has-bottom-spacing {
@include typography.body-small-bottom-spacing;
}
}
&.is-inline {
display: inline;
}
&.is-centered {
text-align: center;
}
}

View File

@@ -0,0 +1,47 @@
import clsx from 'clsx'
import React, { CSSProperties, ReactNode } from 'react'
import css from './Text.module.scss'
export enum TextSize {
Default = 'default',
Small = 'small',
}
export interface TextProps {
children?: ReactNode
className?: string
size?: TextSize
style?: CSSProperties
hasBottomSpacing?: boolean
isSpan?: boolean
isCentered?: boolean
}
export function Text({
children,
size = TextSize.Default,
style,
className,
hasBottomSpacing = true,
isSpan,
isCentered,
}: TextProps) {
const Tag = isSpan ? 'span' : 'p'
return (
<Tag
className={clsx(
css['Root'],
css[`is-size-${size}`],
isSpan && css['is-inline'],
isCentered && css['is-centered'],
hasBottomSpacing && css['has-bottom-spacing'],
className
)}
style={style}
>
{children}
</Tag>
)
}

View File

@@ -0,0 +1 @@
export { default } from './Text'

View File

@@ -0,0 +1,42 @@
@use '../../../css/utils/typography.scss';
.Root {
margin-bottom: 0;
margin-top: 0;
&.is-size-default {
@include typography.h2;
&.has-bottom-spacing {
@include typography.h2-bottom-spacing;
}
}
&.is-size-large {
@include typography.h1;
&.has-bottom-spacing {
@include typography.h1-bottom-spacing;
}
}
&.is-size-small {
@include typography.h3;
&.has-bottom-spacing {
@include typography.h3-bottom-spacing;
}
}
&.is-size-smaller {
@include typography.h4;
&.has-bottom-spacing {
@include typography.h4-bottom-spacing;
}
}
&.is-centered {
text-align: center;
}
}

View File

@@ -0,0 +1,43 @@
import clsx from 'clsx'
import React, { ReactNode } from 'react'
import css from './Title.module.scss'
export enum TitleSize {
Default = 'default',
Large = 'large',
Small = 'small',
Smaller = 'smaller',
}
export interface TitleProps {
children?: ReactNode
size?: TitleSize
headingLevel?: 1 | 2 | 3 | 4 | 5 | 6
hasBottomSpacing?: boolean
isCentered?: boolean
}
export function Title({
children,
size = TitleSize.Default,
headingLevel = 2,
hasBottomSpacing = true,
isCentered,
}: TitleProps) {
// hahahaha wtf
const Tag: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' = `h${headingLevel}`
return (
<Tag
className={clsx(
css['Root'],
css[`is-size-${size}`],
isCentered && css['is-centered'],
hasBottomSpacing && css['has-bottom-spacing']
)}
>
{children}
</Tag>
)
}

View File

@@ -0,0 +1 @@
export { default } from './Title'

74
src/css/custom.css Normal file
View File

@@ -0,0 +1,74 @@
/**
* 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.
*/
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;600&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap');
@font-face {
font-family: 'PolySans';
src: url('../assets/fonts/PolySans-Bulky.woff');
}
html {
font-family: 'Poppins', sans-serif;
}
/* You can override the default Infima variables here. */
:root {
--ifm-color-primary: var(--doc-color-noodl-orange);
--ifm-color-primary-dark: var(--doc-color-noodl-orange-180);
--ifm-color-primary-darker: var(--doc-color-noodl-orange-160);
--ifm-color-primary-darkest: var(--doc-color-noodl-orange-140);
--ifm-color-primary-light: var(--doc-color-noodl-orange-80);
--ifm-color-primary-lighter: var(--doc-color-noodl-orange-60);
--ifm-color-primary-lightest: var(--doc-color-noodl-orange-40);
--ifm-link-color: var(--doc-color-noodl-blue-40);
--ifm-link-hover-color: var(--doc-color-noodl-blue-60);
--ifm-leading: 0;
--ifm-code-font-size: 95%;
--ifm-container-width-xl: 1100px;
--ifm-container-width: 670px;
}
.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);
}
body {
font-size: 100%;
line-height: 1.5;
color: var(--doc-color-text);
}
.main-wrapper {
background-color: var(--doc-color-noodl-black-dark);
}
.container {
max-width: 1100px;
}
* {
text-decoration: none !important;
}
a {
transition: all 0.1s linear;
}
.github-corner {
z-index: 5;
}
.is-hidden {
display: none;
}

326
src/css/markdown.scss Normal file
View File

@@ -0,0 +1,326 @@
@use '../css/utils/typography.scss';
.markdown h1:first-child {
margin-bottom: 0;
}
.theme-doc-markdown,
.markdown,
.markdown section {
margin: 0 auto;
> h1 {
@include typography.h1;
&:not(:last-child) {
@include typography.h1-bottom-spacing;
}
}
> h2 {
@include typography.h2;
&:not(:last-child) {
@include typography.h2-bottom-spacing;
}
&:not(:first-child) {
@include typography.h2-top-spacing;
}
}
> h3 {
@include typography.h3;
&:not(:last-child) {
@include typography.h3-bottom-spacing;
}
&:not(:first-child) {
@include typography.h3-top-spacing;
}
}
> h4 {
@include typography.h4;
&:not(:last-child) {
@include typography.h4-bottom-spacing;
}
}
> p,
> ol,
> ul {
@include typography.body;
&:not(:last-child) {
@include typography.body-bottom-spacing;
}
}
> p,
> ol,
> ul {
max-width: 830px;
}
> a {
border-bottom: 1px solid transparent;
&:hover {
border-bottom: 1px solid var(--ifm-link-hover-color);
}
}
.prism-code {
margin-bottom: 40px;
}
p code {
border-radius: 5px;
padding-left: 4px;
padding-right: 2px;
background-color: rgb(52, 56, 72);
letter-spacing: 0.03em;
font-weight: normal;
@extend %font-smoothing;
}
> table {
margin-bottom: 40px;
th {
text-align: left;
}
}
}
/****** COPY TO CLIPBOARD ******/
.docsify-copy-code-button {
font-size: 0.7em !important;
}
/****** IMAGES ******/
.ndl-images {
position: relative;
text-align: center;
display: flex;
justify-content: center;
}
.ndl-image {
height: auto;
margin: 5px;
align-self: flex-start;
}
.ndl-image.small {
width: 30%;
}
.ndl-image.med {
width: 50%;
}
.ndl-image.large {
width: 100%;
}
.ndl-image-with-background,
.ndl-video {
background: linear-gradient(
45deg,
var(--doc-color-noodl-orange),
var(--doc-color-noodl-blue)
);
text-align: center;
position: relative;
margin-bottom: 40px;
p {
padding-bottom: 0 !important;
}
img,
video {
height: auto;
border-radius: 4px;
box-shadow: -5px 5px 30px -5px #38341caa, 5px -5px 30px -5px #203c2caa;
}
}
.ndl-video {
video {
margin: 2.5%;
width: 95%;
}
}
.ndl-image-with-background {
video,
img {
width: 50%;
margin: 50px;
}
&.xl {
img,
video {
width: 95%;
margin: 2.5%;
}
}
&.l {
video,
img {
width: 80%;
}
}
&.s {
video,
img {
width: 30%;
}
}
}
.ndl-image-with-background .ndl-copy-nodes-button {
position: absolute;
top: 0;
right: 0;
color: black;
box-shadow: 0px 0px 10px 0px #38341ceaa;
}
.tutorial-img-link:hover {
opacity: 75%;
}
/****** COPY NODES BUTTON ******/
.ndl-copy-nodes-button {
position: absolute;
z-index: 1;
top: 0;
right: 0;
overflow: visible;
padding: 0.65em 0.8em;
border: 0;
border-radius: 0;
outline: 0;
font-size: 10px;
background: #808080;
background: var(--doc-color-noodl-orange);
color: #fff;
cursor: pointer;
transition: transform 200ms ease-in-out;
}
.ndl-copy-nodes-button:hover {
transform: scale(1.05);
}
.ndl-copy-nodes-button::after {
content: 'COPY NODES';
}
.ndl-copy-nodes-button:active {
background: var(--doc-color-noodl-orange-140);
}
.ndl-import-button {
position: absolute;
z-index: 1;
top: 0;
right: 0;
overflow: visible;
padding: 0.65em 0.8em;
border: 0;
border-radius: 0;
outline: 0;
font-size: 14px;
background: var(--doc-color-noodl-orange);
color: var(--doc-color-noodl-black-darkest);
cursor: pointer;
}
.ndl-import-button::after {
content: 'IMPORT';
}
.ndl-import-button.no-content::after {
content: '';
}
.ndl-import-button:active {
background: var(--doc-color-noodl-orange-140);
}
.img-size-xs {
display: block;
margin-left: auto;
margin-right: auto;
width: 15%;
}
.img-size-s {
display: block;
margin-left: auto;
margin-right: auto;
width: 30%;
}
.img-size-m {
display: block;
margin-left: auto;
margin-right: auto;
width: 50%;
}
.img-size-l {
display: block;
margin-left: auto;
margin-right: auto;
width: 100%;
}
.johan-test {
font-weight: bold;
}
/* Modules table */
.modules-table img {
width: 150px;
}
.ndl-data {
font-weight: bold;
color: var(--doc-color-noodl-orange-80);
}
.ndl-signal {
font-weight: bold;
color: var(--doc-color-noodl-green-80);
}
.ndl-deprecated {
font-weight: bold;
color: rgb(228, 57, 57);
}
.hidden-props-for-editor {
display: none;
}
.ndl-table-35-65 table {
display: table;
width: 100%;
}
.ndl-table-35-65 table td:nth-child(1) {
width: 35%;
}
.ndl-table-35-65 table td:nth-child(2) {
width: 65%;
}

105
src/css/navbar.scss Normal file
View File

@@ -0,0 +1,105 @@
@use './utils/mixins.scss';
:root {
--ifm-navbar-background-color: var(--doc-color-noodl-black-darker);
--ifm-navbar-link-color: var(--doc-color-noodl-white-65);
--ifm-navbar-padding-horizontal: 28px;
--ifm-navbar-height: 64px;
}
.navbar {
border-bottom: 1px solid var(--doc-color-noodl-black-light);
padding: 0;
&__toggle {
@media (max-width: 996px) {
display: flex;
align-items: center;
padding-right: 10px;
}
}
&__logo {
height: 2.5rem;
}
&__inner {
padding: 0 var(--ifm-navbar-padding-horizontal);
}
&__title {
margin-left: 10px;
font-size: 18px;
color: var(--doc-color-noodl-white);
}
&__items {
align-items: stretch;
}
&__item {
padding: calc(var(--ifm-navbar-padding-vertical) + 10px) 20px
calc(var(--ifm-navbar-padding-vertical) + 5px);
font-size: 16px;
font-weight: var(--doc-font-semibold);
&:not(.is-download-button) {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
&.has-divider {
padding-right: 50px;
margin-right: 30px;
position: relative;
&:after {
content: '';
position: absolute;
top: 12px;
bottom: 12px;
right: 0;
width: 1px;
background-color: var(--doc-color-noodl-white-65);
}
}
&.is-download-button {
background-color: var(--doc-color-noodl-orange);
color: var(--doc-color-noodl-black);
padding: 0 40px;
margin-left: 20px;
display: flex;
align-items: center;
margin-right: calc(var(--ifm-navbar-padding-horizontal) * -1);
transition-duration: 100ms;
&:hover {
background-color: var(--doc-color-noodl-orange-140);
}
}
&.is-discord {
display: block;
font-size: 0;
width: 36px;
box-sizing: content-box;
padding-top: 0;
padding-bottom: 0;
display: flex;
align-items: center;
opacity: 0.65;
&:hover {
opacity: 1;
}
&:after {
content: '';
height: 36px;
width: 36px;
background-image: url('../../static/img/discord.svg');
}
}
}
}

42
src/css/pagination.scss Normal file
View File

@@ -0,0 +1,42 @@
@use '../css/utils/typography.scss';
.pagination-nav {
border-top: 1px solid var(--doc-color-noodl-white-65);
padding-bottom: 180px;
&__link {
border: none;
}
&__sublabel,
&__label {
@include typography.h3;
}
&__label {
&:after,
&:before {
content: '' !important;
}
}
&__sublabel {
color: var(--doc-color-noodl-white-65) !important;
padding-top: 32px;
opacity: 0.5;
transition: opacity var(--ifm-transition-fast)
var(--ifm-transition-timing-default);
.pagination-nav__link:hover & {
opacity: 0.65;
}
.pagination-nav__item--next &:after {
content: '';
}
.pagination-nav__item:not(.pagination-nav__item--next) &:before {
content: '';
}
}
}

53
src/css/searchbar.scss Normal file
View File

@@ -0,0 +1,53 @@
[data-theme='dark'] .DocSearch {
--docsearch-text-color: var(--ifm-font-color-base);
--docsearch-muted-color: var(--ifm-color-secondary-darkest);
--docsearch-container-background: rgba(0, 0, 0, 0.8);
/* Modal */
--docsearch-modal-background: var(--doc-color-noodl-black-light);
/* Search box */
--docsearch-searchbox-background: var(--doc-color-noodl-black-darker);
--docsearch-searchbox-focus-background: var(--ifm-color-black);
/* Hit */
--docsearch-hit-color: var(--ifm-font-color-base);
--docsearch-hit-active-color: var(--ifm-color-white);
--docsearch-hit-background: var(--ifm-color-emphasis-100);
/* Footer */
--docsearch-footer-background: var(--ifm-background-surface-color);
--docsearch-key-gradient: linear-gradient(
-26.5deg,
var(--ifm-color-emphasis-200) 0%,
var(--ifm-color-emphasis-100) 100%
);
}
.DocSearch {
padding: 0 !important;
main & {
margin-bottom: 80px;
}
.navbar & {
display: none;
}
}
.navbar__items--right :last-child {
display: none !important;
}
.DocSearch-Button {
height: auto !important;
border-radius: 2px !important;
border: 1px solid var(--doc-color-noodl-black-light) !important;
width: 100% !important;
padding: 20px 24px !important;
}
.search-bar-outer > :first-child {
padding: 0;
}
.DocSearch-Hit[aria-selected='true'] a {
background-color: var(--doc-color-noodl-blue-80) !important;
}

115
src/css/sidebar.scss Normal file
View File

@@ -0,0 +1,115 @@
// This file has a lot of messy overwrites as
// we dont really want change the markup of the sidebar
// component if we would have to update docusaurus in the
// future. Future me: I hope that is a good desicion.
:root {
--ifm-menu-color-background-hover: none;
--ifm-menu-color-background-active: ;
}
.menu {
background-color: var(--doc-color-noodl-black-darker);
padding: 0 0 30px !important;
height: 100vh;
.theme-doc-sidebar-menu {
padding: 0;
> li {
border-bottom: 1px solid var(--doc-color-noodl-black-light);
> div > .menu__link {
padding: 13px var(--ifm-menu-link-padding-horizontal);
}
}
> .theme-doc-sidebar-item-link > .menu__link {
padding: 13px var(--ifm-menu-link-padding-horizontal);
}
}
.theme-doc-sidebar-item-link-level-3 {
border-left: 1px solid var(--doc-color-noodl-white-65);
padding-bottom: 5px;
}
&__list {
margin: 0 !important;
& & {
background-color: var(--doc-color-noodl-black-darkest);
a:not(.menu__link--active) {
color: var(--doc-color-noodl-white-65) !important;
&:hover {
color: var(--doc-color-noodl-white) !important;
}
}
}
}
&__list-item {
margin: 0 !important;
&:not(.theme-doc-sidebar-item-link-level-3):not(.theme-doc-sidebar-item-category-level-1) {
&:last-child {
padding-bottom: 13px;
}
}
&.theme-doc-sidebar-item-link-level-3 {
&:first-child {
margin-top: 13px !important;
}
&:last-child {
margin-bottom: 13px !important;
}
}
}
&__list-item-collapsible {
&--active {
background: transparent;
}
}
&__link {
transition: color var(--ifm-transition-fast)
var(--ifm-transition-timing-default),
background-color var(--ifm-transition-fast)
var(--ifm-transition-timing-default) !important;
&[aria-expanded='true'] {
&,
&:hover {
background-color: var(
--doc-color-noodl-black-darkest
) !important;
}
}
&--active {
color: var(--doc-color-noodl-orange) !important;
}
}
&__caret {
margin-left: 0;
transition: background-color var(--ifm-transition-fast)
var(--ifm-transition-timing-default) !important;
display: flex;
align-items: center;
.menu__list-item:not(.menu__list-item--collapsed) & {
background-color: var(--doc-color-noodl-black-darkest);
}
}
&__link--sublist-caret:after,
&__caret:before {
background: var(--ifm-menu-link-sublist-icon) 50% / 1.5rem 1.5rem;
}
}

57
src/css/utils/mixins.scss Normal file
View File

@@ -0,0 +1,57 @@
@function _get-pseudo($pseudo) {
@if ($pseudo == false) {
$pseudo: '&::after';
} @else if
(
_contains(
('after', ':after', '::after', '&:after', '&::after'),
$pseudo
)
)
{
$pseudo: '&::after';
} @else if
(
_contains(
('before', ':before', '::before', '&:before', '&::before'),
$pseudo
)
)
{
$pseudo: '&::before';
}
@return $pseudo;
}
@function _is-pseudo($pseudo) {
@return ($pseudo == '&::after' or $pseudo == '&::before');
}
@function _contains($list, $item) {
@return index($list, $item) != null;
}
/**
* aspect-ratio()
*
* Give a heightless element a height based on its width.
* Always follows the set width:height ratio.
*
* @param {number} $width - Without CSS unit
* @param {number} $height - Without CSS unit
* @param {string} $pseudo - Optional selector to use insted of default pseudo-element
*/
@mixin aspect-ratio($width, $height, $pseudo: false) {
$pseudo: _get-pseudo($pseudo);
#{$pseudo} {
@if (_is-pseudo($pseudo)) {
content: '';
}
display: block;
height: 0;
padding-bottom: calc((100% / $width) * $height);
}
}

View File

@@ -0,0 +1,98 @@
%font-smoothing {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
%title-base {
font-family: var(--doc-font-title);
font-weight: 600;
margin-top: 0;
margin-bottom: 0;
padding-top: 0;
padding-bottom: 0;
[data-theme='dark'] & {
@extend %font-smoothing;
color: var(--doc-color-title);
}
}
@mixin h1 {
@extend %title-base;
font-family: var(--doc-font-supertitle);
font-size: 48px;
line-height: 48px;
}
@mixin h1-bottom-spacing {
padding-bottom: 40px;
}
@mixin h2 {
@extend %title-base;
font-family: var(--doc-font-supertitle);
font-size: 32px;
line-height: 40px;
}
@mixin h2-bottom-spacing {
padding-bottom: 40px;
}
@mixin h2-top-spacing {
margin-top: 120px;
}
@mixin h3 {
@extend %title-base;
font-size: 24px;
line-height: 28px;
}
@mixin h3-top-spacing {
margin-top: 40px;
}
@mixin h3-bottom-spacing {
padding-bottom: 24px;
}
@mixin h4 {
@extend %title-base;
font-size: 16px;
line-height: 24px;
}
@mixin h4-bottom-spacing {
padding-bottom: 8px;
}
%body-base {
font-family: var(--doc-font-text);
[data-theme='dark'] & {
@extend %font-smoothing;
}
}
@mixin body {
@extend %body-base;
font-size: 16px;
line-height: 24px;
}
@mixin body-bottom-spacing {
padding-bottom: 40px;
}
@mixin body-small {
@extend %body-base;
font-size: 14px;
line-height: 18px;
}
@mixin body-small-bottom-spacing {
padding-bottom: 14px;
}

57
src/css/variables.css Normal file
View File

@@ -0,0 +1,57 @@
:root {
/* Colors */
--doc-color-noodl-black: #1f1f1f;
--doc-color-noodl-black-dark: #141416;
--doc-color-noodl-black-darker: #0e0e0e;
--doc-color-noodl-black-darkest: #000000;
--doc-color-noodl-black-light: #232326;
--doc-color-noodl-white: #f6f6f6;
--doc-color-noodl-white-85: #dcdcdc;
--doc-color-noodl-white-65: #a5a5a5;
--doc-color-noodl-orange: #f5bc41;
--doc-color-noodl-orange-80: #f7c967;
--doc-color-noodl-orange-60: #f9d78d;
--doc-color-noodl-orange-40: #fbe4b3;
--doc-color-noodl-orange-20: #fce9c2;
--doc-color-noodl-orange-180: #f3b224;
--doc-color-noodl-orange-160: #f3ac15;
--doc-color-noodl-orange-140: #ce900b;
--doc-color-noodl-green: #1ca5b8;
--doc-color-noodl-green-80: #49b7c6;
--doc-color-noodl-green-60: #77c9d4;
--doc-color-noodl-green-40: #92d4dd;
--doc-color-noodl-green-20: #a8dde4;
--doc-color-noodl-green-180: #1994a6;
--doc-color-noodl-green-160: #188c9c;
--doc-color-noodl-green-140: #147381;
--doc-color-noodl-blue: #5836f5;
--doc-color-noodl-blue-80: #795ef7;
--doc-color-noodl-blue-60: #9b86f9;
--doc-color-noodl-blue-40: #bcaffb;
--doc-color-noodl-blue-20: #c9bffc;
--doc-color-noodl-blue-180: #401af4;
--doc-color-noodl-blue-160: #350cf2;
--doc-color-noodl-blue-140: #2c0ac7;
--doc-color-visual-node: #324e6b;
--doc-color-data-node: #697844;
--doc-color-custom-node: #8c436c;
--doc-color-logic-node: #3b3e48;
--doc-color-connection-node: #5f3789;
--doc-color-title: var(--doc-color-noodl-white);
--doc-color-text: var(--doc-color-noodl-white-85);
/* Typography */
--doc-font-title: 'Poppins', sans-serif;
--doc-font-supertitle: 'PolySans', sans-serif;
--doc-font-text: 'Inter', sans-serif;
--doc-font-semibold: 600;
/* Sizes */
--doc-size-page-top-spacing-l: 120px;
}

112
src/pages/index.js Normal file
View File

@@ -0,0 +1,112 @@
import React from 'react'
import Layout from '@theme/Layout'
import useDocusaurusContext from '@docusaurus/useDocusaurusContext'
import { Container } from '../components/layout/Container/Container'
import { ModuleListing } from '../blocks/ModuleListing'
import { frontpageData, FrontpageBlocks } from '../../static/data/frontpage'
import { Text } from '../components/typography/Text/Text'
import { HeroBlock } from '../blocks/HeroBlock'
import { GuideListing } from '../blocks/GuideListing'
import { VideoListing } from '../blocks/VideoListing'
import { ProjectListing } from '../blocks/ProjectListing'
import { PrefabListing } from '../blocks/PrefabListing'
import SearchBar from '@theme-original/SearchBar'
import Head from '@docusaurus/Head'
export default function Home() {
const { siteConfig } = useDocusaurusContext()
return (
<Layout title={`${siteConfig.title}`}>
<Head>
<meta
property="og:image"
content="https://docs.noodl.net/noodl-docs.png"
/>
<meta property="og:title" content="Noodl Documentation" />
<meta
property="og:description"
content="Explore Noodl guides, tutorials, videos, modules, and reference documentation here. Noodl is the low-code platform for designers + developers to build custom web apps and experiences."
/>
</Head>
<Container>
<main>
{frontpageData.map((item, i) => {
switch (item.type) {
case FrontpageBlocks.SearchBar:
return (
<div className="search-bar-outer" key={i}>
<SearchBar />
</div>
)
case FrontpageBlocks.Hero:
return (
<HeroBlock
key={i}
title={item.title}
text={item.text}
gridItems={item.gridItems}
playIcon={item.playIcon}
/>
)
case FrontpageBlocks.FeaturedModules:
return (
<ModuleListing
key={i}
title="Featured modules"
isFeaturedOnly
/>
)
case FrontpageBlocks.FeaturedGuides:
return (
<GuideListing
key={i}
title="Featured guides"
isFeaturedOnly
/>
)
case FrontpageBlocks.FeaturedProjects:
return (
<ProjectListing
key={i}
title="Featured example projects"
/>
)
case FrontpageBlocks.FeaturedVideos:
return (
<VideoListing
key={i}
title="Featured videos"
isFeaturedOnly
/>
)
case FrontpageBlocks.FeaturedPrefabs:
return (
<PrefabListing
key={i}
title="Featured prefabs"
isFeaturedOnly
/>
)
default:
return (
<Text key={i}>
Error: Wrong type provided in frontpage
data
</Text>
)
}
})}
</main>
</Container>
</Layout>
)
}

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,7 @@
---
title: Markdown page example
---
# Markdown page example
You don't need React to write simple standalone pages.

View File

@@ -0,0 +1,79 @@
.Root {
> div {
justify-content: center;
padding-top: var(--doc-size-page-top-spacing-l);
> div:first-child {
:global(html:not(.docs-doc-id-overview):not(.docs-doc-id-modules\/overview):not(.docs-doc-id-prefabs\/overview):not(.docs-doc-id-getting-started\/overview):not(.docs-doc-id-learn):not(.docs-doc-id-examples\/overview))
& {
max-width: var(--ifm-container-width) !important;
}
}
}
> div > div {
max-width: 100% !important;
}
}
.Lightbox {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(30,30,30, 0.7);
z-index: 666;
animation: show 200ms ease-out both;
}
.Lightbox,
.LightboxImageContainer {
display: flex;
align-items: center;
justify-content: center;
}
@keyframes show {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.LightboxImageContainer {
position: relative;
width: 85%;
height: 85%;
background: linear-gradient(45deg, var(--doc-color-noodl-orange), var(--doc-color-noodl-blue));
border-radius: 10px;
box-shadow: 0 0 30px 5px rgba(0,0,0,0.3), 0 0 20px 5px rgba(0, 0, 0, 0.3);
}
.LightboxClose {
font-size: 20px;
font-weight: bold;
color: var(--doc-color-noodl-black-light);
position: absolute;
top: 10px;
right: 10px;
border: 0;
background: none;
cursor: pointer;
transition: color var(--speed-turbo) var(--easing-base);
&:hover {
color: var(--doc-color-noodl-black-darkest);
}
}
.LightboxAsset {
max-width: 90%;
max-height: 90%;
object-fit: contain;
border-radius: 5px;
box-shadow: -5px 5px 30px -5px rgba(56, 52, 28, 0.6666666667), 5px -5px 30px -5px rgba(32, 60, 44, 0.6666666667);
}

View File

@@ -0,0 +1,60 @@
import React from 'react';
import DocItem from '@theme-original/DocItem';
import css from './DocItemWrapper.module.scss';
export default function DocItemWrapper(props) {
const docItemRootRef = React.useRef();
const [lightboxAsset, setLightboxAsset] = React.useState(null);
function handleImageClick(e) {
setLightboxAsset({
src: e.target.src,
type: e.target.tagName,
});
}
React.useEffect(() => {
if (!docItemRootRef.current) return;
const images = Array.from(docItemRootRef.current.querySelectorAll('img'));
const videos = Array.from(docItemRootRef.current.querySelectorAll('video'));
[...images, ...videos].forEach((asset) => {
asset.style.cursor = 'pointer';
asset.addEventListener('click', handleImageClick);
});
return () => {
[...images, ...videos].forEach((asset) => {
asset.removeEventListener('click', handleImageClick);
});
};
}, [docItemRootRef.current]);
return (
<>
{lightboxAsset && (
<div className={css['Lightbox']} onClick={() => setLightboxAsset(null)}>
<div className={css['LightboxImageContainer']}>
<button className={css['LightboxClose']}></button>
{lightboxAsset.type === 'IMG' && (
<img className={css['LightboxAsset']} src={lightboxAsset.src} />
)}
{lightboxAsset.type === 'VIDEO' && (
<video
className={css['LightboxAsset']}
src={lightboxAsset.src}
autoPlay
loop
/>
)}
</div>
</div>
)}
<div ref={docItemRootRef} className={css['Root']}>
<DocItem {...props} />
</div>
</>
);
}

View File

@@ -0,0 +1,15 @@
import React from 'react'
import Content from '@theme-original/DocSidebar/Desktop/Content'
import SearchBar from '@theme-original/SearchBar'
export default function ContentWrapper(props) {
return (
<>
<div className="search-bar-outer">
<SearchBar />
</div>
<Content {...props} />
</>
)
}

View File

@@ -0,0 +1,58 @@
import config from '@generated/docusaurus.config'
interface IImportOptions {
/** Name of import */
name: string
/** Image shown in project import */
thumb: string
/** Cloudformation, JSON that creates a backend */
cf?: string
}
export interface ImportIntoNoodlArgs {
path: string
options: IImportOptions
}
export function importIntoNoodl(path: string, options: IImportOptions) {
let query = []
if (options && options.name !== undefined)
query.push('name=' + encodeURIComponent(options.name))
if (options && options.thumb !== undefined)
query.push(
'thumb=' +
encodeURIComponent(
location.protocol +
'//' +
location.host +
config.baseUrl +
options.thumb
)
)
if (options && options.cf !== undefined)
query.push(
'cf=' +
encodeURIComponent(
location.protocol +
'//' +
location.host +
config.baseUrl +
'/' +
options.cf
)
)
var uri =
'noodl:import/' +
location.protocol +
'//' +
location.host +
config.baseUrl +
path +
(query.length > 0 ? '?' + query.join('&') : '')
console.log('Importing into Noodl:', uri)
console.log(path)
window.location.href = uri
}