mirror of
https://github.com/codeaashu/claude-code.git
synced 2026-04-08 22:28:48 +03:00
claude-code
This commit is contained in:
151
web/lib/ink-compat/Box.tsx
Normal file
151
web/lib/ink-compat/Box.tsx
Normal file
@@ -0,0 +1,151 @@
|
||||
import React, { type CSSProperties, type MouseEvent, type KeyboardEvent as ReactKeyboardEvent, type PropsWithChildren, type Ref } from 'react'
|
||||
import { inkBoxPropsToCSS, type InkStyleProps } from './prop-mapping'
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Web-compat event shims that mirror the Ink event interface.
|
||||
* Components that only inspect `.stopImmediatePropagation()` or basic
|
||||
* event info will work without changes.
|
||||
*/
|
||||
export type WebClickEvent = {
|
||||
x: number
|
||||
y: number
|
||||
button: 'left' | 'right' | 'middle'
|
||||
stopImmediatePropagation(): void
|
||||
}
|
||||
|
||||
export type WebFocusEvent = {
|
||||
stopImmediatePropagation(): void
|
||||
}
|
||||
|
||||
export type WebKeyboardEvent = {
|
||||
key: string
|
||||
ctrl: boolean
|
||||
shift: boolean
|
||||
meta: boolean
|
||||
stopImmediatePropagation(): void
|
||||
}
|
||||
|
||||
export type BoxProps = PropsWithChildren<
|
||||
InkStyleProps & {
|
||||
ref?: Ref<HTMLDivElement>
|
||||
tabIndex?: number
|
||||
autoFocus?: boolean
|
||||
/** onClick receives a shim that mirrors the Ink ClickEvent interface. */
|
||||
onClick?: (event: WebClickEvent) => void
|
||||
onFocus?: (event: WebFocusEvent) => void
|
||||
onFocusCapture?: (event: WebFocusEvent) => void
|
||||
onBlur?: (event: WebFocusEvent) => void
|
||||
onBlurCapture?: (event: WebFocusEvent) => void
|
||||
onKeyDown?: (event: WebKeyboardEvent) => void
|
||||
onKeyDownCapture?: (event: WebKeyboardEvent) => void
|
||||
onMouseEnter?: () => void
|
||||
onMouseLeave?: () => void
|
||||
/** Pass-through className for web-specific styling. */
|
||||
className?: string
|
||||
/** Pass-through inline style overrides applied on top of Ink-mapped styles. */
|
||||
style?: CSSProperties
|
||||
}
|
||||
>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Adapters
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function adaptClickEvent(e: MouseEvent<HTMLDivElement>): WebClickEvent {
|
||||
let stopped = false
|
||||
const shim: WebClickEvent = {
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
button: e.button === 2 ? 'right' : e.button === 1 ? 'middle' : 'left',
|
||||
stopImmediatePropagation() {
|
||||
if (!stopped) {
|
||||
stopped = true
|
||||
e.stopPropagation()
|
||||
}
|
||||
},
|
||||
}
|
||||
return shim
|
||||
}
|
||||
|
||||
function adaptFocusEvent(e: React.FocusEvent<HTMLDivElement>): WebFocusEvent {
|
||||
return {
|
||||
stopImmediatePropagation() {
|
||||
e.stopPropagation()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function adaptKeyboardEvent(e: ReactKeyboardEvent<HTMLDivElement>): WebKeyboardEvent {
|
||||
return {
|
||||
key: e.key,
|
||||
ctrl: e.ctrlKey,
|
||||
shift: e.shiftKey,
|
||||
meta: e.metaKey || e.altKey,
|
||||
stopImmediatePropagation() {
|
||||
e.stopPropagation()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Component
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Web-compat `<Box>` — renders as a `<div>` with `display: flex` and maps
|
||||
* all Ink layout props to CSS. Drop-in replacement for Ink's `<Box>`.
|
||||
*/
|
||||
export const Box = React.forwardRef<HTMLDivElement, BoxProps>(function Box(
|
||||
{
|
||||
children,
|
||||
className,
|
||||
style: styleProp,
|
||||
tabIndex,
|
||||
autoFocus,
|
||||
onClick,
|
||||
onFocus,
|
||||
onFocusCapture,
|
||||
onBlur,
|
||||
onBlurCapture,
|
||||
onKeyDown,
|
||||
onKeyDownCapture,
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
// Ink style props — everything else
|
||||
...inkProps
|
||||
},
|
||||
ref,
|
||||
) {
|
||||
const inkCSS = inkBoxPropsToCSS(inkProps)
|
||||
const mergedStyle: CSSProperties = { ...inkCSS, ...styleProp }
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={className}
|
||||
style={mergedStyle}
|
||||
tabIndex={tabIndex}
|
||||
// eslint-disable-next-line jsx-a11y/no-autofocus
|
||||
autoFocus={autoFocus}
|
||||
onClick={onClick ? (e) => onClick(adaptClickEvent(e)) : undefined}
|
||||
onFocus={onFocus ? (e) => onFocus(adaptFocusEvent(e)) : undefined}
|
||||
onFocusCapture={onFocusCapture ? (e) => onFocusCapture(adaptFocusEvent(e)) : undefined}
|
||||
onBlur={onBlur ? (e) => onBlur(adaptFocusEvent(e)) : undefined}
|
||||
onBlurCapture={onBlurCapture ? (e) => onBlurCapture(adaptFocusEvent(e)) : undefined}
|
||||
onKeyDown={onKeyDown ? (e) => onKeyDown(adaptKeyboardEvent(e)) : undefined}
|
||||
onKeyDownCapture={onKeyDownCapture ? (e) => onKeyDownCapture(adaptKeyboardEvent(e)) : undefined}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
Box.displayName = 'Box'
|
||||
|
||||
export default Box
|
||||
93
web/lib/ink-compat/color-mapping.ts
Normal file
93
web/lib/ink-compat/color-mapping.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* Maps Ink/ANSI color values to CSS color strings.
|
||||
*
|
||||
* Ink Color types:
|
||||
* RGBColor = `rgb(${number},${number},${number})`
|
||||
* HexColor = `#${string}`
|
||||
* Ansi256Color = `ansi256(${number})`
|
||||
* AnsiColor = `ansi:black` | `ansi:red` | ...
|
||||
*/
|
||||
|
||||
// Standard ANSI 16-color palette mapped to CSS hex values
|
||||
const ANSI_COLORS: Record<string, string> = {
|
||||
black: '#000000',
|
||||
red: '#cc0000',
|
||||
green: '#4e9a06',
|
||||
yellow: '#c4a000',
|
||||
blue: '#3465a4',
|
||||
magenta: '#75507b',
|
||||
cyan: '#06989a',
|
||||
white: '#d3d7cf',
|
||||
blackBright: '#555753',
|
||||
redBright: '#ef2929',
|
||||
greenBright: '#8ae234',
|
||||
yellowBright: '#fce94f',
|
||||
blueBright: '#729fcf',
|
||||
magentaBright: '#ad7fa8',
|
||||
cyanBright: '#34e2e2',
|
||||
whiteBright: '#eeeeec',
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an index in the xterm 256-color palette to a CSS hex color string.
|
||||
*/
|
||||
function ansi256ToHex(index: number): string {
|
||||
// 0–15: standard palette
|
||||
if (index < 16) {
|
||||
const names = [
|
||||
'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white',
|
||||
'blackBright', 'redBright', 'greenBright', 'yellowBright',
|
||||
'blueBright', 'magentaBright', 'cyanBright', 'whiteBright',
|
||||
]
|
||||
return ANSI_COLORS[names[index]!] ?? '#000000'
|
||||
}
|
||||
|
||||
// 232–255: grayscale ramp
|
||||
if (index >= 232) {
|
||||
const level = (index - 232) * 10 + 8
|
||||
const hex = level.toString(16).padStart(2, '0')
|
||||
return `#${hex}${hex}${hex}`
|
||||
}
|
||||
|
||||
// 16–231: 6×6×6 color cube
|
||||
const i = index - 16
|
||||
const b = i % 6
|
||||
const g = Math.floor(i / 6) % 6
|
||||
const r = Math.floor(i / 36)
|
||||
const toChannel = (v: number) => (v === 0 ? 0 : v * 40 + 55)
|
||||
const rh = toChannel(r).toString(16).padStart(2, '0')
|
||||
const gh = toChannel(g).toString(16).padStart(2, '0')
|
||||
const bh = toChannel(b).toString(16).padStart(2, '0')
|
||||
return `#${rh}${gh}${bh}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an Ink Color string (or theme-resolved raw color) to a CSS color string.
|
||||
* Returns `undefined` if `color` is undefined/empty.
|
||||
*/
|
||||
export function inkColorToCSS(color: string | undefined): string | undefined {
|
||||
if (!color) return undefined
|
||||
|
||||
// Pass through hex colors
|
||||
if (color.startsWith('#')) return color
|
||||
|
||||
// Normalise `rgb(r,g,b)` → `rgb(r, g, b)` (browsers accept both, but clean)
|
||||
if (color.startsWith('rgb(')) {
|
||||
return color.replace(/\s+/g, '')
|
||||
}
|
||||
|
||||
// ansi256(N)
|
||||
const ansi256Match = color.match(/^ansi256\((\d+)\)$/)
|
||||
if (ansi256Match) {
|
||||
return ansi256ToHex(parseInt(ansi256Match[1]!, 10))
|
||||
}
|
||||
|
||||
// ansi:name
|
||||
if (color.startsWith('ansi:')) {
|
||||
const name = color.slice(5)
|
||||
return ANSI_COLORS[name] ?? color
|
||||
}
|
||||
|
||||
// Unknown format — return as-is (browser may understand it)
|
||||
return color
|
||||
}
|
||||
328
web/lib/ink-compat/prop-mapping.ts
Normal file
328
web/lib/ink-compat/prop-mapping.ts
Normal file
@@ -0,0 +1,328 @@
|
||||
import type { CSSProperties } from 'react'
|
||||
import { inkColorToCSS } from './color-mapping'
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Dimension helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Convert an Ink dimension (number = character cells, or `"50%"` percent string)
|
||||
* to a CSS value. We use `ch` for character-width units so the layout
|
||||
* approximates the terminal in a monospace font.
|
||||
*/
|
||||
function toCSSSize(value: number | string | undefined): string | undefined {
|
||||
if (value === undefined) return undefined
|
||||
if (typeof value === 'string') return value // already "50%" etc.
|
||||
return `${value}ch`
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Border style mapping
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Maps Ink/cli-boxes border style names to CSS border-style values.
|
||||
const BORDER_STYLE_MAP: Record<string, CSSProperties['borderStyle']> = {
|
||||
single: 'solid',
|
||||
double: 'double',
|
||||
round: 'solid', // approximated; borderRadius added below
|
||||
bold: 'solid',
|
||||
singleDouble: 'solid',
|
||||
doubleSingle: 'solid',
|
||||
classic: 'solid',
|
||||
arrow: 'solid',
|
||||
ascii: 'solid',
|
||||
dashed: 'dashed',
|
||||
// cli-boxes names
|
||||
none: 'none',
|
||||
}
|
||||
|
||||
const BORDER_BOLD_STYLES = new Set(['bold'])
|
||||
const BORDER_ROUND_STYLES = new Set(['round'])
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Main mapping function
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export type InkStyleProps = {
|
||||
// Position
|
||||
position?: 'absolute' | 'relative'
|
||||
top?: number | string
|
||||
bottom?: number | string
|
||||
left?: number | string
|
||||
right?: number | string
|
||||
|
||||
// Margin
|
||||
margin?: number
|
||||
marginX?: number
|
||||
marginY?: number
|
||||
marginTop?: number
|
||||
marginBottom?: number
|
||||
marginLeft?: number
|
||||
marginRight?: number
|
||||
|
||||
// Padding
|
||||
padding?: number
|
||||
paddingX?: number
|
||||
paddingY?: number
|
||||
paddingTop?: number
|
||||
paddingBottom?: number
|
||||
paddingLeft?: number
|
||||
paddingRight?: number
|
||||
|
||||
// Flex
|
||||
flexDirection?: 'row' | 'column' | 'row-reverse' | 'column-reverse'
|
||||
flexGrow?: number
|
||||
flexShrink?: number
|
||||
flexBasis?: number | string
|
||||
flexWrap?: 'nowrap' | 'wrap' | 'wrap-reverse'
|
||||
alignItems?: 'flex-start' | 'center' | 'flex-end' | 'stretch'
|
||||
alignSelf?: 'flex-start' | 'center' | 'flex-end' | 'auto'
|
||||
justifyContent?: 'flex-start' | 'flex-end' | 'space-between' | 'space-around' | 'space-evenly' | 'center'
|
||||
|
||||
// Gap
|
||||
gap?: number
|
||||
columnGap?: number
|
||||
rowGap?: number
|
||||
|
||||
// Sizing
|
||||
width?: number | string
|
||||
height?: number | string
|
||||
minWidth?: number | string
|
||||
minHeight?: number | string
|
||||
maxWidth?: number | string
|
||||
maxHeight?: number | string
|
||||
|
||||
// Display
|
||||
display?: 'flex' | 'none'
|
||||
|
||||
// Overflow (Ink only has 'hidden')
|
||||
overflow?: 'hidden' | 'visible'
|
||||
overflowX?: 'hidden' | 'visible'
|
||||
overflowY?: 'hidden' | 'visible'
|
||||
|
||||
// Border
|
||||
borderStyle?: string | { top: string; bottom: string; left: string; right: string; topLeft: string; topRight: string; bottomLeft: string; bottomRight: string }
|
||||
borderTop?: boolean
|
||||
borderBottom?: boolean
|
||||
borderLeft?: boolean
|
||||
borderRight?: boolean
|
||||
borderColor?: string
|
||||
borderTopColor?: string
|
||||
borderBottomColor?: string
|
||||
borderLeftColor?: string
|
||||
borderRightColor?: string
|
||||
borderDimColor?: boolean
|
||||
borderTopDimColor?: boolean
|
||||
borderBottomDimColor?: boolean
|
||||
borderLeftDimColor?: boolean
|
||||
borderRightDimColor?: boolean
|
||||
}
|
||||
|
||||
export type InkTextStyleProps = {
|
||||
color?: string
|
||||
backgroundColor?: string
|
||||
bold?: boolean
|
||||
dim?: boolean
|
||||
italic?: boolean
|
||||
underline?: boolean
|
||||
strikethrough?: boolean
|
||||
inverse?: boolean
|
||||
wrap?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Ink Box layout props to a React CSSProperties object.
|
||||
*/
|
||||
export function inkBoxPropsToCSS(props: InkStyleProps): CSSProperties {
|
||||
const css: CSSProperties = {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
boxSizing: 'border-box',
|
||||
}
|
||||
|
||||
// Display
|
||||
if (props.display === 'none') {
|
||||
css.display = 'none'
|
||||
return css
|
||||
}
|
||||
|
||||
// Position
|
||||
if (props.position) css.position = props.position
|
||||
if (props.top !== undefined) css.top = toCSSSize(props.top)
|
||||
if (props.bottom !== undefined) css.bottom = toCSSSize(props.bottom)
|
||||
if (props.left !== undefined) css.left = toCSSSize(props.left)
|
||||
if (props.right !== undefined) css.right = toCSSSize(props.right)
|
||||
|
||||
// Flex
|
||||
if (props.flexDirection) css.flexDirection = props.flexDirection
|
||||
if (props.flexGrow !== undefined) css.flexGrow = props.flexGrow
|
||||
if (props.flexShrink !== undefined) css.flexShrink = props.flexShrink
|
||||
if (props.flexBasis !== undefined) css.flexBasis = toCSSSize(props.flexBasis)
|
||||
if (props.flexWrap) css.flexWrap = props.flexWrap
|
||||
if (props.alignItems) css.alignItems = props.alignItems
|
||||
if (props.alignSelf) css.alignSelf = props.alignSelf
|
||||
if (props.justifyContent) css.justifyContent = props.justifyContent
|
||||
|
||||
// Gap
|
||||
if (props.gap !== undefined) css.gap = `${props.gap}ch`
|
||||
if (props.columnGap !== undefined) css.columnGap = `${props.columnGap}ch`
|
||||
if (props.rowGap !== undefined) css.rowGap = `${props.rowGap}ch`
|
||||
|
||||
// Sizing
|
||||
if (props.width !== undefined) css.width = toCSSSize(props.width)
|
||||
if (props.height !== undefined) css.height = toCSSSize(props.height)
|
||||
if (props.minWidth !== undefined) css.minWidth = toCSSSize(props.minWidth)
|
||||
if (props.minHeight !== undefined) css.minHeight = toCSSSize(props.minHeight)
|
||||
if (props.maxWidth !== undefined) css.maxWidth = toCSSSize(props.maxWidth)
|
||||
if (props.maxHeight !== undefined) css.maxHeight = toCSSSize(props.maxHeight)
|
||||
|
||||
// Margin (shorthand resolution: margin → marginX/Y → individual sides)
|
||||
const mt = props.marginTop ?? props.marginY ?? props.margin
|
||||
const mb = props.marginBottom ?? props.marginY ?? props.margin
|
||||
const ml = props.marginLeft ?? props.marginX ?? props.margin
|
||||
const mr = props.marginRight ?? props.marginX ?? props.margin
|
||||
if (mt !== undefined) css.marginTop = toCSSSize(mt)
|
||||
if (mb !== undefined) css.marginBottom = toCSSSize(mb)
|
||||
if (ml !== undefined) css.marginLeft = toCSSSize(ml)
|
||||
if (mr !== undefined) css.marginRight = toCSSSize(mr)
|
||||
|
||||
// Padding
|
||||
const pt = props.paddingTop ?? props.paddingY ?? props.padding
|
||||
const pb = props.paddingBottom ?? props.paddingY ?? props.padding
|
||||
const pl = props.paddingLeft ?? props.paddingX ?? props.padding
|
||||
const pr = props.paddingRight ?? props.paddingX ?? props.padding
|
||||
if (pt !== undefined) css.paddingTop = toCSSSize(pt)
|
||||
if (pb !== undefined) css.paddingBottom = toCSSSize(pb)
|
||||
if (pl !== undefined) css.paddingLeft = toCSSSize(pl)
|
||||
if (pr !== undefined) css.paddingRight = toCSSSize(pr)
|
||||
|
||||
// Overflow
|
||||
if (props.overflow) css.overflow = props.overflow
|
||||
if (props.overflowX) css.overflowX = props.overflowX
|
||||
if (props.overflowY) css.overflowY = props.overflowY
|
||||
|
||||
// Border
|
||||
if (props.borderStyle) {
|
||||
const styleName = typeof props.borderStyle === 'string' ? props.borderStyle : 'single'
|
||||
const cssBorderStyle = BORDER_STYLE_MAP[styleName] ?? 'solid'
|
||||
const isBold = BORDER_BOLD_STYLES.has(styleName)
|
||||
const borderWidth = isBold ? '2px' : '1px'
|
||||
|
||||
const showTop = props.borderTop !== false
|
||||
const showBottom = props.borderBottom !== false
|
||||
const showLeft = props.borderLeft !== false
|
||||
const showRight = props.borderRight !== false
|
||||
|
||||
const resolveColor = (side: string | undefined, fallback: string | undefined, dim: boolean | undefined) => {
|
||||
const raw = side ?? fallback
|
||||
const cssColor = inkColorToCSS(raw) ?? 'currentColor'
|
||||
return dim ? `color-mix(in srgb, ${cssColor} 60%, transparent)` : cssColor
|
||||
}
|
||||
|
||||
const dimAll = props.borderDimColor
|
||||
|
||||
if (showTop) {
|
||||
css.borderTopStyle = cssBorderStyle
|
||||
css.borderTopWidth = borderWidth
|
||||
css.borderTopColor = resolveColor(
|
||||
props.borderTopColor ?? props.borderColor,
|
||||
props.borderColor,
|
||||
props.borderTopDimColor ?? dimAll,
|
||||
)
|
||||
}
|
||||
if (showBottom) {
|
||||
css.borderBottomStyle = cssBorderStyle
|
||||
css.borderBottomWidth = borderWidth
|
||||
css.borderBottomColor = resolveColor(
|
||||
props.borderBottomColor ?? props.borderColor,
|
||||
props.borderColor,
|
||||
props.borderBottomDimColor ?? dimAll,
|
||||
)
|
||||
}
|
||||
if (showLeft) {
|
||||
css.borderLeftStyle = cssBorderStyle
|
||||
css.borderLeftWidth = borderWidth
|
||||
css.borderLeftColor = resolveColor(
|
||||
props.borderLeftColor ?? props.borderColor,
|
||||
props.borderColor,
|
||||
props.borderLeftDimColor ?? dimAll,
|
||||
)
|
||||
}
|
||||
if (showRight) {
|
||||
css.borderRightStyle = cssBorderStyle
|
||||
css.borderRightWidth = borderWidth
|
||||
css.borderRightColor = resolveColor(
|
||||
props.borderRightColor ?? props.borderColor,
|
||||
props.borderColor,
|
||||
props.borderRightDimColor ?? dimAll,
|
||||
)
|
||||
}
|
||||
|
||||
if (BORDER_ROUND_STYLES.has(styleName)) {
|
||||
css.borderRadius = '4px'
|
||||
}
|
||||
}
|
||||
|
||||
return css
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Ink Text style props to a React CSSProperties object.
|
||||
*/
|
||||
export function inkTextPropsToCSS(props: InkTextStyleProps): CSSProperties {
|
||||
const css: CSSProperties = {}
|
||||
|
||||
const fg = inkColorToCSS(props.color)
|
||||
const bg = inkColorToCSS(props.backgroundColor)
|
||||
|
||||
if (props.inverse) {
|
||||
// Swap foreground and background
|
||||
if (fg) css.backgroundColor = fg
|
||||
if (bg) css.color = bg
|
||||
if (!fg) css.filter = 'invert(1)'
|
||||
} else {
|
||||
if (fg) css.color = fg
|
||||
if (bg) css.backgroundColor = bg
|
||||
}
|
||||
|
||||
if (props.bold) css.fontWeight = 'bold'
|
||||
if (props.dim) css.opacity = 0.6
|
||||
if (props.italic) css.fontStyle = 'italic'
|
||||
|
||||
const decorations: string[] = []
|
||||
if (props.underline) decorations.push('underline')
|
||||
if (props.strikethrough) decorations.push('line-through')
|
||||
if (decorations.length > 0) css.textDecoration = decorations.join(' ')
|
||||
|
||||
if (props.wrap) {
|
||||
switch (props.wrap) {
|
||||
case 'wrap':
|
||||
case 'wrap-trim':
|
||||
css.whiteSpace = 'pre-wrap'
|
||||
css.overflowWrap = 'anywhere'
|
||||
break
|
||||
case 'truncate':
|
||||
case 'truncate-end':
|
||||
case 'end':
|
||||
css.overflow = 'hidden'
|
||||
css.textOverflow = 'ellipsis'
|
||||
css.whiteSpace = 'nowrap'
|
||||
break
|
||||
case 'truncate-middle':
|
||||
case 'middle':
|
||||
// CSS can't do mid-truncation; use ellipsis as fallback
|
||||
css.overflow = 'hidden'
|
||||
css.textOverflow = 'ellipsis'
|
||||
css.whiteSpace = 'nowrap'
|
||||
break
|
||||
case 'truncate-start':
|
||||
css.overflow = 'hidden'
|
||||
css.direction = 'rtl'
|
||||
css.textOverflow = 'ellipsis'
|
||||
css.whiteSpace = 'nowrap'
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return css
|
||||
}
|
||||
Reference in New Issue
Block a user