kopia lustrzana https://github.com/Tldraw/Tldraw
Fix text-wrapping on Safari (#1980)
Co-authored-by: Alex Alex@dytry.ch closes [#1978](https://github.com/tldraw/tldraw/issues/1978) Text was wrapping on Safari because the measure text div was rendered differently on different browsers. Interestingly, when forcing the text-measure div to be visible and on-screen in Chrome, the same text-wrapping behaviour was apparent. By setting white-space to 'pre' when width hasn't been set by the user, we can ensure that only line breaks the user has inputted are rendered by default on all browsers. ### Change Type - [x] `patch` — Bug fix - [ ] `minor` — New feature - [ ] `major` — Breaking change - [ ] `dependencies` — Changes to package dependencies[^1] - [ ] `documentation` — Changes to the documentation only[^2] - [ ] `tests` — Changes to any test code only[^2] - [ ] `internal` — Any other changes that don't affect the published package[^2] - [ ] I don't know [^1]: publishes a `patch` release, for devDependencies use `internal` [^2]: will not publish a new version ### Test Plan 1. On Safari 2. Make a new text shape and start typing 3. At a certain point the text starts to wrap without the width having been set ### Release Notes - Fix text wrapping differently on Safari and Chrome/Firefox Before/After <image width="350" src="https://github.com/tldraw/tldraw/assets/98838967/320171b4-61e0-4a41-b8d3-830bd90bea65"> <image width="350" src="https://github.com/tldraw/tldraw/assets/98838967/b42d7156-0ce9-4894-9692-9338dc931b79">pull/1981/head
rodzic
da33179a31
commit
f73bf9a7fe
|
@ -7,7 +7,7 @@ export function sleep(ms: number) {
|
|||
}
|
||||
|
||||
const measureTextOptions = {
|
||||
width: 'fit-content',
|
||||
width: null,
|
||||
fontFamily: 'var(--tl-font-draw)',
|
||||
fontSize: 24,
|
||||
lineHeight: 1.35,
|
||||
|
|
|
@ -63,7 +63,12 @@ export class TextManager {
|
|||
fontFamily: string
|
||||
fontSize: number
|
||||
lineHeight: number
|
||||
width: string
|
||||
/**
|
||||
* When width is a number, the text will be wrapped to that width. When
|
||||
* width is null, the text will be measured without wrapping, but explicit
|
||||
* line breaks and space are preserved.
|
||||
*/
|
||||
width: null | number
|
||||
minWidth?: string
|
||||
maxWidth: string
|
||||
padding: string
|
||||
|
@ -77,13 +82,18 @@ export class TextManager {
|
|||
elm.style.setProperty('font-weight', opts.fontWeight)
|
||||
elm.style.setProperty('font-size', opts.fontSize + 'px')
|
||||
elm.style.setProperty('line-height', opts.lineHeight * opts.fontSize + 'px')
|
||||
elm.style.setProperty('width', opts.width)
|
||||
if (opts.width === null) {
|
||||
elm.style.setProperty('white-space', 'pre')
|
||||
elm.style.setProperty('width', 'fit-content')
|
||||
} else {
|
||||
elm.style.setProperty('width', opts.width + 'px')
|
||||
elm.style.setProperty('white-space', 'pre-wrap')
|
||||
}
|
||||
elm.style.setProperty('min-width', opts.minWidth ?? null)
|
||||
elm.style.setProperty('max-width', opts.maxWidth)
|
||||
elm.style.setProperty('padding', opts.padding)
|
||||
|
||||
elm.textContent = normalizeTextForDom(textToMeasure)
|
||||
|
||||
const rect = elm.getBoundingClientRect()
|
||||
|
||||
return {
|
||||
|
|
|
@ -289,7 +289,7 @@ export function registerDefaultExternalContentHandlers(
|
|||
...TEXT_PROPS,
|
||||
fontFamily: FONT_FAMILIES[defaultProps.font],
|
||||
fontSize: FONT_SIZES[defaultProps.size],
|
||||
width: 'fit-content',
|
||||
width: null,
|
||||
})
|
||||
|
||||
const minWidth = Math.min(
|
||||
|
@ -302,7 +302,7 @@ export function registerDefaultExternalContentHandlers(
|
|||
...TEXT_PROPS,
|
||||
fontFamily: FONT_FAMILIES[defaultProps.font],
|
||||
fontSize: FONT_SIZES[defaultProps.size],
|
||||
width: minWidth + 'px',
|
||||
width: minWidth,
|
||||
})
|
||||
w = shrunkSize.w
|
||||
h = shrunkSize.h
|
||||
|
|
|
@ -112,7 +112,7 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
|
|||
...TEXT_PROPS,
|
||||
fontFamily: FONT_FAMILIES[shape.props.font],
|
||||
fontSize: ARROW_LABEL_FONT_SIZES[shape.props.size],
|
||||
width: 'fit-content',
|
||||
width: null,
|
||||
})
|
||||
|
||||
let width = w
|
||||
|
@ -127,7 +127,7 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
|
|||
...TEXT_PROPS,
|
||||
fontFamily: FONT_FAMILIES[shape.props.font],
|
||||
fontSize: ARROW_LABEL_FONT_SIZES[shape.props.size],
|
||||
width: width + 'px',
|
||||
width: width,
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -144,7 +144,7 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
|
|||
...TEXT_PROPS,
|
||||
fontFamily: FONT_FAMILIES[shape.props.font],
|
||||
fontSize: ARROW_LABEL_FONT_SIZES[shape.props.size],
|
||||
width: width + 'px',
|
||||
width: width,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -1053,7 +1053,7 @@ function getLabelSize(editor: Editor, shape: TLGeoShape) {
|
|||
...TEXT_PROPS,
|
||||
fontFamily: FONT_FAMILIES[shape.props.font],
|
||||
fontSize: LABEL_FONT_SIZES[shape.props.size],
|
||||
width: 'fit-content',
|
||||
width: null,
|
||||
maxWidth: '100px',
|
||||
})
|
||||
|
||||
|
@ -1069,7 +1069,7 @@ function getLabelSize(editor: Editor, shape: TLGeoShape) {
|
|||
...TEXT_PROPS,
|
||||
fontFamily: FONT_FAMILIES[shape.props.font],
|
||||
fontSize: LABEL_FONT_SIZES[shape.props.size],
|
||||
width: 'fit-content',
|
||||
width: null,
|
||||
minWidth: minSize.w + 'px',
|
||||
maxWidth:
|
||||
Math.max(
|
||||
|
|
|
@ -194,7 +194,7 @@ function getGrowY(editor: Editor, shape: TLNoteShape, prevGrowY = 0) {
|
|||
...TEXT_PROPS,
|
||||
fontFamily: FONT_FAMILIES[shape.props.font],
|
||||
fontSize: LABEL_FONT_SIZES[shape.props.size],
|
||||
width: NOTE_SIZE - PADDING * 2 + 'px',
|
||||
width: NOTE_SIZE - PADDING * 2,
|
||||
})
|
||||
|
||||
const nextHeight = nextTextSize.h + PADDING * 2
|
||||
|
|
|
@ -373,9 +373,9 @@ function getTextSize(editor: Editor, props: TLTextShape['props']) {
|
|||
const fontSize = FONT_SIZES[size]
|
||||
|
||||
const cw = autoSize
|
||||
? 'fit-content'
|
||||
? null
|
||||
: // `measureText` floors the number so we need to do the same here to avoid issues.
|
||||
Math.floor(Math.max(minWidth, w)) + 'px'
|
||||
Math.floor(Math.max(minWidth, w))
|
||||
|
||||
const result = editor.textMeasure.measureText(text, {
|
||||
...TEXT_PROPS,
|
||||
|
|
|
@ -81,7 +81,7 @@ export class TestEditor extends Editor {
|
|||
fontFamily: string
|
||||
fontSize: number
|
||||
lineHeight: number
|
||||
width: string
|
||||
width: null | number
|
||||
maxWidth: string
|
||||
}
|
||||
): Box2dModel => {
|
||||
|
@ -95,18 +95,17 @@ export class TestEditor extends Editor {
|
|||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: opts.width.includes('px') ? Math.max(w, +opts.width.replace('px', '')) : w,
|
||||
w: opts.width === null ? w : Math.max(w, opts.width),
|
||||
h:
|
||||
(opts.width.includes('px')
|
||||
? Math.ceil(w % +opts.width.replace('px', '')) + breaks.length
|
||||
: breaks.length) * opts.fontSize,
|
||||
(opts.width === null ? breaks.length : Math.ceil(w % opts.width) + breaks.length) *
|
||||
opts.fontSize,
|
||||
}
|
||||
}
|
||||
|
||||
this.textMeasure.measureTextSpans = (textToMeasure, opts) => {
|
||||
const box = this.textMeasure.measureText(textToMeasure, {
|
||||
...opts,
|
||||
width: `${opts.width}px`,
|
||||
width: opts.width,
|
||||
padding: `${opts.padding}px`,
|
||||
maxWidth: 'auto',
|
||||
})
|
||||
|
|
Ładowanie…
Reference in New Issue