diff --git a/.changeset/tidy-mails-sneeze.md b/.changeset/tidy-mails-sneeze.md new file mode 100644 index 0000000000..518ef68de4 --- /dev/null +++ b/.changeset/tidy-mails-sneeze.md @@ -0,0 +1,5 @@ +--- +"docs": patch +--- + +feat(docs): Add dynamic navigation (based on mdx content) diff --git a/docs/content/components/menuItem.mdx b/docs/content/components/menu-item.mdx similarity index 100% rename from docs/content/components/menuItem.mdx rename to docs/content/components/menu-item.mdx diff --git a/docs/content/components/validationMessage.mdx b/docs/content/components/validation-message.mdx similarity index 100% rename from docs/content/components/validationMessage.mdx rename to docs/content/components/validation-message.mdx diff --git a/docs/content/foundation/design-tokens/colors.mdx b/docs/content/foundation/colors.mdx similarity index 100% rename from docs/content/foundation/design-tokens/colors.mdx rename to docs/content/foundation/colors.mdx diff --git a/docs/content/foundation/design-tokens/icons.mdx b/docs/content/foundation/icons.mdx similarity index 100% rename from docs/content/foundation/design-tokens/icons.mdx rename to docs/content/foundation/icons.mdx diff --git a/docs/content/foundation/design-language/theming.mdx b/docs/content/foundation/theming.mdx similarity index 100% rename from docs/content/foundation/design-language/theming.mdx rename to docs/content/foundation/theming.mdx diff --git a/docs/gatsby-config.js b/docs/gatsby-config.js index b1aeb35d3b..ed0c28ae68 100644 --- a/docs/gatsby-config.js +++ b/docs/gatsby-config.js @@ -3,8 +3,10 @@ module.exports = { siteMetadata: { title: 'Docs for Marigold Design System', siteUrl: 'https://marigold-ui.github.io/marigold/', + navigation: ['foundation', 'components', 'themes'], }, plugins: [ + 'gatsby-plugin-image', 'gatsby-plugin-sharp', 'gatsby-transformer-sharp', 'gatsby-plugin-react-helmet', diff --git a/docs/package.json b/docs/package.json index 120663e423..968d9abb69 100644 --- a/docs/package.json +++ b/docs/package.json @@ -15,6 +15,7 @@ "babel-preset-gatsby": "1.8.0", "gatsby": "3.8.0", "gatsby-plugin-emotion": "6.8.0", + "gatsby-plugin-image": "^1.7.1", "gatsby-plugin-manifest": "3.8.0", "gatsby-plugin-mdx": "2.8.0", "gatsby-plugin-react-helmet": "4.8.0", diff --git a/docs/src/components/Layout.tsx b/docs/src/components/Layout.tsx index 504f8db2a6..692920b1e8 100644 --- a/docs/src/components/Layout.tsx +++ b/docs/src/components/Layout.tsx @@ -1,10 +1,11 @@ import React from 'react'; import { Helmet } from 'react-helmet'; -import { Column, Columns } from '@marigold/components'; +import { Column, Columns, Stack } from '@marigold/components'; -import { MarigoldThemeSwitch } from './ThemeSwitch'; -import { marigoldThemes, ThemeSelect } from './ThemeSelect'; +import { Link } from './Link'; +import { Logo } from './Logo'; import { Navigation } from './Navigation'; +import { ThemeSelect } from './ThemeSelect'; export const Layout: React.FC = ({ children }) => { return ( @@ -12,22 +13,22 @@ export const Layout: React.FC = ({ children }) => { - Marigold Design System - + - - - - + + + + - {children} - + + + {children} ); diff --git a/docs/src/components/Link.tsx b/docs/src/components/Link.tsx new file mode 100644 index 0000000000..ee2b8725ab --- /dev/null +++ b/docs/src/components/Link.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { Link as GatsbyLink, GatsbyLinkProps } from 'gatsby'; +import { Link as MarigoldLink } from '@marigold/components'; + +export const Link: React.FC> = ({ + children, + ...props +}) => ( + + {children} + +); diff --git a/docs/src/components/Logo/Logo.tsx b/docs/src/components/Logo/Logo.tsx new file mode 100644 index 0000000000..15307f8125 --- /dev/null +++ b/docs/src/components/Logo/Logo.tsx @@ -0,0 +1,6 @@ +import React from 'react'; +import { StaticImage } from 'gatsby-plugin-image'; + +export const Logo: React.FC = () => ( + +); diff --git a/docs/src/components/Logo/index.ts b/docs/src/components/Logo/index.ts new file mode 100644 index 0000000000..d97c6951e2 --- /dev/null +++ b/docs/src/components/Logo/index.ts @@ -0,0 +1 @@ +export * from './Logo'; diff --git a/docs/src/components/Logo/logo.png b/docs/src/components/Logo/logo.png new file mode 100644 index 0000000000..4ad76a28b7 Binary files /dev/null and b/docs/src/components/Logo/logo.png differ diff --git a/docs/src/components/Navigation.tsx b/docs/src/components/Navigation.tsx deleted file mode 100644 index f4a13bb289..0000000000 --- a/docs/src/components/Navigation.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import React from 'react'; -import Link from 'gatsby-link'; -import { Text } from '@marigold/components'; - -export const Navigation: React.FC = () => { - return ( - - Marigold -
    -
  • - Guides -
      -
    • Installation
    • -
    • Theme & Variants
    • -
    -
  • -
  • - Foundation -
      -
    • - Theming -
    • -
    • Layout
    • -
    • Box Primitive
    • -
    • - Iconography -
    • -
    -
  • -
  • - Components -
      -
    • - Alert -
    • -
    • - Badge -
    • -
    • - Box -
    • -
    • - Button -
    • -
    • - Column -
    • -
    • - Columns -
    • -
    • - Checkbox -
    • -
    • - Container -
    • -
    • - Dialog -
    • -
    • - Divider -
    • -
    • - Field -
    • -
    • - Heading -
    • -
    • - Hidden -
    • -
    • - Input -
    • -
    • - Label -
    • -
    • - Link -
    • -
    • - Menu -
    • -
    • - MenuItem -
    • -
    • - Message -
    • -
    • - Radio -
    • -
    • - Select -
    • -
    • - Slider -
    • -
    • - Stack -
    • -
    • - Text -
    • -
    • - Textarea -
    • -
    • - ValidationMessage -
    • -
    -
  • -
  • - Themes -
      -
    • - B2B -
    • -
    • - Unicorn -
    • -
    • Marigold Docs
    • -
    -
  • -
-
- ); -}; diff --git a/docs/src/components/Navigation/Navigation.tsx b/docs/src/components/Navigation/Navigation.tsx new file mode 100644 index 0000000000..348bd6ed6d --- /dev/null +++ b/docs/src/components/Navigation/Navigation.tsx @@ -0,0 +1,63 @@ +import React from 'react'; +import { Box } from '@marigold/components'; + +import { Link } from '../Link'; +import { NavigationItem, NavigationTree, useNavigation } from './useNavigation'; + +type NavigationSectionProps = { + name: string; + children: NavigationTree; +}; + +// Helper +// --------------- +const dirToText = (dir: string) => + dir + .split('/')[0] + .split('-') + .map(word => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); + +// Components +// --------------- +const NavigationItemComponent = ({ title, slug }: NavigationItem) => ( + + {title} + +); + +const NavigationSection = ({ name, children }: NavigationSectionProps) => { + return ( +
+ {Boolean(name.length) && ( + + {dirToText(name)} + + )} + + {children.map(child => + 'title' in child ? ( + + ) : ( + + ) + )} + +
+ ); +}; + +export const Navigation: React.FC = () => { + const tree = useNavigation(); + console.log(tree); + + return ( + + + + ); +}; diff --git a/docs/src/components/Navigation/index.ts b/docs/src/components/Navigation/index.ts new file mode 100644 index 0000000000..489deb7c42 --- /dev/null +++ b/docs/src/components/Navigation/index.ts @@ -0,0 +1 @@ +export * from './Navigation'; \ No newline at end of file diff --git a/docs/src/components/Navigation/useNavigation.ts b/docs/src/components/Navigation/useNavigation.ts new file mode 100644 index 0000000000..3333b36626 --- /dev/null +++ b/docs/src/components/Navigation/useNavigation.ts @@ -0,0 +1,124 @@ +import { graphql, useStaticQuery } from 'gatsby'; + +// Types +// --------------- +type NavigationNode = { + slug: string; + frontmatter: { + title?: string; + }; + headings: { value: string }[]; +}; + +type NavigationData = NavigationNode & { + path: string[]; +}; + +export type NavigationItem = { title: string; slug: string }; +export type NavigationEdge = { + name: string; + children: NavigationTree; +}; +export type NavigationTree = (NavigationEdge | NavigationItem)[]; + +// Helper +// --------------- + +// Stolen from: https://gist.github.com/nblackburn/875e6ff75bc8ce171c758bf75f304707 +const camelToKebabCase = (val: string) => + val.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase(); + +const createNavigation = ( + tree: NavigationTree, + data: NavigationData +): NavigationTree => { + const [name, ...rest] = data.path; + + // Can't go down further, create an item (a.k.a leave) + if (!rest.length) { + tree.push({ + title: data.frontmatter.title || data.headings[0].value, + slug: camelToKebabCase(data.slug), + }); + return tree; + } + + // Does the edge already exist? + let edge = tree.find(l => 'name' in l && l.name === name) as + | NavigationEdge + | undefined; + // If not create an empty edge and add it to the tree + if (!edge) { + edge = { + name, + children: [], + }; + tree.push(edge); + } + + // Create subtree with rest of the path + return createNavigation(edge.children, { + ...data, + path: rest, + }); +}; + +const sortNavigation = (tree: NavigationTree, order: string[]) => + tree.sort((a, b) => { + // Get value to compare to + const left = 'slug' in a ? a.slug : a.name; + const leftIndex = order.indexOf(left); + + const right = 'slug' in b ? b.slug : b.name; + const rightIndex = order.indexOf(right); + /** + * Fallback: If none is present in the order array + * -> sort alphabetically (localized to ignore case and such) + */ + if (leftIndex === -1 && rightIndex === -1) { + return left.localeCompare(right); + } + + // Use array index to sort. + return leftIndex - rightIndex; + }); + +// Hook +// --------------- +export const useNavigation = () => { + // Get all MDX pages + const { + allMdx: { nodes }, + site, + } = useStaticQuery(graphql` + query NavigationQuery { + allMdx(sort: { order: ASC, fields: slug }) { + nodes { + slug + frontmatter { + title + } + headings(depth: h1) { + value + } + } + } + site { + siteMetadata { + navigation + } + } + } + `) as { + allMdx: { nodes: NavigationNode[] }; + site: { siteMetadata: { navigation: string[] } }; + }; + + // Create tree structure from nodes + const tree: NavigationTree = []; + nodes + .map(n => ({ path: n.slug.split('/'), ...n })) + .forEach(d => createNavigation(tree, d)); + + return sortNavigation(tree, site.siteMetadata.navigation); +}; diff --git a/docs/src/components/ThemeSelect.tsx b/docs/src/components/ThemeSelect.tsx index 2b8fb124d0..d93492920b 100644 --- a/docs/src/components/ThemeSelect.tsx +++ b/docs/src/components/ThemeSelect.tsx @@ -1,13 +1,6 @@ import * as React from 'react'; import { useThemeSwitch } from './ThemeSwitch'; import { Box, SelectProps } from '@marigold/components'; -import unicornTheme from '@marigold/theme-unicorn'; -import b2bTheme from '@marigold/theme-b2b'; - -export const marigoldThemes = { - b2bTheme, - unicornTheme, -}; export const ThemeSelect: React.FC = () => { const { current, themes, setTheme } = useThemeSwitch(); diff --git a/docs/src/components/ThemeSwitch.tsx b/docs/src/components/ThemeSwitch.tsx index 1343b13277..6aa86403ff 100644 --- a/docs/src/components/ThemeSwitch.tsx +++ b/docs/src/components/ThemeSwitch.tsx @@ -1,6 +1,14 @@ import React, { useState, useContext } from 'react'; import { BaseTheme } from '@marigold/components'; +import unicornTheme from '@marigold/theme-unicorn'; +import b2bTheme from '@marigold/theme-b2b'; + +export const themes = { + b2bTheme, + unicornTheme, +}; + // Context // --------------- export type ThemeSwitchContextType = { diff --git a/docs/src/theme/components.ts b/docs/src/theme/components.ts index 7c4b442d25..8e696f2cd8 100644 --- a/docs/src/theme/components.ts +++ b/docs/src/theme/components.ts @@ -1,5 +1,3 @@ -import { colors } from './colors'; - const button = { primary: { appearance: 'none', @@ -27,6 +25,42 @@ const button = { }, }; +const select = { + themeSwitch: { + fontFamily: 'body', + fontSize: 'body', + color: 'gray.00', + bg: 'gray.90', + p: 'xsmall', + mt: 'xxsmall', + mr: 'small', + border: 'none', + borderRadius: 'medium', + cursor: 'pointer', + }, +}; + +const navigation = { + wrapper: { + fontFamily: 'body', + }, + header: { + textTransform: 'uppercase', + color: 'gray.80', + fontSize: 'xxxsmall', + fontWeight: 'bold', + letterSpacing: '0.1em', + pt: 'large', + pb: 'small', + }, + item: { + fontSize: 'small', + fontWeight: 'medium', + lineHeight: 'cap', + pb: 'small', + }, +}; + export const components = { button: { primary: { @@ -61,19 +95,6 @@ export const components = { bg: 'transparent', }, }, - select: { - themeSwitch: { - fontFamily: 'body', - fontSize: 'body', - color: colors.gray['00'], - background: colors.gray['90'], - right: 'none', - padding: 'xsmall', - marginTop: 'xxsmall', - marginRight: 'small', - border: 'none', - position: 'absolute', - cursor: 'pointer', - }, - }, + navigation, + select, }; diff --git a/docs/src/theme/typography.ts b/docs/src/theme/typography.ts index b34b956363..87ece263b6 100644 --- a/docs/src/theme/typography.ts +++ b/docs/src/theme/typography.ts @@ -15,6 +15,7 @@ export const typography = { }, fontSizes: { body: '1rem', + xxxsmall: '0.75rem', xxsmall: '0.875rem', xsmall: '1rem', small: '1.125rem', @@ -25,6 +26,7 @@ export const typography = { fontWeights: { body: 400, heading: 900, + medium: 600, bold: 700, }, lineHeights: { @@ -82,8 +84,9 @@ export const typography = { }, link: { color: 'primary', - ':hover': { - textDecoration: 'none', + textDecoration: 'none', + '&:hover': { + textDecoration: 'underline', }, }, }, diff --git a/docs/src/wrapper.tsx b/docs/src/wrapper.tsx index ee6ed0dda0..f3fb32a386 100644 --- a/docs/src/wrapper.tsx +++ b/docs/src/wrapper.tsx @@ -6,6 +6,7 @@ import { ThemeProvider } from '@marigold/system'; import { theme } from './theme'; import { Layout } from './components/Layout'; +import { MarigoldThemeSwitch, themes } from './components/ThemeSwitch'; import * as mdxComponents from './mdx'; export const WrapPageElement: GatsbyBrowser['wrapPageElement'] = ({ @@ -17,7 +18,9 @@ export const WrapRootElement: GatsbyBrowser['wrapRootElement'] = ({ element, }) => ( - {element}; + + {element} + ); diff --git a/yarn.lock b/yarn.lock index 6f6335c66d..bcda71e151 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9445,6 +9445,13 @@ __metadata: languageName: node linkType: hard +"babel-jsx-utils@npm:^1.1.0": + version: 1.1.0 + resolution: "babel-jsx-utils@npm:1.1.0" + checksum: 0adb20364fca5fb8b2bd77f959a3118011d1b27d6b73912ffa7634637ad6b297baa9400e4d37a00cc190ac33e56f806294bf483cd766dc8104f465c2eaee8116 + languageName: node + linkType: hard + "babel-loader@npm:8.2.2, babel-loader@npm:^8.2.2": version: 8.2.2 resolution: "babel-loader@npm:8.2.2" @@ -9714,7 +9721,7 @@ __metadata: languageName: node linkType: hard -"babel-plugin-remove-graphql-queries@npm:^3.8.0": +"babel-plugin-remove-graphql-queries@npm:^3.7.1, babel-plugin-remove-graphql-queries@npm:^3.8.0": version: 3.8.0 resolution: "babel-plugin-remove-graphql-queries@npm:3.8.0" peerDependencies: @@ -13076,6 +13083,7 @@ __metadata: babel-preset-gatsby: 1.8.0 gatsby: 3.8.0 gatsby-plugin-emotion: 6.8.0 + gatsby-plugin-image: ^1.7.1 gatsby-plugin-manifest: 3.8.0 gatsby-plugin-mdx: 2.8.0 gatsby-plugin-react-helmet: 4.8.0 @@ -15955,7 +15963,7 @@ fsevents@^1.2.7: languageName: node linkType: hard -"gatsby-core-utils@npm:^2.8.0": +"gatsby-core-utils@npm:^2.7.1, gatsby-core-utils@npm:^2.8.0": version: 2.8.0 resolution: "gatsby-core-utils@npm:2.8.0" dependencies: @@ -16034,6 +16042,33 @@ fsevents@^1.2.7: languageName: node linkType: hard +"gatsby-plugin-image@npm:^1.7.1": + version: 1.7.1 + resolution: "gatsby-plugin-image@npm:1.7.1" + dependencies: + "@babel/code-frame": ^7.14.0 + "@babel/parser": ^7.14.0 + "@babel/traverse": ^7.14.0 + babel-jsx-utils: ^1.1.0 + babel-plugin-remove-graphql-queries: ^3.7.1 + camelcase: ^5.3.1 + chokidar: ^3.5.1 + common-tags: ^1.8.0 + fs-extra: ^8.1.0 + gatsby-core-utils: ^2.7.1 + objectFitPolyfill: ^2.3.0 + prop-types: ^15.7.2 + peerDependencies: + "@babel/core": ^7.12.3 + gatsby: ^3.0.0-next.0 + gatsby-plugin-sharp: ^3.0.0-next.0 + gatsby-source-filesystem: ^3.0.0-next.0 + react: ^16.9.0 || ^17.0.0 + react-dom: ^16.9.0 || ^17.0.0 + checksum: e250ebab168b21818f2adbd46bcbd73c5551aabf1d196e303e89d35c9084d02f7ed95242f525e341d44855abed56a54a111861e8852cf3696558cce7dd705006 + languageName: node + linkType: hard + "gatsby-plugin-manifest@npm:3.8.0": version: 3.8.0 resolution: "gatsby-plugin-manifest@npm:3.8.0" @@ -22506,6 +22541,13 @@ fsevents@^1.2.7: languageName: node linkType: hard +"objectFitPolyfill@npm:^2.3.0": + version: 2.3.5 + resolution: "objectFitPolyfill@npm:2.3.5" + checksum: b094a17ddf194186527ec805336f023ad3a32f532c3b7139f72c25cce0a5c7beb5d78ff95e8f9776e9adfe5a080782daed56d14e7182d6fbf4d59ce631681658 + languageName: node + linkType: hard + "objectorarray@npm:^1.0.4": version: 1.0.4 resolution: "objectorarray@npm:1.0.4"