Replaces isDarkMode with meta, a more flexible option for custom rendering context
This commit is contained in:
parent
7d18be27cf
commit
64d00dc427
47 changed files with 391 additions and 336 deletions
5
.vscode/snippets.code-snippets
vendored
5
.vscode/snippets.code-snippets
vendored
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"createComment": {
|
||||
"scope": "typescript,typescriptreact",
|
||||
"prefix": "/**",
|
||||
"prefix": "ccc",
|
||||
"body": [
|
||||
"/**",
|
||||
" * ${1:description}",
|
||||
|
@ -10,8 +10,7 @@
|
|||
" *",
|
||||
" *```ts",
|
||||
" * ${2:example}",
|
||||
" *```",
|
||||
" */"
|
||||
" *```"
|
||||
],
|
||||
"description": "comment"
|
||||
}
|
||||
|
|
|
@ -19,11 +19,12 @@
|
|||
"test": "jest",
|
||||
"test:watch": "test --watchAll",
|
||||
"lerna": "lerna",
|
||||
"start": "yarn build:packages && lerna run start --parallel",
|
||||
"start": "lerna run start --stream --parallel",
|
||||
"start:www": "yarn build:packages && lerna run start --parallel & cd packages/www && yarn dev",
|
||||
"build": "yarn build:packages && cd packages/www && yarn build",
|
||||
"build:packages": "cd packages/core && yarn build && cd ../tldraw && yarn build",
|
||||
"publish:patch": "yarn build:packages && lerna publish patch"
|
||||
"publish:patch": "yarn build:packages && lerna publish patch",
|
||||
"docs": "lerna run docs --stream"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-syntax-import-meta": "^7.10.4",
|
||||
|
@ -46,6 +47,7 @@
|
|||
"react-dom": "^17.0.2",
|
||||
"ts-jest": "^27.0.5",
|
||||
"tslib": "^2.3.0",
|
||||
"typedoc": "^0.21.9",
|
||||
"typescript": "^4.4.2"
|
||||
},
|
||||
"dependencies": {},
|
||||
|
|
|
@ -23,13 +23,13 @@ interface CanvasProps<T extends TLShape> {
|
|||
hideBounds?: boolean
|
||||
hideHandles?: boolean
|
||||
hideIndicators?: boolean
|
||||
isDarkMode?: boolean
|
||||
meta?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export const Canvas = React.memo(function Canvas<T extends TLShape>({
|
||||
page,
|
||||
pageState,
|
||||
isDarkMode = false,
|
||||
meta,
|
||||
hideHandles = false,
|
||||
hideBounds = false,
|
||||
hideIndicators = false,
|
||||
|
@ -58,7 +58,7 @@ export const Canvas = React.memo(function Canvas<T extends TLShape>({
|
|||
hideBounds={hideBounds}
|
||||
hideIndicators={hideIndicators}
|
||||
hideHandles={hideHandles}
|
||||
isDarkMode={isDarkMode}
|
||||
meta={meta}
|
||||
/>
|
||||
<Brush />
|
||||
</g>
|
||||
|
|
|
@ -11,7 +11,6 @@ describe('page', () => {
|
|||
hideBounds={false}
|
||||
hideIndicators={false}
|
||||
hideHandles={false}
|
||||
isDarkMode={false}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
|
|
@ -13,22 +13,30 @@ interface PageProps<T extends TLShape> {
|
|||
hideBounds: boolean
|
||||
hideHandles: boolean
|
||||
hideIndicators: boolean
|
||||
isDarkMode: boolean
|
||||
meta?: Record<string, unknown>
|
||||
}
|
||||
|
||||
/**
|
||||
* The Page component renders the current page.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
*```ts
|
||||
* example
|
||||
*``` */
|
||||
export function Page<T extends TLShape>({
|
||||
page,
|
||||
pageState,
|
||||
hideBounds,
|
||||
hideHandles,
|
||||
hideIndicators,
|
||||
isDarkMode,
|
||||
meta,
|
||||
}: PageProps<T>): JSX.Element {
|
||||
const { callbacks, shapeUtils } = useTLContext()
|
||||
|
||||
useRenderOnResize()
|
||||
|
||||
const shapeTree = useShapeTree(page, pageState, shapeUtils, isDarkMode, callbacks.onChange)
|
||||
const shapeTree = useShapeTree(page, pageState, shapeUtils, meta, callbacks.onChange)
|
||||
|
||||
const { shapeWithHandles } = useHandles(page, pageState)
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ import type {
|
|||
TLShape,
|
||||
TLPage,
|
||||
TLPageState,
|
||||
TLSettings,
|
||||
TLCallbacks,
|
||||
TLShapeUtils,
|
||||
TLTheme,
|
||||
|
@ -13,30 +12,79 @@ import type {
|
|||
import { Canvas } from '../canvas'
|
||||
import { useTLTheme, TLContext } from '../../hooks'
|
||||
|
||||
export interface RendererProps<T extends TLShape>
|
||||
extends Partial<TLSettings>,
|
||||
Partial<TLCallbacks> {
|
||||
export interface RendererProps<T extends TLShape, M extends Record<string, unknown>>
|
||||
extends Partial<TLCallbacks> {
|
||||
/**
|
||||
* An object containing instances of your shape classes.
|
||||
*/
|
||||
shapeUtils: TLShapeUtils<T>
|
||||
/**
|
||||
* The current page, containing shapes and bindings.
|
||||
*/
|
||||
page: TLPage<T, TLBinding>
|
||||
/**
|
||||
* The current page state.
|
||||
*/
|
||||
pageState: TLPageState
|
||||
/**
|
||||
* An object of custom theme colors.
|
||||
*/
|
||||
theme?: Partial<TLTheme>
|
||||
/**
|
||||
* When true, the renderer will not show the bounds for selected objects.
|
||||
*/
|
||||
hideBounds?: boolean
|
||||
/**
|
||||
* When true, the renderer will not show the handles of shapes with handles.
|
||||
*/
|
||||
hideHandles?: boolean
|
||||
/**
|
||||
* When true, the renderer will not show indicators for selected or hovered objects,
|
||||
*/
|
||||
hideIndicators?: boolean
|
||||
isDarkMode?: boolean
|
||||
/**
|
||||
* When true, the renderer will ignore all inputs that were not made by a stylus or pen-type device.
|
||||
*/
|
||||
isPenMode?: boolean
|
||||
/**
|
||||
* An object of custom options that should be passed to rendered shapes.
|
||||
*/
|
||||
meta?: M
|
||||
}
|
||||
|
||||
export function Renderer<T extends TLShape>({
|
||||
/**
|
||||
The Renderer component is the main component of the library. It accepts the current `page`, the `shapeUtils` needed to interpret and render the shapes and bindings on the `page`, and the current `pageState`.
|
||||
|
||||
* It also (optionally) accepts several settings and visibility flags,
|
||||
* a `theme` to use, and callbacks to respond to various user interactions.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
*```tsx
|
||||
* <Renderer
|
||||
* shapeUtils={shapeUtils}
|
||||
* page={page}
|
||||
* pageState={pageState}
|
||||
* />
|
||||
*```
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Renderer component is the main component of the library. It accepts the current `page`, the `shapeUtils` needed to interpret and render the shapes and bindings on the `page`, and the current `pageState`.
|
||||
* @param props
|
||||
* @returns
|
||||
*/
|
||||
export function Renderer<T extends TLShape, M extends Record<string, unknown>>({
|
||||
shapeUtils,
|
||||
page,
|
||||
pageState,
|
||||
theme,
|
||||
meta,
|
||||
hideHandles = false,
|
||||
hideIndicators = false,
|
||||
hideBounds = false,
|
||||
isDarkMode = false,
|
||||
...rest
|
||||
}: RendererProps<T>): JSX.Element {
|
||||
}: RendererProps<T, M>): JSX.Element {
|
||||
useTLTheme(theme)
|
||||
const rScreenBounds = React.useRef<TLBounds>(null)
|
||||
const rPageState = React.useRef<TLPageState>(pageState)
|
||||
|
@ -60,7 +108,7 @@ export function Renderer<T extends TLShape>({
|
|||
hideBounds={hideBounds}
|
||||
hideIndicators={hideIndicators}
|
||||
hideHandles={hideHandles}
|
||||
isDarkMode={isDarkMode}
|
||||
meta={meta}
|
||||
/>
|
||||
</TLContext.Provider>
|
||||
)
|
||||
|
|
|
@ -2,19 +2,21 @@ import { useTLContext } from '+hooks'
|
|||
import * as React from 'react'
|
||||
import type { TLShapeUtil, TLRenderInfo, TLShape } from '+types'
|
||||
|
||||
interface EditingShapeProps<T extends TLShape> extends TLRenderInfo {
|
||||
interface EditingShapeProps<T extends TLShape, M extends Record<string, unknown>>
|
||||
extends TLRenderInfo {
|
||||
shape: T
|
||||
utils: TLShapeUtil<T>
|
||||
meta?: M
|
||||
}
|
||||
|
||||
export function EditingTextShape({
|
||||
export function EditingTextShape<M extends Record<string, unknown>>({
|
||||
shape,
|
||||
utils,
|
||||
isEditing,
|
||||
isBinding,
|
||||
isDarkMode,
|
||||
isCurrentParent,
|
||||
}: EditingShapeProps<TLShape>) {
|
||||
meta,
|
||||
}: EditingShapeProps<TLShape, M>) {
|
||||
const {
|
||||
callbacks: { onTextChange, onTextBlur, onTextFocus, onTextKeyDown, onTextKeyUp },
|
||||
} = useTLContext()
|
||||
|
@ -26,11 +28,11 @@ export function EditingTextShape({
|
|||
isEditing,
|
||||
isCurrentParent,
|
||||
isBinding,
|
||||
isDarkMode,
|
||||
onTextChange,
|
||||
onTextBlur,
|
||||
onTextFocus,
|
||||
onTextKeyDown,
|
||||
onTextKeyUp,
|
||||
meta,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,32 +1,34 @@
|
|||
import * as React from 'react'
|
||||
import type { TLShapeUtil, TLRenderInfo, TLShape } from '+types'
|
||||
|
||||
interface RenderedShapeProps<T extends TLShape> extends TLRenderInfo {
|
||||
interface RenderedShapeProps<T extends TLShape, M extends Record<string, unknown>>
|
||||
extends TLRenderInfo {
|
||||
shape: T
|
||||
utils: TLShapeUtil<T>
|
||||
meta?: M
|
||||
}
|
||||
|
||||
export const RenderedShape = React.memo(
|
||||
function RenderedShape({
|
||||
function RenderedShape<M extends Record<string, unknown>>({
|
||||
shape,
|
||||
utils,
|
||||
isEditing,
|
||||
isBinding,
|
||||
isDarkMode,
|
||||
isCurrentParent,
|
||||
}: RenderedShapeProps<TLShape>) {
|
||||
meta,
|
||||
}: RenderedShapeProps<TLShape, M>) {
|
||||
return utils.render(shape, {
|
||||
isEditing,
|
||||
isBinding,
|
||||
isDarkMode,
|
||||
isCurrentParent,
|
||||
meta,
|
||||
})
|
||||
},
|
||||
(prev, next) => {
|
||||
if (
|
||||
prev.isEditing !== next.isEditing ||
|
||||
prev.isDarkMode !== next.isDarkMode ||
|
||||
prev.isBinding !== next.isBinding ||
|
||||
prev.meta !== next.meta ||
|
||||
prev.isCurrentParent !== next.isCurrentParent
|
||||
) {
|
||||
return false
|
||||
|
|
|
@ -3,15 +3,22 @@ import type { IShapeTreeNode } from '+types'
|
|||
import { Shape } from './shape'
|
||||
|
||||
export const ShapeNode = React.memo(
|
||||
({ shape, children, isEditing, isDarkMode, isBinding, isCurrentParent }: IShapeTreeNode) => {
|
||||
<M extends Record<string, unknown>>({
|
||||
shape,
|
||||
children,
|
||||
isEditing,
|
||||
isBinding,
|
||||
isCurrentParent,
|
||||
meta,
|
||||
}: IShapeTreeNode<M>) => {
|
||||
return (
|
||||
<>
|
||||
<Shape
|
||||
shape={shape}
|
||||
isEditing={isEditing}
|
||||
isDarkMode={isDarkMode}
|
||||
isBinding={isBinding}
|
||||
isCurrentParent={isCurrentParent}
|
||||
meta={meta}
|
||||
/>
|
||||
{children &&
|
||||
children.map((childNode) => <ShapeNode key={childNode.shape.id} {...childNode} />)}
|
||||
|
|
|
@ -9,7 +9,6 @@ describe('handles', () => {
|
|||
shape={mockUtils.box.create({})}
|
||||
isEditing={false}
|
||||
isBinding={false}
|
||||
isDarkMode={false}
|
||||
isCurrentParent={false}
|
||||
/>
|
||||
)
|
||||
|
|
|
@ -5,7 +5,13 @@ import { RenderedShape } from './rendered-shape'
|
|||
import { EditingTextShape } from './editing-text-shape'
|
||||
|
||||
export const Shape = React.memo(
|
||||
({ shape, isEditing, isBinding, isDarkMode, isCurrentParent }: IShapeTreeNode) => {
|
||||
<M extends Record<string, unknown>>({
|
||||
shape,
|
||||
isEditing,
|
||||
isBinding,
|
||||
isCurrentParent,
|
||||
meta,
|
||||
}: IShapeTreeNode<M>) => {
|
||||
const { shapeUtils } = useTLContext()
|
||||
const events = useShapeEvents(shape.id, isCurrentParent)
|
||||
const utils = shapeUtils[shape.type]
|
||||
|
@ -26,9 +32,9 @@ export const Shape = React.memo(
|
|||
shape={shape}
|
||||
isBinding={false}
|
||||
isCurrentParent={false}
|
||||
isDarkMode={isDarkMode}
|
||||
isEditing={true}
|
||||
utils={utils}
|
||||
meta={meta}
|
||||
/>
|
||||
) : (
|
||||
<RenderedShape
|
||||
|
@ -36,8 +42,8 @@ export const Shape = React.memo(
|
|||
utils={utils}
|
||||
isBinding={isBinding}
|
||||
isCurrentParent={isCurrentParent}
|
||||
isDarkMode={isDarkMode}
|
||||
isEditing={isEditing}
|
||||
meta={meta}
|
||||
/>
|
||||
)}
|
||||
</g>
|
||||
|
|
|
@ -10,26 +10,26 @@ import type {
|
|||
} from '+types'
|
||||
import { Utils, Vec } from '+utils'
|
||||
|
||||
function addToShapeTree<T extends TLShape>(
|
||||
function addToShapeTree<T extends TLShape, M extends Record<string, unknown>>(
|
||||
shape: TLShape,
|
||||
branch: IShapeTreeNode[],
|
||||
branch: IShapeTreeNode<M>[],
|
||||
shapes: TLPage<T, TLBinding>['shapes'],
|
||||
selectedIds: string[],
|
||||
info: {
|
||||
pageState: {
|
||||
bindingId?: string
|
||||
hoveredId?: string
|
||||
currentParentId?: string
|
||||
editingId?: string
|
||||
editingBindingId?: string
|
||||
isDarkMode?: boolean
|
||||
}
|
||||
},
|
||||
meta?: M
|
||||
) {
|
||||
const node: IShapeTreeNode = {
|
||||
const node: IShapeTreeNode<M> = {
|
||||
shape,
|
||||
isCurrentParent: info.currentParentId === shape.id,
|
||||
isEditing: info.editingId === shape.id,
|
||||
isBinding: info.bindingId === shape.id,
|
||||
isDarkMode: info.isDarkMode || false,
|
||||
isCurrentParent: pageState.currentParentId === shape.id,
|
||||
isEditing: pageState.editingId === shape.id,
|
||||
isBinding: pageState.bindingId === shape.id,
|
||||
meta,
|
||||
}
|
||||
|
||||
branch.push(node)
|
||||
|
@ -41,16 +41,16 @@ function addToShapeTree<T extends TLShape>(
|
|||
.sort((a, b) => a.childIndex - b.childIndex)
|
||||
.forEach((childShape) =>
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
addToShapeTree(childShape, node.children!, shapes, selectedIds, info)
|
||||
addToShapeTree(childShape, node.children!, shapes, selectedIds, pageState, meta)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export function useShapeTree<T extends TLShape>(
|
||||
export function useShapeTree<T extends TLShape, M extends Record<string, unknown>>(
|
||||
page: TLPage<T, TLBinding>,
|
||||
pageState: TLPageState,
|
||||
shapeUtils: TLShapeUtils<T>,
|
||||
isDarkMode: boolean,
|
||||
meta?: M,
|
||||
onChange?: TLCallbacks['onChange']
|
||||
) {
|
||||
const rPreviousCount = React.useRef(0)
|
||||
|
@ -102,13 +102,11 @@ export function useShapeTree<T extends TLShape>(
|
|||
|
||||
// Populate the shape tree
|
||||
|
||||
const tree: IShapeTreeNode[] = []
|
||||
const tree: IShapeTreeNode<M>[] = []
|
||||
|
||||
shapesToRender
|
||||
.sort((a, b) => a.childIndex - b.childIndex)
|
||||
.forEach((shape) =>
|
||||
addToShapeTree(shape, tree, page.shapes, selectedIds, { ...pageState, isDarkMode })
|
||||
)
|
||||
.forEach((shape) => addToShapeTree(shape, tree, page.shapes, selectedIds, pageState, meta))
|
||||
|
||||
return tree
|
||||
}
|
||||
|
|
|
@ -2,7 +2,3 @@ export * from './components'
|
|||
export * from './types'
|
||||
export * from './utils'
|
||||
export * from './inputs'
|
||||
|
||||
export interface Steve {
|
||||
age: string
|
||||
}
|
||||
|
|
|
@ -54,17 +54,17 @@ export interface TLShape {
|
|||
|
||||
export type TLShapeUtils<T extends TLShape> = Record<string, TLShapeUtil<T>>
|
||||
|
||||
export interface TLRenderInfo<T extends SVGElement | HTMLElement = any> {
|
||||
export interface TLRenderInfo<M = any, T extends SVGElement | HTMLElement = any> {
|
||||
ref?: React.RefObject<T>
|
||||
isEditing: boolean
|
||||
isBinding: boolean
|
||||
isDarkMode: boolean
|
||||
isCurrentParent: boolean
|
||||
ref?: React.RefObject<T>
|
||||
onTextChange?: TLCallbacks['onTextChange']
|
||||
onTextBlur?: TLCallbacks['onTextBlur']
|
||||
onTextFocus?: TLCallbacks['onTextFocus']
|
||||
onTextKeyDown?: TLCallbacks['onTextKeyDown']
|
||||
onTextKeyUp?: TLCallbacks['onTextKeyUp']
|
||||
meta: M extends any ? M : never
|
||||
}
|
||||
|
||||
export interface TLTool {
|
||||
|
@ -79,12 +79,6 @@ export interface TLBinding {
|
|||
fromId: string
|
||||
}
|
||||
|
||||
export interface TLSettings {
|
||||
isDebugMode: boolean
|
||||
isDarkMode: boolean
|
||||
isPenMode: boolean
|
||||
}
|
||||
|
||||
export interface TLTheme {
|
||||
brushFill?: string
|
||||
brushStroke?: string
|
||||
|
@ -375,24 +369,26 @@ export abstract class TLShapeUtil<T extends TLShape> {
|
|||
|
||||
/* -------------------- Internal -------------------- */
|
||||
|
||||
export interface IShapeTreeNode {
|
||||
export interface IShapeTreeNode<M extends Record<string, unknown>> {
|
||||
shape: TLShape
|
||||
children?: IShapeTreeNode[]
|
||||
children?: IShapeTreeNode<M>[]
|
||||
isEditing: boolean
|
||||
isBinding: boolean
|
||||
isDarkMode: boolean
|
||||
isCurrentParent: boolean
|
||||
meta?: M
|
||||
}
|
||||
|
||||
/* -------------------------------------------------- */
|
||||
/* Utility Types */
|
||||
/* -------------------------------------------------- */
|
||||
|
||||
/** @internal */
|
||||
export type MappedByType<T extends { type: string }> = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[P in T['type']]: T extends any ? (P extends T['type'] ? T : never) : never
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export type RequiredKeys<T> = {
|
||||
[K in keyof T]-?: Record<string, unknown> extends Pick<T, K> ? never : K
|
||||
}[keyof T]
|
||||
|
|
|
@ -1718,14 +1718,6 @@ export default Utils
|
|||
|
||||
// Helper types
|
||||
|
||||
export type DeepPartial<T> = T extends Function
|
||||
? T
|
||||
: T extends object
|
||||
? T extends unknown[]
|
||||
? DeepPartial<T[number]>[]
|
||||
: { [P in keyof T]?: DeepPartial<T[P]> }
|
||||
: T
|
||||
|
||||
type Entry<T> = {
|
||||
[K in keyof T]: [K, T[K]]
|
||||
}[keyof T]
|
||||
|
|
|
@ -5,7 +5,13 @@ This package contains the [tldraw](https://tldraw.com) editor as a standalone Re
|
|||
## Installation
|
||||
|
||||
```bash
|
||||
yarn add @tldraw/tldraw --peer
|
||||
npm i @tldraw/tldraw
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```bash
|
||||
yarn add @tldraw/tldraw
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
|
|
@ -18,13 +18,16 @@ import type { Data } from '~types'
|
|||
const sortedSelector = (s: Data) =>
|
||||
Object.values(s.document.pages).sort((a, b) => (a.childIndex || 0) - (b.childIndex || 0))
|
||||
|
||||
const currentPageSelector = (s: Data) => s.document.pages[s.appState.currentPageId]
|
||||
const currentPageNameSelector = (s: Data) => s.document.pages[s.appState.currentPageId].name
|
||||
|
||||
const currentPageIdSelector = (s: Data) => s.document.pages[s.appState.currentPageId].id
|
||||
|
||||
export function PagePanel(): JSX.Element {
|
||||
const rIsOpen = React.useRef(false)
|
||||
const [isOpen, setIsOpen] = React.useState(false)
|
||||
const { useSelector } = useTLDrawContext()
|
||||
|
||||
const { tlstate, useSelector } = useTLDrawContext()
|
||||
const rIsOpen = React.useRef(false)
|
||||
|
||||
const [isOpen, setIsOpen] = React.useState(false)
|
||||
|
||||
React.useEffect(() => {
|
||||
if (rIsOpen.current !== isOpen) {
|
||||
|
@ -32,67 +35,83 @@ export function PagePanel(): JSX.Element {
|
|||
}
|
||||
}, [isOpen])
|
||||
|
||||
const handleClose = React.useCallback(() => {
|
||||
setIsOpen(false)
|
||||
}, [setIsOpen])
|
||||
|
||||
const handleOpenChange = React.useCallback(
|
||||
(isOpen: boolean) => {
|
||||
if (rIsOpen.current !== isOpen) {
|
||||
setIsOpen(isOpen)
|
||||
}
|
||||
},
|
||||
[setIsOpen]
|
||||
)
|
||||
const currentPageName = useSelector(currentPageNameSelector)
|
||||
|
||||
return (
|
||||
<DropdownMenu.Root dir="ltr" open={isOpen} onOpenChange={handleOpenChange}>
|
||||
<FloatingContainer>
|
||||
<RowButton as={DropdownMenu.Trigger} bp={breakpoints} variant="noIcon">
|
||||
<span>{currentPageName || 'Page'}</span>
|
||||
</RowButton>
|
||||
</FloatingContainer>
|
||||
<MenuContent as={DropdownMenu.Content} sideOffset={8} align="start">
|
||||
{isOpen && <PageMenuContent onClose={handleClose} />}
|
||||
</MenuContent>
|
||||
</DropdownMenu.Root>
|
||||
)
|
||||
}
|
||||
|
||||
function PageMenuContent({ onClose }: { onClose: () => void }) {
|
||||
const { tlstate, useSelector } = useTLDrawContext()
|
||||
|
||||
const sortedPages = useSelector(sortedSelector)
|
||||
|
||||
const currentPageId = useSelector(currentPageIdSelector)
|
||||
|
||||
const handleCreatePage = React.useCallback(() => {
|
||||
tlstate.createPage()
|
||||
}, [tlstate])
|
||||
|
||||
const handleChangePage = React.useCallback(
|
||||
(id: string) => {
|
||||
setIsOpen(false)
|
||||
onClose()
|
||||
tlstate.changePage(id)
|
||||
},
|
||||
[tlstate]
|
||||
)
|
||||
|
||||
const currentPage = useSelector(currentPageSelector)
|
||||
|
||||
const sortedPages = useSelector(sortedSelector)
|
||||
|
||||
return (
|
||||
<DropdownMenu.Root
|
||||
dir="ltr"
|
||||
open={isOpen}
|
||||
onOpenChange={(isOpen) => {
|
||||
if (rIsOpen.current !== isOpen) {
|
||||
setIsOpen(isOpen)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<FloatingContainer>
|
||||
<RowButton as={DropdownMenu.Trigger} bp={breakpoints} variant="noIcon">
|
||||
<span>{currentPage.name || 'Page'}</span>
|
||||
</RowButton>
|
||||
</FloatingContainer>
|
||||
<MenuContent as={DropdownMenu.Content} sideOffset={8} align="start">
|
||||
<DropdownMenu.RadioGroup value={currentPage.id} onValueChange={handleChangePage}>
|
||||
{sortedPages.map((page) => (
|
||||
<ButtonWithOptions key={page.id}>
|
||||
<DropdownMenu.RadioItem
|
||||
as={RowButton}
|
||||
bp={breakpoints}
|
||||
value={page.id}
|
||||
variant="pageButton"
|
||||
>
|
||||
<span>{page.name || 'Page'}</span>
|
||||
<DropdownMenu.ItemIndicator>
|
||||
<IconWrapper size="small">
|
||||
<CheckIcon />
|
||||
</IconWrapper>
|
||||
</DropdownMenu.ItemIndicator>
|
||||
</DropdownMenu.RadioItem>
|
||||
<PageOptionsDialog page={page} />
|
||||
</ButtonWithOptions>
|
||||
))}
|
||||
</DropdownMenu.RadioGroup>
|
||||
<DropdownMenuDivider />
|
||||
<DropdownMenuButton onSelect={handleCreatePage}>
|
||||
<span>Create Page</span>
|
||||
<IconWrapper size="small">
|
||||
<PlusIcon />
|
||||
</IconWrapper>
|
||||
</DropdownMenuButton>
|
||||
</MenuContent>
|
||||
</DropdownMenu.Root>
|
||||
<>
|
||||
<DropdownMenu.RadioGroup value={currentPageId} onValueChange={handleChangePage}>
|
||||
{sortedPages.map((page) => (
|
||||
<ButtonWithOptions key={page.id}>
|
||||
<DropdownMenu.RadioItem
|
||||
as={RowButton}
|
||||
bp={breakpoints}
|
||||
value={page.id}
|
||||
variant="pageButton"
|
||||
>
|
||||
<span>{page.name || 'Page'}</span>
|
||||
<DropdownMenu.ItemIndicator>
|
||||
<IconWrapper size="small">
|
||||
<CheckIcon />
|
||||
</IconWrapper>
|
||||
</DropdownMenu.ItemIndicator>
|
||||
</DropdownMenu.RadioItem>
|
||||
<PageOptionsDialog page={page} />
|
||||
</ButtonWithOptions>
|
||||
))}
|
||||
</DropdownMenu.RadioGroup>
|
||||
<DropdownMenuDivider />
|
||||
<DropdownMenuButton onSelect={handleCreatePage}>
|
||||
<span>Create Page</span>
|
||||
<IconWrapper size="small">
|
||||
<PlusIcon />
|
||||
</IconWrapper>
|
||||
</DropdownMenuButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -53,6 +53,8 @@ export function TLDraw({ document, currentPageId, onMount, onChange: _onChange }
|
|||
// Hide indicators when not using the select tool, or when in session
|
||||
const hideIndicators = !isSelecting || isInSession
|
||||
|
||||
const meta = React.useMemo(() => ({ isDarkMode }), [isDarkMode])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!document) return
|
||||
tlstate.loadDocument(document, _onChange)
|
||||
|
@ -92,7 +94,7 @@ export function TLDraw({ document, currentPageId, onMount, onChange: _onChange }
|
|||
pageState={pageState}
|
||||
shapeUtils={tldrawShapeUtils}
|
||||
theme={theme}
|
||||
isDarkMode={isDarkMode}
|
||||
meta={meta}
|
||||
hideBounds={hideBounds}
|
||||
hideHandles={hideHandles}
|
||||
hideIndicators={hideIndicators}
|
||||
|
|
|
@ -4,7 +4,6 @@ import {
|
|||
Utils,
|
||||
Vec,
|
||||
TLTransformInfo,
|
||||
TLRenderInfo,
|
||||
Intersect,
|
||||
TLHandle,
|
||||
TLPointerInfo,
|
||||
|
@ -20,6 +19,7 @@ import {
|
|||
DashStyle,
|
||||
TLDrawShape,
|
||||
ArrowBinding,
|
||||
TLDrawRenderInfo,
|
||||
} from '~types'
|
||||
|
||||
export class Arrow extends TLDrawShapeUtil<ArrowShape> {
|
||||
|
@ -70,7 +70,7 @@ export class Arrow extends TLDrawShapeUtil<ArrowShape> {
|
|||
return next.handles !== prev.handles || next.style !== prev.style
|
||||
}
|
||||
|
||||
render = (shape: ArrowShape, { isDarkMode }: TLRenderInfo) => {
|
||||
render = (shape: ArrowShape, { meta }: TLDrawRenderInfo) => {
|
||||
const {
|
||||
handles: { start, bend, end },
|
||||
decorations = {},
|
||||
|
@ -79,57 +79,11 @@ export class Arrow extends TLDrawShapeUtil<ArrowShape> {
|
|||
|
||||
const isDraw = style.dash === DashStyle.Draw
|
||||
|
||||
// if (!isDraw) {
|
||||
// const styles = getShapeStyle(style, isDarkMode)
|
||||
|
||||
// const { strokeWidth } = styles
|
||||
|
||||
// const arrowDist = Vec.dist(start.point, end.point)
|
||||
|
||||
// const sw = strokeWidth * 1.618
|
||||
|
||||
// const { strokeDasharray, strokeDashoffset } = getPerfectDashProps(
|
||||
// arrowDist,
|
||||
// sw,
|
||||
// shape.style.dash,
|
||||
// 2
|
||||
// )
|
||||
|
||||
// const path = getArrowPath(shape)
|
||||
|
||||
// return (
|
||||
// <g pointerEvents="none">
|
||||
// <path
|
||||
// d={path}
|
||||
// fill="none"
|
||||
// stroke="transparent"
|
||||
// strokeWidth={Math.max(8, strokeWidth * 2)}
|
||||
// strokeDasharray="none"
|
||||
// strokeDashoffset="none"
|
||||
// strokeLinecap="round"
|
||||
// strokeLinejoin="round"
|
||||
// pointerEvents="stroke"
|
||||
// />
|
||||
// <path
|
||||
// d={path}
|
||||
// fill={isDraw ? styles.stroke : 'none'}
|
||||
// stroke={styles.stroke}
|
||||
// strokeWidth={sw}
|
||||
// strokeDasharray={strokeDasharray}
|
||||
// strokeDashoffset={strokeDashoffset}
|
||||
// strokeLinecap="round"
|
||||
// strokeLinejoin="round"
|
||||
// pointerEvents="stroke"
|
||||
// />
|
||||
// </g>
|
||||
// )
|
||||
// }
|
||||
|
||||
// TODO: Improve drawn arrows
|
||||
|
||||
const isStraightLine = Vec.dist(bend.point, Vec.round(Vec.med(start.point, end.point))) < 1
|
||||
|
||||
const styles = getShapeStyle(style, isDarkMode)
|
||||
const styles = getShapeStyle(style, meta.isDarkMode)
|
||||
|
||||
const { strokeWidth } = styles
|
||||
|
||||
|
@ -724,21 +678,6 @@ function renderCurvedFreehandArrowShaft(shape: ArrowShape, circle: number[]) {
|
|||
return path
|
||||
}
|
||||
|
||||
function getArrowHeadPath(shape: ArrowShape, point: number[], inset: number[]) {
|
||||
const { left, right } = getArrowHeadPoints(shape, point, inset)
|
||||
return ['M', left, 'L', point, right].join(' ')
|
||||
}
|
||||
|
||||
function getArrowHeadPoints(shape: ArrowShape, point: number[], inset: number[]) {
|
||||
// Use the shape's random seed to create minor offsets for the angles
|
||||
const getRandom = Utils.rng(shape.id)
|
||||
|
||||
return {
|
||||
left: Vec.rotWith(inset, point, Math.PI / 6 + (Math.PI / 12) * getRandom()),
|
||||
right: Vec.rotWith(inset, point, -Math.PI / 6 + (Math.PI / 12) * getRandom()),
|
||||
}
|
||||
}
|
||||
|
||||
function getCtp(shape: ArrowShape) {
|
||||
const { start, end, bend } = shape.handles
|
||||
return Utils.circleFromThreePoints(start.point, end.point, bend.point)
|
||||
|
@ -844,52 +783,67 @@ function getArrowPath(shape: ArrowShape) {
|
|||
return path.join(' ')
|
||||
}
|
||||
|
||||
function getDrawArrowPath(shape: ArrowShape) {
|
||||
const {
|
||||
decorations,
|
||||
handles: { start, end, bend: _bend },
|
||||
style,
|
||||
} = shape
|
||||
// function getArrowHeadPath(shape: ArrowShape, point: number[], inset: number[]) {
|
||||
// const { left, right } = getArrowHeadPoints(shape, point, inset)
|
||||
// return ['M', left, 'L', point, right].join(' ')
|
||||
// }
|
||||
|
||||
const { strokeWidth } = getShapeStyle(style, false)
|
||||
// function getArrowHeadPoints(shape: ArrowShape, point: number[], inset: number[]) {
|
||||
// // Use the shape's random seed to create minor offsets for the angles
|
||||
// const getRandom = Utils.rng(shape.id)
|
||||
|
||||
const arrowDist = Vec.dist(start.point, end.point)
|
||||
// return {
|
||||
// left: Vec.rotWith(inset, point, Math.PI / 6 + (Math.PI / 12) * getRandom()),
|
||||
// right: Vec.rotWith(inset, point, -Math.PI / 6 + (Math.PI / 12) * getRandom()),
|
||||
// }
|
||||
// }
|
||||
|
||||
const arrowHeadLength = Math.min(arrowDist / 3, strokeWidth * 8)
|
||||
// function getDrawArrowPath(shape: ArrowShape) {
|
||||
// const {
|
||||
// decorations,
|
||||
// handles: { start, end, bend: _bend },
|
||||
// style,
|
||||
// } = shape
|
||||
|
||||
const path: (string | number)[] = []
|
||||
// const { strokeWidth } = getShapeStyle(style, false)
|
||||
|
||||
const isStraightLine = Vec.dist(_bend.point, Vec.round(Vec.med(start.point, end.point))) < 1
|
||||
// const arrowDist = Vec.dist(start.point, end.point)
|
||||
|
||||
if (isStraightLine) {
|
||||
// Path (line segment)
|
||||
path.push(`M ${start.point} L ${end.point}`)
|
||||
// const arrowHeadLength = Math.min(arrowDist / 3, strokeWidth * 8)
|
||||
|
||||
// Start arrow head
|
||||
if (decorations?.start) {
|
||||
path.push(getStraightArrowHeadPath(start.point, end.point, arrowHeadLength))
|
||||
}
|
||||
// const path: (string | number)[] = []
|
||||
|
||||
// End arrow head
|
||||
if (decorations?.end) {
|
||||
path.push(getStraightArrowHeadPath(end.point, start.point, arrowHeadLength))
|
||||
}
|
||||
} else {
|
||||
const { center, radius, length } = getArrowArc(shape)
|
||||
// const isStraightLine = Vec.dist(_bend.point, Vec.round(Vec.med(start.point, end.point))) < 1
|
||||
|
||||
// Path (arc)
|
||||
path.push(`M ${start.point} A ${radius} ${radius} 0 0 ${length > 0 ? '1' : '0'} ${end.point}`)
|
||||
// if (isStraightLine) {
|
||||
// // Path (line segment)
|
||||
// path.push(`M ${start.point} L ${end.point}`)
|
||||
|
||||
// Start Arrow head
|
||||
if (decorations?.start) {
|
||||
path.push(getCurvedArrowHeadPath(start.point, arrowHeadLength, center, radius, length < 0))
|
||||
}
|
||||
// // Start arrow head
|
||||
// if (decorations?.start) {
|
||||
// path.push(getStraightArrowHeadPath(start.point, end.point, arrowHeadLength))
|
||||
// }
|
||||
|
||||
// End arrow head
|
||||
if (decorations?.end) {
|
||||
path.push(getCurvedArrowHeadPath(end.point, arrowHeadLength, center, radius, length >= 0))
|
||||
}
|
||||
}
|
||||
// // End arrow head
|
||||
// if (decorations?.end) {
|
||||
// path.push(getStraightArrowHeadPath(end.point, start.point, arrowHeadLength))
|
||||
// }
|
||||
// } else {
|
||||
// const { center, radius, length } = getArrowArc(shape)
|
||||
|
||||
return path.join(' ')
|
||||
}
|
||||
// // Path (arc)
|
||||
// path.push(`M ${start.point} A ${radius} ${radius} 0 0 ${length > 0 ? '1' : '0'} ${end.point}`)
|
||||
|
||||
// // Start Arrow head
|
||||
// if (decorations?.start) {
|
||||
// path.push(getCurvedArrowHeadPath(start.point, arrowHeadLength, center, radius, length < 0))
|
||||
// }
|
||||
|
||||
// // End arrow head
|
||||
// if (decorations?.end) {
|
||||
// path.push(getCurvedArrowHeadPath(end.point, arrowHeadLength, center, radius, length >= 0))
|
||||
// }
|
||||
// }
|
||||
|
||||
// return path.join(' ')
|
||||
// }
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
import * as React from 'react'
|
||||
import { TLBounds, Utils, Vec, TLTransformInfo, TLRenderInfo, Intersect } from '@tldraw/core'
|
||||
import { TLBounds, Utils, Vec, TLTransformInfo, Intersect } from '@tldraw/core'
|
||||
import getStroke, { getStrokePoints } from 'perfect-freehand'
|
||||
import { defaultStyle, getShapeStyle } from '~shape/shape-styles'
|
||||
import { DrawShape, DashStyle, TLDrawShapeUtil, TLDrawShapeType, TLDrawToolType } from '~types'
|
||||
import {
|
||||
DrawShape,
|
||||
DashStyle,
|
||||
TLDrawShapeUtil,
|
||||
TLDrawShapeType,
|
||||
TLDrawToolType,
|
||||
TLDrawRenderInfo,
|
||||
} from '~types'
|
||||
|
||||
export class Draw extends TLDrawShapeUtil<DrawShape> {
|
||||
type = TLDrawShapeType.Draw as const
|
||||
|
@ -30,10 +37,10 @@ export class Draw extends TLDrawShapeUtil<DrawShape> {
|
|||
return next.points !== prev.points || next.style !== prev.style
|
||||
}
|
||||
|
||||
render(shape: DrawShape, { isDarkMode, isEditing }: TLRenderInfo): JSX.Element {
|
||||
render(shape: DrawShape, { meta, isEditing }: TLDrawRenderInfo): JSX.Element {
|
||||
const { points, style } = shape
|
||||
|
||||
const styles = getShapeStyle(style, isDarkMode)
|
||||
const styles = getShapeStyle(style, meta.isDarkMode)
|
||||
|
||||
const strokeWidth = styles.strokeWidth
|
||||
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
import * as React from 'react'
|
||||
import { Utils, TLTransformInfo, TLBounds, Intersect, Vec, TLRenderInfo } from '@tldraw/core'
|
||||
import { DashStyle, EllipseShape, TLDrawShapeType, TLDrawShapeUtil, TLDrawToolType } from '~types'
|
||||
import { Utils, TLTransformInfo, TLBounds, Intersect, Vec } from '@tldraw/core'
|
||||
import {
|
||||
DashStyle,
|
||||
EllipseShape,
|
||||
TLDrawRenderInfo,
|
||||
TLDrawShapeType,
|
||||
TLDrawShapeUtil,
|
||||
TLDrawToolType,
|
||||
} from '~types'
|
||||
import { defaultStyle, getPerfectDashProps, getShapeStyle } from '~shape/shape-styles'
|
||||
import getStroke from 'perfect-freehand'
|
||||
|
||||
|
@ -26,13 +33,13 @@ export class Ellipse extends TLDrawShapeUtil<EllipseShape> {
|
|||
return next.radius !== prev.radius || next.style !== prev.style
|
||||
}
|
||||
|
||||
render(shape: EllipseShape, { isDarkMode, isBinding }: TLRenderInfo) {
|
||||
render(shape: EllipseShape, { meta, isBinding }: TLDrawRenderInfo) {
|
||||
const {
|
||||
radius: [radiusX, radiusY],
|
||||
style,
|
||||
} = shape
|
||||
|
||||
const styles = getShapeStyle(style, isDarkMode)
|
||||
const styles = getShapeStyle(style, meta.isDarkMode)
|
||||
const strokeWidth = +styles.strokeWidth
|
||||
|
||||
const rx = Math.max(0, radiusX - strokeWidth / 2)
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
import * as React from 'react'
|
||||
import { TLBounds, Utils, Vec, TLTransformInfo, TLRenderInfo, Intersect } from '@tldraw/core'
|
||||
import { TLBounds, Utils, Vec, TLTransformInfo, Intersect } from '@tldraw/core'
|
||||
import getStroke from 'perfect-freehand'
|
||||
import { getPerfectDashProps, defaultStyle, getShapeStyle } from '~shape/shape-styles'
|
||||
import { RectangleShape, DashStyle, TLDrawShapeUtil, TLDrawShapeType, TLDrawToolType } from '~types'
|
||||
import {
|
||||
RectangleShape,
|
||||
DashStyle,
|
||||
TLDrawShapeUtil,
|
||||
TLDrawShapeType,
|
||||
TLDrawToolType,
|
||||
TLDrawRenderInfo,
|
||||
} from '~types'
|
||||
|
||||
export class Rectangle extends TLDrawShapeUtil<RectangleShape> {
|
||||
type = TLDrawShapeType.Rectangle as const
|
||||
|
@ -27,9 +34,9 @@ export class Rectangle extends TLDrawShapeUtil<RectangleShape> {
|
|||
return next.size !== prev.size || next.style !== prev.style
|
||||
}
|
||||
|
||||
render(shape: RectangleShape, { isBinding, isDarkMode }: TLRenderInfo) {
|
||||
render(shape: RectangleShape, { isBinding, meta }: TLDrawRenderInfo) {
|
||||
const { id, size, style } = shape
|
||||
const styles = getShapeStyle(style, isDarkMode)
|
||||
const styles = getShapeStyle(style, meta.isDarkMode)
|
||||
const strokeWidth = +styles.strokeWidth
|
||||
|
||||
if (style.dash === DashStyle.Draw) {
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
import * as React from 'react'
|
||||
import { TLBounds, Utils, Vec, TLTransformInfo, TLRenderInfo, Intersect } from '@tldraw/core'
|
||||
import { TLBounds, Utils, Vec, TLTransformInfo, Intersect } from '@tldraw/core'
|
||||
import { getShapeStyle, getFontSize, getFontStyle, defaultStyle } from '~shape/shape-styles'
|
||||
import { TextShape, TLDrawShapeUtil, TLDrawShapeType, TLDrawToolType } from '~types'
|
||||
import {
|
||||
TextShape,
|
||||
TLDrawShapeUtil,
|
||||
TLDrawShapeType,
|
||||
TLDrawRenderInfo,
|
||||
TLDrawToolType,
|
||||
} from '~types'
|
||||
import styled from '~styles'
|
||||
import TextAreaUtils from './text-utils'
|
||||
|
||||
|
@ -77,17 +83,17 @@ export class Text extends TLDrawShapeUtil<TextShape> {
|
|||
shape: TextShape,
|
||||
{
|
||||
ref,
|
||||
meta,
|
||||
isEditing,
|
||||
isDarkMode,
|
||||
onTextBlur,
|
||||
onTextChange,
|
||||
onTextFocus,
|
||||
onTextKeyDown,
|
||||
onTextKeyUp,
|
||||
}: TLRenderInfo
|
||||
}: TLDrawRenderInfo
|
||||
): JSX.Element {
|
||||
const { id, text, style } = shape
|
||||
const styles = getShapeStyle(style, isDarkMode)
|
||||
const styles = getShapeStyle(style, meta.isDarkMode)
|
||||
const font = getFontStyle(shape.style)
|
||||
|
||||
const bounds = this.getBounds(shape)
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { Utils } from '@tldraw/core'
|
||||
import { AlignType } from '~types'
|
||||
import type { Data, Command } from '~types'
|
||||
import { AlignType, TLDrawCommand } from '~types'
|
||||
import type { Data } from '~types'
|
||||
import { TLDR } from '~state/tldr'
|
||||
|
||||
export function align(data: Data, ids: string[], type: AlignType): Command {
|
||||
export function align(data: Data, ids: string[], type: AlignType): TLDrawCommand {
|
||||
const { currentPageId } = data.appState
|
||||
const initialShapes = ids.map((id) => TLDR.getShape(data, id, currentPageId))
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import type { TLDrawShape, Data, Command } from '~types'
|
||||
import { TLDR } from '~state/tldr'
|
||||
import type { Data, TLDrawCommand } from '~types'
|
||||
|
||||
export function changePage(data: Data, pageId: string): Command {
|
||||
export function changePage(data: Data, pageId: string): TLDrawCommand {
|
||||
return {
|
||||
id: 'change_page',
|
||||
before: {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type { Data, Command } from '~types'
|
||||
import type { Data, TLDrawCommand } from '~types'
|
||||
import { Utils } from '@tldraw/core'
|
||||
|
||||
export function createPage(data: Data): Command {
|
||||
export function createPage(data: Data): TLDrawCommand {
|
||||
const newId = Utils.uniqueId()
|
||||
const { currentPageId } = data.appState
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import type { DeepPartial } from '~../../core/dist/types/utils/utils'
|
||||
import type { Patch } from 'rko'
|
||||
import { TLDR } from '~state/tldr'
|
||||
import type { TLDrawShape, Data, Command } from '~types'
|
||||
import type { TLDrawShape, Data, TLDrawCommand } from '~types'
|
||||
|
||||
export function create(data: Data, shapes: TLDrawShape[]): Command {
|
||||
export function create(data: Data, shapes: TLDrawShape[]): TLDrawCommand {
|
||||
const { currentPageId } = data.appState
|
||||
const beforeShapes: Record<string, DeepPartial<TLDrawShape> | undefined> = {}
|
||||
const afterShapes: Record<string, DeepPartial<TLDrawShape> | undefined> = {}
|
||||
const beforeShapes: Record<string, Patch<TLDrawShape> | undefined> = {}
|
||||
const afterShapes: Record<string, Patch<TLDrawShape> | undefined> = {}
|
||||
|
||||
shapes.forEach((shape) => {
|
||||
beforeShapes[shape.id] = undefined
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type { Data, Command } from '~types'
|
||||
import type { Data, TLDrawCommand } from '~types'
|
||||
|
||||
export function deletePage(data: Data, pageId: string): Command {
|
||||
export function deletePage(data: Data, pageId: string): TLDrawCommand {
|
||||
const { currentPageId } = data.appState
|
||||
|
||||
const pagesArr = Object.values(data.document.pages).sort(
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { TLDR } from '~state/tldr'
|
||||
import type { Data, Command, PagePartial } from '~types'
|
||||
import type { Data, TLDrawCommand, PagePartial } from '~types'
|
||||
|
||||
// - [x] Delete shapes
|
||||
// - [x] Delete bindings too
|
||||
// - [ ] Update parents and possibly delete parents
|
||||
|
||||
export function deleteShapes(data: Data, ids: string[]): Command {
|
||||
export function deleteShapes(data: Data, ids: string[]): TLDrawCommand {
|
||||
const { currentPageId } = data.appState
|
||||
|
||||
const before: PagePartial = {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { Utils } from '@tldraw/core'
|
||||
import { DistributeType, TLDrawShape, Data, Command } from '~types'
|
||||
import { DistributeType, TLDrawShape, Data, TLDrawCommand } from '~types'
|
||||
import { TLDR } from '~state/tldr'
|
||||
|
||||
export function distribute(data: Data, ids: string[], type: DistributeType): Command {
|
||||
export function distribute(data: Data, ids: string[], type: DistributeType): TLDrawCommand {
|
||||
const { currentPageId } = data.appState
|
||||
const initialShapes = ids.map((id) => TLDR.getShape(data, id, currentPageId))
|
||||
const deltaMap = Object.fromEntries(getDistributions(initialShapes, type).map((d) => [d.id, d]))
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type { Data, Command } from '~types'
|
||||
import type { Data, TLDrawCommand } from '~types'
|
||||
import { Utils } from '@tldraw/core'
|
||||
|
||||
export function duplicatePage(data: Data, pageId: string): Command {
|
||||
export function duplicatePage(data: Data, pageId: string): TLDrawCommand {
|
||||
const newId = Utils.uniqueId()
|
||||
const { currentPageId } = data.appState
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { Utils, Vec } from '@tldraw/core'
|
||||
import { TLDR } from '~state/tldr'
|
||||
import type { Data, Command } from '~types'
|
||||
import type { Data, TLDrawCommand } from '~types'
|
||||
|
||||
export function duplicate(data: Data, ids: string[]): Command {
|
||||
export function duplicate(data: Data, ids: string[]): TLDrawCommand {
|
||||
const { currentPageId } = data.appState
|
||||
const delta = Vec.div([16, 16], TLDR.getCamera(data, currentPageId).zoom)
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { FlipType } from '~types'
|
||||
import { TLBoundsCorner, Utils } from '@tldraw/core'
|
||||
import type { Data, Command } from '~types'
|
||||
import type { Data, TLDrawCommand } from '~types'
|
||||
import { TLDR } from '~state/tldr'
|
||||
|
||||
export function flip(data: Data, ids: string[], type: FlipType): Command {
|
||||
export function flip(data: Data, ids: string[], type: FlipType): TLDrawCommand {
|
||||
const { currentPageId } = data.appState
|
||||
const initialShapes = ids.map((id) => TLDR.getShape(data, id, currentPageId))
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { MoveType, Data, TLDrawShape, Command } from '~types'
|
||||
import { MoveType, Data, TLDrawShape, TLDrawCommand } from '~types'
|
||||
import { TLDR } from '~state/tldr'
|
||||
|
||||
export function move(data: Data, ids: string[], type: MoveType): Command {
|
||||
export function move(data: Data, ids: string[], type: MoveType): TLDrawCommand {
|
||||
const { currentPageId } = data.appState
|
||||
|
||||
// Get the unique parent ids for the selected elements
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type { Data, Command } from '~types'
|
||||
import type { Data, TLDrawCommand } from '~types'
|
||||
|
||||
export function renamePage(data: Data, pageId: string, name: string): Command {
|
||||
export function renamePage(data: Data, pageId: string, name: string): TLDrawCommand {
|
||||
const page = data.document.pages[pageId]
|
||||
return {
|
||||
id: 'rename_page',
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { Utils, Vec } from '@tldraw/core'
|
||||
import type { Command, Data } from '~types'
|
||||
import type { TLDrawCommand, Data } from '~types'
|
||||
import { TLDR } from '~state/tldr'
|
||||
|
||||
const PI2 = Math.PI * 2
|
||||
|
||||
export function rotate(data: Data, ids: string[], delta = -PI2 / 4): Command {
|
||||
export function rotate(data: Data, ids: string[], delta = -PI2 / 4): TLDrawCommand {
|
||||
const { currentPageId } = data.appState
|
||||
const initialShapes = ids.map((id) => TLDR.getShape(data, id, currentPageId))
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { TLBoundsCorner, Utils } from '@tldraw/core'
|
||||
import { StretchType } from '~types'
|
||||
import type { Data, Command } from '~types'
|
||||
import type { Data, TLDrawCommand } from '~types'
|
||||
import { TLDR } from '~state/tldr'
|
||||
|
||||
export function stretch(data: Data, ids: string[], type: StretchType): Command {
|
||||
export function stretch(data: Data, ids: string[], type: StretchType): TLDrawCommand {
|
||||
const { currentPageId } = data.appState
|
||||
|
||||
const initialShapes = ids.map((id) => TLDR.getShape(data, id, currentPageId))
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type { ShapeStyles, Command, Data } from '~types'
|
||||
import type { ShapeStyles, TLDrawCommand, Data } from '~types'
|
||||
import { TLDR } from '~state/tldr'
|
||||
|
||||
export function style(data: Data, ids: string[], changes: Partial<ShapeStyles>): Command {
|
||||
export function style(data: Data, ids: string[], changes: Partial<ShapeStyles>): TLDrawCommand {
|
||||
const { currentPageId } = data.appState
|
||||
|
||||
const { before, after } = TLDR.mutateShapes(
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
import { Decoration } from '~types'
|
||||
import type { ArrowShape, Command, Data } from '~types'
|
||||
import type { ArrowShape, TLDrawCommand, Data } from '~types'
|
||||
import { TLDR } from '~state/tldr'
|
||||
|
||||
export function toggleDecoration(data: Data, ids: string[], handleId: 'start' | 'end'): Command {
|
||||
export function toggleDecoration(
|
||||
data: Data,
|
||||
ids: string[],
|
||||
handleId: 'start' | 'end'
|
||||
): TLDrawCommand {
|
||||
const { currentPageId } = data.appState
|
||||
const { before, after } = TLDR.mutateShapes<ArrowShape>(
|
||||
data,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type { TLDrawShape, Data, Command } from '~types'
|
||||
import type { TLDrawShape, Data, TLDrawCommand } from '~types'
|
||||
import { TLDR } from '~state/tldr'
|
||||
|
||||
export function toggle(data: Data, ids: string[], prop: keyof TLDrawShape): Command {
|
||||
export function toggle(data: Data, ids: string[], prop: keyof TLDrawShape): TLDrawCommand {
|
||||
const { currentPageId } = data.appState
|
||||
const initialShapes = ids.map((id) => TLDR.getShape(data, id, currentPageId))
|
||||
const isAllToggled = initialShapes.every((shape) => shape[prop])
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { Vec } from '@tldraw/core'
|
||||
import type { Data, Command, PagePartial } from '~types'
|
||||
import type { Data, TLDrawCommand, PagePartial } from '~types'
|
||||
import { TLDR } from '~state/tldr'
|
||||
|
||||
export function translate(data: Data, ids: string[], delta: number[]): Command {
|
||||
export function translate(data: Data, ids: string[], delta: number[]): TLDrawCommand {
|
||||
const before: PagePartial = {
|
||||
shapes: {},
|
||||
bindings: {},
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { brushUpdater, Utils, Vec } from '@tldraw/core'
|
||||
import { Data, Session, TLDrawStatus } from '~types'
|
||||
import { Data, Session, TLDrawPatch, TLDrawStatus } from '~types'
|
||||
import { getShapeUtils } from '~shape'
|
||||
import { TLDR } from '~state/tldr'
|
||||
import type { DeepPartial } from '~../../core/dist/types/utils/utils'
|
||||
|
||||
export class BrushSession implements Session {
|
||||
id = 'brush'
|
||||
|
@ -17,7 +16,7 @@ export class BrushSession implements Session {
|
|||
|
||||
start = () => void null
|
||||
|
||||
update = (data: Data, point: number[], containMode = false): DeepPartial<Data> => {
|
||||
update = (data: Data, point: number[], containMode = false): TLDrawPatch => {
|
||||
const { snapshot, origin } = this
|
||||
const { currentPageId } = data.appState
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { TLPageState, Utils, Vec } from '@tldraw/core'
|
||||
import { TLDrawShape, TLDrawBinding, Session, Data, Command, TLDrawStatus } from '~types'
|
||||
import { TLDrawShape, TLDrawBinding, Session, Data, TLDrawCommand, TLDrawStatus } from '~types'
|
||||
import { TLDR } from '~state/tldr'
|
||||
|
||||
export class TranslateSession implements Session {
|
||||
|
@ -184,7 +184,7 @@ export class TranslateSession implements Session {
|
|||
}
|
||||
}
|
||||
|
||||
complete(data: Data): Command {
|
||||
complete(data: Data): TLDrawCommand {
|
||||
const pageId = data.appState.currentPageId
|
||||
|
||||
const { initialShapes, bindingsToDelete, clones, clonedBindings } = this.snapshot
|
||||
|
|
|
@ -2,13 +2,14 @@ import { TLBounds, TLTransformInfo, Vec, Utils, TLPageState } from '@tldraw/core
|
|||
import { getShapeUtils } from '~shape'
|
||||
import type {
|
||||
Data,
|
||||
DeepPartial,
|
||||
ShapeStyles,
|
||||
ShapesWithProp,
|
||||
TLDrawShape,
|
||||
TLDrawShapeUtil,
|
||||
TLDrawBinding,
|
||||
TLDrawPage,
|
||||
TLDrawCommand,
|
||||
TLDrawPatch,
|
||||
} from '~types'
|
||||
|
||||
export class TLDR {
|
||||
|
@ -445,12 +446,8 @@ export class TLDR {
|
|||
}
|
||||
}
|
||||
|
||||
static createShapes(
|
||||
data: Data,
|
||||
shapes: TLDrawShape[],
|
||||
pageId: string
|
||||
): { before: DeepPartial<Data>; after: DeepPartial<Data> } {
|
||||
const before: DeepPartial<Data> = {
|
||||
static createShapes(data: Data, shapes: TLDrawShape[], pageId: string): TLDrawCommand {
|
||||
const before: TLDrawPatch = {
|
||||
document: {
|
||||
pages: {
|
||||
[pageId]: {
|
||||
|
@ -477,7 +474,7 @@ export class TLDR {
|
|||
},
|
||||
}
|
||||
|
||||
const after: DeepPartial<Data> = {
|
||||
const after: TLDrawPatch = {
|
||||
document: {
|
||||
pages: {
|
||||
[pageId]: {
|
||||
|
@ -516,7 +513,7 @@ export class TLDR {
|
|||
data: Data,
|
||||
shapes: TLDrawShape[] | string[],
|
||||
pageId?: string
|
||||
): { before: DeepPartial<Data>; after: DeepPartial<Data> } {
|
||||
): TLDrawCommand {
|
||||
pageId = pageId ? pageId : data.appState.currentPageId
|
||||
|
||||
const page = this.getPage(data, pageId)
|
||||
|
@ -526,7 +523,7 @@ export class TLDR {
|
|||
? (shapes as string[])
|
||||
: (shapes as TLDrawShape[]).map((shape) => shape.id)
|
||||
|
||||
const before: DeepPartial<Data> = {
|
||||
const before: TLDrawPatch = {
|
||||
document: {
|
||||
pages: {
|
||||
[pageId]: {
|
||||
|
@ -565,7 +562,7 @@ export class TLDR {
|
|||
},
|
||||
}
|
||||
|
||||
const after: DeepPartial<Data> = {
|
||||
const after: TLDrawPatch = {
|
||||
document: {
|
||||
pages: {
|
||||
[pageId]: {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
import type { TLBinding } from '@tldraw/core'
|
||||
import type { TLBinding, TLRenderInfo } from '@tldraw/core'
|
||||
import { TLShape, TLShapeUtil, TLHandle } from '@tldraw/core'
|
||||
import type { TLPage, TLPageState, TLSettings } from '@tldraw/core'
|
||||
import type { TLPage, TLPageState } from '@tldraw/core'
|
||||
import type { StoreApi } from 'zustand'
|
||||
import type { Command, Patch } from 'rko'
|
||||
|
||||
export type TLStore = StoreApi<Data>
|
||||
|
||||
|
@ -17,12 +18,21 @@ export interface TLDrawDocument {
|
|||
pageStates: Record<string, TLPageState>
|
||||
}
|
||||
|
||||
export interface TLDrawSettings extends TLSettings {
|
||||
export interface TLDrawSettings {
|
||||
isDarkMode: boolean
|
||||
isDebugMode: boolean
|
||||
isPenMode: boolean
|
||||
isReadonlyMode: boolean
|
||||
nudgeDistanceSmall: number
|
||||
nudgeDistanceLarge: number
|
||||
}
|
||||
|
||||
export interface TLDrawMeta {
|
||||
isDarkMode: boolean
|
||||
}
|
||||
|
||||
export type TLDrawRenderInfo = TLRenderInfo<TLDrawMeta>
|
||||
|
||||
export interface Data {
|
||||
document: TLDrawDocument
|
||||
settings: TLDrawSettings
|
||||
|
@ -41,30 +51,13 @@ export interface Data {
|
|||
}
|
||||
}
|
||||
|
||||
export type TLDrawPatch = DeepPartial<Data>
|
||||
export type TLDrawPatch = Patch<Data>
|
||||
|
||||
export type TLDrawCommand = Command<Data>
|
||||
|
||||
export type PagePartial = {
|
||||
shapes: DeepPartial<TLDrawPage['shapes']>
|
||||
bindings: DeepPartial<TLDrawPage['bindings']>
|
||||
}
|
||||
|
||||
export type DeepPartial<T> = T extends Function
|
||||
? T
|
||||
: T extends object
|
||||
? T extends unknown[]
|
||||
? DeepPartial<T[number]>[]
|
||||
: { [P in keyof T]?: DeepPartial<T[P]> }
|
||||
: T
|
||||
|
||||
export interface Command {
|
||||
id: string
|
||||
before: TLDrawPatch
|
||||
after: TLDrawPatch
|
||||
}
|
||||
|
||||
export interface History {
|
||||
pointer: number
|
||||
stack: Command[]
|
||||
shapes: Patch<TLDrawPage['shapes']>
|
||||
bindings: Patch<TLDrawPage['bindings']>
|
||||
}
|
||||
|
||||
export interface SelectHistory {
|
||||
|
@ -77,7 +70,7 @@ export interface Session {
|
|||
status: TLDrawStatus
|
||||
start: (data: Readonly<Data>, ...args: any[]) => TLDrawPatch | undefined
|
||||
update: (data: Readonly<Data>, ...args: any[]) => TLDrawPatch | undefined
|
||||
complete: (data: Readonly<Data>, ...args: any[]) => TLDrawPatch | Command | undefined
|
||||
complete: (data: Readonly<Data>, ...args: any[]) => TLDrawPatch | TLDrawCommand | undefined
|
||||
cancel: (data: Readonly<Data>, ...args: any[]) => TLDrawPatch | undefined
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
"experimentalDecorators": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"importsNotUsedAsValues": "error",
|
||||
"stripInternal": true,
|
||||
"incremental": true,
|
||||
"importHelpers": true,
|
||||
"moduleResolution": "node",
|
||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue