diff --git a/packages/dev/esbuild.config.mjs b/packages/dev/esbuild.config.mjs
index bc5dce38a..83116f0f4 100644
--- a/packages/dev/esbuild.config.mjs
+++ b/packages/dev/esbuild.config.mjs
@@ -21,6 +21,10 @@ esbuild
minify: false,
sourcemap: true,
incremental: isDevServer,
+ assetNames: 'assets/[name]-[hash]',
+ loader: {
+ '.woff': 'file',
+ },
target: ['chrome58', 'firefox57', 'safari11', 'edge18'],
define: {
'process.env.NODE_ENV': isDevServer ? '"development"' : '"production"',
diff --git a/packages/dev/package.json b/packages/dev/package.json
index 6e7e65326..897248336 100644
--- a/packages/dev/package.json
+++ b/packages/dev/package.json
@@ -34,4 +34,4 @@
"typescript": "4.2.3"
},
"gitHead": "a7dac0f83ad998e205c2aab58182cb4ba4e099a6"
-}
+}
\ No newline at end of file
diff --git a/packages/dev/src/assets/VerveineRegular.woff b/packages/dev/src/assets/VerveineRegular.woff
new file mode 100644
index 000000000..098d9fb17
Binary files /dev/null and b/packages/dev/src/assets/VerveineRegular.woff differ
diff --git a/packages/dev/src/components/editor.tsx b/packages/dev/src/components/editor.tsx
index c699c8e7a..c242be516 100644
--- a/packages/dev/src/components/editor.tsx
+++ b/packages/dev/src/components/editor.tsx
@@ -1,110 +1,6 @@
import * as React from 'react'
-import {
- TLDraw,
- TLDrawDocument,
- ColorStyle,
- DashStyle,
- SizeStyle,
- TLDrawShapeType,
- TLDrawState,
- TLDrawPatch,
-} from '@tldraw/tldraw'
-import { usePersistence } from '../hooks/usePersistence'
-
-const initialDoc: TLDrawDocument = {
- id: 'doc',
- pages: {
- page1: {
- id: 'page1',
- shapes: {
- rect1: {
- id: 'rect1',
- parentId: 'page1',
- name: 'Rectangle',
- childIndex: 1,
- type: TLDrawShapeType.Rectangle,
- point: [32, 32],
- size: [100, 100],
- style: {
- dash: DashStyle.Draw,
- size: SizeStyle.Medium,
- color: ColorStyle.Blue,
- },
- },
- ellipse1: {
- id: 'ellipse1',
- parentId: 'page1',
- name: 'Ellipse',
- childIndex: 2,
- type: TLDrawShapeType.Ellipse,
- point: [132, 132],
- radius: [50, 50],
- style: {
- dash: DashStyle.Draw,
- size: SizeStyle.Medium,
- color: ColorStyle.Cyan,
- },
- },
- draw1: {
- id: 'draw1',
- parentId: 'page1',
- name: 'Draw',
- childIndex: 3,
- type: TLDrawShapeType.Draw,
- point: [232, 232],
- points: [
- [50, 0],
- [100, 100],
- [0, 100],
- [50, 0],
- [100, 100],
- [0, 100],
- [50, 0],
- [56, 5],
- ],
- style: {
- dash: DashStyle.Draw,
- size: SizeStyle.Medium,
- color: ColorStyle.Green,
- },
- },
- },
- bindings: {},
- },
- },
- pageStates: {
- page1: {
- id: 'page1',
- selectedIds: [],
- currentParentId: 'page1',
- camera: {
- point: [0, 0],
- zoom: 1,
- },
- },
- },
-}
+import { TLDraw } from '@tldraw/tldraw'
export default function Editor(): JSX.Element {
- // const { value, setValue, status } = usePersistence('doc', initialDoc)
-
- // const handleChange = React.useCallback(
- // (tlstate: TLDrawState, patch: TLDrawPatch, reason: string) => {
- // if (reason.startsWith('session')) {
- // return
- // }
-
- // setValue(tlstate.document)
- // },
- // [setValue]
- // )
-
- // if (status === 'loading' || value === null) {
- // return
- // }
-
- // return
-
- // Will automatically persist data under the provided id, too
return
}
diff --git a/packages/tldraw/scripts/build.js b/packages/tldraw/scripts/build.js
index 9bfb93f40..a1efb7642 100644
--- a/packages/tldraw/scripts/build.js
+++ b/packages/tldraw/scripts/build.js
@@ -9,6 +9,14 @@ async function main() {
fs.mkdirSync('./dist')
}
+ fs.copyFileSync('package.json', 'dist/package.json', (err) => {
+ if (err) throw err
+ })
+
+ fs.copyFileSync('README.md', 'dist/README.md', (err) => {
+ if (err) throw err
+ })
+
try {
esbuild.buildSync({
entryPoints: ['./src/index.ts'],
@@ -36,13 +44,6 @@ async function main() {
external: ['react', 'react-dom'],
})
- fs.copyFile('package.json', 'dist/package.json', (err) => {
- if (err) throw err
- })
- fs.copyFile('README.md', 'dist/README.md', (err) => {
- if (err) throw err
- })
-
console.log(`✔ ${name}: Built package.`)
} catch (e) {
console.log(`× ${name}: Build failed due to an error.`)
diff --git a/packages/tldraw/scripts/dev.js b/packages/tldraw/scripts/dev.js
index 2e788bcbf..d300f6f57 100644
--- a/packages/tldraw/scripts/dev.js
+++ b/packages/tldraw/scripts/dev.js
@@ -9,8 +9,8 @@ async function main() {
outdir: 'dist/cjs',
minify: false,
bundle: true,
- format: 'esm',
- target: 'cjs',
+ format: 'cjs',
+ target: 'es6',
jsxFactory: 'React.createElement',
jsxFragment: 'React.Fragment',
tsconfig: './tsconfig.build.json',
diff --git a/packages/tldraw/scripts/pre-dev.js b/packages/tldraw/scripts/pre-dev.js
index a8c513301..88a2c39f7 100644
--- a/packages/tldraw/scripts/pre-dev.js
+++ b/packages/tldraw/scripts/pre-dev.js
@@ -1,5 +1,6 @@
/* eslint-disable */
const fs = require('fs')
+const path = require('path')
const esbuild = require('esbuild')
async function main() {
@@ -21,6 +22,12 @@ async function main() {
tsconfig: './tsconfig.build.json',
external: ['react', 'react-dom'],
})
+
+ var files = fs.readdirSync('src/assets')
+
+ for (var i = 0; i < files.length; i++) {
+ fs.copyFileSync(path.join('src/assets', files[i]), path.join('dist', files[i]))
+ }
}
main()
diff --git a/packages/tldraw/src/assets/VerveineRegular.woff b/packages/tldraw/src/assets/VerveineRegular.woff
new file mode 100644
index 000000000..098d9fb17
Binary files /dev/null and b/packages/tldraw/src/assets/VerveineRegular.woff differ
diff --git a/packages/tldraw/src/components/tldraw/tldraw.tsx b/packages/tldraw/src/components/tldraw/tldraw.tsx
index f52d3cb74..8a62aab3e 100644
--- a/packages/tldraw/src/components/tldraw/tldraw.tsx
+++ b/packages/tldraw/src/components/tldraw/tldraw.tsx
@@ -4,7 +4,7 @@ import { Renderer } from '@tldraw/core'
import styled from '~styles'
import type { Data, TLDrawDocument } from '~types'
import { TLDrawState } from '~state'
-import { useKeyboardShortcuts, TLDrawContext } from '~hooks'
+import { useKeyboardShortcuts, TLDrawContext, useCustomFonts } from '~hooks'
import { tldrawShapeUtils } from '~shape'
import { ContextMenu } from '~components/context-menu'
import { StylePanel } from '~components/style-panel'
@@ -63,6 +63,8 @@ export function TLDraw({ id, document, currentPageId, onMount, onChange: _onChan
useKeyboardShortcuts(tlstate)
+ useCustomFonts()
+
const page = context.useSelector(pageSelector)
const pageState = context.useSelector(pageStateSelector)
diff --git a/packages/tldraw/src/hooks/index.ts b/packages/tldraw/src/hooks/index.ts
index 37f885098..bf38556de 100644
--- a/packages/tldraw/src/hooks/index.ts
+++ b/packages/tldraw/src/hooks/index.ts
@@ -1,3 +1,4 @@
export * from './useKeyboardShortcuts'
export * from './useTLDrawContext'
export * from './useTheme'
+export * from './useCustomFonts'
diff --git a/packages/tldraw/src/hooks/useCustomFonts.ts b/packages/tldraw/src/hooks/useCustomFonts.ts
new file mode 100644
index 000000000..99944f5b5
--- /dev/null
+++ b/packages/tldraw/src/hooks/useCustomFonts.ts
@@ -0,0 +1,18 @@
+import { useStyle, css } from '~hooks/useStyle'
+
+// const customFonts = css`
+// @font-face {
+// font-family: 'Verveine Regular';
+// font-style: normal;
+// font-weight: normal;
+// src: local('Verveine Regular'), url('/VerveineRegular.woff') format('woff');
+// }
+// `
+
+const customFonts = css`
+ @import url('https://fonts.googleapis.com/css2?family=Caveat+Brush&display=swap');
+`
+
+export function useCustomFonts() {
+ useStyle('tldraw-fonts', customFonts)
+}
diff --git a/packages/tldraw/src/hooks/useStyle.ts b/packages/tldraw/src/hooks/useStyle.ts
new file mode 100644
index 000000000..7aa82eb24
--- /dev/null
+++ b/packages/tldraw/src/hooks/useStyle.ts
@@ -0,0 +1,30 @@
+import * as React from 'react'
+
+const styles = new Map()
+
+export const css = (strings: TemplateStringsArray, ...args: unknown[]) =>
+ strings.reduce(
+ (acc, string, index) => acc + string + (index < args.length ? args[index] : ''),
+ ''
+ )
+
+export function useStyle(uid: string, rules: string) {
+ React.useLayoutEffect(() => {
+ if (styles.get(uid)) {
+ return () => void null
+ }
+
+ const style = document.createElement('style')
+ style.innerHTML = rules
+ style.setAttribute('id', uid)
+ document.head.appendChild(style)
+ styles.set(uid, style)
+
+ return () => {
+ if (style && document.head.contains(style)) {
+ document.head.removeChild(style)
+ styles.delete(uid)
+ }
+ }
+ }, [uid, rules])
+}
diff --git a/packages/tldraw/src/shape/shape-styles.ts b/packages/tldraw/src/shape/shape-styles.ts
index d1d3c3800..5751e9d6f 100644
--- a/packages/tldraw/src/shape/shape-styles.ts
+++ b/packages/tldraw/src/shape/shape-styles.ts
@@ -69,7 +69,7 @@ export function getFontStyle(style: ShapeStyles): string {
const fontSize = getFontSize(style.size)
const { scale = 1 } = style
- return `${fontSize * scale}px/1.4 Verveine Regular`
+ return `${fontSize * scale}px/1.3 "Caveat Brush"`
}
export function getShapeStyle(
diff --git a/packages/tldraw/src/shape/shapes/text/text.tsx b/packages/tldraw/src/shape/shapes/text/text.tsx
index 2a5fe49d4..352cccb0c 100644
--- a/packages/tldraw/src/shape/shapes/text/text.tsx
+++ b/packages/tldraw/src/shape/shapes/text/text.tsx
@@ -16,16 +16,16 @@ function normalizeText(text: string) {
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
-let mdiv: any
+let melm: any
function getMeasurementDiv() {
// A div used for measurement
document.getElementById('__textMeasure')?.remove()
- const mdiv = document.createElement('pre')
- mdiv.id = '__textMeasure'
+ const pre = document.createElement('pre')
+ pre.id = '__textMeasure'
- Object.assign(mdiv.style, {
+ Object.assign(pre.style, {
whiteSpace: 'pre',
width: 'auto',
border: '1px solid red',
@@ -42,14 +42,14 @@ function getMeasurementDiv() {
dominantBaseline: 'mathematical',
})
- mdiv.tabIndex = -1
+ pre.tabIndex = -1
- document.body.appendChild(mdiv)
- return mdiv
+ document.body.appendChild(pre)
+ return pre
}
if (typeof window !== 'undefined') {
- getMeasurementDiv()
+ melm = getMeasurementDiv()
}
export class Text extends TLDrawShapeUtil {
@@ -69,10 +69,17 @@ export class Text extends TLDrawShapeUtil {
childIndex: 1,
point: [0, 0],
rotation: 0,
- text: 'hi',
+ text: ' ',
style: defaultStyle,
}
+ create(props: Partial): TextShape {
+ const shape = { ...this.defaultProps, ...props }
+ const bounds = this.getBounds(shape)
+ shape.point = Vec.sub(shape.point, [bounds.width / 2, bounds.height / 2])
+ return shape
+ }
+
shouldRender(prev: TextShape, next: TextShape): boolean {
return (
next.text !== prev.text || next.style.scale !== prev.style.scale || next.style !== prev.style
@@ -149,7 +156,7 @@ export class Text extends TLDrawShapeUtil {
}
const fontSize = getFontSize(shape.style.size) * (shape.style.scale || 1)
- const lineHeight = fontSize * 1.4
+ const lineHeight = fontSize * 1.3
if (!isEditing) {
return (
@@ -168,7 +175,7 @@ export class Text extends TLDrawShapeUtil {
key={i}
x={4}
y={4 + fontSize / 2 + i * lineHeight}
- fontFamily="Verveine Regular"
+ fontFamily="Caveat Brush"
fontStyle="normal"
fontWeight="500"
fontSize={fontSize}
@@ -237,15 +244,16 @@ export class Text extends TLDrawShapeUtil {
getBounds(shape: TextShape): TLBounds {
const bounds = Utils.getFromCache(this.boundsCache, shape, () => {
- if (!mdiv) {
+ if (!melm) {
+ console.log('no melm!')
// We're in SSR
return { minX: 0, minY: 0, maxX: 0, maxY: 0, width: 0, height: 0 }
}
- mdiv.innerHTML = `${shape.text}`
- mdiv.style.font = getFontStyle(shape.style)
+ melm.innerHTML = `${shape.text}`
+ melm.style.font = getFontStyle(shape.style)
- const [width, height] = [mdiv.offsetWidth, mdiv.offsetHeight]
+ const [width, height] = [melm.offsetWidth, melm.offsetHeight]
return {
minX: 0,
diff --git a/packages/tldraw/src/state/session/sessions/text/text.session.spec.ts b/packages/tldraw/src/state/session/sessions/text/text.session.spec.ts
index 86a3e85ae..70d0f7e79 100644
--- a/packages/tldraw/src/state/session/sessions/text/text.session.spec.ts
+++ b/packages/tldraw/src/state/session/sessions/text/text.session.spec.ts
@@ -16,7 +16,7 @@ describe('Text session', () => {
})
)
.select('text1')
- .startTextSession()
+ .startTextSession('text1')
.updateTextSession('Hello world')
.completeSession()
.undo()
@@ -35,7 +35,7 @@ describe('Text session', () => {
})
)
.select('text1')
- .startTextSession()
+ .startTextSession('text1')
.updateTextSession('Hello world')
.cancelSession()
diff --git a/packages/tldraw/src/state/session/sessions/text/text.session.ts b/packages/tldraw/src/state/session/sessions/text/text.session.ts
index 13cd36f6b..9f2487a49 100644
--- a/packages/tldraw/src/state/session/sessions/text/text.session.ts
+++ b/packages/tldraw/src/state/session/sessions/text/text.session.ts
@@ -85,6 +85,51 @@ export class TextSession implements Session {
const shape = TLDR.getShape(data, initialShape.id, pageId)
+ // TODO: Delete text shape if its content is empty
+ // TODO: ...and prevent `isCreating` from selecting the deleted shape
+
+ // if (initialShape.text.trim() === '' && shape.text.trim() === '') {
+ // // delete shape
+ // console.log('deleting shape')
+ // return {
+ // id: 'text',
+ // before: {
+ // document: {
+ // pages: {
+ // [pageId]: {
+ // shapes: {
+ // [initialShape.id]: undefined,
+ // },
+ // },
+ // },
+ // pageState: {
+ // [pageId]: {
+ // editingId: undefined,
+ // selectedIds: [initialShape.id],
+ // },
+ // },
+ // },
+ // },
+ // after: {
+ // document: {
+ // pages: {
+ // [pageId]: {
+ // shapes: {
+ // [initialShape.id]: undefined,
+ // },
+ // },
+ // },
+ // pageState: {
+ // [pageId]: {
+ // editingId: undefined,
+ // selectedIds: [],
+ // },
+ // },
+ // },
+ // },
+ // }
+ // }
+
if (shape.text === initialShape.text) return undefined
return {
@@ -121,6 +166,7 @@ export class TextSession implements Session {
pageState: {
[pageId]: {
editingId: undefined,
+ selectedIds: [initialShape.id],
},
},
},
diff --git a/packages/tldraw/src/state/tlstate.ts b/packages/tldraw/src/state/tlstate.ts
index 6f46baa3f..d76427a6c 100644
--- a/packages/tldraw/src/state/tlstate.ts
+++ b/packages/tldraw/src/state/tlstate.ts
@@ -91,15 +91,48 @@ const initialData: Data = {
export class TLDrawState extends StateManager {
_onChange?: (tlstate: TLDrawState, data: Data, reason: string) => void
- constructor(id = Utils.uniqueId()) {
- super(initialData, id, 1)
- }
-
selectHistory: SelectHistory = {
stack: [[]],
pointer: 0,
}
+ clipboard?: TLDrawShape[]
+
+ session?: Session
+
+ pointedId?: string
+
+ pointedHandle?: string
+
+ pointedBoundsHandle?: TLBoundsCorner | TLBoundsEdge | 'rotate'
+
+ isCreating = false
+
+ constructor(id = Utils.uniqueId()) {
+ super(initialData, id, 1)
+
+ this.session = undefined
+ this.pointedId = undefined
+
+ this.patchState({
+ appState: {
+ status: {
+ current: TLDrawStatus.Idle,
+ previous: TLDrawStatus.Idle,
+ },
+ },
+ document: {
+ pageStates: {
+ [this.currentPageId]: {
+ bindingId: undefined,
+ editingId: undefined,
+ hoveredId: undefined,
+ pointedId: undefined,
+ },
+ },
+ },
+ })
+ }
/* -------------------- Internal -------------------- */
protected onStateWillChange = (_state: Data, id: string): void => {
@@ -232,18 +265,6 @@ export class TLDrawState extends StateManager {
})
}
- clipboard?: TLDrawShape[]
-
- session?: Session
-
- pointedId?: string
-
- pointedHandle?: string
-
- pointedBoundsHandle?: TLBoundsCorner | TLBoundsEdge | 'rotate'
-
- isCreating = false
-
/* -------------------- Settings -------------------- */
togglePenMode = (): this => {
@@ -787,6 +808,7 @@ export class TLDrawState extends StateManager {
completeSession(...args: ParametersExceptFirst) {
const { session } = this
+
if (!session) return this
const result = session.complete(this.state, ...args)
@@ -1531,6 +1553,11 @@ export class TLDrawState extends StateManager {
break
}
}
+ break
+ }
+ case TLDrawStatus.EditingText: {
+ this.completeSession()
+ break
}
}
}
@@ -1624,10 +1651,6 @@ export class TLDrawState extends StateManager {
}
break
}
- case TLDrawStatus.EditingText: {
- this.completeSession()
- break
- }
}
}
@@ -1698,8 +1721,18 @@ export class TLDrawState extends StateManager {
// Unused
}
- onDoubleClickShape: TLPointerEventHandler = () => {
- // TODO (drill into group)
+ onDoubleClickShape: TLPointerEventHandler = (info) => {
+ switch (this.appState.status.current) {
+ case TLDrawStatus.PointingBounds: {
+ switch (this.appState.activeTool) {
+ case 'select': {
+ this.startTextSession(info.target)
+ break
+ }
+ }
+ break
+ }
+ }
}
onRightPointShape: TLPointerEventHandler = (info) => {
@@ -1753,11 +1786,11 @@ export class TLDrawState extends StateManager {
}
onDoubleClickBounds: TLBoundsEventHandler = () => {
- // TODO
+ // Unused
}
onRightPointBounds: TLBoundsEventHandler = () => {
- // TODO
+ // Unused
}
onDragBounds: TLBoundsEventHandler = () => {
@@ -1765,11 +1798,11 @@ export class TLDrawState extends StateManager {
}
onHoverBounds: TLBoundsEventHandler = () => {
- // TODO
+ // Unused
}
onUnhoverBounds: TLBoundsEventHandler = () => {
- // TODO
+ // Unused
}
onReleaseBounds: TLBoundsEventHandler = (info) => {
diff --git a/packages/tldraw/src/styles/stitches.config.ts b/packages/tldraw/src/styles/stitches.config.ts
index 9534c65d9..d48dfb840 100644
--- a/packages/tldraw/src/styles/stitches.config.ts
+++ b/packages/tldraw/src/styles/stitches.config.ts
@@ -99,8 +99,6 @@ const { styled, css, theme, getCssString } = createCss({
},
})
-const light = theme({})
-
const dark = theme({
colors: {
brushFill: 'rgba(180, 180, 180, .05)',
@@ -138,4 +136,4 @@ const dark = theme({
export default styled
-export { css, getCssString, light, dark }
+export { css, getCssString, dark }