diff --git a/website/src/components/layout/site/header.tsx b/website/src/components/layout/site/header.tsx index 8534fd7b6e0..f3a1d96c87a 100644 --- a/website/src/components/layout/site/header.tsx +++ b/website/src/components/layout/site/header.tsx @@ -17,7 +17,12 @@ import { WorkshopNdcOsloImage, WorkshopOnlineImage, } from "@/components/images"; -import { IconContainer, Link, SearchModal } from "@/components/misc"; +import { + IconContainer, + Link, + LinkButton, + SearchModal, +} from "@/components/misc"; import { Icon, Logo } from "@/components/sprites"; import { GitHubStarButton } from "@/components/widgets"; import { @@ -502,7 +507,10 @@ const PlatformNavItem: FC = ({ -
Nitro (fka Banana Cake Pop)
+
+ Nitro (fka Banana Cake + Pop) +
GraphQL IDE / API Cockpit
@@ -689,10 +697,11 @@ const DeveloperNavItem: FC = ({ {products.map((product, index) => ( @@ -927,7 +936,7 @@ const DemoAndLaunch: FC = ({ tools }) => { > Request a Demo - Launch + Launch ); }; @@ -1179,9 +1188,9 @@ const SubNav = styled.div.attrs({ overflow-y: initial; ${ApplyBackdropBlur( - 48, - `background-color: ${THEME_COLORS.backgroundMenu};` -)} + 48, + `background-color: ${THEME_COLORS.backgroundMenu};` + )} } `; @@ -1528,31 +1537,6 @@ const Tools = styled.div` } `; -const LaunchLink = styled(Link)` - display: flex; - flex: 0 0 auto; - align-items: center; - box-sizing: border-box; - border-radius: var(--button-border-radius); - height: 38px; - padding: 0 30px; - border: 2px solid ${THEME_COLORS.primaryButtonBorder}; - color: ${THEME_COLORS.primaryButtonText}; - background-color: ${THEME_COLORS.primaryButton}; - font-family: ${FONT_FAMILY_HEADING}; - font-size: 0.875rem; - text-decoration: none; - font-weight: 500; - transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out, - color 0.2s ease-in-out; - - :hover { - border-color: ${THEME_COLORS.primaryButtonBorder}; - color: ${THEME_COLORS.primaryButtonHoverText}; - background-color: ${THEME_COLORS.primaryButtonHover}; - } -`; - const RequestDemoLink = styled(Link)` display: flex; flex: 0 0 auto; diff --git a/website/src/components/mdx/code-block.tsx b/website/src/components/mdx/code-block.tsx index 3582d0213c8..1eaee959ef8 100644 --- a/website/src/components/mdx/code-block.tsx +++ b/website/src/components/mdx/code-block.tsx @@ -3,17 +3,22 @@ import Prism from "prismjs"; import React, { FC } from "react"; import styled, { css } from "styled-components"; +import { Play } from "@/components/mdx/play"; import { FONT_FAMILY_CODE, THEME_COLORS } from "@/style"; import { Copy } from "./copy"; export interface CodeBlockProps { readonly children: any; readonly language?: Language; + readonly hideLanguageIndicator?: boolean; + readonly playUrl?: string; } export const CodeBlock: FC = ({ children, language: fallbackLanguage, + hideLanguageIndicator, + playUrl, }) => { const language = (children.props?.className?.replace(/language-/, "") as Language) || @@ -24,10 +29,16 @@ export const CodeBlock: FC = ({ return ( - - - - + {!hideLanguageIndicator && language ? ( + + ) : null} + + + {playUrl ? : null} + + + + pre[class*="language-"] { margin: 0; - padding: 30px 20px; + padding: 40px 30px; + width: 100%; + overflow: visible; + } + + @media only screen and (min-width: 700px) { + > pre[class*="language-"] { + padding: 40px 40px; + } } `; -const CopyPosition = styled.div` +const ActionsContainer = styled.div` position: absolute; - z-index: 1; - top: 0; - right: 0; + z-index: 2; + top: 12px; + right: 12px; + display: flex; + align-items: center; + gap: 6px; +`; + +const sharedButtonStyles = css` + border-radius: var(--button-border-radius) !important; + border: 1px solid ${THEME_COLORS.boxBorder} !important; + padding: 10px !important; + background-color: ${THEME_COLORS.background} !important; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2) !important; + transition: all 0.2s ease-in-out !important; + + svg { + width: 20px !important; + height: 20px !important; + } + + > div { + width: 20px !important; + height: 20px !important; + } + + &:hover { + background-color: ${THEME_COLORS.backgroundAlt} !important; + transform: translateY(-2px); + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.25) !important; + } +`; + +const StyledCopy = styled(Copy)` + ${sharedButtonStyles} +`; + +const StyledPlay = styled(Play)` + ${sharedButtonStyles} `; diff --git a/website/src/components/mdx/copy.tsx b/website/src/components/mdx/copy.tsx index eaf9559e645..c4db1b61cf8 100644 --- a/website/src/components/mdx/copy.tsx +++ b/website/src/components/mdx/copy.tsx @@ -22,27 +22,34 @@ function copyToClipboard(content: string): void { export interface CopyProps { readonly content: string; + readonly hideToast?: boolean; + readonly className?: string; } -export const Copy: FC = ({ content }) => { +export const Copy: FC = ({ content, hideToast, className }) => { const [showToast, setShowToast] = useState(false); return ( <> { copyToClipboard(content); - setShowToast(true); - setTimeout(() => { - setShowToast(false); - }, 3000); + if (!hideToast) { + setShowToast(true); + setTimeout(() => { + setShowToast(false); + }, 3000); + } }} > + {showToast && } ); diff --git a/website/src/components/mdx/play.tsx b/website/src/components/mdx/play.tsx new file mode 100644 index 00000000000..cbb83315b60 --- /dev/null +++ b/website/src/components/mdx/play.tsx @@ -0,0 +1,47 @@ +import { IconContainer } from "@/components/misc"; +import { Icon } from "@/components/sprites"; +import { THEME_COLORS } from "@/style"; +import React, { FC } from "react"; +import styled from "styled-components"; + +// Icons +import PlayIconSvg from "@/images/icons/debug-start.svg"; + +export interface PlayProps { + readonly content: string; + readonly url: string; + readonly className?: string; +} + +export const Play: FC = ({ content, url, className }) => { + return ( + { + // TODO: Open Nitro with url and content + + alert(`Open '${content}' in Nitro with URL '${url}'`); + }} + > + + + + + ); +}; + +const PlayIconButton = styled.button` + display: flex; + align-items: center; + justify-content: center; + border-radius: 0 var(--box-border-radius) 0 var(--button-border-radius); + border: 1px solid ${THEME_COLORS.boxBorder}; + border-top: 0 none; + border-right: 0 none; + padding: 6px; + + svg { + fill: #eb64b9; + } +`; diff --git a/website/src/components/widgets/community-section.tsx b/website/src/components/widgets/community-section.tsx index 55c634bc300..5437742ffd3 100644 --- a/website/src/components/widgets/community-section.tsx +++ b/website/src/components/widgets/community-section.tsx @@ -32,7 +32,7 @@ export const CommunityVisualization: FC = () => { - 6k + 7.1k Slack Users @@ -40,11 +40,11 @@ export const CommunityVisualization: FC = () => { Package Downloads - 5k + 5.6k GitHub Stars - 3.2k + 4.6k Pull Requests diff --git a/website/src/images/icons/debug-start.svg b/website/src/images/icons/debug-start.svg new file mode 100644 index 00000000000..2350ed475dd --- /dev/null +++ b/website/src/images/icons/debug-start.svg @@ -0,0 +1,3 @@ + + + diff --git a/website/src/pages/example-graphql-api.tsx b/website/src/pages/example-graphql-api.tsx new file mode 100644 index 00000000000..d0c30a234c5 --- /dev/null +++ b/website/src/pages/example-graphql-api.tsx @@ -0,0 +1,251 @@ +import { SiteLayout } from "@/components/layout"; +import { CodeBlock } from "@/components/mdx/code-block"; +import { Copy } from "@/components/mdx/copy"; +import { Video } from "@/components/mdx/video"; +import { IconContainer, Link, LinkButton, SEO } from "@/components/misc"; +import { Icon } from "@/components/sprites"; +import ArrowRightIconSvg from "@/images/icons/arrow-right.svg"; +import CheckIconSvg from "@/images/icons/check.svg"; +import { IsMobile, IsPhablet, IsTablet, THEME_COLORS } from "@/style"; +import React, { FC } from "react"; +import styled from "styled-components"; + +const exampleQuery = `query GetProducts { + products { + edges { + node { + id + name + reviews { + edges { + node { + stars + body + author { + name + } + } + } + } + } + } + } +}`; + +const serverUrl = "https://demo.chillicream.cloud/graphql"; +const features = [ + "Relay compatible: Includes Connections for paging and the node field", + "Modern GraphQL: Contains @defer, @stream, and @oneOf", + "No CORS: Use it directly in your frontend", + "Subscriptions", +]; + +const GraphQLExamplePage: FC = () => { + return ( + + + + + + Example GraphQL API + + + {serverUrl} + + + + + + + + Open in Nitro + + + + + + + + + Example Query + + + + + Features + + {features.map((feature) => ( + + + + + {feature} + + ))} + + + + Get to know Nitro + + + + + ); +}; + +export default GraphQLExamplePage; + +const Layout = styled.div` + display: grid; + grid-template-columns: 1fr 720px 440px 1fr; + grid-template-rows: 1fr; + gap: 20px; + + ${IsTablet(` + grid-template-columns: 1fr; + `)} + + width: 100%; + height: 100%; + padding-top: 26px; + overflow: visible; +`; + +const HeaderLeft = styled.div` + grid-row: 1; + grid-column: 2; + + ${IsTablet(` + grid-column: 1; + `)} +`; + +const HeaderRight = styled.div` + grid-row: 1; + grid-column: 3; + + ${IsTablet(` + grid-row: 2; + grid-column: 1; + `)} +`; + +const OpenButtonContainer = styled.div` + display: flex; + justify-content: center; + align-items: center; + height: 100%; + + ${IsTablet(` + justify-content: flex-start; + width: 100%; + `)} +`; + +const LeftContent = styled.div` + grid-row: 2; + grid-column: 2; + + ${IsTablet(` + grid-row: 3; + grid-column: 1; + `)} +`; + +const RightContent = styled.div` + grid-row: 2; + grid-column: 3; + + ${IsTablet(` + grid-row: 4; + grid-column: 1; + `)} +`; + +const UrlContainer = styled.div` + display: flex; +`; + +const PageTitle = styled.h1` + font-size: 2.5rem; + font-weight: normal; +`; + +const Title = styled.h2` + margin-right: 16px; + margin-bottom: 16px; + margin-left: 16px; + font-size: 2rem; + font-weight: normal; + + @media only screen and (min-width: 860px) { + margin-right: 0; + margin-left: 0; + } +`; + +const OpenButton = styled(LinkButton)` + flex-shrink: 0; + justify-content: center; + + > ${IconContainer} { + margin-left: 8px; + margin-right: 0; + + svg { + fill: currentColor; + transition: transform 0.2s ease-in-out; + } + } + + &:hover > ${IconContainer} svg { + transform: translateX(2px); + } + + ${IsTablet(` + width: 100%; + `)} +`; + +const CodeBlockContainer = styled.div` + width: 100%; + max-width: 1000px; + margin: 0 auto; + + @media only screen and (min-width: 992px) { + max-width: 1200px; + } +`; + +const Features = styled.ul.attrs({ + className: "text-2", +})` + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: flex-start; + grid-row: 3; + box-sizing: border-box; + margin: 0; + list-style-type: none; +`; + +const Feature = styled.li` + display: flex; + flex-direction: row; + align-items: center; + gap: 8px; + + ${IconContainer} > svg { + fill: ${THEME_COLORS.text}; + } +`;