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:
Mitja Bezenšek 2023-05-19 12:23:43 +02:00 committed by GitHub
parent ddca01918f
commit f59bfe01b1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 254 additions and 21 deletions

View 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

View 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

View 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

View file

@ -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",

View file

@ -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

View file

@ -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),

View file

@ -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

View file

@ -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,

View file

@ -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;

View file

@ -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;

View file

@ -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',

View file

@ -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',

View file

@ -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,

View file

@ -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?

View file

@ -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"

View file

@ -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

View file

@ -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' },

View file

@ -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,

View file

@ -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
})

View file

@ -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;

View file

@ -227,6 +227,7 @@ export {
type TLStyleItem,
type TLStyleProps,
type TLStyleType,
type TLVerticalAlignType,
} from './style-types'
export {
TL_CURSOR_TYPES,

View file

@ -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 = {

View file

@ -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',

View file

@ -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,
}
},
},
},
})

View file

@ -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[]

View file

@ -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

View file

@ -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,
})}
>

View file

@ -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}
/>
<Button
title={msg('style-panel.position')}
data-wd="position"
icon="align-center-center"
disabled
/>
{verticalAlign === undefined ? (
<Button
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>

View file

@ -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'

View file

@ -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',

View file

@ -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',