Adds article to core example

core-article-example
Steve Ruiz 2021-09-25 19:39:15 +01:00
rodzic 31638c7c90
commit 9463ef7f3b
2 zmienionych plików z 192 dodań i 5 usunięć

Wyświetl plik

@ -0,0 +1,131 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
/* refresh-reset */
import * as React from 'react'
import { TLShape, Utils, TLBounds, ShapeUtil, HTMLContainer } from '@tldraw/core'
// Define a custom shape
export interface ArticleShape extends TLShape {
type: 'rectangle'
size: number[]
text: string
}
// Create a "shape utility" class that interprets that shape
export const Article = new ShapeUtil<ArticleShape, HTMLDivElement, { isDarkMode: boolean }>(() => ({
type: 'rectangle',
defaultProps: {
id: 'example1',
type: 'rectangle',
parentId: 'page1',
childIndex: 0,
name: 'Example Shape',
point: [0, 0],
size: [100, 100],
rotation: 0,
text: 'Hello world!',
},
Component({ shape, events, meta, onShapeChange, isEditing }, ref) {
const color = meta.isDarkMode ? 'white' : 'black'
const rInput = React.useRef<HTMLDivElement>(null)
function updateShapeSize() {
const elm = rInput.current!
onShapeChange?.({
...shape,
text: elm.innerText,
size: [elm.offsetWidth + 44, elm.offsetHeight + 44],
})
}
React.useLayoutEffect(() => {
const elm = rInput.current!
const observer = new MutationObserver(updateShapeSize)
observer.observe(elm, {
attributes: true,
characterData: true,
subtree: true,
})
elm.innerText = shape.text
updateShapeSize()
return () => {
observer.disconnect()
}
}, [])
React.useEffect(() => {
if (isEditing) {
rInput.current!.focus()
}
}, [isEditing])
return (
<HTMLContainer ref={ref}>
<div
{...events}
style={{
pointerEvents: 'all',
width: shape.size[0],
height: shape.size[1],
display: 'flex',
fontSize: 20,
fontFamily: 'sans-serif',
alignItems: 'center',
justifyContent: 'center',
border: `2px solid ${color}`,
color,
}}
>
<div onPointerDown={(e) => isEditing && e.stopPropagation()}>
<div
ref={rInput}
style={{
whiteSpace: 'nowrap',
overflow: 'hidden',
textAlign: 'center',
outline: 'none',
userSelect: isEditing ? 'all' : 'none',
}}
contentEditable={isEditing}
/>
</div>
</div>
</HTMLContainer>
)
},
Indicator({ shape }) {
return (
<rect
fill="none"
stroke="blue"
strokeWidth={1}
width={shape.size[0]}
height={shape.size[1]}
pointerEvents="none"
/>
)
},
getBounds(shape) {
const bounds = Utils.getFromCache(this.boundsCache, shape, () => {
const [width, height] = shape.size
return {
minX: 0,
maxX: width,
minY: 0,
maxY: height,
width,
height,
} as TLBounds
})
return Utils.translateBounds(bounds, shape.point)
},
}))

Wyświetl plik

@ -5,14 +5,22 @@ import type {
TLPointerEventHandler,
TLShapeChangeHandler,
} from '@tldraw/core'
import type { RectangleShape } from './rectangle'
import type { LabelShape } from './label'
import { ShapeUtil } from '@tldraw/core'
import { Article, ArticleShape } from './article'
import { Rectangle, RectangleShape } from './rectangle'
import { Label, LabelShape } from './label'
import { StateManager } from 'rko'
type Shapes = RectangleShape | LabelShape
const shapeUtils: Record<string, any> = {
rectangle: Rectangle,
label: Label,
article: Article,
}
type Shape = RectangleShape | LabelShape | ArticleShape
interface State {
page: TLPage<Shapes, TLBinding>
page: TLPage<Shape, TLBinding>
pageState: TLPageState
meta: {
isDarkMode: boolean
@ -58,10 +66,58 @@ class AppState extends StateManager<State> {
})
}
/**
* Manually create shapes on the page.
* @param shapes An array of shape partials, containing the initial props for the shapes.
* @command
*/
createShapes = (...shapes: ({ id: string; type: Shape['type'] } & Partial<Shape>)[]): this => {
if (shapes.length === 0) return this
return this.create(
...shapes.map((shape) => {
return shapeUtils[shape.type].create({
...shape,
parentId: shape.parentId || this.state.page.id,
})
})
)
}
/**
* Manually update a set of shapes.
* @param shapes An array of shape partials, containing the changes to be made to each shape.
* @command
*/
updateShapes = (...shapes: ({ id: string } & Partial<Shape>)[]): this => {
const pageShapes = this.state.page.shapes
const shapesToUpdate = shapes.filter((shape) => pageShapes[shape.id])
if (shapesToUpdate.length === 0) return this
return this.setState(
Commands.update(this.state, shapesToUpdate, this.state.page.id),
'updated_shapes'
)
}
/**
* Create one or more shapes.
* @param shapes An array of shapes.
* @command
*/
create = (...shapes: TLDrawShape[]): this => {
if (shapes.length === 0) return this
return this.setState(Commands.create(this.state, shapes))
}
/* --------------------- Events --------------------- */
onPointCanvas: TLPointerEventHandler = (info) => {
this.deselect()
if (this.state.pageState.selectedIds.length > 0) {
this.deselect()
} else {
if (info.shiftKey) {
this.create
}
}
}
onPointShape: TLPointerEventHandler = (info) => {