Remove core example
This commit is contained in:
parent
b6f2e2940f
commit
742f9421d6
4 changed files with 0 additions and 450 deletions
|
@ -1,138 +0,0 @@
|
|||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
/* refresh-reset */
|
||||
|
||||
import * as React from 'react'
|
||||
import { TLShape, Utils, TLBounds, TLShapeUtil, HTMLContainer, SVGContainer } from '@tldraw/core'
|
||||
|
||||
// Define a custom shape
|
||||
|
||||
export interface BoxShape extends TLShape {
|
||||
type: 'box'
|
||||
size: number[]
|
||||
text: string
|
||||
}
|
||||
|
||||
export const boxShape: BoxShape = {
|
||||
id: 'example1',
|
||||
type: 'box',
|
||||
parentId: 'page',
|
||||
childIndex: 0,
|
||||
name: 'Example Shape',
|
||||
point: [0, 0],
|
||||
size: [100, 100],
|
||||
rotation: 0,
|
||||
text: 'Hello world!',
|
||||
}
|
||||
// Create a "shape utility" class that interprets that shape
|
||||
|
||||
export class BoxUtil extends TLShapeUtil<BoxShape, HTMLDivElement> {
|
||||
age = 100
|
||||
|
||||
Component = TLShapeUtil.Component<BoxShape, HTMLDivElement>(
|
||||
({ shape, events, onShapeChange, isEditing, meta }, 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 = TLShapeUtil.Indicator<BoxShape>(({ shape }) => {
|
||||
return (
|
||||
<SVGContainer>
|
||||
<rect
|
||||
fill="none"
|
||||
stroke="blue"
|
||||
strokeWidth={1}
|
||||
width={shape.size[0]}
|
||||
height={shape.size[1]}
|
||||
pointerEvents="none"
|
||||
/>
|
||||
</SVGContainer>
|
||||
)
|
||||
})
|
||||
|
||||
getBounds = (shape: BoxShape) => {
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import { Renderer, TLShapeUtilsMap } from '@tldraw/core'
|
||||
import { BoxShape, BoxUtil } from './box'
|
||||
import { LabelUtil, LabelShape } from './label'
|
||||
import { appState } from './state'
|
||||
|
||||
const shapeUtils: TLShapeUtilsMap<BoxShape | LabelShape> = {
|
||||
box: new BoxUtil(),
|
||||
label: new LabelUtil(),
|
||||
}
|
||||
|
||||
export default function Core() {
|
||||
const page = appState.useStore((s) => s.page)
|
||||
const pageState = appState.useStore((s) => s.pageState)
|
||||
const meta = appState.useStore((s) => s.meta)
|
||||
|
||||
return (
|
||||
<div className="tldraw">
|
||||
<Renderer
|
||||
shapeUtils={shapeUtils}
|
||||
page={page}
|
||||
pageState={pageState}
|
||||
meta={meta}
|
||||
onDoubleClickBounds={appState.onDoubleClickBounds}
|
||||
onDoubleClickShape={appState.onDoubleClickShape}
|
||||
onPointShape={appState.onPointShape}
|
||||
onPointCanvas={appState.onPointCanvas}
|
||||
onPointerDown={appState.onPointerDown}
|
||||
onPointerMove={appState.onPointerMove}
|
||||
onPointerUp={appState.onPointerUp}
|
||||
onShapeChange={appState.onShapeChange}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
/* refresh-reset */
|
||||
|
||||
import * as React from 'react'
|
||||
import {
|
||||
TLShape,
|
||||
Utils,
|
||||
TLBounds,
|
||||
HTMLContainer,
|
||||
TLComponent,
|
||||
TLShapeUtil,
|
||||
TLIndicator,
|
||||
} from '@tldraw/core'
|
||||
import { appState } from './state'
|
||||
|
||||
// Define a custom shape
|
||||
|
||||
export interface LabelShape extends TLShape {
|
||||
type: 'label'
|
||||
text: string
|
||||
}
|
||||
|
||||
// Create a "shape utility" class that interprets that shape
|
||||
|
||||
export class LabelUtil extends TLShapeUtil<LabelShape, HTMLDivElement> {
|
||||
type = 'label' as const
|
||||
|
||||
isStateful = true
|
||||
|
||||
Component: TLComponent<LabelShape, HTMLDivElement> = (
|
||||
{ shape, events, isSelected, onShapeChange, meta },
|
||||
ref
|
||||
) => {
|
||||
const color = meta.isDarkMode ? 'white' : 'black'
|
||||
const bounds = this.getBounds(shape)
|
||||
const rInput = React.useRef<HTMLDivElement>(null)
|
||||
function updateShapeSize() {
|
||||
const elm = rInput.current!
|
||||
appState.changeShapeText(shape.id, elm.innerText)
|
||||
onShapeChange?.({
|
||||
id: shape.id,
|
||||
text: elm.innerText,
|
||||
})
|
||||
}
|
||||
React.useLayoutEffect(() => {
|
||||
const elm = rInput.current!
|
||||
elm.innerText = shape.text
|
||||
updateShapeSize()
|
||||
const observer = new MutationObserver(updateShapeSize)
|
||||
observer.observe(elm, {
|
||||
attributes: true,
|
||||
characterData: true,
|
||||
subtree: true,
|
||||
})
|
||||
}, [])
|
||||
return (
|
||||
<HTMLContainer>
|
||||
<div
|
||||
{...events}
|
||||
style={{
|
||||
width: bounds.width,
|
||||
height: bounds.height,
|
||||
pointerEvents: 'all',
|
||||
display: 'flex',
|
||||
fontSize: 20,
|
||||
fontFamily: 'sans-serif',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
border: `2px solid ${color}`,
|
||||
color,
|
||||
}}
|
||||
>
|
||||
<div ref={ref} onPointerDown={(e) => isSelected && e.stopPropagation()}>
|
||||
<div
|
||||
ref={rInput}
|
||||
style={{
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
textAlign: 'center',
|
||||
outline: 'none',
|
||||
userSelect: isSelected ? 'all' : 'none',
|
||||
}}
|
||||
contentEditable={isSelected}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</HTMLContainer>
|
||||
)
|
||||
}
|
||||
|
||||
Indicator: TLIndicator<LabelShape> = ({ shape }) => {
|
||||
const bounds = this.getBounds(shape)
|
||||
|
||||
return (
|
||||
<rect
|
||||
fill="none"
|
||||
stroke="blue"
|
||||
strokeWidth={1}
|
||||
width={bounds.width}
|
||||
height={bounds.height}
|
||||
pointerEvents="none"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
getBounds = (shape: LabelShape) => {
|
||||
const bounds = Utils.getFromCache(this.boundsCache, shape, () => {
|
||||
const ref = this.getRef(shape)
|
||||
const width = ref.current?.offsetWidth || 0
|
||||
const height = ref.current?.offsetHeight || 0
|
||||
|
||||
return {
|
||||
minX: 0,
|
||||
maxX: width,
|
||||
minY: 0,
|
||||
maxY: height,
|
||||
width,
|
||||
height,
|
||||
} as TLBounds
|
||||
})
|
||||
|
||||
return Utils.translateBounds(bounds, shape.point)
|
||||
}
|
||||
}
|
|
@ -1,153 +0,0 @@
|
|||
import type {
|
||||
TLBinding,
|
||||
TLPage,
|
||||
TLPageState,
|
||||
TLPointerEventHandler,
|
||||
TLShapeChangeHandler,
|
||||
} from '@tldraw/core'
|
||||
import type { BoxShape } from './box'
|
||||
import type { LabelShape } from './label'
|
||||
import { StateManager } from 'rko'
|
||||
|
||||
type Shapes = BoxShape | LabelShape
|
||||
|
||||
interface State {
|
||||
page: TLPage<Shapes, TLBinding>
|
||||
pageState: TLPageState
|
||||
meta: {
|
||||
isDarkMode: boolean
|
||||
}
|
||||
}
|
||||
|
||||
class AppState extends StateManager<State> {
|
||||
/* ----------------------- API ---------------------- */
|
||||
|
||||
selectShape(shapeId: string) {
|
||||
this.patchState({
|
||||
pageState: {
|
||||
selectedIds: [shapeId],
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
deselect() {
|
||||
this.patchState({
|
||||
pageState: {
|
||||
selectedIds: [],
|
||||
editingId: undefined,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
startEditingShape(shapeId: string) {
|
||||
this.patchState({
|
||||
pageState: {
|
||||
selectedIds: [shapeId],
|
||||
editingId: shapeId,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
changeShapeText = (id: string, text: string) => {
|
||||
this.patchState({
|
||||
page: {
|
||||
shapes: {
|
||||
[id]: { text },
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/* --------------------- Events --------------------- */
|
||||
|
||||
onPointCanvas: TLPointerEventHandler = (info) => {
|
||||
this.deselect()
|
||||
}
|
||||
|
||||
onPointShape: TLPointerEventHandler = (info) => {
|
||||
this.selectShape(info.target)
|
||||
}
|
||||
|
||||
onDoubleClickShape: TLPointerEventHandler = (info) => {
|
||||
this.startEditingShape(info.target)
|
||||
}
|
||||
|
||||
onDoubleClickBounds: TLPointerEventHandler = (info) => {
|
||||
// Todo
|
||||
}
|
||||
|
||||
onPointerDown: TLPointerEventHandler = (info) => {
|
||||
// Todo
|
||||
}
|
||||
|
||||
onPointerUp: TLPointerEventHandler = (info) => {
|
||||
// Todo
|
||||
}
|
||||
|
||||
onPointerMove: TLPointerEventHandler = (info) => {
|
||||
// Todo
|
||||
}
|
||||
|
||||
onShapeChange: TLShapeChangeHandler<Shapes> = (shape) => {
|
||||
if (shape.type === 'box' && shape.size) {
|
||||
this.patchState({
|
||||
page: {
|
||||
shapes: {
|
||||
[shape.id]: { ...shape, size: [...shape.size] },
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const appState = new AppState({
|
||||
page: {
|
||||
id: 'page1',
|
||||
shapes: {
|
||||
rect1: {
|
||||
id: 'rect1',
|
||||
parentId: 'page1',
|
||||
name: 'Rectangle',
|
||||
childIndex: 1,
|
||||
type: 'box',
|
||||
point: [0, 0],
|
||||
rotation: 0,
|
||||
size: [100, 100],
|
||||
text: 'Hello world!',
|
||||
},
|
||||
label1: {
|
||||
id: 'label1',
|
||||
parentId: 'page1',
|
||||
name: 'Label',
|
||||
childIndex: 2,
|
||||
type: 'label',
|
||||
point: [-200, -200],
|
||||
rotation: 0,
|
||||
text: 'My shape is stateful, I should still render while off screen!',
|
||||
},
|
||||
label2: {
|
||||
id: 'label2',
|
||||
parentId: 'page1',
|
||||
name: 'Label',
|
||||
childIndex: 2,
|
||||
type: 'label',
|
||||
point: [200, 200],
|
||||
rotation: 0,
|
||||
text: 'Hello world!',
|
||||
},
|
||||
},
|
||||
bindings: {},
|
||||
},
|
||||
pageState: {
|
||||
id: 'page1',
|
||||
selectedIds: [],
|
||||
camera: {
|
||||
point: [0, 0],
|
||||
zoom: 1,
|
||||
},
|
||||
},
|
||||
meta: {
|
||||
isDarkMode: false,
|
||||
},
|
||||
})
|
Loading…
Reference in a new issue