Add migration for horizontal alignment (#1443)

This adds a migration to migrate existing alignment options to their
legacy counter parts (`start` -> `start-legacy`, `end` -> `end-legacy`,
`middle` -> `middle-legacy`).

With this change the legacy options don't show any align as active in
the Styles panel:
![CleanShot 2023-05-23 at 19 53
45](https://github.com/tldraw/tldraw/assets/2523721/4017e03a-9492-4a02-b991-ac206f40ae17)

I think this is probably what we want.

### Change Type

- [x] `patch` — Bug Fix

### Test Plan

1. Use some old preview link to create Geo and Note shapes with old
alignment options. You can use this one:
https://examples-kzwtf68jr-tldraw.vercel.app/
2. Copy and paste these shapes over to staging. Nothing should change
visually.
3. Also try out exporting to svg (with both old and new alignment
options)

- [x] Unit Tests
- [ ] Webdriver tests

### Release Notes

- Add support for legacy alignment options.

---------

Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
pull/1454/head
Mitja Bezenšek 2023-05-24 13:34:13 +02:00 zatwierdzone przez GitHub
rodzic 89428edffc
commit 787eab75d6
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
11 zmienionych plików z 216 dodań i 10 usunięć

Wyświetl plik

@ -1090,15 +1090,18 @@ input,
opacity: 0;
}
.tl-text-label[data-align='start'] {
.tl-text-label[data-align='start'],
.tl-text-label[data-align='start-legacy'] {
text-align: left;
}
.tl-text-label[data-align='middle'] {
.tl-text-label[data-align='middle'],
.tl-text-label[data-align='middle-legacy'] {
text-align: center;
}
.tl-text-label[data-align='end'] {
.tl-text-label[data-align='end'],
.tl-text-label[data-align='end-legacy'] {
text-align: right;
}

Wyświetl plik

@ -3,10 +3,13 @@ import { uniqueId } from '../../utils/data'
import { App } from '../App'
import { TextHelpers } from '../shapeutils/TLTextUtil/TextHelpers'
const textAlignmentsForLtr: Record<TLAlignType, string> = {
const textAlignmentsForLtr = {
start: 'left',
'start-legacy': 'left',
middle: 'center',
'middle-legacy': 'center',
end: 'right',
'end-legacy': 'right',
}
type OverflowMode = 'wrap' | 'truncate-ellipsis' | 'truncate-clip'

Wyświetl plik

@ -15,6 +15,7 @@ import {
import { TLDashType, TLGeoShape, TLGeoShapeProps } from '@tldraw/tlschema'
import { SVGContainer } from '../../../components/SVGContainer'
import { FONT_FAMILIES, LABEL_FONT_SIZES, TEXT_PROPS } from '../../../constants'
import { getLegacyOffsetX } from '../../../utils/legacy'
import { App } from '../../App'
import { createTextSvgElementFromSpans } from '../shared/createTextSvgElementFromSpans'
import { HyperlinkButton } from '../shared/HyperlinkButton'
@ -646,9 +647,14 @@ export class TLGeoUtil extends TLBoxUtil<TLGeoShape> {
width: Math.ceil(bounds.width),
height: Math.ceil(bounds.height),
overflow: 'wrap' as const,
offsetX: 0,
}
const spans = this.app.textMeasure.measureTextSpans(props.text, opts)
const offsetX = getLegacyOffsetX(shape.props.align, padding, spans, bounds.width)
if (offsetX) {
opts.offsetX = offsetX
}
const groupEl = document.createElementNS('http://www.w3.org/2000/svg', 'g')

Wyświetl plik

@ -1,6 +1,7 @@
import { Box2d, toDomPrecision, Vec2d } from '@tldraw/primitives'
import { TLNoteShape } from '@tldraw/tlschema'
import { FONT_FAMILIES, LABEL_FONT_SIZES, TEXT_PROPS } from '../../../constants'
import { getLegacyOffsetX } from '../../../utils/legacy'
import { App } from '../../App'
import { createTextSvgElementFromSpans } from '../shared/createTextSvgElementFromSpans'
import { HyperlinkButton } from '../shared/HyperlinkButton'
@ -149,6 +150,11 @@ export class TLNoteUtil extends TLShapeUtil<TLNoteShape> {
const spans = this.app.textMeasure.measureTextSpans(shape.props.text, opts)
opts.width = bounds.width
const offsetX = getLegacyOffsetX(shape.props.align, PADDING, spans, bounds.width)
if (offsetX) {
opts.offsetX = offsetX
}
opts.padding = PADDING
const textElm = createTextSvgElementFromSpans(this.app, spans, opts)

Wyświetl plik

@ -9,6 +9,7 @@ import {
import React from 'react'
import { LABEL_FONT_SIZES, TEXT_PROPS } from '../../../constants'
import { stopEventPropagation } from '../../../utils/dom'
import { isLegacyAlign } from '../../../utils/legacy'
import { TextHelpers } from '../TLTextUtil/TextHelpers'
import { useEditableText } from './useEditableText'
@ -50,6 +51,7 @@ export const TextLabel = React.memo(function TextLabel<
const isInteractive = isEditing || isEditableFromHover
const finalText = TextHelpers.normalizeTextForDom(text)
const hasText = finalText.trim().length > 0
const legacyAlign = isLegacyAlign(align)
return (
<div
@ -62,7 +64,7 @@ export const TextLabel = React.memo(function TextLabel<
style={
hasText || isInteractive
? {
justifyContent: align === 'middle' ? 'center' : align,
justifyContent: align === 'middle' || legacyAlign ? 'center' : align,
alignItems: verticalAlign === 'middle' ? 'center' : verticalAlign,
}
: undefined

Wyświetl plik

@ -0,0 +1,26 @@
import { Box2d } from '@tldraw/primitives'
import { Box2dModel, TLAlignType } from '@tldraw/tlschema'
export function getLegacyOffsetX(
align: TLAlignType | string,
padding: number,
spans: { text: string; box: Box2dModel }[],
totalWidth: number
): number | undefined {
if ((align === 'start-legacy' || align === 'end-legacy') && spans.length !== 0) {
const spansBounds = Box2d.From(spans[0].box)
for (const { box } of spans) {
spansBounds.union(box)
}
if (align === 'start-legacy') {
return (totalWidth - 2 * padding - spansBounds.width) / 2
} else if (align === 'end-legacy') {
return -(totalWidth - 2 * padding - spansBounds.width) / 2
}
}
}
// sneaky TLAlignType for legacies
export function isLegacyAlign(align: TLAlignType | string): boolean {
return align === 'start-legacy' || align === 'middle-legacy' || align === 'end-legacy'
}

Wyświetl plik

@ -704,6 +704,60 @@ describe('Add verticalAlign to props for next shape', () => {
})
})
describe('Migrate GeoShape legacy horizontal alignment', () => {
const { up, down } = geoShapeTypeMigrations.migrators[6]
test('up works as expected', () => {
expect(up({ props: { align: 'start', type: 'ellipse' } })).toEqual({
props: { align: 'start-legacy', type: 'ellipse' },
})
expect(up({ props: { align: 'middle', type: 'ellipse' } })).toEqual({
props: { align: 'middle-legacy', type: 'ellipse' },
})
expect(up({ props: { align: 'end', type: 'ellipse' } })).toEqual({
props: { align: 'end-legacy', type: 'ellipse' },
})
})
test('down works as expected', () => {
expect(down({ props: { align: 'start-legacy', type: 'ellipse' } })).toEqual({
props: { align: 'start', type: 'ellipse' },
})
expect(down({ props: { align: 'middle-legacy', type: 'ellipse' } })).toEqual({
props: { align: 'middle', type: 'ellipse' },
})
expect(down({ props: { align: 'end-legacy', type: 'ellipse' } })).toEqual({
props: { align: 'end', type: 'ellipse' },
})
})
})
describe('Migrate NoteShape legacy horizontal alignment', () => {
const { up, down } = noteShapeTypeMigrations.migrators[3]
test('up works as expected', () => {
expect(up({ props: { align: 'start', color: 'red' } })).toEqual({
props: { align: 'start-legacy', color: 'red' },
})
expect(up({ props: { align: 'middle', color: 'red' } })).toEqual({
props: { align: 'middle-legacy', color: 'red' },
})
expect(up({ props: { align: 'end', color: 'red' } })).toEqual({
props: { align: 'end-legacy', color: 'red' },
})
})
test('down works as expected', () => {
expect(down({ props: { align: 'start-legacy', color: 'red' } })).toEqual({
props: { align: 'start', color: 'red' },
})
expect(down({ props: { align: 'middle-legacy', color: 'red' } })).toEqual({
props: { align: 'middle', color: 'red' },
})
expect(down({ props: { align: 'end-legacy', color: 'red' } })).toEqual({
props: { align: 'end', color: 'red' },
})
})
})
describe('Removing isReadOnly from user_document', () => {
const { up, down } = userDocumentTypeMigrations.migrators[userDocumentVersions.RemoveIsReadOnly]
const prev = {

Wyświetl plik

@ -74,11 +74,12 @@ const Versions = {
RemoveJustify: 3,
AddCheckBox: 4,
AddVerticalAlign: 5,
MigrateLegacyAlign: 6,
} as const
/** @public */
export const geoShapeTypeMigrations = defineMigrations({
currentVersion: Versions.AddVerticalAlign,
currentVersion: Versions.MigrateLegacyAlign,
migrators: {
[Versions.AddUrlProp]: {
up: (shape) => {
@ -158,5 +159,51 @@ export const geoShapeTypeMigrations = defineMigrations({
}
},
},
[Versions.MigrateLegacyAlign]: {
up: (shape) => {
let newAlign: TLAlignType
switch (shape.props.align) {
case 'start':
newAlign = 'start-legacy' as TLAlignType
break
case 'end':
newAlign = 'end-legacy' as TLAlignType
break
default:
newAlign = 'middle-legacy' as TLAlignType
break
}
return {
...shape,
props: {
...shape.props,
align: newAlign,
},
}
},
down: (shape) => {
let oldAlign: TLAlignType
switch (shape.props.align) {
case 'start-legacy':
oldAlign = 'start'
break
case 'end-legacy':
oldAlign = 'end'
break
case 'middle-legacy':
oldAlign = 'middle'
break
default:
oldAlign = shape.props.align
}
return {
...shape,
props: {
...shape.props,
align: oldAlign,
},
}
},
},
},
})

Wyświetl plik

@ -43,11 +43,12 @@ export const noteShapeTypeValidator: T.Validator<TLNoteShape> = createShapeValid
const Versions = {
AddUrlProp: 1,
RemoveJustify: 2,
MigrateLegacyAlign: 3,
} as const
/** @public */
export const noteShapeTypeMigrations = defineMigrations({
currentVersion: Versions.RemoveJustify,
currentVersion: Versions.MigrateLegacyAlign,
migrators: {
[Versions.AddUrlProp]: {
up: (shape) => {
@ -77,5 +78,52 @@ export const noteShapeTypeMigrations = defineMigrations({
return { ...shape }
},
},
[Versions.MigrateLegacyAlign]: {
up: (shape) => {
let newAlign: TLAlignType
switch (shape.props.align) {
case 'start':
newAlign = 'start-legacy' as TLAlignType
break
case 'end':
newAlign = 'end-legacy' as TLAlignType
break
default:
newAlign = 'middle-legacy' as TLAlignType
break
}
return {
...shape,
props: {
...shape.props,
align: newAlign,
},
}
},
down: (shape) => {
let oldAlign: TLAlignType
switch (shape.props.align) {
case 'start-legacy':
oldAlign = 'start'
break
case 'end-legacy':
oldAlign = 'end'
break
case 'middle-legacy':
oldAlign = 'middle'
break
default:
oldAlign = shape.props.align
}
return {
...shape,
props: {
...shape.props,
align: oldAlign,
},
}
},
},
},
})

Wyświetl plik

@ -125,6 +125,14 @@ export interface TLFontStyle extends TLBaseStyle {
/** @public */
export const TL_ALIGN_TYPES = new Set(['start', 'middle', 'end'] as const)
/** @internal */
export const TL_ALIGN_TYPES_WITH_LEGACY_STUFF = new Set([
...TL_ALIGN_TYPES,
'start-legacy',
'end-legacy',
'middle-legacy',
] as const)
/** @public */
export type TLAlignType = SetValue<typeof TL_ALIGN_TYPES>
@ -142,7 +150,7 @@ export interface TLAlignStyle extends TLBaseStyle {
// Geo Text Vertical Align
/** @public */
export const TL_VERTICAL_ALIGN_TYPES = TL_ALIGN_TYPES
export const TL_VERTICAL_ALIGN_TYPES = new Set(['start', 'middle', 'end'] as const)
/** @public */
export type TLVerticalAlignType = SetValue<typeof TL_VERTICAL_ALIGN_TYPES>

Wyświetl plik

@ -6,7 +6,8 @@ import type { TLPageId } from './records/TLPage'
import type { TLParentId, TLShapeId } from './records/TLShape'
import type { TLUserId } from './records/TLUser'
import {
TL_ALIGN_TYPES,
TLAlignType,
TL_ALIGN_TYPES_WITH_LEGACY_STUFF,
TL_ARROWHEAD_TYPES,
TL_COLOR_TYPES,
TL_DASH_TYPES,
@ -63,7 +64,9 @@ export const sizeValidator = T.setEnum(TL_SIZE_TYPES)
/** @internal */
export const fontValidator = T.setEnum(TL_FONT_TYPES)
/** @internal */
export const alignValidator = T.setEnum(TL_ALIGN_TYPES)
export const alignValidator = T.setEnum<TLAlignType>(
TL_ALIGN_TYPES_WITH_LEGACY_STUFF as Set<TLAlignType>
)
/** @internal */
export const verticalAlignValidator = T.setEnum(TL_VERTICAL_ALIGN_TYPES)
/** @internal */