UNPKG

@atlaskit/primitives

Version:

Primitives are token-backed low-level building blocks.

111 lines (107 loc) 3.48 kB
/** @jsx jsx */ import { createContext, Fragment, useContext } from 'react'; import { css, jsx } from '@emotion/react'; import invariant from 'tiny-invariant'; import { bodyTextStylesMap, inverseColorMap, textColorStylesMap, uiTextStylesMap } from '../xcss/style-maps.partial'; import { useSurface } from './internal/surface-provider'; const asAllowlist = ['span', 'p', 'strong', 'em']; // We're doing this because our CSS reset can add top margins to elements such as `p` which is totally insane. // Long term we should remove those instances from the reset - it should be a reset to 0. // For now, at least we know <Text> will be unaffected by this. const resetStyles = css({ margin: 0 }); const variantStyles = { ...bodyTextStylesMap, ...uiTextStylesMap }; const strongStyles = css({ fontWeight: "var(--ds-font-weight-bold, bold)" }); const emStyles = css({ fontStyle: 'italic' }); const textAlignMap = { center: css({ textAlign: 'center' }), end: css({ textAlign: 'end' }), start: css({ textAlign: 'start' }) }; const truncateStyles = css({ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }); /** * Custom hook designed to abstract the parsing of the color props and make it clearer in the future how color is reconciled between themes and tokens. */ const useColor = colorProp => { const surface = useSurface(); const inverseTextColor = inverseColorMap[surface]; /** * Where the color of the surface is inverted we override the user choice * as there is no valid choice that is not covered by the override. */ const color = inverseTextColor !== null && inverseTextColor !== void 0 ? inverseTextColor : colorProp; return color; }; const HasTextAncestorContext = /*#__PURE__*/createContext(false); const useHasTextAncestor = () => useContext(HasTextAncestorContext); /** * __Text__ * * Text is a primitive component that has the Atlassian Design System's design guidelines baked in. * This includes considerations for text attributes such as color, font size, font weight, and line height. * It renders a `span` by default. * * @internal */ const Text = ({ children, ...props }) => { const { as: asElement, color: colorProp, shouldTruncate = false, textAlign, testId, id, variant = 'body' } = props; let Component = asElement; if (!Component) { if (variant.includes('body')) { Component = 'p'; } else { // ui text and default => span Component = 'span'; } } invariant(asAllowlist.includes(Component), `@atlaskit/primitives: Text received an invalid "as" value of "${Component}"`); const color = useColor(colorProp); const isWrapped = useHasTextAncestor(); /** * If the text is already wrapped and applies no props we can just * render the children directly as a fragment. */ if (isWrapped && Object.keys(props).length === 0) { return jsx(Fragment, null, children); } const component = jsx(Component, { css: [resetStyles, variant && variantStyles[variant], color && textColorStylesMap[color], shouldTruncate && truncateStyles, textAlign && textAlignMap[textAlign], asElement === 'em' && emStyles, asElement === 'strong' && strongStyles], "data-testid": testId, id: id }, children); return isWrapped ? // no need to re-apply context if the text is already wrapped component : jsx(HasTextAncestorContext.Provider, { value: true }, component); }; export default Text;