/** * @title Text * @description 본문·설명·컴포넌트 안 글자처럼 제목이 아닌 모든 텍스트를 토큰 크기·굵기·색으로 보여줘요. */import * as React from 'react';import { cn } from '@/lib/utils';import { UNSAFE_inverseColorMap, UNSAFE_useSurface } from '@/components/ui/primitives';// Atlassian @atlaskit/primitives — compiled/components/text 스펙const fontSizeMap = { small: 'ds-font-body-small', medium: 'ds-font-body', large: 'ds-font-body-large',} as const;const fontWeightMap = { regular: 'font-ds-regular', medium: 'font-ds-medium', semibold: 'font-ds-semibold', bold: 'font-ds-bold',} as const;const textColorMap = { 'color.text': 'text-ds-text', 'color.text.accent.lime': 'text-ds-text-accent-lime', 'color.text.accent.lime.bolder': 'text-ds-text-accent-lime-bolder', 'color.text.accent.red': 'text-ds-text-accent-red', 'color.text.accent.red.bolder': 'text-ds-text-accent-red-bolder', 'color.text.accent.orange': 'text-ds-text-accent-orange', 'color.text.accent.orange.bolder': 'text-ds-text-accent-orange-bolder', 'color.text.accent.yellow': 'text-ds-text-accent-yellow', 'color.text.accent.yellow.bolder': 'text-ds-text-accent-yellow-bolder', 'color.text.accent.green': 'text-ds-text-accent-green', 'color.text.accent.green.bolder': 'text-ds-text-accent-green-bolder', 'color.text.accent.teal': 'text-ds-text-accent-teal', 'color.text.accent.teal.bolder': 'text-ds-text-accent-teal-bolder', 'color.text.accent.blue': 'text-ds-text-accent-blue', 'color.text.accent.blue.bolder': 'text-ds-text-accent-blue-bolder', 'color.text.accent.purple': 'text-ds-text-accent-purple', 'color.text.accent.purple.bolder': 'text-ds-text-accent-purple-bolder', 'color.text.accent.magenta': 'text-ds-text-accent-magenta', 'color.text.accent.magenta.bolder': 'text-ds-text-accent-magenta-bolder', 'color.text.accent.gray': 'text-ds-text-accent-gray', 'color.text.accent.gray.bolder': 'text-ds-text-accent-gray-bolder', 'color.text.disabled': 'text-ds-text-disabled', 'color.text.inverse': 'text-ds-text-inverse', 'color.text.selected': 'text-ds-text-selected', 'color.text.brand': 'text-ds-text-brand', 'color.text.danger': 'text-ds-text-danger', 'color.text.warning': 'text-ds-text-warning', 'color.text.warning.inverse': 'text-ds-text-warning-inverse', 'color.text.success': 'text-ds-text-success', 'color.text.discovery': 'text-ds-text-discovery', 'color.text.information': 'text-ds-text-information', 'color.text.subtlest': 'text-ds-text-subtlest', 'color.text.subtle': 'text-ds-text-subtle', 'color.link': 'text-ds-link', 'color.link.pressed': 'text-ds-link-pressed', 'color.link.visited': 'text-ds-link-visited', 'color.link.visited.pressed': 'text-ds-link-visited-pressed',} as const;const alignMap = { start: 'text-start', center: 'text-center', end: 'text-end',} as const;export type TextColor = keyof typeof textColorMap;export type TextSize = keyof typeof fontSizeMap;export type TextWeight = keyof typeof fontWeightMap;export type TextAlign = keyof typeof alignMap;/** Text 안에 Text 가 있으면 크기·색을 부모에게서 물려받는다 */const HasTextAncestorContext = React.createContext(false);export interface TextProps extends Omit<React.HTMLAttributes<HTMLElement>, 'color'> { /** 렌더링할 요소. strong 은 굵게, em 은 기울임이 자동으로 들어가요. */ as?: 'span' | 'p' | 'strong' | 'em'; /** 글자 크기. 다른 Text 안에서는 지정하지 않으면 부모 크기를 따라가요. */ size?: TextSize; weight?: TextWeight; /** 색 토큰. 'inherit' 이면 부모 색을 그대로 써요. */ color?: TextColor | 'inherit'; align?: TextAlign; /** 이 줄 수를 넘으면 말줄임(…)으로 잘라요. */ maxLines?: number; testId?: string; ref?: React.Ref<HTMLElement>;}function useTextColor(colorProp: TextProps['color'], hasTextAncestor: boolean) { const surface = UNSAFE_useSurface(); if (colorProp === 'inherit') return undefined; if (colorProp) return colorProp; if (hasTextAncestor) return undefined; return UNSAFE_inverseColorMap[surface] ?? 'color.text';}function Text({ as: Comp = 'span', size: sizeProp, weight, color: colorProp, align, maxLines, testId, className, style, ...props}: TextProps) { const hasTextAncestor = React.useContext(HasTextAncestorContext); const color = useTextColor(colorProp, hasTextAncestor); const size = sizeProp ?? (hasTextAncestor ? undefined : 'medium'); const Element = Comp as React.ElementType; const node = ( <Element data-slot="text" data-testid={testId} className={cn( 'm-0 [overflow-wrap:anywhere]', size && fontSizeMap[size], color && textColorMap[color], maxLines && 'overflow-hidden [display:-webkit-box] [-webkit-box-orient:vertical]', maxLines === 1 && 'break-all', align && alignMap[align], Comp === 'em' && 'italic', Comp === 'strong' && 'font-ds-bold', weight && fontWeightMap[weight], className, )} style={{ WebkitLineClamp: maxLines, ...style }} {...props} /> ); return hasTextAncestor ? node : <HasTextAncestorContext.Provider value>{node}</HasTextAncestorContext.Provider>;}export { Text };