@spark-web/box
Version:
--- title: Box storybookPath: page-layout-box--default isExperimentalPackage: false ---
335 lines (316 loc) • 12.5 kB
JavaScript
import { useTheme } from '@spark-web/theme';
import { createContext, useContext } from 'react';
import { jsx } from 'react/jsx-runtime';
import _objectSpread from '@babel/runtime/helpers/esm/objectSpread2';
import _objectWithoutProperties from '@babel/runtime/helpers/esm/objectWithoutProperties';
import { cx, css } from '@emotion/css';
import { resetElementStyles, buildDataAttributes } from '@spark-web/utils/internal';
import { forwardRefWithAs } from '@spark-web/utils/ts';
// prepare context
var backgroundContext = /*#__PURE__*/createContext('body');
var InternalBackgroundProvider = backgroundContext.Provider;
var useBackground = function useBackground() {
return useContext(backgroundContext);
};
// conditional provider
function renderBackgroundProvider(background, element) {
return background ? /*#__PURE__*/jsx(InternalBackgroundProvider, {
value: background,
children: element
}) : element;
}
// a11y contrast utility
var useBackgroundLightness = function useBackgroundLightness(backgroundOverride) {
var backgroundFromContext = useBackground();
var background = backgroundOverride || backgroundFromContext;
var theme = useTheme();
var defaultLightness = theme.backgroundLightness.body;
// used by the consumer-facing/external BackgroundProvider
if (background === 'UNKNOWN_DARK') {
return 'dark';
}
if (background === 'UNKNOWN_LIGHT') {
return 'light';
}
return background ? theme.backgroundLightness[background] || defaultLightness : defaultLightness;
};
/** Enforce background "lightness" without applying a background color. */
var BackgroundProvider = function BackgroundProvider(_ref) {
var type = _ref.type,
children = _ref.children;
return renderBackgroundProvider(type === 'dark' ? 'UNKNOWN_DARK' : 'UNKNOWN_LIGHT', children);
};
// TODO perf review
// TODO: review responsive props! Now that we're using object syntax, un-mapped properties don't behave as expected
// types
// Hook
// ------------------------------
var useBoxStyles = function useBoxStyles(_ref) {
var alignItems = _ref.alignItems,
alignSelf = _ref.alignSelf,
background = _ref.background,
border = _ref.border,
borderTop = _ref.borderTop,
borderBottom = _ref.borderBottom,
borderLeft = _ref.borderLeft,
borderRight = _ref.borderRight,
borderRadius = _ref.borderRadius,
_ref$borderWidth = _ref.borderWidth,
borderWidth = _ref$borderWidth === void 0 ? 'standard' : _ref$borderWidth,
bottom = _ref.bottom,
cursor = _ref.cursor,
display = _ref.display,
flex = _ref.flex,
flexDirection = _ref.flexDirection,
flexGrow = _ref.flexGrow,
flexShrink = _ref.flexShrink,
flexWrap = _ref.flexWrap,
gap = _ref.gap,
height = _ref.height,
justifyContent = _ref.justifyContent,
left = _ref.left,
margin = _ref.margin,
marginBottom = _ref.marginBottom,
marginLeft = _ref.marginLeft,
marginRight = _ref.marginRight,
marginTop = _ref.marginTop,
marginX = _ref.marginX,
marginY = _ref.marginY,
minHeight = _ref.minHeight,
minWidth = _ref.minWidth,
opacity = _ref.opacity,
overflow = _ref.overflow,
padding = _ref.padding,
paddingBottom = _ref.paddingBottom,
paddingLeft = _ref.paddingLeft,
paddingRight = _ref.paddingRight,
paddingTop = _ref.paddingTop,
paddingX = _ref.paddingX,
paddingY = _ref.paddingY,
position = _ref.position,
right = _ref.right,
shadow = _ref.shadow,
top = _ref.top,
userSelect = _ref.userSelect,
width = _ref.width,
zIndex = _ref.zIndex;
var theme = useTheme();
var unresponsiveProps = {
background: background ? theme.color.background[background] : undefined,
boxShadow: shadow ? theme.shadow[shadow] : undefined,
cursor: cursor,
minHeight: minHeight,
minWidth: minWidth,
opacity: opacity,
overflow: overflow,
userSelect: userSelect
};
var conditionalBorderStyles = _objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread({}, border ? {
borderStyle: 'solid',
borderColor: theme.utils.mapResponsiveScale(border, theme.border.color),
borderWidth: theme.utils.mapResponsiveScale(borderWidth, theme.border.width)
} : {}), borderTop ? {
borderTopStyle: 'solid',
borderTopColor: theme.utils.mapResponsiveScale(borderTop, theme.border.color),
borderTopWidth: theme.utils.mapResponsiveScale(borderWidth, theme.border.width)
} : {}), borderBottom ? {
borderBottomStyle: 'solid',
borderBottomColor: theme.utils.mapResponsiveScale(borderBottom, theme.border.color),
borderBottomWidth: theme.utils.mapResponsiveScale(borderWidth, theme.border.width)
} : {}), borderLeft ? {
borderLeftStyle: 'solid',
borderLeftColor: theme.utils.mapResponsiveScale(borderLeft, theme.border.color),
borderLeftWidth: theme.utils.mapResponsiveScale(borderWidth, theme.border.width)
} : {}), borderRight ? {
borderRightStyle: 'solid',
borderRightColor: theme.utils.mapResponsiveScale(borderRight, theme.border.color),
borderRightWidth: theme.utils.mapResponsiveScale(borderWidth, theme.border.width)
} : {});
return theme.utils.resolveResponsiveProps(_objectSpread(_objectSpread(_objectSpread({}, unresponsiveProps), conditionalBorderStyles), {}, {
// allow padding and height/width props to play nice
display: theme.utils.mapResponsiveProp(display),
// margin
marginBottom: theme.utils.mapResponsiveScale(marginBottom || marginY || margin, theme.spacing),
marginTop: theme.utils.mapResponsiveScale(marginTop || marginY || margin, theme.spacing),
marginLeft: theme.utils.mapResponsiveScale(marginLeft || marginX || margin, theme.spacing),
marginRight: theme.utils.mapResponsiveScale(marginRight || marginX || margin, theme.spacing),
// padding
paddingBottom: theme.utils.mapResponsiveScale(paddingBottom || paddingY || padding, theme.spacing),
paddingTop: theme.utils.mapResponsiveScale(paddingTop || paddingY || padding, theme.spacing),
paddingLeft: theme.utils.mapResponsiveScale(paddingLeft || paddingX || padding, theme.spacing),
paddingRight: theme.utils.mapResponsiveScale(paddingRight || paddingX || padding, theme.spacing),
// border
borderRadius: theme.utils.mapResponsiveScale(borderRadius, theme.border.radius),
// flex: parent
alignItems: theme.utils.mapResponsiveScale(alignItems, flexMap.alignItems),
gap: theme.utils.mapResponsiveScale(gap, theme.spacing),
flexDirection: theme.utils.mapResponsiveScale(flexDirection, flexMap.flexDirection),
justifyContent: theme.utils.mapResponsiveScale(justifyContent, flexMap.justifyContent),
flexWrap: theme.utils.mapResponsiveProp(flexWrap),
// flex: child
alignSelf: theme.utils.mapResponsiveScale(alignSelf, flexMap.alignItems),
flex: theme.utils.mapResponsiveProp(flex),
flexGrow: theme.utils.mapResponsiveProp(flexGrow),
flexShrink: theme.utils.mapResponsiveProp(flexShrink),
// dimension
height: theme.utils.mapResponsiveScale(height, theme.sizing),
width: theme.utils.mapResponsiveScale(width, theme.sizing),
// position
position: theme.utils.mapResponsiveProp(position),
bottom: theme.utils.mapResponsiveProp(bottom),
left: theme.utils.mapResponsiveProp(left),
right: theme.utils.mapResponsiveProp(right),
top: theme.utils.mapResponsiveProp(top),
zIndex: theme.utils.mapResponsiveScale(zIndex, theme.elevation)
}));
};
// Flex shorthand / adjustments
// ------------------------------
var flexMap = {
alignItems: {
start: 'flex-start',
center: 'center',
end: 'flex-end',
stretch: 'stretch'
},
justifyContent: {
start: 'flex-start',
center: 'center',
end: 'flex-end',
spaceBetween: 'space-between',
stretch: 'stretch'
},
flexDirection: {
row: 'row',
rowReverse: 'row-reverse',
column: 'column',
columnReverse: 'column-reverse'
}
};
var _excluded$1 = ["alignItems", "alignSelf", "background", "border", "borderRadius", "borderWidth", "borderTop", "borderBottom", "borderLeft", "borderRight", "bottom", "cursor", "display", "flex", "flexDirection", "flexGrow", "flexShrink", "flexWrap", "gap", "height", "justifyContent", "left", "margin", "marginBottom", "marginLeft", "marginRight", "marginTop", "marginX", "marginY", "minHeight", "minWidth", "opacity", "overflow", "padding", "paddingBottom", "paddingLeft", "paddingRight", "paddingTop", "paddingX", "paddingY", "position", "right", "shadow", "top", "userSelect", "width", "zIndex"];
/** Separate the style properties from the element attributes. */
function useBoxProps(props) {
var alignItems = props.alignItems,
alignSelf = props.alignSelf,
background = props.background,
border = props.border,
borderRadius = props.borderRadius,
borderWidth = props.borderWidth,
borderTop = props.borderTop,
borderBottom = props.borderBottom,
borderLeft = props.borderLeft,
borderRight = props.borderRight,
bottom = props.bottom,
cursor = props.cursor,
display = props.display,
flex = props.flex,
flexDirection = props.flexDirection,
flexGrow = props.flexGrow,
flexShrink = props.flexShrink,
flexWrap = props.flexWrap,
gap = props.gap,
height = props.height,
justifyContent = props.justifyContent,
left = props.left,
margin = props.margin,
marginBottom = props.marginBottom,
marginLeft = props.marginLeft,
marginRight = props.marginRight,
marginTop = props.marginTop,
marginX = props.marginX,
marginY = props.marginY,
minHeight = props.minHeight,
minWidth = props.minWidth,
opacity = props.opacity,
overflow = props.overflow,
padding = props.padding,
paddingBottom = props.paddingBottom,
paddingLeft = props.paddingLeft,
paddingRight = props.paddingRight,
paddingTop = props.paddingTop,
paddingX = props.paddingX,
paddingY = props.paddingY,
position = props.position,
right = props.right,
shadow = props.shadow,
top = props.top,
userSelect = props.userSelect,
width = props.width,
zIndex = props.zIndex,
attributes = _objectWithoutProperties(props, _excluded$1);
var styles = useBoxStyles({
alignItems: alignItems,
alignSelf: alignSelf,
background: background,
border: border,
borderRadius: borderRadius,
borderWidth: borderWidth,
borderBottom: borderBottom,
borderLeft: borderLeft,
borderRight: borderRight,
borderTop: borderTop,
bottom: bottom,
cursor: cursor,
display: display,
flex: flex,
flexDirection: flexDirection,
flexGrow: flexGrow,
flexShrink: flexShrink,
flexWrap: flexWrap,
gap: gap,
height: height,
justifyContent: justifyContent,
left: left,
margin: margin,
marginBottom: marginBottom,
marginLeft: marginLeft,
marginRight: marginRight,
marginTop: marginTop,
marginX: marginX,
marginY: marginY,
minHeight: minHeight,
minWidth: minWidth,
opacity: opacity,
overflow: overflow,
padding: padding,
paddingBottom: paddingBottom,
paddingLeft: paddingLeft,
paddingRight: paddingRight,
paddingTop: paddingTop,
paddingX: paddingX,
paddingY: paddingY,
position: position,
right: right,
shadow: shadow,
top: top,
userSelect: userSelect,
width: width,
zIndex: zIndex
});
return {
styles: styles,
attributes: attributes
};
}
var _excluded = ["as", "asElement", "className", "data", "id"];
/** Exposes a prop-based API for adding styles to a view, within the constraints of the theme. */
var Box = forwardRefWithAs(function (_ref, forwardedRef) {
var _ref$as = _ref.as,
Tag = _ref$as === void 0 ? 'div' : _ref$as,
asElement = _ref.asElement,
className = _ref.className,
data = _ref.data,
id = _ref.id,
props = _objectWithoutProperties(_ref, _excluded);
var _useBoxProps = useBoxProps(props),
styles = _useBoxProps.styles,
attributes = _useBoxProps.attributes;
var resetStyles = resetElementStyles(asElement !== null && asElement !== void 0 ? asElement : Tag);
var element = /*#__PURE__*/jsx(Tag, _objectSpread(_objectSpread({}, data ? buildDataAttributes(data) : undefined), {}, {
ref: forwardedRef,
id: id,
className: cx(css(resetStyles), css(styles), className)
}, attributes));
return renderBackgroundProvider(props.background, element);
});
export { BackgroundProvider, Box, useBackground, useBackgroundLightness };