sanity
Version:
Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches
179 lines (146 loc) • 4.78 kB
text/typescript
/* eslint-disable camelcase */
import {hues} from '@sanity/color'
import {Box, Flex, type Theme} from '@sanity/ui'
import {getTheme_v2, rgba} from '@sanity/ui/theme'
import styled, {css} from 'styled-components'
import {TEXT_BULLET_MARKERS, TEXT_NUMBER_FORMATS} from './constants'
import {createListName} from './helpers'
interface TextBlockStyleProps {
$level: number
}
function textBlockStyle(props: TextBlockStyleProps & {theme: Theme}) {
const {$level} = props
const {color, font, radius, space} = getTheme_v2(props.theme)
const numberMarker = TEXT_NUMBER_FORMATS[($level - 1) % TEXT_NUMBER_FORMATS.length]
const bulletMarker = TEXT_BULLET_MARKERS[($level - 1) % TEXT_BULLET_MARKERS.length]
return css`
--marker-bg-color: transparent;
mix-blend-mode: ${color._dark ? 'screen' : 'multiply'};
position: relative;
& > [data-ui='TextBlock_inner'] {
position: relative;
flex: 1;
}
& > div:before {
content: '';
position: absolute;
top: -${space[1]}px;
bottom: -${space[1]}px;
left: -${space[1]}px;
right: -${space[1]}px;
border-radius: ${radius[2]}px;
background-color: var(--marker-bg-color);
// This is to make sure the marker is always behind the text
z-index: -1;
}
&[data-markers] {
--marker-bg-color: ${color._dark ? hues.purple[950].hex : hues.purple[50].hex};
}
&[data-warning] {
--card-border-color: ${color.button.ghost.caution.enabled.border};
--marker-bg-color: ${color.button.ghost.caution.hovered.bg};
}
&[data-error] {
--card-border-color: ${color.button.ghost.critical.enabled.border};
--marker-bg-color: ${color.button.ghost.critical.hovered.bg};
}
& [data-list-prefix] {
position: absolute;
margin-left: -4.5rem;
width: 3.75rem;
text-align: right;
box-sizing: border-box;
}
&[data-list-item='number'] [data-list-prefix] {
font-variant-numeric: tabular-nums;
& > span:before {
content: counter(${createListName($level)}) '.';
content: counter(${createListName($level)}, ${numberMarker}) '.';
}
}
&[data-list-item='bullet'] [data-list-prefix] {
& > span {
position: relative;
top: -0.1875em;
&:before {
content: '${bulletMarker}';
font-size: 0.46666em;
}
}
}
& [data-text] {
overflow-wrap: anywhere;
text-transform: none;
white-space: pre-wrap;
font-family: ${font.text.family};
flex: 1;
*::selection {
background-color: ${rgba(color.focusRing, 0.3)};
}
}
`
}
export const TextRoot = styled.div<TextBlockStyleProps>(textBlockStyle)
// Because of a weird bug in Google Chrome regarding the @sanity/ui Flex component and spellchecking,
// this is set to be a Box with 'display: flex'. Using the Flex component here results in Chrome
// using 20% CPU when idle when spellchecking is on for some reason.
export const TextBlockFlexWrapper = styled(Box)`
position: relative;
display: flex;
`
export const ListPrefixWrapper = styled.div`
user-select: none;
white-space: nowrap;
`
export const BlockExtrasContainer = styled(Box)`
user-select: none;
`
export const BlockActionsOuter = styled(Box)`
line-height: 0;
width: 25px;
position: relative;
`
export const BlockActionsInner = styled(Flex)(({theme}: {theme: Theme}) => {
const {fonts, space} = theme.sanity
const textSize1 = fonts.text.sizes[1]
const textSize2 = fonts.text.sizes[2]
const capHeight1 = textSize1.lineHeight - textSize1.ascenderHeight - textSize1.descenderHeight
const capHeight2 = textSize2.lineHeight - textSize2.ascenderHeight - textSize2.descenderHeight
const buttonHeight = capHeight1 + space[2] + space[2]
// This calculates the following:
// > var buttonHeight = 25px
// > var capHeight2 = 11px
// > 0 - (buttonHeight - capHeight2) / 2 = -7px
const negativeTop = 0 - (buttonHeight - capHeight2) / 2
return css`
user-select: none;
position: absolute;
right: 0;
top: ${negativeTop}px;
`
})
export const TooltipBox = styled(Box)`
max-width: 250px;
`
export const TextFlex = styled(Flex)<{$level?: number}>`
position: relative;
padding-left: ${({$level}) => ($level ? $level * 32 : 0)}px;
`
export const ChangeIndicatorWrapper = styled.div<{$hasChanges: boolean}>(
({theme, $hasChanges}: {theme: Theme; $hasChanges: boolean}) => {
const {space} = theme.sanity
return css`
position: absolute;
width: ${space[2]}px;
right: 0;
top: 0;
bottom: 0;
padding-left: ${space[1]}px;
user-select: none;
${!$hasChanges &&
css`
display: none;
`}
`
},
)