From b3293625a49a3f1272e881b2785effdb00ab8724 Mon Sep 17 00:00:00 2001 From: deme Date: Mon, 17 Apr 2023 18:54:10 -0300 Subject: [PATCH 1/2] [#754,landing page][xl]: added hero section and feature section, enabled dark mode, updated navbar, enabled toc --- site/components/BaseLink.tsx | 15 + site/components/Features.tsx | 68 + site/components/Hero.tsx | 195 +++ site/components/Layout.tsx | 120 +- site/components/MDXPage.tsx | 4 +- site/components/MobileNavigation.tsx | 165 +++ site/components/Nav.js | 96 -- site/components/Nav.tsx | 136 ++ site/components/NavItem.tsx | 80 ++ site/components/ThemeSelector.tsx | 37 + site/content/config.js | 14 +- site/package-lock.json | 1159 ++++++--------- site/package.json | 11 +- site/pages/[...slug].tsx | 48 +- site/pages/index.tsx | 72 +- site/public/images/datopian_logo.png | Bin 0 -> 42343 bytes .../public/images/icon-batteries-included.svg | 492 +++++++ site/public/images/icon-dev-friendly.svg | 1264 +++++++++++++++++ site/public/images/icon-easy-to-theme.svg | 547 +++++++ site/public/images/icon-extensible.svg | 175 +++ site/public/images/icon-unified-sites.svg | 254 ++++ site/public/images/icon-well-documented.svg | 429 ++++++ site/public/images/theme-button.svg | 7 + site/styles/globals.css | 1 + site/styles/prism.css | 47 + site/tailwind.config.js | 28 +- 26 files changed, 4556 insertions(+), 908 deletions(-) create mode 100644 site/components/BaseLink.tsx create mode 100644 site/components/Features.tsx create mode 100644 site/components/Hero.tsx create mode 100644 site/components/MobileNavigation.tsx delete mode 100644 site/components/Nav.js create mode 100644 site/components/Nav.tsx create mode 100644 site/components/NavItem.tsx create mode 100644 site/components/ThemeSelector.tsx create mode 100644 site/public/images/datopian_logo.png create mode 100644 site/public/images/icon-batteries-included.svg create mode 100644 site/public/images/icon-dev-friendly.svg create mode 100644 site/public/images/icon-easy-to-theme.svg create mode 100644 site/public/images/icon-extensible.svg create mode 100644 site/public/images/icon-unified-sites.svg create mode 100644 site/public/images/icon-well-documented.svg create mode 100644 site/public/images/theme-button.svg create mode 100644 site/styles/prism.css diff --git a/site/components/BaseLink.tsx b/site/components/BaseLink.tsx new file mode 100644 index 00000000..3fa39c91 --- /dev/null +++ b/site/components/BaseLink.tsx @@ -0,0 +1,15 @@ +import Link from "next/link"; +import { forwardRef } from "react"; + +const BaseLink = forwardRef((props: any, ref) => { + const { href, children, ...rest } = props; + return ( + + {children} + + ); +}); + +BaseLink.displayName = "BaseLink"; + +export default BaseLink; diff --git a/site/components/Features.tsx b/site/components/Features.tsx new file mode 100644 index 00000000..c0c58f73 --- /dev/null +++ b/site/components/Features.tsx @@ -0,0 +1,68 @@ +const features: { title: string; description: string; icon: string }[] = [ + { + title: 'Unified sites', + description: + 'Present data and content in one seamless site, pulling datasets from a DMS (e.g. CKAN) and content from a CMS (e.g. wordpress) with a common internal API', + icon: '/images/icon-unified-sites.svg', + }, + { + title: 'Developer friendly', + description: 'Built with familiar frontend tech Javascript, React etc', + icon: '/images/icon-dev-friendly.svg', + }, + { + title: 'Batteries included', + description: + 'Full set of portal components out of the box e.g. catalog search, dataset showcase, blog etc.', + icon: '/images/icon-batteries-included.svg', + }, + { + title: 'Easy to theme and customize', + description: + 'installable themes, use standard CSS and React+CSS tooling. Add new routes quickly.', + icon: '/images/icon-easy-to-theme.svg', + }, + { + title: 'Extensible', + description: 'quickly extend and develop/import your own React components', + icon: '/images/icon-extensible.svg', + }, + { + title: 'Well documented', + description: + 'full set of documentation plus the documentation of NextJS and Apollo.', + icon: '/images/icon-well-documented.svg', + }, +]; + +export default function Features() { + return ( +
+

How Portal.JS works?

+

+ Portal.JS is built in JavaScript and React on top of the popular Next.js + framework, assuming a "decoupled" approach where the frontend is a + separate service from the backend and interacts with backend(s) via an + API. It can be used with any backend and has out of the box support for + CKAN. +

+
+ {features.map((feature) => ( +
+
+
+ +

+ + {feature.title} +

+

+ {feature.description} +

+
+
+ ))} +
+
+ ); +} diff --git a/site/components/Hero.tsx b/site/components/Hero.tsx new file mode 100644 index 00000000..f03cdb30 --- /dev/null +++ b/site/components/Hero.tsx @@ -0,0 +1,195 @@ +import clsx from 'clsx'; +import Highlight, { defaultProps } from 'prism-react-renderer'; +import { Fragment, useRef } from 'react'; + +const codeLanguage = 'javascript'; +const code = `export default { + strategy: 'predictive', + engine: { + cpus: 12, + backups: ['./storage/cache.wtf'], + }, +}`; + +const tabs = [ + { name: 'cache-advance.config.js', isActive: true }, + { name: 'package.json', isActive: false }, +]; + +function TrafficLightsIcon(props) { + return ( + + ); +} + +/* eslint jsx-a11y/label-has-associated-control: off */ +export function Hero() { + const el = useRef(null); + + return ( +
+
+ {/* Commented code on line 37, 39 and 113 will reenable the two columns hero */} + {/*
*/} +
+ {/*
*/} +
+
+

+ The JavaScript framework for data portals +

+
+

+ Portal.JS is a framework for rapidly building rich data portal + frontends using a modern frontend approach. It can be used to + present a single dataset or build a full-scale data + catalog/portal. +

+
+

+ Sign up to get notified about updates +

+
+ + + + + + +
+ {/*

+ We are actively trialling and developing Flowershow. If you'd + like to get notified about our progress and important updates, + please sign up. +

*/} +
+

+ A project of + + Datopian + Datopian + +

+
+ {/*
+
+
+
+
+ +
+ {tabs.map((tab) => ( +
+
+ {tab.name} +
+
+ ))} +
+
+ + + {({ + className, + style, + tokens, + getLineProps, + getTokenProps, + }) => ( +
+                        
+                          {tokens.map((line, lineIndex) => (
+                            
+ {line.map((token, tokenIndex) => ( + + ))} +
+ ))} +
+
+ )} +
+
+
+
+
*/} +
+
+
+ ); +} diff --git a/site/components/Layout.tsx b/site/components/Layout.tsx index 626cd44a..836737ab 100644 --- a/site/components/Layout.tsx +++ b/site/components/Layout.tsx @@ -1,27 +1,91 @@ -import { NextSeo } from "next-seo"; +import { siteConfig } from '@/config/siteConfig'; +import { NextSeo } from 'next-seo'; +import Link from 'next/link'; +import { useCallback, useEffect, useState } from 'react'; -import Nav from "./Nav"; +import Nav from './Nav'; + +function useTableOfContents(tableOfContents) { + const [currentSection, setCurrentSection] = useState(tableOfContents[0]?.id); + + const getHeadings = useCallback((toc) => { + return toc + .flatMap((node) => [node.id, ...node.children.map((child) => child.id)]) + .map((id) => { + const el = document.getElementById(id); + if (!el) return null; + + const style = window.getComputedStyle(el); + const scrollMt = parseFloat(style.scrollMarginTop); + + const top = window.scrollY + el.getBoundingClientRect().top - scrollMt; + return { id, top }; + }) + .filter((el) => !!el); + }, []); + + useEffect(() => { + if (tableOfContents.length === 0) return; + const headings = getHeadings(tableOfContents); + function onScroll() { + const top = window.scrollY + 4.5; + let current = headings[0].id; + headings.forEach((heading) => { + if (top >= heading.top) { + current = heading.id; + } + return current; + }); + setCurrentSection(current); + } + window.addEventListener('scroll', onScroll, { passive: true }); + onScroll(); + return () => { + window.removeEventListener('scroll', onScroll); + }; + }, [getHeadings, tableOfContents]); + + return currentSection; +} export default function Layout({ children, title, + tableOfContents = [], }: { children; title?: string; + tableOfContents?; }) { + const { toc } = children.props; + + const currentSection = useTableOfContents(tableOfContents); + + function isActive(section) { + if (section.id === currentSection) { + return true; + } + if (!section.children) { + return false; + } + return section.children.findIndex(isActive) > -1; + } + return ( <> {title && }