Adds text again

This commit is contained in:
Steve Ruiz 2021-08-30 21:17:04 +01:00
parent c28440b878
commit 8fe83ef6c3
18 changed files with 207 additions and 163 deletions

View file

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

View file

@ -34,4 +34,4 @@
"typescript": "4.2.3"
},
"gitHead": "a7dac0f83ad998e205c2aab58182cb4ba4e099a6"
}
}

Binary file not shown.

View file

@ -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 <div />
// }
// return <TLDraw document={value} onChange={handleChange} />
// Will automatically persist data under the provided id, too
return <TLDraw id="tldraw" />
}

View file

@ -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.`)

View file

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

View file

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

Binary file not shown.

View file

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

View file

@ -1,3 +1,4 @@
export * from './useKeyboardShortcuts'
export * from './useTLDrawContext'
export * from './useTheme'
export * from './useCustomFonts'

View file

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

View file

@ -0,0 +1,30 @@
import * as React from 'react'
const styles = new Map<string, HTMLStyleElement>()
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])
}

View file

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

View file

@ -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<TextShape> {
@ -69,10 +69,17 @@ export class Text extends TLDrawShapeUtil<TextShape> {
childIndex: 1,
point: [0, 0],
rotation: 0,
text: 'hi',
text: ' ',
style: defaultStyle,
}
create(props: Partial<TextShape>): 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<TextShape> {
}
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<TextShape> {
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<TextShape> {
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}&zwj;`
mdiv.style.font = getFontStyle(shape.style)
melm.innerHTML = `${shape.text}&zwj;`
melm.style.font = getFontStyle(shape.style)
const [width, height] = [mdiv.offsetWidth, mdiv.offsetHeight]
const [width, height] = [melm.offsetWidth, melm.offsetHeight]
return {
minX: 0,

View file

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

View file

@ -85,6 +85,51 @@ export class TextSession implements Session {
const shape = TLDR.getShape<TextShape>(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],
},
},
},

View file

@ -91,15 +91,48 @@ const initialData: Data = {
export class TLDrawState extends StateManager<Data> {
_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<Data> {
})
}
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<Data> {
completeSession<T extends Session>(...args: ParametersExceptFirst<T['complete']>) {
const { session } = this
if (!session) return this
const result = session.complete(this.state, ...args)
@ -1531,6 +1553,11 @@ export class TLDrawState extends StateManager<Data> {
break
}
}
break
}
case TLDrawStatus.EditingText: {
this.completeSession()
break
}
}
}
@ -1624,10 +1651,6 @@ export class TLDrawState extends StateManager<Data> {
}
break
}
case TLDrawStatus.EditingText: {
this.completeSession()
break
}
}
}
@ -1698,8 +1721,18 @@ export class TLDrawState extends StateManager<Data> {
// 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<Data> {
}
onDoubleClickBounds: TLBoundsEventHandler = () => {
// TODO
// Unused
}
onRightPointBounds: TLBoundsEventHandler = () => {
// TODO
// Unused
}
onDragBounds: TLBoundsEventHandler = () => {
@ -1765,11 +1798,11 @@ export class TLDrawState extends StateManager<Data> {
}
onHoverBounds: TLBoundsEventHandler = () => {
// TODO
// Unused
}
onUnhoverBounds: TLBoundsEventHandler = () => {
// TODO
// Unused
}
onReleaseBounds: TLBoundsEventHandler = (info) => {

View file

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