import * as React from 'react' import {FC} from 'react' import {BleedCrops} from '../../components/Prepress' import {AnimationBag} from '../../hooks/useAnimation' /** * @category Props */ export interface BodyProps { /** * An {@link AnimationBag} object returned from useAnimation. */ animation?: AnimationBag /** * This prop passes classNames to the extra element that provides the backdrop for the entire template. * @example * ```jsx * * this template has a **black** background colour. * * ``` */ backdropClassName?: string /** * A style object applied to the extra element that provides the backdrop for the entire template. It’s preferable to use the `backdropClassName` prop instead so that the colours relate to the brand’s preset colours, but `backdropStyle` is useful for when you need to add a custom property or arbitrary colour. * @example * ```jsx * * this template has a **red **background colour. * * ``` */ backdropStyle?: React.CSSProperties /** * Whether the bleed and crops should be rendered. Only used for print templates that have `Features.BLEED` added to their config’s `features` array. */ bleed?: boolean /** * Optionally provide a {@link Popup} component to be rendered in the body. This is useful for displaying a "terms and conditions" or "privacy policy" link, which when activated, popups up further details. */ popup?: React.ReactNode className?: string style?: React.CSSProperties } /** * @internal */ const defaultAnimationBag = { scripts: <>, bodyClassName: ``, } /** * * ## About * * As `body` and `html` elements are concerns that are handled external to a template, we rely on `Body` as * the root-most component of a template. * * The element that `Body` renders has an inline style of `display: contents` which causes it to have no affect on layout. * Its children behave like children of the closest parent to `Body` that does not also have `display: contents`, ie. the * native `body` element. As `body` is hardcoded to be `display: flex; flex-direction: column;`, this makes any `Body` * children flex items for styling purposes. Similarly, note also that passing `background-color:some-color` classes via * `Body`’s `className` prop will have no affect, as the `Body` element has no width or height. This is why that class * should instead be passed to the background layer proxy (which is a CenteredLayer component) via `backdropClassName`. * * One important reason we have `Body` outputting a wrapping element at all is to provide the opportunity to add global font styling classes that cascades to the rest of the template. * * You may also set a `style` attribute to provide global styles that cascade down to the children. This is particularly useful for setting global CSS custom properties as it makes them easy to retrieve for animation purposes. Ie. `{--offset-x: 100}` can be retrieved via `document.getElementById('Exoplanet').style.getPropertyValue('--offset-x')`. * * ## Properties * * | prop | value type | * | :---------------------- | :-------------------------- | * | **`className`** | `string` \| `undefined` | * | **`animation`** | result of useAnimation hook | * | **`backdropClassName`** | `string` \| `undefined` | * | **`backdropStyle`** | `object` \| `undefined` | * | **`bleed`** | `boolean` \| `undefined` | * | **`popup`** | `ReactNode` \| `undefined` | * | **`children`** | `ReactNode` \| `undefined` | * * ## Basic usage * * ```jsx * template JSX goes here * ``` * * ## With global classes and background colour * * Use `backdropClassName` to apply global `background-color` styling. These are applied to an overlay child layer (via the * CenteredLayer component). * * Use `backdropStyle` to apply inline styles to the backdrop. * * Use `className` to add other global styles. * * ```jsx * * template JSX goes here * * ` * ``` * * ## Bleed and crops * * Templates intended to produce print media outputs should be created with extra bleed. Passing the boolean `bleed` prop * will add the required slug area and crop marks. * * Note that you will need to manually add extra bleed columns and rows to surround your main grid layout in order to * control which elements should stretch into those areas. A nice conventions is to use the following grid area names for * the bleed rows and columns `—— ll ▍▍ ▂▂` with liberal space to better isolate the more unique parts of your template * grid. * * ```jsx * *
* * template JSX goes here * *
* * ` * ``` * * ## Passing in an animation * * When a template is animated, the object returned from `useAnimation` must be passed to the `Body` component via its * `animation` prop. This allows the animation script tags to be automatically added, along with other helpers. For * example, the `is-static` class is conditionally applied to the `Body`’s DOM element, enabling a templater to adjust * styling depending on whether an export type is animated or static. * * ```jsx * import useAnimation from '@/hooks/useAnimation' * import {Body} from '@myadbox/exoplanet-toolkit' * * const Template = (props) => { * // store the result of useAnimation and pass it to `Body` * const animation = useAnimation(props) * // `is-static` class is added to the DOM element of `Body` if either * // `props.cancelAnimation` or `props.content.cancelAnimation` is true. * // This enables `is-static__visibility:visible` to override `visibility:hidden` * // when static/non-animated output is requested. * return ( * *

* Initially hidden when output is animated *

* *

* Initially hidden when output is animated, but always visible when output is static. *

* * ) * } * ``` * * * `Body` is the root component of every template. * * @param props - an object of {@link BodyProps} * * @category Components * * @remarks * We don't have access to the real element, so this element provides similar functionality. The div itself will have no affect on layout (due to display:contents) so its children will become flex items because the hard-coded is set to be display:flex with flex-direction:column (vertically stacked children). Being flex items helps them stretch to fill the document height. * * You can use `Body` to add CSS styles that cascade down to its children via the `className` prop, or set the `style` prop. It also provides ways of customising the styles passed to its intrinsic elements, such as {@link BodyProps.backdropClassName} and {@link BodyProps.backdropStyle}. It also handles much of the setup to make animated templates work (see the {@link BodyProps.animation} prop) and renders bleed and crops when setting {@link BodyProps.bleed} to `true`. * * In addition to these custom props, you may set a `style` attribute to provide global styles that cascade down to the children. This is particularly useful for setting global CSS custom properties as it makes them easy to retrieve for animation purposes. Ie. `{--offset-x: 100}` can be retrieved via `document.getElementById('Exoplanet').style.getPropertyValue('--offset-x')`. */ export const Body: FC = ({ className = ``, style = {}, animation = defaultAnimationBag, backdropClassName = ``, backdropStyle = {backgroundColor: `white`}, bleed = false, popup = null, children, ...props }) => { const Wrapper = bleed ? BleedCrops : React.Fragment return ( <>
{children} {popup} {/* position:absolute bottom layer as replacement for body background colour */}
{animation.scripts} {/* Template service adds a clickTag overlay here */} {/* */} ) } export default Body