codevise/pageflow

View on GitHub
entry_types/scrolled/package/src/frontend/layouts/Center.js

Summary

Maintainability
A
1 hr
Test Coverage
import React from 'react';
import classNames from 'classnames';

import {api} from '../api';
import {ContentElements} from '../ContentElements';

import {widths, widthName} from './widths';

import styles from './Center.module.css';

const floatedPositions = ['left', 'right'];

export function Center(props) {

  return (
    <div className={classNames(styles.root)}>
      <div ref={props.contentAreaRef} />
      {props.items.map((item, index) => {
        const customMargin = hasCustomMargin(item);
        const position = item.position;
        const width = widthName(getWidth(item));

        return (
          <ContentElements key={item.id} sectionProps={props.sectionProps} items={[item]} customMargin={customMargin}>
            {(item, child) =>
              <div key={item.id} className={outerClassName(props.items, index)}>
                <div className={classNames(styles.item,
                                           styles[`item-${position}-${width}`])}>
                  {props.children(
                    <div className={classNames(styles[`inner-${item.position}`],
                                               styles[`inner-${width}`],
                                               {[styles[`sideBySide`]]: sideBySideFloat(props.items, index)})}>
                      {child}
                    </div>,
                    boxProps(props.items, item, index)
                  )}
                </div>
              </div>
            }
          </ContentElements>
        );
      })}
      {renderPlaceholder(props.placeholder)}
    </div>
  );
}

function outerClassName(items, index) {
  const item = items[index];

  return classNames(
    styles.outer,
    styles[`outer-${widthName(getWidth(item))}`],
    {[styles.customMargin]: hasCustomMargin(item)},
    {[styles.clear]: clearItem(items, index)}
  );
}

function boxProps(items, item, index) {
  const previous = items[index - 1];
  const next = items[index + 1];
  const customMargin = hasCustomMargin(item);
  const width = getWidth(item);

  return {
    position: item.position,
    width,
    customMargin,
    selfClear: selfClear(items, index),
    openStart: previous &&
               !customMargin &&
               !hasCustomMargin(previous) &&
               !isWideOrFull(item) && !isWideOrFull(previous),
    openEnd: next &&
             !customMargin &&
             !hasCustomMargin(next) &&
             !isWideOrFull(item) && !isWideOrFull(next)
  }
}

function isWideOrFull(item) {
  return item.position === 'inline' && getWidth(item) > widths.md;
}

function selfClear(items, index) {
  const item = items[index];
  const next = items[index + 1];

  if (supportsWrappingAroundFloats(item) ||
      (isFloated(item) && (!next || clearItem(items, index + 1)))) {
    return 'both';
  }
  else if (isFloated(item)) {
    return item.position === 'left' ? 'right' : 'left';
  }

  return 'none';
}

function clearItem(items, index) {
  return supportsWrappingAroundFloats(items[index]) ?
         followsSideBySideElements(items, index) :
         !isFloatedFollowingOppositeFloated(items, index)
}

function followsSideBySideElements(items, index) {
  return index > 1 && (
    (items[index - 1].position === 'left' && items[index - 2].position === 'right') ||
    (items[index - 1].position === 'right' && items[index - 2].position === 'left')
  );
}

function sideBySideFloat(items, index) {
  return isFloatedFollowingOppositeFloated(items, index) ||
         isFloatedFollowedByOppositeFloated(items, index);
}

function isFloatedFollowingOppositeFloated(items, index) {
  return index > 0 &&
         isFloated(items[index]) &&
         isFloated(items[index - 1]) &&
         items[index].position !== items[index - 1].position;
}

function isFloatedFollowedByOppositeFloated(items, index) {
  return index < items.length - 1 &&
         isFloated(items[index]) &&
         isFloated(items[index + 1]) &&
         items[index].position !== items[index + 1].position;
}

function isFloated(item) {
  return floatedPositions.includes(item.position);
}

function supportsWrappingAroundFloats(item) {
  const {supportsWrappingAroundFloats} = api.contentElementTypes.getOptions(item.type);
  return supportsWrappingAroundFloats;
}

function hasCustomMargin(item) {
  const position = item.position;
  const {customMargin: elementSupportsCustomMargin} =
    api.contentElementTypes.getOptions(item.type) || {};
  return !!(elementSupportsCustomMargin &&
            position === 'inline' &&
            getWidth(item) < widths.full);
}

function getWidth(item) {
  return item.width || widths.md;
}

function renderPlaceholder(placeholder) {
  if (!placeholder) {
    return null;
  }

  return (
    <div className={classNames(styles.outer)}>
      <div className={classNames(styles.item)}>
        {placeholder}
      </div>
    </div>
  )
}