improve canvas positioning, fix zaxis reordering
This commit is contained in:
parent
9c0de7ad9c
commit
b90c57bbc3
12 changed files with 58 additions and 35 deletions
|
@ -59,7 +59,7 @@
|
|||
"dependencies": {
|
||||
"@tldraw/intersect": "^0.0.98",
|
||||
"@tldraw/vec": "^0.0.98",
|
||||
"@use-gesture/react": "^10.0.0-beta.24"
|
||||
"@use-gesture/react": "^10.0.0-beta.26"
|
||||
},
|
||||
"gitHead": "5cb031ddc264846ec6732d7179511cddea8ef034"
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ export function Canvas<T extends TLShape, M extends Record<string, unknown>>({
|
|||
}: CanvasProps<T, M>): JSX.Element {
|
||||
const rCanvas = React.useRef<HTMLDivElement>(null)
|
||||
const rContainer = React.useRef<HTMLDivElement>(null)
|
||||
const rLayer = React.useRef<HTMLDivElement>(null)
|
||||
|
||||
useResizeObserver(rCanvas)
|
||||
|
||||
|
@ -50,7 +51,7 @@ export function Canvas<T extends TLShape, M extends Record<string, unknown>>({
|
|||
|
||||
const events = useCanvasEvents()
|
||||
|
||||
const rLayer = useCameraCss(rContainer, pageState)
|
||||
useCameraCss(rLayer, rContainer, pageState)
|
||||
|
||||
const preventScrolling = React.useCallback((e: React.UIEvent<HTMLDivElement, UIEvent>) => {
|
||||
e.currentTarget.scrollTo(0, 0)
|
||||
|
|
|
@ -3,9 +3,9 @@ import type { TLBounds } from '+types'
|
|||
import { usePosition } from '+hooks'
|
||||
|
||||
interface ContainerProps {
|
||||
id?: string
|
||||
bounds: TLBounds
|
||||
rotation?: number
|
||||
id?: string
|
||||
className?: string
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ export function Page<T extends TLShape, M extends Record<string, unknown>>({
|
|||
return (
|
||||
<>
|
||||
{bounds && !hideBounds && <BoundsBg bounds={bounds} rotation={rotation} />}
|
||||
{shapeTree.map((node, i) => (
|
||||
{shapeTree.map((node) => (
|
||||
<ShapeNode key={node.shape.id} utils={shapeUtils} {...node} />
|
||||
))}
|
||||
{bounds && !hideBounds && (
|
||||
|
|
|
@ -31,7 +31,7 @@ export const ShapeNode = React.memo(
|
|||
meta={meta}
|
||||
/>
|
||||
{children &&
|
||||
children.map((childNode) => (
|
||||
children.map((childNode, i) => (
|
||||
<ShapeNode key={childNode.shape.id} utils={utils} {...childNode} />
|
||||
))}
|
||||
</>
|
||||
|
|
|
@ -2,18 +2,34 @@
|
|||
import * as React from 'react'
|
||||
import type { TLPageState } from '+types'
|
||||
|
||||
export function useCameraCss(ref: React.RefObject<HTMLDivElement>, pageState: TLPageState) {
|
||||
const rLayer = React.useRef<HTMLDivElement>(null)
|
||||
|
||||
export function useCameraCss(
|
||||
layerRef: React.RefObject<HTMLDivElement>,
|
||||
containerRef: React.RefObject<HTMLDivElement>,
|
||||
pageState: TLPageState
|
||||
) {
|
||||
// Update the tl-zoom CSS variable when the zoom changes
|
||||
React.useEffect(() => {
|
||||
ref.current!.style.setProperty('--tl-zoom', pageState.camera.zoom.toString())
|
||||
}, [pageState.camera.zoom])
|
||||
const rZoom = React.useRef(pageState.camera.zoom)
|
||||
|
||||
React.useEffect(() => {
|
||||
ref.current!.style.setProperty('--tl-camera-x', pageState.camera.point[0] + 'px')
|
||||
ref.current!.style.setProperty('--tl-camera-y', pageState.camera.point[1] + 'px')
|
||||
}, [pageState.camera.point])
|
||||
React.useLayoutEffect(() => {
|
||||
const {
|
||||
zoom,
|
||||
point: [x, y],
|
||||
} = pageState.camera
|
||||
|
||||
return rLayer
|
||||
if (zoom !== rZoom.current) {
|
||||
rZoom.current = zoom
|
||||
|
||||
const container = containerRef.current
|
||||
|
||||
if (container) {
|
||||
container.style.setProperty('--tl-zoom', zoom.toString())
|
||||
}
|
||||
}
|
||||
|
||||
const layer = layerRef.current
|
||||
|
||||
if (layer) {
|
||||
layer.style.setProperty('transform', `scale(${zoom}) translate(${x}px, ${y}px)`)
|
||||
}
|
||||
}, [pageState.camera])
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import * as React from 'react'
|
||||
import type { TLBounds } from '+types'
|
||||
|
||||
export function usePosition(bounds: TLBounds, rotation = 0) {
|
||||
export function usePosition(bounds: TLBounds, rotation = 0, zIndex = 0) {
|
||||
const rBounds = React.useRef<HTMLDivElement>(null)
|
||||
|
||||
// Update the transform
|
||||
|
@ -24,6 +24,8 @@ export function usePosition(bounds: TLBounds, rotation = 0) {
|
|||
'height',
|
||||
`calc(${Math.floor(bounds.height)}px + (var(--tl-padding) * 2))`
|
||||
)
|
||||
|
||||
elm.style.setProperty('z-index', zIndex + '')
|
||||
}, [bounds, rotation])
|
||||
|
||||
return rBounds
|
||||
|
|
|
@ -111,7 +111,8 @@ export function useShapeTree<
|
|||
shapesIdsToRender.clear()
|
||||
|
||||
Object.values(page.shapes)
|
||||
.filter((shape) => {
|
||||
.sort((a, b) => a.childIndex - b.childIndex)
|
||||
.forEach((shape) => {
|
||||
// Don't hide selected shapes (this breaks certain drag interactions)
|
||||
if (
|
||||
selectedIds.includes(shape.id) ||
|
||||
|
@ -123,7 +124,6 @@ export function useShapeTree<
|
|||
}
|
||||
}
|
||||
})
|
||||
.sort((a, b) => a.childIndex - b.childIndex)
|
||||
|
||||
// Call onChange callback when number of rendering shapes changes
|
||||
|
||||
|
|
|
@ -111,8 +111,6 @@ const tlcss = css`
|
|||
.tl-container {
|
||||
--tl-zoom: 1;
|
||||
--tl-scale: calc(1 / var(--tl-zoom));
|
||||
--tl-camera-x: 0px;
|
||||
--tl-camera-y: 0px;
|
||||
--tl-padding: calc(64px * max(1, var(--tl-scale)));
|
||||
position: relative;
|
||||
top: 0px;
|
||||
|
@ -150,8 +148,6 @@ const tlcss = css`
|
|||
left: 0;
|
||||
height: 0;
|
||||
width: 0;
|
||||
contain: layout size;
|
||||
transform: scale(var(--tl-zoom)) translate(var(--tl-camera-x), var(--tl-camera-y));
|
||||
}
|
||||
|
||||
.tl-absolute {
|
||||
|
@ -171,7 +167,6 @@ const tlcss = css`
|
|||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: clip;
|
||||
contain: layout size paint;
|
||||
}
|
||||
|
||||
.tl-positioned-svg {
|
||||
|
|
|
@ -925,8 +925,11 @@ export class TLDrawState extends StateManager<Data> {
|
|||
}))
|
||||
)
|
||||
}
|
||||
|
||||
try {
|
||||
if (!('clipboard' in navigator && navigator.clipboard.readText)) {
|
||||
throw Error('This browser does not support the clipboard API.')
|
||||
}
|
||||
|
||||
navigator.clipboard.readText().then((result) => {
|
||||
try {
|
||||
const data: { type: string; shapes: TLDrawShape[] } = JSON.parse(result)
|
||||
|
@ -937,6 +940,8 @@ export class TLDrawState extends StateManager<Data> {
|
|||
|
||||
pasteInCurrentPage(data.shapes)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
|
||||
const shapeId = Utils.uniqueId()
|
||||
|
||||
this.createShapes({
|
||||
|
@ -951,7 +956,8 @@ export class TLDrawState extends StateManager<Data> {
|
|||
this.select(shapeId)
|
||||
}
|
||||
})
|
||||
} catch {
|
||||
} catch (e: any) {
|
||||
console.warn(e.message)
|
||||
// Navigator does not support clipboard. Note that this fallback will
|
||||
// not support pasting from one document to another.
|
||||
if (this.clipboard) {
|
||||
|
@ -1122,7 +1128,7 @@ export class TLDrawState extends StateManager<Data> {
|
|||
|
||||
if (shapes.length === 0) return this
|
||||
|
||||
const bounds = Utils.getCommonBounds(Object.values(shapes).map(TLDR.getBounds))
|
||||
const bounds = Utils.getCommonBounds(shapes.map(TLDR.getBounds))
|
||||
|
||||
const zoom = TLDR.getCameraZoom(
|
||||
this.bounds.width < this.bounds.height
|
||||
|
@ -1135,7 +1141,7 @@ export class TLDrawState extends StateManager<Data> {
|
|||
|
||||
return this.setCamera(
|
||||
Vec.round(Vec.add([-bounds.minX, -bounds.minY], [mx, my])),
|
||||
this.pageState.camera.zoom,
|
||||
zoom,
|
||||
`zoomed_to_fit`
|
||||
)
|
||||
}
|
||||
|
@ -2326,6 +2332,7 @@ export class TLDrawState extends StateManager<Data> {
|
|||
// const nextZoom = TLDR.getCameraZoom(i * 0.25)
|
||||
// this.zoomTo(nextZoom, inputs.pointer?.point)
|
||||
// }
|
||||
this.undoSelect()
|
||||
this.setStatus(TLDrawStatus.Idle)
|
||||
}
|
||||
|
||||
|
|
|
@ -16,12 +16,14 @@ export default function Editor({ id = 'home' }: EditorProps) {
|
|||
|
||||
// Send events to gtag as actions.
|
||||
const handleChange = React.useCallback((_tlstate: TLDrawState, _state: Data, reason: string) => {
|
||||
gtag.event({
|
||||
action: reason,
|
||||
category: 'editor',
|
||||
label: `page:${id}`,
|
||||
value: 0,
|
||||
})
|
||||
if (reason.startsWith('command')) {
|
||||
gtag.event({
|
||||
action: reason,
|
||||
category: 'editor',
|
||||
label: `page:${id}`,
|
||||
value: 0,
|
||||
})
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
|
|
|
@ -4127,7 +4127,7 @@
|
|||
resolved "https://registry.yarnpkg.com/@use-gesture/core/-/core-10.0.0-beta.26.tgz#275a69ed65c6463cb59872a6f0b24b66212e8bf5"
|
||||
integrity sha512-Y+fwWlYHzGlc/c4aE3fUkQy7cE4+Tbd1CWQqAlO4czN2nGpgZksW7V98FMgCHrmMGXi+VdNSTS8WBHmw8kq3Zg==
|
||||
|
||||
"@use-gesture/react@^10.0.0-beta.24":
|
||||
"@use-gesture/react@^10.0.0-beta.26":
|
||||
version "10.0.0-beta.26"
|
||||
resolved "https://registry.yarnpkg.com/@use-gesture/react/-/react-10.0.0-beta.26.tgz#9b5c3a9111d6cda62de125612fb830efcc1df571"
|
||||
integrity sha512-TENwg1CgB+okRTBWvcMtEaKYgQvktU0ezG4sQdo/mqZXNvTzuk9g/qK4v++8UzIhj/srqRp49NPBbY0qzsgg1w==
|
||||
|
|
Loading…
Reference in a new issue