kopia lustrzana https://github.com/Tldraw/Tldraw
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
rodzic
89428edffc
commit
787eab75d6
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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')
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
}
|
|
@ -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 = {
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 */
|
||||
|
|
Ładowanie…
Reference in New Issue