{itemsB.map((item) => {
return (
diff --git a/packages/tldraw/src/lib/ui/components/menu-items.tsx b/packages/tldraw/src/lib/ui/components/menu-items.tsx
index 156a81764..a94295a57 100644
--- a/packages/tldraw/src/lib/ui/components/menu-items.tsx
+++ b/packages/tldraw/src/lib/ui/components/menu-items.tsx
@@ -123,7 +123,9 @@ export function ToggleTransparentBgMenuItem() {
() => !editor.getInstanceState().exportBackground,
[editor]
)
- return
+ return (
+
+ )
}
/** @public */
export function UnlockAllMenuItem() {
diff --git a/packages/tldraw/src/lib/ui/components/primitives/TldrawUiIcon.tsx b/packages/tldraw/src/lib/ui/components/primitives/TldrawUiIcon.tsx
index 0ffd656c0..28c7ed0ec 100644
--- a/packages/tldraw/src/lib/ui/components/primitives/TldrawUiIcon.tsx
+++ b/packages/tldraw/src/lib/ui/components/primitives/TldrawUiIcon.tsx
@@ -40,6 +40,18 @@ export const TldrawUiIcon = memo(function TldrawUi({
}
}, [ref, asset, icon])
+ if (icon === 'none') {
+ return (
+
+ )
+ }
+
return (
Promise
| void
+ toggle?: boolean
checked?: boolean
disabled?: boolean
}
@@ -36,6 +37,7 @@ export function TldrawUiMenuCheckboxItem<
label,
readonlyOk,
onSelect,
+ toggle = false,
disabled = false,
checked = false,
}: TLUiMenuCheckboxItemProps) {
@@ -63,7 +65,10 @@ export function TldrawUiMenuCheckboxItem<
disabled={disabled}
checked={checked}
>
-
+
{labelStr && (
{labelStr}
@@ -87,7 +92,10 @@ export function TldrawUiMenuCheckboxItem<
disabled={disabled}
checked={checked}
>
-
+
{labelStr && (
{labelStr}
diff --git a/packages/tldraw/src/lib/ui/hooks/clipboard/pasteExcalidrawContent.ts b/packages/tldraw/src/lib/ui/hooks/clipboard/pasteExcalidrawContent.ts
index bf8452a74..34b4a3eed 100644
--- a/packages/tldraw/src/lib/ui/hooks/clipboard/pasteExcalidrawContent.ts
+++ b/packages/tldraw/src/lib/ui/hooks/clipboard/pasteExcalidrawContent.ts
@@ -11,6 +11,7 @@ import {
TLDefaultFontStyle,
TLDefaultHorizontalAlignStyle,
TLDefaultSizeStyle,
+ TLDefaultTextAlignStyle,
TLOpacityType,
TLShapeId,
Vec,
@@ -282,7 +283,7 @@ export async function pasteExcalidrawContent(editor: Editor, clipboard: any, poi
font: fontFamilyToFontType[element.fontFamily] ?? 'draw',
color: colorsToColors[element.strokeColor] ?? 'black',
text: element.text,
- align: textAlignToAlignTypes[element.textAlign],
+ textAlign: textAlignToTextAlignTypes[element.textAlign],
},
})
break
@@ -472,6 +473,12 @@ const textAlignToAlignTypes: Record = {
right: 'end',
}
+const textAlignToTextAlignTypes: Record = {
+ left: 'start',
+ center: 'middle',
+ right: 'end',
+}
+
const arrowheadsToArrowheadTypes: Record = {
arrow: 'arrow',
dot: 'dot',
diff --git a/packages/tldraw/src/lib/ui/hooks/useTools.tsx b/packages/tldraw/src/lib/ui/hooks/useTools.tsx
index fd05a340b..4e0fae9e9 100644
--- a/packages/tldraw/src/lib/ui/hooks/useTools.tsx
+++ b/packages/tldraw/src/lib/ui/hooks/useTools.tsx
@@ -182,7 +182,7 @@ export function ToolsProvider({ overrides, children }: TLUiToolsProviderProps) {
{
id: 'embed',
label: 'tool.embed',
- icon: 'tool-embed',
+ icon: 'dot',
onSelect(source) {
addDialog({ component: EmbedDialog })
trackEvent('select-tool', { source, id: 'embed' })
diff --git a/packages/tldraw/src/lib/ui/icon-types.ts b/packages/tldraw/src/lib/ui/icon-types.ts
index 623e78273..55b70b103 100644
--- a/packages/tldraw/src/lib/ui/icon-types.ts
+++ b/packages/tldraw/src/lib/ui/icon-types.ts
@@ -3,20 +3,11 @@
/** @public */
export type TLUiIconType =
- | 'align-bottom-center'
- | 'align-bottom-left'
- | 'align-bottom-right'
| 'align-bottom'
- | 'align-center-center'
| 'align-center-horizontal'
- | 'align-center-left'
- | 'align-center-right'
| 'align-center-vertical'
| 'align-left'
| 'align-right'
- | 'align-top-center'
- | 'align-top-left'
- | 'align-top-right'
| 'align-top'
| 'arrow-left'
| 'arrowhead-arrow'
@@ -27,15 +18,12 @@ export type TLUiIconType =
| 'arrowhead-square'
| 'arrowhead-triangle-inverted'
| 'arrowhead-triangle'
- | 'aspect-ratio'
- | 'avatar'
| 'blob'
| 'bring-forward'
| 'bring-to-front'
+ | 'broken'
| 'check-circle'
| 'check'
- | 'checkbox-checked'
- | 'checkbox-empty'
| 'chevron-down'
| 'chevron-left'
| 'chevron-right'
@@ -44,17 +32,14 @@ export type TLUiIconType =
| 'chevrons-sw'
| 'clipboard-copied'
| 'clipboard-copy'
- | 'code'
- | 'collab'
| 'color'
- | 'comment'
| 'cross-2'
| 'cross-circle'
- | 'cross'
| 'dash-dashed'
| 'dash-dotted'
| 'dash-draw'
| 'dash-solid'
+ | 'disconnected'
| 'discord'
| 'distribute-horizontal'
| 'distribute-vertical'
@@ -64,9 +49,7 @@ export type TLUiIconType =
| 'drag-handle-dots'
| 'duplicate'
| 'edit'
- | 'error'
| 'external-link'
- | 'file'
| 'fill-none'
| 'fill-pattern'
| 'fill-semi'
@@ -98,18 +81,16 @@ export type TLUiIconType =
| 'geo-x-box'
| 'github'
| 'group'
- | 'hidden'
- | 'image'
+ | 'horizontal-align-end'
+ | 'horizontal-align-middle'
+ | 'horizontal-align-start'
| 'info-circle'
| 'leading'
| 'link'
- | 'lock-small'
- | 'lock'
| 'menu'
| 'minus'
| 'mixed'
| 'pack'
- | 'page'
| 'plus'
| 'question-mark-circle'
| 'question-mark'
@@ -117,33 +98,22 @@ export type TLUiIconType =
| 'reset-zoom'
| 'rotate-ccw'
| 'rotate-cw'
- | 'ruler'
- | 'search'
| 'send-backward'
| 'send-to-back'
- | 'settings-horizontal'
- | 'settings-vertical-1'
- | 'settings-vertical'
- | 'share-1'
- | 'share-2'
| 'size-extra-large'
| 'size-large'
| 'size-medium'
| 'size-small'
- | 'spline-cubic'
- | 'spline-line'
| 'stack-horizontal'
| 'stack-vertical'
- | 'status-offline'
- | 'status-online'
| 'stretch-horizontal'
| 'stretch-vertical'
| 'text-align-center'
- | 'text-align-justify'
| 'text-align-left'
| 'text-align-right'
+ | 'toggle-off'
+ | 'toggle-on'
| 'tool-arrow'
- | 'tool-embed'
| 'tool-eraser'
| 'tool-frame'
| 'tool-hand'
@@ -154,39 +124,26 @@ export type TLUiIconType =
| 'tool-note'
| 'tool-pencil'
| 'tool-pointer'
+ | 'tool-screenshot'
| 'tool-text'
| 'trash'
- | 'triangle-down'
- | 'triangle-up'
| 'twitter'
| 'undo'
| 'ungroup'
- | 'unlock-small'
- | 'unlock'
- | 'vertical-align-center'
| 'vertical-align-end'
+ | 'vertical-align-middle'
| 'vertical-align-start'
- | 'visible'
| 'warning-triangle'
| 'zoom-in'
| 'zoom-out'
/** @public */
export const iconTypes = [
- 'align-bottom-center',
- 'align-bottom-left',
- 'align-bottom-right',
'align-bottom',
- 'align-center-center',
'align-center-horizontal',
- 'align-center-left',
- 'align-center-right',
'align-center-vertical',
'align-left',
'align-right',
- 'align-top-center',
- 'align-top-left',
- 'align-top-right',
'align-top',
'arrow-left',
'arrowhead-arrow',
@@ -197,15 +154,12 @@ export const iconTypes = [
'arrowhead-square',
'arrowhead-triangle-inverted',
'arrowhead-triangle',
- 'aspect-ratio',
- 'avatar',
'blob',
'bring-forward',
'bring-to-front',
+ 'broken',
'check-circle',
'check',
- 'checkbox-checked',
- 'checkbox-empty',
'chevron-down',
'chevron-left',
'chevron-right',
@@ -214,17 +168,14 @@ export const iconTypes = [
'chevrons-sw',
'clipboard-copied',
'clipboard-copy',
- 'code',
- 'collab',
'color',
- 'comment',
'cross-2',
'cross-circle',
- 'cross',
'dash-dashed',
'dash-dotted',
'dash-draw',
'dash-solid',
+ 'disconnected',
'discord',
'distribute-horizontal',
'distribute-vertical',
@@ -234,9 +185,7 @@ export const iconTypes = [
'drag-handle-dots',
'duplicate',
'edit',
- 'error',
'external-link',
- 'file',
'fill-none',
'fill-pattern',
'fill-semi',
@@ -268,18 +217,16 @@ export const iconTypes = [
'geo-x-box',
'github',
'group',
- 'hidden',
- 'image',
+ 'horizontal-align-end',
+ 'horizontal-align-middle',
+ 'horizontal-align-start',
'info-circle',
'leading',
'link',
- 'lock-small',
- 'lock',
'menu',
'minus',
'mixed',
'pack',
- 'page',
'plus',
'question-mark-circle',
'question-mark',
@@ -287,33 +234,22 @@ export const iconTypes = [
'reset-zoom',
'rotate-ccw',
'rotate-cw',
- 'ruler',
- 'search',
'send-backward',
'send-to-back',
- 'settings-horizontal',
- 'settings-vertical-1',
- 'settings-vertical',
- 'share-1',
- 'share-2',
'size-extra-large',
'size-large',
'size-medium',
'size-small',
- 'spline-cubic',
- 'spline-line',
'stack-horizontal',
'stack-vertical',
- 'status-offline',
- 'status-online',
'stretch-horizontal',
'stretch-vertical',
'text-align-center',
- 'text-align-justify',
'text-align-left',
'text-align-right',
+ 'toggle-off',
+ 'toggle-on',
'tool-arrow',
- 'tool-embed',
'tool-eraser',
'tool-frame',
'tool-hand',
@@ -324,19 +260,15 @@ export const iconTypes = [
'tool-note',
'tool-pencil',
'tool-pointer',
+ 'tool-screenshot',
'tool-text',
'trash',
- 'triangle-down',
- 'triangle-up',
'twitter',
'undo',
'ungroup',
- 'unlock-small',
- 'unlock',
- 'vertical-align-center',
'vertical-align-end',
+ 'vertical-align-middle',
'vertical-align-start',
- 'visible',
'warning-triangle',
'zoom-in',
'zoom-out',
diff --git a/packages/tldraw/src/lib/utils/tldr/buildFromV1Document.ts b/packages/tldraw/src/lib/utils/tldr/buildFromV1Document.ts
index 1d894703d..c94515c08 100644
--- a/packages/tldraw/src/lib/utils/tldr/buildFromV1Document.ts
+++ b/packages/tldraw/src/lib/utils/tldr/buildFromV1Document.ts
@@ -13,6 +13,7 @@ import {
TLDefaultFontStyle,
TLDefaultHorizontalAlignStyle,
TLDefaultSizeStyle,
+ TLDefaultTextAlignStyle,
TLDrawShape,
TLGeoShape,
TLImageShape,
@@ -436,7 +437,7 @@ export function buildFromV1Document(editor: Editor, document: LegacyTldrawDocume
color: getV2Color(v1Shape.style.color),
size: getV2TextSize(v1Shape.style.size),
font: getV2Font(v1Shape.style.font),
- align: getV2Align(v1Shape.style.textAlign),
+ textAlign: getV2TextAlign(v1Shape.style.textAlign),
scale: v1Shape.style.scale ?? 1,
},
},
@@ -1106,6 +1107,13 @@ const v1AlignsToV2Aligns: Record = {
[AlignStyle.Justify]: 'start',
}
+const v1TextAlignsToV2TextAligns: Record = {
+ [AlignStyle.Start]: 'start',
+ [AlignStyle.Middle]: 'middle',
+ [AlignStyle.End]: 'end',
+ [AlignStyle.Justify]: 'start',
+}
+
const v1TextSizesToV2TextSizes: Record = {
[SizeStyle.Small]: 's',
[SizeStyle.Medium]: 'l',
@@ -1137,6 +1145,10 @@ function getV2Align(align: AlignStyle | undefined): TLDefaultHorizontalAlignStyl
return align ? v1AlignsToV2Aligns[align] ?? 'middle' : 'middle'
}
+function getV2TextAlign(align: AlignStyle | undefined): TLDefaultTextAlignStyle {
+ return align ? v1TextAlignsToV2TextAligns[align] ?? 'middle' : 'middle'
+}
+
function getV2TextSize(size: SizeStyle | undefined): TLDefaultSizeStyle {
return size ? v1TextSizesToV2TextSizes[size] ?? 'm' : 'm'
}
diff --git a/packages/tldraw/src/test/text.test.ts b/packages/tldraw/src/test/text.test.ts
index d18e6ed68..3d7c04467 100644
--- a/packages/tldraw/src/test/text.test.ts
+++ b/packages/tldraw/src/test/text.test.ts
@@ -1,4 +1,4 @@
-import { createShapeId } from '@tldraw/editor'
+import { TLTextShape, createShapeId } from '@tldraw/editor'
import { TestEditor } from './TestEditor'
let editor: TestEditor
@@ -13,7 +13,7 @@ afterEach(() => {
describe('When editing text', () => {
it('preserves the top center when center aligned', () => {
const id = createShapeId()
- editor.createShapes([
+ editor.createShapes([
{
id,
type: 'text',
@@ -21,7 +21,7 @@ describe('When editing text', () => {
y: 0,
props: {
text: 'Hello',
- align: 'middle',
+ textAlign: 'middle',
scale: 2,
},
},
@@ -47,7 +47,7 @@ describe('When editing text', () => {
it('preserved the right center when center aligned and rotated 90deg', () => {
const id = createShapeId()
- editor.createShapes([
+ editor.createShapes([
{
id,
type: 'text',
@@ -56,7 +56,7 @@ describe('When editing text', () => {
rotation: Math.PI / 2,
props: {
text: 'Hello',
- align: 'middle',
+ textAlign: 'middle',
scale: 2,
},
},
@@ -73,7 +73,7 @@ describe('When editing text', () => {
it('preserves the top left corner when start aligned', () => {
const id = createShapeId()
- editor.createShapes([
+ editor.createShapes([
{
id,
type: 'text',
@@ -81,7 +81,7 @@ describe('When editing text', () => {
y: 0,
props: {
text: 'Hello',
- align: 'start',
+ textAlign: 'start',
scale: 2,
},
},
@@ -107,7 +107,7 @@ describe('When editing text', () => {
it('preserves the top right edge when end aligned', () => {
const id = createShapeId()
- editor.createShapes([
+ editor.createShapes([
{
id,
type: 'text',
@@ -115,7 +115,7 @@ describe('When editing text', () => {
y: 0,
props: {
text: 'Hello',
- align: 'end',
+ textAlign: 'end',
scale: 2,
},
},
@@ -143,7 +143,7 @@ describe('When editing text', () => {
describe('When changing text size', () => {
it('preserves the center when center aligned', () => {
const id = createShapeId()
- editor.createShapes([
+ editor.createShapes([
{
id,
type: 'text',
@@ -152,7 +152,7 @@ describe('When changing text size', () => {
props: {
text: 'Hello',
size: 'm',
- align: 'middle',
+ textAlign: 'middle',
scale: 2,
},
},
@@ -178,7 +178,7 @@ describe('When changing text size', () => {
it('preserves the center left point when start aligned', () => {
const id = createShapeId()
- editor.createShapes([
+ editor.createShapes([
{
id,
type: 'text',
@@ -187,7 +187,7 @@ describe('When changing text size', () => {
props: {
text: 'Hello',
size: 'm',
- align: 'start',
+ textAlign: 'start',
scale: 2,
},
},
@@ -213,7 +213,7 @@ describe('When changing text size', () => {
it('preserves the top right edge when end aligned', () => {
const id = createShapeId()
- editor.createShapes([
+ editor.createShapes([
{
id,
type: 'text',
@@ -222,7 +222,7 @@ describe('When changing text size', () => {
props: {
text: 'Hello',
size: 'm',
- align: 'end',
+ textAlign: 'end',
scale: 2,
},
},
diff --git a/packages/tlschema/api-report.md b/packages/tlschema/api-report.md
index 13da9d330..979b14ae0 100644
--- a/packages/tlschema/api-report.md
+++ b/packages/tlschema/api-report.md
@@ -200,6 +200,9 @@ export const defaultShapeSchemas: {
// @public (undocumented)
export const DefaultSizeStyle: EnumStyleProp<"l" | "m" | "s" | "xl">;
+// @public (undocumented)
+export const DefaultTextAlignStyle: EnumStyleProp<"end" | "middle" | "start">;
+
// @public (undocumented)
export const DefaultVerticalAlignStyle: EnumStyleProp<"end" | "middle" | "start">;
@@ -790,13 +793,13 @@ export const textShapeMigrations: TLShapePropsMigrations;
// @public (undocumented)
export const textShapeProps: {
- align: EnumStyleProp<"end-legacy" | "end" | "middle-legacy" | "middle" | "start-legacy" | "start">;
autoSize: T.Validator;
color: EnumStyleProp<"black" | "blue" | "green" | "grey" | "light-blue" | "light-green" | "light-red" | "light-violet" | "orange" | "red" | "violet" | "white" | "yellow">;
font: EnumStyleProp<"draw" | "mono" | "sans" | "serif">;
scale: T.Validator;
size: EnumStyleProp<"l" | "m" | "s" | "xl">;
text: T.Validator;
+ textAlign: EnumStyleProp<"end" | "middle" | "start">;
w: T.Validator;
};
@@ -954,6 +957,9 @@ export type TLDefaultShape = TLArrowShape | TLBookmarkShape | TLDrawShape | TLEm
// @public (undocumented)
export type TLDefaultSizeStyle = T.TypeOf;
+// @public (undocumented)
+export type TLDefaultTextAlignStyle = T.TypeOf;
+
// @public (undocumented)
export type TLDefaultVerticalAlignStyle = T.TypeOf;
diff --git a/packages/tlschema/src/index.ts b/packages/tlschema/src/index.ts
index 886ff30ed..b4e32baa6 100644
--- a/packages/tlschema/src/index.ts
+++ b/packages/tlschema/src/index.ts
@@ -166,6 +166,7 @@ export {
type TLDefaultHorizontalAlignStyle,
} from './styles/TLHorizontalAlignStyle'
export { DefaultSizeStyle, type TLDefaultSizeStyle } from './styles/TLSizeStyle'
+export { DefaultTextAlignStyle, type TLDefaultTextAlignStyle } from './styles/TLTextAlignStyle'
export {
DefaultVerticalAlignStyle,
type TLDefaultVerticalAlignStyle,
diff --git a/packages/tlschema/src/migrations.test.ts b/packages/tlschema/src/migrations.test.ts
index bb3f985e2..1178e8d48 100644
--- a/packages/tlschema/src/migrations.test.ts
+++ b/packages/tlschema/src/migrations.test.ts
@@ -1549,6 +1549,18 @@ describe('Add font size adjustment to notes', () => {
})
})
+describe('Add text align to text shapes', () => {
+ const { up, down } = getTestMigration(textShapeVersions.AddTextAlign)
+
+ test('up works as expected', () => {
+ expect(up({ props: { align: 'middle' } })).toEqual({ props: { textAlign: 'middle' } })
+ })
+
+ test('down works as expected', () => {
+ expect(down({ props: { textAlign: 'middle' } })).toEqual({ props: { align: 'middle' } })
+ })
+})
+
/* --- PUT YOUR MIGRATIONS TESTS ABOVE HERE --- */
// check that all migrator fns were called at least once
diff --git a/packages/tlschema/src/shapes/TLTextShape.ts b/packages/tlschema/src/shapes/TLTextShape.ts
index 63e90915f..43a239ad9 100644
--- a/packages/tlschema/src/shapes/TLTextShape.ts
+++ b/packages/tlschema/src/shapes/TLTextShape.ts
@@ -6,8 +6,8 @@ import {
} from '../records/TLShape'
import { DefaultColorStyle } from '../styles/TLColorStyle'
import { DefaultFontStyle } from '../styles/TLFontStyle'
-import { DefaultHorizontalAlignStyle } from '../styles/TLHorizontalAlignStyle'
import { DefaultSizeStyle } from '../styles/TLSizeStyle'
+import { DefaultTextAlignStyle } from '../styles/TLTextAlignStyle'
import { ShapePropsType, TLBaseShape } from './TLBaseShape'
/** @public */
@@ -15,7 +15,7 @@ export const textShapeProps = {
color: DefaultColorStyle,
size: DefaultSizeStyle,
font: DefaultFontStyle,
- align: DefaultHorizontalAlignStyle,
+ textAlign: DefaultTextAlignStyle,
w: T.nonZeroNumber,
text: T.string,
scale: T.nonZeroNumber,
@@ -30,6 +30,7 @@ export type TLTextShape = TLBaseShape<'text', TLTextShapeProps>
const Versions = createShapePropsMigrationIds('text', {
RemoveJustify: 1,
+ AddTextAlign: 2,
})
export { Versions as textShapeVersions }
@@ -46,5 +47,16 @@ export const textShapeMigrations = createShapePropsMigrationSequence({
},
down: RETIRED_DOWN_MIGRATION,
},
+ {
+ id: Versions.AddTextAlign,
+ up: (props) => {
+ props.textAlign = props.align
+ delete props.align
+ },
+ down: (props) => {
+ props.align = props.textAlign
+ delete props.textAlign
+ },
+ },
],
})
diff --git a/packages/tlschema/src/styles/TLTextAlignStyle.ts b/packages/tlschema/src/styles/TLTextAlignStyle.ts
new file mode 100644
index 000000000..999477fe8
--- /dev/null
+++ b/packages/tlschema/src/styles/TLTextAlignStyle.ts
@@ -0,0 +1,11 @@
+import { T } from '@tldraw/validate'
+import { StyleProp } from './StyleProp'
+
+/** @public */
+export const DefaultTextAlignStyle = StyleProp.defineEnum('tldraw:textAlign', {
+ defaultValue: 'start',
+ values: ['start', 'middle', 'end'],
+})
+
+/** @public */
+export type TLDefaultTextAlignStyle = T.TypeOf