@gatsby-mdx-suite/mdx-layout
Version:
281 lines (256 loc) • 8.33 kB
JavaScript
import React from 'react'
import propTypes from 'prop-types'
import styled from '@emotion/styled'
import { css } from '@emotion/react'
import tw from 'twin.macro'
import Image from 'gatsby-theme-mdx-suite-core/src/components/image'
import Video from '@gatsby-mdx-suite/mdx-video/video-renderer'
import ColorSet from '@gatsby-mdx-suite/mdx-color-set/color-set'
import centerToContentColumn from '@gatsby-mdx-suite/helpers/styling/center-to-content-column'
import convertToFlexAlignment from '@gatsby-mdx-suite/helpers/styling/convert-to-flex-alignment'
import selectColor from '@gatsby-mdx-suite/helpers/styling/select-color'
import debugMode from '@gatsby-mdx-suite/helpers/styling/debug-mode'
const BackgroundVideo = styled(Video)`
${tw`static`}
`
const SectionWrapper = styled.section(({ textShadow, theme, minHeight }) => {
return css`
${tw`relative`}
background: ${theme.colors.background};
color: ${theme.colors.text};
${minHeight &&
css`
min-height: ${minHeight};
`}
${textShadow &&
css`
text-shadow: 0 0 5px rgba(0, 0, 0, 0.13);
`}
${debugMode({ color: 'red', title: 'Section', type: 'border' })}
`
})
const SectionContentWrapper = styled.div(
({ minHeight, verticalAlign, horizontalAlign }) => {
return css`
${tw`relative`}
${(minHeight || horizontalAlign) && tw`flex flex-col h-full w-full`}
${minHeight &&
css`
min-height: ${minHeight};
justify-content: ${verticalAlign};
`}
${horizontalAlign &&
css`
align-items: ${horizontalAlign};
`}
`
}
)
const SectionContent = styled.div(
({ horizontalAlign, ...props }) => css`
${horizontalAlign
? tw`px-content-column-padding`
: centerToContentColumn(props)}
padding-top: ${calcGapValue(props.gap, props.theme)};
padding-bottom: ${calcGapValue(props.gap, props.theme)};
${debugMode({
color: 'tomato',
title: 'Section Content',
labelPosition: 'outside',
})}
`
)
function calcGapValue(gap, theme) {
const customSize = gap && theme.spacing[gap]
return customSize || theme.spacing['section-gap']
}
const BackgroundMediaWrapper = styled.div(
() => css`
${tw`absolute z-0 inset-0 w-full h-full`}
overflow: hidden;
& video {
${tw`absolute inset-0 z-10 object-cover object-center w-full h-full`}
}
`
)
const BackgroundImage = styled(Image)(({ backgroundImageOpacity }) => [
tw`absolute inset-0 block w-full h-full`,
backgroundImageOpacity !== '1' &&
css`
opacity: ${backgroundImageOpacity};
`,
])
const BackgroundMediaOverlay = styled.div(
({ overlayColor, overlayOpacity, theme }) => css`
${tw`absolute inset-0 z-20`}
background: ${selectColor(theme.colors, overlayColor)};
opacity: ${overlayOpacity};
`
)
/**
* Sections are content seperators and serve multiple purposes:
*
* * Ensures the given content is rendered within the content column
* * Adds a gap between multiple sections
* * They can be colored and supports background images
*
* @example <caption>Default</caption>
* <Section>
*
* # Doggy
*
* On a plain section, without any styling
*
* 
*
* </Section>
* @example <caption>Coloring</caption>
* <Section colorSet="teal">
*
* # Cats
*
* On a section with the color set `teal` applied.
*
* <Columns>
* <Image src="https://source.unsplash.com/400x300/weekly?cat" alt="Picture of a cat" />
* <Image src="https://source.unsplash.com/400x300/weekly?cat" alt="Picture of a cat" />
* </Columns>
* </Section>
* @example <caption>Custom colors</caption>
* <Section colors={{ background:"tomato", primary: "#48C9B0", secondary: "heading"}}>
*
* # Parrots
*
* On a section with custom background, primary and secondary color
*
* <Columns>
* <Image src="https://source.unsplash.com/400x300/weekly?parrot" alt="Picture of a parrot" />
* <Image src="https://source.unsplash.com/400x300/weekly?parrot" alt="Picture of a parrot" />
* </Columns>
* </Section>
*
* @example <caption>Background image</caption>
* <Section backgroundImageId="randomPictureId">
*
* # Ghosts
*
* On a section with a background image
*
* <Columns>
* <Image src="https://source.unsplash.com/400x300/weekly?ghost" alt="Picture of a ghost" />
* <Image src="https://source.unsplash.com/400x300/weekly?ghost" alt="Picture of a ghost" />
* </Columns>
* </Section>
*/
export default function Section({
children,
backgroundImageId,
backgroundImageOpacity,
colorSet,
colors,
textShadow,
gap,
minHeight,
verticalAlign,
horizontalAlign,
backgroundVideoId,
overlayOpacity,
overlayColor,
backgroundForceRendering,
...props
}) {
if (backgroundImageId && !colorSet) {
colorSet = 'background-image'
}
const hasBackgroundMedia = !!(backgroundImageId || backgroundVideoId)
return (
<ColorSet name={colorSet} {...colors}>
<SectionWrapper textShadow={textShadow} minHeight={minHeight} {...props}>
{hasBackgroundMedia && (
<BackgroundMediaWrapper>
{parseFloat(overlayOpacity) > 0 && (
<BackgroundMediaOverlay
overlayOpacity={overlayOpacity}
overlayColor={overlayColor}
/>
)}
{backgroundImageId && (
<BackgroundImage
backgroundImageOpacity={backgroundImageOpacity}
contextKey="screen"
id={backgroundImageId}
fit="cover"
/>
)}
{backgroundVideoId && (
<BackgroundVideo
autoPlay
loop
muted
controls={false}
id={backgroundVideoId}
forceRendering={backgroundForceRendering}
/>
)}
</BackgroundMediaWrapper>
)}
{children && (
<SectionContentWrapper
minHeight={minHeight}
verticalAlign={convertToFlexAlignment(verticalAlign)}
horizontalAlign={convertToFlexAlignment(horizontalAlign)}
>
<SectionContent
gap={gap}
horizontalAlign={convertToFlexAlignment(horizontalAlign)}
>
{children}
</SectionContent>
</SectionContentWrapper>
)}
</SectionWrapper>
</ColorSet>
)
}
Section.defaultProps = {
colorSet: null,
colors: {},
textShadow: false,
verticalAlign: 'center',
backgroundImageOpacity: '1',
overlayOpacity: 0,
overlayColor: 'black',
backgroundForceRendering: false,
}
Section.propTypes = {
children: propTypes.node,
/** Id the background image */
backgroundImageId: propTypes.string,
/** Set the opacity for the background image */
backgroundImageOpacity: propTypes.oneOfType([
propTypes.string,
propTypes.number,
]),
/** Id of the background video */
backgroundVideoId: propTypes.string,
/** Define a color set for this box */
colorSet: propTypes.string,
/** Set the opacity of the background media overlay */
overlayOpacity: propTypes.oneOfType([propTypes.string, propTypes.number]),
/** Set the color for the background media overlay */
overlayColor: propTypes.string,
/** Overwrite specific colors */
colors: propTypes.object,
/** Optional slight text shadow to increase readability when using background images */
textShadow: propTypes.bool,
/** Overwrite default vertical margin. See <Link to="/docs/style-guide/#sizes">theme documentation for available sizes</Link> */
gap: propTypes.string,
/** Set the minimum size for the section. Usually used with `100vh` to achieve full screen sizes. See <Link to="/docs/style-guide/#sizes">theme documentation for available sizes</Link> */
minHeight: propTypes.string,
/** Vertical alignment if the available space exceeds the content height */
verticalAlign: propTypes.oneOf(['start', 'center', 'end']),
/** Horizontal alignment. Will detach the content from the content column. */
horizontalAlign: propTypes.oneOf(['start', 'center', 'end']),
/** Force background media to be rendered. Set this to true when a section is likely to be rendered above the fold. */
backgroundForceRendering: propTypes.bool,
}