Vertical text alignment for geo shapes (#1414)
Vertical text alignment for geo shapes. ### Change Type - [x] `minor` — New Feature ### Test Plan 1. Add a step-by-step description of how to test your PR here. 2. - [ ] Unit Tests - [ ] Webdriver tests ### Release Notes - This adds vertical text alignment property to geo shapes. --------- Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
This commit is contained in:
parent
ddca01918f
commit
f59bfe01b1
32 changed files with 254 additions and 21 deletions
7
assets/icons/icon/vertical-align-center.svg
Normal file
7
assets/icons/icon/vertical-align-center.svg
Normal file
|
@ -0,0 +1,7 @@
|
|||
<svg width="30" height="31" viewBox="0 0 30 31" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="15" y="19.2969" width="2" height="8" rx="1" transform="rotate(45 15 19.2969)" fill="black"/>
|
||||
<rect x="13.5858" y="20.7111" width="2" height="8" rx="1" transform="rotate(-45 13.5858 20.7111)" fill="black"/>
|
||||
<rect x="15" y="11.1179" width="2" height="8" rx="1" transform="rotate(-135 15 11.1179)" fill="black"/>
|
||||
<rect x="16.4142" y="9.70374" width="2" height="8" rx="1" transform="rotate(135 16.4142 9.70374)" fill="black"/>
|
||||
<rect x="4" y="16.2069" width="2" height="22" rx="1" transform="rotate(-90 4 16.2069)" fill="black"/>
|
||||
</svg>
|
After Width: | Height: | Size: 637 B |
5
assets/icons/icon/vertical-align-end.svg
Normal file
5
assets/icons/icon/vertical-align-end.svg
Normal file
|
@ -0,0 +1,5 @@
|
|||
<svg width="30" height="31" viewBox="0 0 30 31" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="26" y="19.7069" width="2" height="22" rx="1" transform="rotate(90 26 19.7069)" fill="black"/>
|
||||
<rect x="15" y="16.6169" width="2" height="8" rx="1" transform="rotate(-135 15 16.6169)" fill="black"/>
|
||||
<rect x="16.4142" y="15.2028" width="2" height="8" rx="1" transform="rotate(135 16.4142 15.2028)" fill="black"/>
|
||||
</svg>
|
After Width: | Height: | Size: 423 B |
5
assets/icons/icon/vertical-align-start.svg
Normal file
5
assets/icons/icon/vertical-align-start.svg
Normal file
|
@ -0,0 +1,5 @@
|
|||
<svg width="30" height="31" viewBox="0 0 30 31" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="4" y="10.7069" width="2" height="22" rx="1" transform="rotate(-90 4 10.7069)" fill="black"/>
|
||||
<rect x="15" y="13.7986" width="2" height="8" rx="1" transform="rotate(45 15 13.7986)" fill="black"/>
|
||||
<rect x="13.5858" y="15.2128" width="2" height="8" rx="1" transform="rotate(-45 13.5858 15.2128)" fill="black"/>
|
||||
</svg>
|
After Width: | Height: | Size: 420 B |
|
@ -272,6 +272,7 @@
|
|||
"shortcuts-dialog.view": "View",
|
||||
"style-panel.title": "Styles",
|
||||
"style-panel.align": "Align",
|
||||
"style-panel.vertical-align": "Vertical align",
|
||||
"style-panel.position": "Position",
|
||||
"style-panel.arrowheads": "Arrowheads",
|
||||
"style-panel.arrowhead-start": "Start",
|
||||
|
|
3
packages/assets/imports.d.ts
vendored
3
packages/assets/imports.d.ts
vendored
|
@ -165,6 +165,9 @@ export function getAssetUrlsByImport(opts?: AssetUrlOptions): {
|
|||
ungroup: string
|
||||
'unlock-small': string
|
||||
unlock: string
|
||||
'vertical-align-center': string
|
||||
'vertical-align-end': string
|
||||
'vertical-align-start': string
|
||||
visible: string
|
||||
'warning-triangle': string
|
||||
'zoom-in': string
|
||||
|
|
|
@ -176,6 +176,9 @@ import iconsUndo from './icons/icon/undo.svg'
|
|||
import iconsUngroup from './icons/icon/ungroup.svg'
|
||||
import iconsUnlockSmall from './icons/icon/unlock-small.svg'
|
||||
import iconsUnlock from './icons/icon/unlock.svg'
|
||||
import iconsVerticalAlignCenter from './icons/icon/vertical-align-center.svg'
|
||||
import iconsVerticalAlignEnd from './icons/icon/vertical-align-end.svg'
|
||||
import iconsVerticalAlignStart from './icons/icon/vertical-align-start.svg'
|
||||
import iconsVisible from './icons/icon/visible.svg'
|
||||
import iconsWarningTriangle from './icons/icon/warning-triangle.svg'
|
||||
import iconsZoomIn from './icons/icon/zoom-in.svg'
|
||||
|
@ -403,6 +406,9 @@ export function getAssetUrlsByImport(opts) {
|
|||
ungroup: formatAssetUrl(iconsUngroup, opts),
|
||||
'unlock-small': formatAssetUrl(iconsUnlockSmall, opts),
|
||||
unlock: formatAssetUrl(iconsUnlock, opts),
|
||||
'vertical-align-center': formatAssetUrl(iconsVerticalAlignCenter, opts),
|
||||
'vertical-align-end': formatAssetUrl(iconsVerticalAlignEnd, opts),
|
||||
'vertical-align-start': formatAssetUrl(iconsVerticalAlignStart, opts),
|
||||
visible: formatAssetUrl(iconsVisible, opts),
|
||||
'warning-triangle': formatAssetUrl(iconsWarningTriangle, opts),
|
||||
'zoom-in': formatAssetUrl(iconsZoomIn, opts),
|
||||
|
|
3
packages/assets/urls.d.ts
vendored
3
packages/assets/urls.d.ts
vendored
|
@ -165,6 +165,9 @@ export function getAssetUrlsByMetaUrl(opts?: AssetUrlOptions): {
|
|||
ungroup: string
|
||||
'unlock-small': string
|
||||
unlock: string
|
||||
'vertical-align-center': string
|
||||
'vertical-align-end': string
|
||||
'vertical-align-start': string
|
||||
visible: string
|
||||
'warning-triangle': string
|
||||
'zoom-in': string
|
||||
|
|
|
@ -539,6 +539,18 @@ export function getAssetUrlsByMetaUrl(opts) {
|
|||
opts
|
||||
),
|
||||
unlock: formatAssetUrl(new URL('./icons/icon/unlock.svg', import.meta.url).href, opts),
|
||||
'vertical-align-center': formatAssetUrl(
|
||||
new URL('./icons/icon/vertical-align-center.svg', import.meta.url).href,
|
||||
opts
|
||||
),
|
||||
'vertical-align-end': formatAssetUrl(
|
||||
new URL('./icons/icon/vertical-align-end.svg', import.meta.url).href,
|
||||
opts
|
||||
),
|
||||
'vertical-align-start': formatAssetUrl(
|
||||
new URL('./icons/icon/vertical-align-start.svg', import.meta.url).href,
|
||||
opts
|
||||
),
|
||||
visible: formatAssetUrl(new URL('./icons/icon/visible.svg', import.meta.url).href, opts),
|
||||
'warning-triangle': formatAssetUrl(
|
||||
new URL('./icons/icon/warning-triangle.svg', import.meta.url).href,
|
||||
|
|
|
@ -1720,7 +1720,7 @@ export abstract class TLBoxTool extends StateNode {
|
|||
// (undocumented)
|
||||
abstract shapeType: string;
|
||||
// (undocumented)
|
||||
styles: ("align" | "arrowheadEnd" | "arrowheadStart" | "color" | "dash" | "fill" | "font" | "geo" | "icon" | "labelColor" | "opacity" | "size" | "spline")[];
|
||||
styles: ("align" | "arrowheadEnd" | "arrowheadStart" | "color" | "dash" | "fill" | "font" | "geo" | "icon" | "labelColor" | "opacity" | "size" | "spline" | "verticalAlign")[];
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
|
@ -2127,6 +2127,7 @@ export class TLGeoUtil extends TLBoxUtil<TLGeoShape> {
|
|||
opacity: "0.1" | "0.25" | "0.5" | "0.75" | "1";
|
||||
font: "draw" | "mono" | "sans" | "serif";
|
||||
align: "end" | "middle" | "start";
|
||||
verticalAlign: "end" | "middle" | "start";
|
||||
url: string;
|
||||
w: number;
|
||||
h: number;
|
||||
|
@ -2155,6 +2156,7 @@ export class TLGeoUtil extends TLBoxUtil<TLGeoShape> {
|
|||
opacity: "0.1" | "0.25" | "0.5" | "0.75" | "1";
|
||||
font: "draw" | "mono" | "sans" | "serif";
|
||||
align: "end" | "middle" | "start";
|
||||
verticalAlign: "end" | "middle" | "start";
|
||||
url: string;
|
||||
w: number;
|
||||
h: number;
|
||||
|
|
|
@ -1000,7 +1000,6 @@ input,
|
|||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: var(--color-text);
|
||||
text-shadow: var(--tl-text-outline);
|
||||
overflow: hidden;
|
||||
|
|
|
@ -1053,6 +1053,7 @@ export class TLArrowUtil extends TLShapeUtil<TLArrowShape> {
|
|||
fontFamily: font,
|
||||
padding: 0,
|
||||
textAlign: 'middle' as const,
|
||||
verticalTextAlign: 'middle' as const,
|
||||
width: labelSize.w,
|
||||
height: labelSize.h,
|
||||
fontStyle: 'normal',
|
||||
|
|
|
@ -66,6 +66,7 @@ export class TLGeoUtil extends TLBoxUtil<TLGeoShape> {
|
|||
font: 'draw',
|
||||
text: '',
|
||||
align: 'middle',
|
||||
verticalAlign: 'middle',
|
||||
growY: 0,
|
||||
url: '',
|
||||
}
|
||||
|
@ -343,7 +344,8 @@ export class TLGeoUtil extends TLBoxUtil<TLGeoShape> {
|
|||
const forceSolid = useForceSolid()
|
||||
const strokeWidth = this.app.getStrokeWidth(props.size)
|
||||
|
||||
const { w, color, labelColor, fill, dash, growY, font, align, size, text } = props
|
||||
const { w, color, labelColor, fill, dash, growY, font, align, verticalAlign, size, text } =
|
||||
props
|
||||
|
||||
const getShape = () => {
|
||||
const h = props.h + growY
|
||||
|
@ -448,6 +450,7 @@ export class TLGeoUtil extends TLBoxUtil<TLGeoShape> {
|
|||
fill={fill}
|
||||
size={size}
|
||||
align={align}
|
||||
verticalAlign={verticalAlign}
|
||||
text={text}
|
||||
labelColor={this.app.getCssColor(labelColor)}
|
||||
wrap
|
||||
|
@ -641,6 +644,7 @@ export class TLGeoUtil extends TLBoxUtil<TLGeoShape> {
|
|||
fontSize: LABEL_FONT_SIZES[shape.props.size],
|
||||
fontFamily: font,
|
||||
textAlign: shape.props.align,
|
||||
verticalTextAlign: shape.props.verticalAlign,
|
||||
padding: 16,
|
||||
lineHeight: TEXT_PROPS.lineHeight,
|
||||
fontStyle: 'normal',
|
||||
|
|
|
@ -82,6 +82,7 @@ export class TLNoteUtil extends TLShapeUtil<TLNoteShape> {
|
|||
font={font}
|
||||
size={size}
|
||||
align={align}
|
||||
verticalAlign="middle"
|
||||
text={text}
|
||||
labelColor="inherit"
|
||||
wrap
|
||||
|
@ -135,6 +136,7 @@ export class TLNoteUtil extends TLShapeUtil<TLNoteShape> {
|
|||
fontSize: LABEL_FONT_SIZES[shape.props.size],
|
||||
fontFamily: font,
|
||||
textAlign: shape.props.align,
|
||||
verticalTextAlign: 'middle' as const,
|
||||
width: bounds.width - PADDING * 2,
|
||||
height: bounds.height - PADDING * 2,
|
||||
padding: 0,
|
||||
|
|
|
@ -167,6 +167,7 @@ export class TLTextUtil extends TLShapeUtil<TLTextShape> {
|
|||
fontSize: FONT_SIZES[shape.props.size],
|
||||
fontFamily: font!,
|
||||
textAlign: shape.props.align,
|
||||
verticalTextAlign: 'middle' as const,
|
||||
width,
|
||||
height,
|
||||
padding: 0, // no padding?
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
import { TLAlignType, TLFillType, TLFontType, TLShape, TLSizeType } from '@tldraw/tlschema'
|
||||
import {
|
||||
TLAlignType,
|
||||
TLFillType,
|
||||
TLFontType,
|
||||
TLShape,
|
||||
TLSizeType,
|
||||
TLVerticalAlignType,
|
||||
} from '@tldraw/tlschema'
|
||||
import React from 'react'
|
||||
import { LABEL_FONT_SIZES, TEXT_PROPS } from '../../../constants'
|
||||
import { stopEventPropagation } from '../../../utils/dom'
|
||||
|
@ -15,6 +22,7 @@ export const TextLabel = React.memo(function TextLabel<
|
|||
labelColor,
|
||||
font,
|
||||
align,
|
||||
verticalAlign,
|
||||
wrap,
|
||||
}: {
|
||||
id: T['id']
|
||||
|
@ -23,6 +31,7 @@ export const TextLabel = React.memo(function TextLabel<
|
|||
font: TLFontType
|
||||
fill?: TLFillType
|
||||
align: TLAlignType
|
||||
verticalAlign: TLVerticalAlignType
|
||||
wrap?: boolean
|
||||
text: string
|
||||
labelColor: string
|
||||
|
@ -48,6 +57,7 @@ export const TextLabel = React.memo(function TextLabel<
|
|||
data-hastext={!isEmpty}
|
||||
data-isediting={isEditing}
|
||||
data-textwrap={!!wrap}
|
||||
style={{ alignItems: verticalAlign === 'middle' ? 'center' : verticalAlign }}
|
||||
>
|
||||
<div
|
||||
className="tl-text-label__inner"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { TLAlignType } from '@tldraw/tlschema'
|
||||
import { TLAlignType, TLVerticalAlignType } from '@tldraw/tlschema'
|
||||
import { TEXT_PROPS } from '../../../constants'
|
||||
import { correctSpacesToNbsp } from '../../../utils/string'
|
||||
import { App } from '../../App'
|
||||
|
@ -11,6 +11,7 @@ export function getTextSvgElement(
|
|||
fontSize: number
|
||||
fontFamily: string
|
||||
textAlign: TLAlignType
|
||||
verticalTextAlign: TLVerticalAlignType
|
||||
fontWeight: string
|
||||
fontStyle: string
|
||||
lineHeight: number
|
||||
|
@ -40,7 +41,21 @@ export function getTextSvgElement(
|
|||
|
||||
const innerHeight = lines.length * (opts.lineHeight * opts.fontSize)
|
||||
|
||||
const offsetY = (Math.ceil(opts.height) - innerHeight) / 2
|
||||
let offsetY: number
|
||||
switch (opts.verticalTextAlign) {
|
||||
case 'start': {
|
||||
offsetY = padding
|
||||
break
|
||||
}
|
||||
case 'end': {
|
||||
offsetY = opts.height - padding - innerHeight
|
||||
break
|
||||
}
|
||||
default: {
|
||||
offsetY = (Math.ceil(opts.height) - innerHeight) / 2
|
||||
}
|
||||
}
|
||||
|
||||
const offsetX = padding
|
||||
|
||||
// Create text span elements for each line
|
||||
|
|
|
@ -235,6 +235,11 @@ export const STYLES: TLStyleCollections = {
|
|||
{ id: 'middle', type: 'align', icon: 'text-align-center' },
|
||||
{ id: 'end', type: 'align', icon: 'text-align-right' },
|
||||
],
|
||||
verticalAlign: [
|
||||
{ id: 'start', type: 'verticalAlign', icon: 'vertical-align-start' },
|
||||
{ id: 'middle', type: 'verticalAlign', icon: 'vertical-align-center' },
|
||||
{ id: 'end', type: 'verticalAlign', icon: 'vertical-align-end' },
|
||||
],
|
||||
geo: [
|
||||
{ id: 'rectangle', type: 'geo', icon: 'geo-rectangle' },
|
||||
{ id: 'ellipse', type: 'geo', icon: 'geo-ellipse' },
|
||||
|
|
|
@ -21,6 +21,7 @@ Array [
|
|||
"size": "m",
|
||||
"text": "",
|
||||
"url": "",
|
||||
"verticalAlign": "middle",
|
||||
"w": 100,
|
||||
},
|
||||
"rotation": 3.141592653589793,
|
||||
|
@ -48,6 +49,7 @@ Array [
|
|||
"size": "m",
|
||||
"text": "",
|
||||
"url": "",
|
||||
"verticalAlign": "middle",
|
||||
"w": 100,
|
||||
},
|
||||
"rotation": 0,
|
||||
|
@ -75,6 +77,7 @@ Array [
|
|||
"size": "m",
|
||||
"text": "",
|
||||
"url": "",
|
||||
"verticalAlign": "middle",
|
||||
"w": 100,
|
||||
},
|
||||
"rotation": 0,
|
||||
|
@ -107,6 +110,7 @@ Array [
|
|||
"size": "m",
|
||||
"text": "",
|
||||
"url": "",
|
||||
"verticalAlign": "middle",
|
||||
"w": 100,
|
||||
},
|
||||
"rotation": 0,
|
||||
|
@ -134,6 +138,7 @@ Array [
|
|||
"size": "m",
|
||||
"text": "",
|
||||
"url": "",
|
||||
"verticalAlign": "middle",
|
||||
"w": 100,
|
||||
},
|
||||
"rotation": 0,
|
||||
|
@ -161,6 +166,7 @@ Array [
|
|||
"size": "m",
|
||||
"text": "",
|
||||
"url": "",
|
||||
"verticalAlign": "middle",
|
||||
"w": 100,
|
||||
},
|
||||
"rotation": 0,
|
||||
|
|
|
@ -31,6 +31,7 @@ describe('App.props', () => {
|
|||
size: 'm',
|
||||
font: 'draw',
|
||||
geo: 'rectangle',
|
||||
verticalAlign: 'middle',
|
||||
// h: 100,
|
||||
// w: 100,
|
||||
// growY: 0,
|
||||
|
@ -51,6 +52,7 @@ describe('App.props', () => {
|
|||
size: 'm',
|
||||
font: 'draw',
|
||||
geo: 'rectangle',
|
||||
verticalAlign: 'middle',
|
||||
// h: 100, // blacklisted
|
||||
// w: 100, // blacklisted
|
||||
// growY: 0, // blacklist
|
||||
|
@ -81,6 +83,7 @@ describe('App.props', () => {
|
|||
font: 'draw',
|
||||
geo: 'rectangle',
|
||||
opacity: '1',
|
||||
verticalAlign: 'middle',
|
||||
// h: null, // mixed! but also blacklisted
|
||||
// w: null, // mixed! but also blacklisted
|
||||
// growY: 0, // blacklist
|
||||
|
@ -110,6 +113,7 @@ describe('App.props', () => {
|
|||
w: 100,
|
||||
opacity: '0.5',
|
||||
url: 'https://aol.com',
|
||||
verticalAlign: 'start',
|
||||
},
|
||||
},
|
||||
])
|
||||
|
@ -125,6 +129,7 @@ describe('App.props', () => {
|
|||
size: null,
|
||||
font: null,
|
||||
opacity: null,
|
||||
verticalAlign: null,
|
||||
// growY: null, // blacklist
|
||||
// url: null, // blacklist
|
||||
})
|
||||
|
|
|
@ -531,7 +531,7 @@ export const TL_SIZE_TYPES: Set<"l" | "m" | "s" | "xl">;
|
|||
export const TL_SPLINE_TYPES: Set<"cubic" | "line">;
|
||||
|
||||
// @public (undocumented)
|
||||
export const TL_STYLE_TYPES: Set<"align" | "arrowheadEnd" | "arrowheadStart" | "color" | "dash" | "fill" | "font" | "geo" | "icon" | "labelColor" | "opacity" | "size" | "spline">;
|
||||
export const TL_STYLE_TYPES: Set<"align" | "arrowheadEnd" | "arrowheadStart" | "color" | "dash" | "fill" | "font" | "geo" | "icon" | "labelColor" | "opacity" | "size" | "spline" | "verticalAlign">;
|
||||
|
||||
// @public (undocumented)
|
||||
export const TL_UI_COLOR_TYPES: Set<"accent" | "black" | "muted-1" | "selection-fill" | "selection-stroke" | "white">;
|
||||
|
@ -544,6 +544,14 @@ export interface TLAlignStyle extends TLBaseStyle {
|
|||
type: 'align';
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface TLAlignStyle extends TLBaseStyle {
|
||||
// (undocumented)
|
||||
id: TLAlignType;
|
||||
// (undocumented)
|
||||
type: 'align';
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLAlignType = SetValue<typeof TL_ALIGN_TYPES>;
|
||||
|
||||
|
@ -860,6 +868,7 @@ export type TLGeoShapeProps = {
|
|||
opacity: TLOpacityType;
|
||||
font: TLFontType;
|
||||
align: TLAlignType;
|
||||
verticalAlign: TLVerticalAlignType;
|
||||
url: string;
|
||||
w: number;
|
||||
h: number;
|
||||
|
@ -1235,6 +1244,8 @@ export interface TLStyleCollections {
|
|||
size: TLSizeStyle[];
|
||||
// (undocumented)
|
||||
spline: TLSplineTypeStyle[];
|
||||
// (undocumented)
|
||||
verticalAlign: TLVerticalAlignStyle[];
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
|
@ -1330,6 +1341,9 @@ export const TLUserPresence: RecordType<TLUserPresence, "userId">;
|
|||
// @public (undocumented)
|
||||
export type TLUserPresenceId = ID<TLUserPresence>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLVerticalAlignType = SetValue<typeof TL_VERTICAL_ALIGN_TYPES>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLVideoAsset = TLBaseAsset<'video', {
|
||||
w: number;
|
||||
|
|
|
@ -227,6 +227,7 @@ export {
|
|||
type TLStyleItem,
|
||||
type TLStyleProps,
|
||||
type TLStyleType,
|
||||
type TLVerticalAlignType,
|
||||
} from './style-types'
|
||||
export {
|
||||
TL_CURSOR_TYPES,
|
||||
|
|
|
@ -665,6 +665,42 @@ describe('Adding check-box to geo shape', () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe('Add verticalAlign to geo shape', () => {
|
||||
const { up, down } = geoShapeMigrations.migrators[5]
|
||||
|
||||
test('up works as expected', () => {
|
||||
expect(up({ props: { type: 'ellipse' } })).toEqual({
|
||||
props: { type: 'ellipse', verticalAlign: 'middle' },
|
||||
})
|
||||
})
|
||||
test('down works as expected', () => {
|
||||
expect(down({ props: { verticalAlign: 'middle', type: 'ellipse' } })).toEqual({
|
||||
props: { type: 'ellipse' },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Add verticalAlign to props for next shape', () => {
|
||||
const { up, down } = instanceTypeMigrations.migrators[9]
|
||||
test('up works as expected', () => {
|
||||
expect(up({ propsForNextShape: { color: 'red' } })).toEqual({
|
||||
propsForNextShape: {
|
||||
color: 'red',
|
||||
verticalAlign: 'middle',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test('down works as expected', () => {
|
||||
const instance = { propsForNextShape: { color: 'red', verticalAlign: 'middle' } }
|
||||
expect(down(instance)).toEqual({
|
||||
propsForNextShape: {
|
||||
color: 'red',
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Removing isReadOnly from user_document', () => {
|
||||
const { up, down } = userDocumentTypeMigrations.migrators[userDocumentVersions.RemoveIsReadOnly]
|
||||
const prev = {
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
sizeValidator,
|
||||
splineValidator,
|
||||
userIdValidator,
|
||||
verticalAlignValidator,
|
||||
} from '../validation'
|
||||
import { TLPageId } from './TLPage'
|
||||
import { TLShapeProps } from './TLShape'
|
||||
|
@ -72,6 +73,7 @@ export const instanceTypeValidator: T.Validator<TLInstance> = T.model(
|
|||
opacity: opacityValidator,
|
||||
font: fontValidator,
|
||||
align: alignValidator,
|
||||
verticalAlign: verticalAlignValidator,
|
||||
icon: iconValidator,
|
||||
geo: geoValidator,
|
||||
arrowheadStart: arrowheadValidator,
|
||||
|
@ -102,13 +104,14 @@ const Versions = {
|
|||
AddFollowingUserId: 6,
|
||||
RemoveAlignJustify: 7,
|
||||
AddZoom: 8,
|
||||
AddVerticalAlign: 9,
|
||||
} as const
|
||||
|
||||
/** @public */
|
||||
export const instanceTypeMigrations = defineMigrations({
|
||||
firstVersion: Versions.Initial,
|
||||
// STEP 2: Update the current version to point to your latest version
|
||||
currentVersion: Versions.AddZoom,
|
||||
currentVersion: Versions.AddVerticalAlign,
|
||||
// STEP 3: Add an up+down migration for the new version here
|
||||
migrators: {
|
||||
[Versions.AddTransparentExportBgs]: {
|
||||
|
@ -206,6 +209,24 @@ export const instanceTypeMigrations = defineMigrations({
|
|||
return instance
|
||||
},
|
||||
},
|
||||
[Versions.AddVerticalAlign]: {
|
||||
up: (instance: TLInstance) => {
|
||||
return {
|
||||
...instance,
|
||||
propsForNextShape: {
|
||||
...instance.propsForNextShape,
|
||||
verticalAlign: 'middle',
|
||||
},
|
||||
}
|
||||
},
|
||||
down: (instance: TLInstance) => {
|
||||
const { verticalAlign: _, ...propsForNextShape } = instance.propsForNextShape
|
||||
return {
|
||||
...instance,
|
||||
propsForNextShape,
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -227,6 +248,7 @@ export const TLInstance = createRecordType<TLInstance>('instance', {
|
|||
icon: 'file',
|
||||
font: 'draw',
|
||||
align: 'middle',
|
||||
verticalAlign: 'middle',
|
||||
geo: 'rectangle',
|
||||
arrowheadStart: 'none',
|
||||
arrowheadEnd: 'arrow',
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
TLGeoType,
|
||||
TLOpacityType,
|
||||
TLSizeType,
|
||||
TLVerticalAlignType,
|
||||
} from '../style-types'
|
||||
import {
|
||||
alignValidator,
|
||||
|
@ -19,6 +20,7 @@ import {
|
|||
geoValidator,
|
||||
opacityValidator,
|
||||
sizeValidator,
|
||||
verticalAlignValidator,
|
||||
} from '../validation'
|
||||
import { TLBaseShape, createShapeValidator } from './shape-validation'
|
||||
|
||||
|
@ -33,6 +35,7 @@ export type TLGeoShapeProps = {
|
|||
opacity: TLOpacityType
|
||||
font: TLFontType
|
||||
align: TLAlignType
|
||||
verticalAlign: TLVerticalAlignType
|
||||
url: string
|
||||
w: number
|
||||
h: number
|
||||
|
@ -57,6 +60,7 @@ export const geoShapeTypeValidator: T.Validator<TLGeoShape> = createShapeValidat
|
|||
opacity: opacityValidator,
|
||||
font: fontValidator,
|
||||
align: alignValidator,
|
||||
verticalAlign: verticalAlignValidator,
|
||||
url: T.string,
|
||||
w: T.nonZeroNumber,
|
||||
h: T.nonZeroNumber,
|
||||
|
@ -74,13 +78,14 @@ const Versions = {
|
|||
AddLabelColor: 2,
|
||||
RemoveJustify: 3,
|
||||
AddCheckBox: 4,
|
||||
AddVerticalAlign: 5,
|
||||
} as const
|
||||
|
||||
/** @public */
|
||||
export const geoShapeMigrations = defineMigrations({
|
||||
// STEP 2: Update the current version to point to your latest version
|
||||
firstVersion: Versions.Initial,
|
||||
currentVersion: Versions.AddCheckBox,
|
||||
currentVersion: Versions.AddVerticalAlign,
|
||||
migrators: {
|
||||
// STEP 3: Add an up+down migration for the new version here
|
||||
[Versions.AddUrlProp]: {
|
||||
|
@ -143,5 +148,23 @@ export const geoShapeMigrations = defineMigrations({
|
|||
}
|
||||
},
|
||||
},
|
||||
[Versions.AddVerticalAlign]: {
|
||||
up: (shape) => {
|
||||
return {
|
||||
...shape,
|
||||
props: {
|
||||
...shape.props,
|
||||
verticalAlign: 'middle',
|
||||
},
|
||||
}
|
||||
},
|
||||
down: (shape) => {
|
||||
const { verticalAlign: _, ...props } = shape.props
|
||||
return {
|
||||
...shape,
|
||||
props,
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
@ -11,6 +11,7 @@ export const TL_STYLE_TYPES = new Set([
|
|||
'opacity',
|
||||
'font',
|
||||
'align',
|
||||
'verticalAlign',
|
||||
'icon',
|
||||
'geo',
|
||||
'arrowheadStart',
|
||||
|
@ -133,6 +134,25 @@ export interface TLAlignStyle extends TLBaseStyle {
|
|||
type: 'align'
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface TLAlignStyle extends TLBaseStyle {
|
||||
id: TLAlignType
|
||||
type: 'align'
|
||||
}
|
||||
|
||||
// Geo Text Vertical Align
|
||||
/** @public */
|
||||
export const TL_VERTICAL_ALIGN_TYPES = TL_ALIGN_TYPES
|
||||
|
||||
/** @public */
|
||||
export type TLVerticalAlignType = SetValue<typeof TL_VERTICAL_ALIGN_TYPES>
|
||||
|
||||
/** @public */
|
||||
export interface TLVerticalAlignStyle extends TLBaseStyle {
|
||||
id: TLVerticalAlignType
|
||||
type: 'verticalAlign'
|
||||
}
|
||||
|
||||
// Geo
|
||||
|
||||
/** @public */
|
||||
|
@ -527,6 +547,7 @@ export interface TLStyleCollections {
|
|||
opacity: TLOpacityStyle[]
|
||||
font: TLFontStyle[]
|
||||
align: TLAlignStyle[]
|
||||
verticalAlign: TLVerticalAlignStyle[]
|
||||
geo: TLGeoStyle[]
|
||||
arrowheadStart: TLArrowheadStartStyle[]
|
||||
arrowheadEnd: TLArrowheadEndStyle[]
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
TL_OPACITY_TYPES,
|
||||
TL_SIZE_TYPES,
|
||||
TL_SPLINE_TYPES,
|
||||
TL_VERTICAL_ALIGN_TYPES,
|
||||
} from './style-types'
|
||||
|
||||
/** @internal */
|
||||
|
@ -65,6 +66,8 @@ export const fontValidator = T.setEnum(TL_FONT_TYPES)
|
|||
/** @internal */
|
||||
export const alignValidator = T.setEnum(TL_ALIGN_TYPES)
|
||||
/** @internal */
|
||||
export const verticalAlignValidator = T.setEnum(TL_VERTICAL_ALIGN_TYPES)
|
||||
/** @internal */
|
||||
export const arrowheadValidator = T.setEnum(TL_ARROWHEAD_TYPES)
|
||||
/** @internal */
|
||||
export const opacityValidator = T.setEnum(TL_OPACITY_TYPES)
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -12,7 +12,7 @@ type AllStyles = typeof App.styles
|
|||
|
||||
interface DropdownPickerProps<T extends AllStyles[keyof AllStyles][number]> {
|
||||
id: string
|
||||
label: TLTranslationKey
|
||||
label?: TLTranslationKey
|
||||
items: T[]
|
||||
styleType: TLStyleType
|
||||
value: T['id'] | null
|
||||
|
@ -52,7 +52,8 @@ export const DropdownPicker = React.memo(function DropdownPicker<
|
|||
<DropdownMenu.Content side="left" align="center" alignOffset={0}>
|
||||
<div
|
||||
className={classNames('tlui-button-grid', {
|
||||
'tlui-button-grid__two': items.length < 4,
|
||||
'tlui-button-grid__two': items.length < 3,
|
||||
'tlui-button-grid__three': items.length == 3,
|
||||
'tlui-button-grid__four': items.length >= 4,
|
||||
})}
|
||||
>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { App, TLNullableShapeProps, TLStyleItem, useApp } from '@tldraw/editor'
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
import { useValue } from 'signia-react'
|
||||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||
import { Button } from '../primitives/Button'
|
||||
|
@ -161,7 +162,7 @@ function TextStylePickerSet({ props }: { props: TLNullableShapeProps }) {
|
|||
const msg = useTranslation()
|
||||
const handleValueChange = useStyleChangeCallback()
|
||||
|
||||
const { font, align } = props
|
||||
const { font, align, verticalAlign } = props
|
||||
if (font === undefined && align === undefined) {
|
||||
return null
|
||||
}
|
||||
|
@ -189,12 +190,23 @@ function TextStylePickerSet({ props }: { props: TLNullableShapeProps }) {
|
|||
value={align}
|
||||
onValueChange={handleValueChange}
|
||||
/>
|
||||
{verticalAlign === undefined ? (
|
||||
<Button
|
||||
title={msg('style-panel.position')}
|
||||
data-wd="position"
|
||||
icon="align-center-center"
|
||||
title={msg('style-panel.vertical-align')}
|
||||
data-wd="vertical-align"
|
||||
icon="vertical-align-center"
|
||||
disabled
|
||||
/>
|
||||
) : (
|
||||
<DropdownPicker
|
||||
id="geo-vertical-alignment"
|
||||
styleType="verticalAlign"
|
||||
data-wd="style-panel.geo-vertical-align"
|
||||
items={styles.verticalAlign}
|
||||
value={verticalAlign}
|
||||
onValueChange={handleValueChange}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -276,6 +276,7 @@ export type TLTranslationKey =
|
|||
| 'shortcuts-dialog.view'
|
||||
| 'style-panel.title'
|
||||
| 'style-panel.align'
|
||||
| 'style-panel.vertical-align'
|
||||
| 'style-panel.position'
|
||||
| 'style-panel.arrowheads'
|
||||
| 'style-panel.arrowhead-start'
|
||||
|
|
|
@ -279,6 +279,7 @@ export const DEFAULT_TRANSLATION = {
|
|||
'shortcuts-dialog.view': 'View',
|
||||
'style-panel.title': 'Styles',
|
||||
'style-panel.align': 'Align',
|
||||
'style-panel.vertical-align': 'Vertical align',
|
||||
'style-panel.position': 'Position',
|
||||
'style-panel.arrowheads': 'Arrowheads',
|
||||
'style-panel.arrowhead-start': 'Start',
|
||||
|
|
|
@ -156,6 +156,9 @@ export type TLUiIconType =
|
|||
| 'ungroup'
|
||||
| 'unlock-small'
|
||||
| 'unlock'
|
||||
| 'vertical-align-center'
|
||||
| 'vertical-align-end'
|
||||
| 'vertical-align-start'
|
||||
| 'visible'
|
||||
| 'warning-triangle'
|
||||
| 'zoom-in'
|
||||
|
@ -316,6 +319,9 @@ export const TLUiIconTypes = [
|
|||
'ungroup',
|
||||
'unlock-small',
|
||||
'unlock',
|
||||
'vertical-align-center',
|
||||
'vertical-align-end',
|
||||
'vertical-align-start',
|
||||
'visible',
|
||||
'warning-triangle',
|
||||
'zoom-in',
|
||||
|
|
Loading…
Reference in a new issue