Tldraw/packages/tldraw/src/test/test-jsx.tsx

142 wiersze
3.3 KiB
TypeScript
Czysty Zwykły widok Historia

tldraw zero - package shuffle (#1710) This PR moves code between our packages so that: - @tldraw/editor is a “core” library with the engine and canvas but no shapes, tools, or other things - @tldraw/tldraw contains everything particular to the experience we’ve built for tldraw At first look, this might seem like a step away from customization and configuration, however I believe it greatly increases the configuration potential of the @tldraw/editor while also providing a more accurate reflection of what configuration options actually exist for @tldraw/tldraw. ## Library changes @tldraw/editor re-exports its dependencies and @tldraw/tldraw re-exports @tldraw/editor. - users of @tldraw/editor WITHOUT @tldraw/tldraw should almost always only import things from @tldraw/editor. - users of @tldraw/tldraw should almost always only import things from @tldraw/tldraw. - @tldraw/polyfills is merged into @tldraw/editor - @tldraw/indices is merged into @tldraw/editor - @tldraw/primitives is merged mostly into @tldraw/editor, partially into @tldraw/tldraw - @tldraw/file-format is merged into @tldraw/tldraw - @tldraw/ui is merged into @tldraw/tldraw Many (many) utils and other code is moved from the editor to tldraw. For example, embeds now are entirely an feature of @tldraw/tldraw. The only big chunk of code left in core is related to arrow handling. ## API Changes The editor can now be used without tldraw's assets. We load them in @tldraw/tldraw instead, so feel free to use whatever fonts or images or whatever that you like with the editor. All tools and shapes (except for the `Group` shape) are moved to @tldraw/tldraw. This includes the `select` tool. You should use the editor with at least one tool, however, so you now also need to send in an `initialState` prop to the Editor / <TldrawEditor> component indicating which state the editor should begin in. The `components` prop now also accepts `SelectionForeground`. The complex selection component that we use for tldraw is moved to @tldraw/tldraw. The default component is quite basic but can easily be replaced via the `components` prop. We pass down our tldraw-flavored SelectionFg via `components`. Likewise with the `Scribble` component: the `DefaultScribble` no longer uses our freehand tech and is a simple path instead. We pass down the tldraw-flavored scribble via `components`. The `ExternalContentManager` (`Editor.externalContentManager`) is removed and replaced with a mapping of types to handlers. - Register new content handlers with `Editor.registerExternalContentHandler`. - Register new asset creation handlers (for files and URLs) with `Editor.registerExternalAssetHandler` ### Change Type - [x] `major` — Breaking change ### Test Plan - [x] Unit Tests - [x] End to end tests ### Release Notes - [@tldraw/editor] lots, wip - [@tldraw/ui] gone, merged to tldraw/tldraw - [@tldraw/polyfills] gone, merged to tldraw/editor - [@tldraw/primitives] gone, merged to tldraw/editor / tldraw/tldraw - [@tldraw/indices] gone, merged to tldraw/editor - [@tldraw/file-format] gone, merged to tldraw/tldraw --------- Co-authored-by: alex <alex@dytry.ch>
2023-07-17 21:22:34 +00:00
import {
TLDefaultShape,
TLShapeId,
TLShapePartial,
ZERO_INDEX_KEY,
tldraw zero - package shuffle (#1710) This PR moves code between our packages so that: - @tldraw/editor is a “core” library with the engine and canvas but no shapes, tools, or other things - @tldraw/tldraw contains everything particular to the experience we’ve built for tldraw At first look, this might seem like a step away from customization and configuration, however I believe it greatly increases the configuration potential of the @tldraw/editor while also providing a more accurate reflection of what configuration options actually exist for @tldraw/tldraw. ## Library changes @tldraw/editor re-exports its dependencies and @tldraw/tldraw re-exports @tldraw/editor. - users of @tldraw/editor WITHOUT @tldraw/tldraw should almost always only import things from @tldraw/editor. - users of @tldraw/tldraw should almost always only import things from @tldraw/tldraw. - @tldraw/polyfills is merged into @tldraw/editor - @tldraw/indices is merged into @tldraw/editor - @tldraw/primitives is merged mostly into @tldraw/editor, partially into @tldraw/tldraw - @tldraw/file-format is merged into @tldraw/tldraw - @tldraw/ui is merged into @tldraw/tldraw Many (many) utils and other code is moved from the editor to tldraw. For example, embeds now are entirely an feature of @tldraw/tldraw. The only big chunk of code left in core is related to arrow handling. ## API Changes The editor can now be used without tldraw's assets. We load them in @tldraw/tldraw instead, so feel free to use whatever fonts or images or whatever that you like with the editor. All tools and shapes (except for the `Group` shape) are moved to @tldraw/tldraw. This includes the `select` tool. You should use the editor with at least one tool, however, so you now also need to send in an `initialState` prop to the Editor / <TldrawEditor> component indicating which state the editor should begin in. The `components` prop now also accepts `SelectionForeground`. The complex selection component that we use for tldraw is moved to @tldraw/tldraw. The default component is quite basic but can easily be replaced via the `components` prop. We pass down our tldraw-flavored SelectionFg via `components`. Likewise with the `Scribble` component: the `DefaultScribble` no longer uses our freehand tech and is a simple path instead. We pass down the tldraw-flavored scribble via `components`. The `ExternalContentManager` (`Editor.externalContentManager`) is removed and replaced with a mapping of types to handlers. - Register new content handlers with `Editor.registerExternalContentHandler`. - Register new asset creation handlers (for files and URLs) with `Editor.registerExternalAssetHandler` ### Change Type - [x] `major` — Breaking change ### Test Plan - [x] Unit Tests - [x] End to end tests ### Release Notes - [@tldraw/editor] lots, wip - [@tldraw/ui] gone, merged to tldraw/tldraw - [@tldraw/polyfills] gone, merged to tldraw/editor - [@tldraw/primitives] gone, merged to tldraw/editor / tldraw/tldraw - [@tldraw/indices] gone, merged to tldraw/editor - [@tldraw/file-format] gone, merged to tldraw/tldraw --------- Co-authored-by: alex <alex@dytry.ch>
2023-07-17 21:22:34 +00:00
assert,
assertExists,
createShapeId,
getIndexAbove,
omitFromStackTrace,
} from '@tldraw/editor'
const shapeTypeSymbol = Symbol('shapeJsx')
const createElement = (tag: string) => {
const component = () => {
throw new Error(`Cannot render test tag ${tag}`)
}
;(component as any)[shapeTypeSymbol] = tag
return component
}
type CommonProps = {
x: number
y: number
id?: TLShapeId
rotation?: number
isLocked?: number
ref?: string
children?: React.JSX.Element | React.JSX.Element[]
opacity?: number
}
type ShapeByType<Type extends TLDefaultShape['type']> = Extract<TLDefaultShape, { type: Type }>
type PropsForShape<Type extends string> = Type extends TLDefaultShape['type']
? CommonProps & Partial<ShapeByType<Type>['props']>
: CommonProps & Record<string, unknown>
/**
* TL - jsx helpers for creating tldraw shapes in test cases
*/
export const TL = new Proxy(
{},
{
get(target, key) {
return createElement(key as string)
},
}
[Snapping 3/5] Custom snapping API (#2793) This diff adds an API for customising our existing snap types. These are: 1. Bound snapping. When translating or resizing a shape, it'll snap to certain key points on the bounds of particular shapes. Previously, these were hard-coded to the corners and center of the bounding box of the shape. Now, a shape can bring its own (e.g. a triangle may add snapping for its 3 corners, and it's centroid rather than bounding box center. 2. Handle outline snapping. When dragging a handle, it'll snap to the outline of other shapes geometry. Now, shapes can return different geometry for this sort of snapping if they like. Each of these is customised through a method on `ShapeUtil`: `getBoundsSnapGeometry` and `getHandleSnapGeometry`. These return interfaces describing the different geometry that can be snapped to in both these cases. Currently, each returns an object with a single property, but there are more types of snapping coming in follow-up PRs. When reviewing this PR, start with the definitions of `BoundsSnapGeometry` in `BoundsSnaps.ts` and `HandleSnapGeometry` in `HandleSnaps.ts` This doesn't add point snapping - i'll add that in a follow-up! It'll be customisable with the `getHandleSnapGeometry` API. Fixes TLD-2197 This PR is part of a series - please don't merge it until the things before it have landed! 1. #2827 4. #2831 5. #2793 (you are here) 6. #2841 7. #2845 ### Change Type - [x] `minor` — New feature [^1]: publishes a `patch` release, for devDependencies use `internal` [^2]: will not publish a new version ### Test Plan - [x] Unit Tests ### Release Notes - Add `ShapeUtil.getSnapInfo` for customising shape snaps.
2024-02-15 15:10:04 +00:00
) as { [K in TLDefaultShape['type']]: (props: PropsForShape<K>) => null } & Record<
string,
(props: PropsForShape<string>) => null
>
export function shapesFromJsx(shapes: React.JSX.Element | Array<React.JSX.Element>) {
const ids = {} as Record<string, TLShapeId>
const currentPageShapes: Array<TLShapePartial> = []
function addChildren(
children: React.JSX.Element | Array<React.JSX.Element>,
parentId?: TLShapeId
) {
let nextIndex = ZERO_INDEX_KEY
for (const el of Array.isArray(children) ? children : [children]) {
const shapeType = (el.type as any)[shapeTypeSymbol] as string
if (!shapeType) {
throw new Error(
`Cannot use ${el.type} as a shape. Only TL.* tags are allowed in shape jsx.`
)
}
let id
const ref = (el as any).ref as string | undefined
if (ref) {
assert(!ids[ref], `Duplicate shape ref: ${ref}`)
assert(!el.props.id, `Cannot use both ref and id on shape: ${ref}`)
id = createShapeId(ref)
ids[ref] = id
} else if (el.props.id) {
id = el.props.id
} else {
id = createShapeId()
}
const x: number = assertExists(el.props.x, `Shape ${id} is missing x prop`)
const y: number = assertExists(el.props.y, `Shape ${id} is missing y prop`)
const shapePartial = {
id,
type: shapeType,
x,
y,
index: nextIndex,
props: {},
} as TLShapePartial
nextIndex = getIndexAbove(nextIndex)
if (parentId) {
shapePartial.parentId = parentId
}
for (const [key, value] of Object.entries(el.props)) {
if (key === 'x' || key === 'y' || key === 'ref' || key === 'id' || key === 'children') {
continue
}
if (key === 'rotation' || key === 'isLocked' || key === 'opacity') {
shapePartial[key] = value as any
continue
}
;(shapePartial.props as Record<string, unknown>)[key] = value
}
currentPageShapes.push(shapePartial)
if (el.props.children) {
addChildren(el.props.children, id)
}
}
}
addChildren(shapes)
return {
ids: new Proxy(ids, {
get: omitFromStackTrace((target, key) => {
if (!(key in target)) {
throw new Error(
`Cannot access ID '${String(
key
)}'. No ref with that name was specified.\nAvailable refs: ${Object.keys(ids).join(
', '
)}`
)
}
return target[key as string]
}),
}),
shapes: currentPageShapes,
}
}