mirror of https://github.com/logseq/logseq
wip preview/minimap window
parent
3ae4daaa8d
commit
7def1f4d2c
|
@ -5,29 +5,14 @@ import {
|
|||
AppCanvas,
|
||||
AppProvider,
|
||||
TLReactCallbacks,
|
||||
TLReactComponents,
|
||||
TLReactShapeConstructor,
|
||||
TLReactToolConstructor,
|
||||
TLReactComponents, TLReactToolConstructor
|
||||
} from '@tldraw/react'
|
||||
import * as React from 'react'
|
||||
import { AppUI } from '~components/AppUI'
|
||||
import { ContextBar } from '~components/ContextBar/ContextBar'
|
||||
import { useFileDrop } from '~hooks/useFileDrop'
|
||||
import { LogseqContext } from '~lib/logseq-context'
|
||||
import {
|
||||
BoxShape,
|
||||
DotShape,
|
||||
EllipseShape,
|
||||
HighlighterShape,
|
||||
ImageShape,
|
||||
LineShape,
|
||||
LogseqPortalShape,
|
||||
PenShape,
|
||||
PolygonShape,
|
||||
Shape,
|
||||
TextShape,
|
||||
YouTubeShape,
|
||||
} from '~lib/shapes'
|
||||
import { Shape, shapes } from '~lib/shapes'
|
||||
import {
|
||||
BoxTool,
|
||||
DotTool,
|
||||
|
@ -39,27 +24,13 @@ import {
|
|||
PenTool,
|
||||
PolygonTool,
|
||||
TextTool,
|
||||
YouTubeTool,
|
||||
YouTubeTool
|
||||
} from '~lib/tools'
|
||||
|
||||
const components: TLReactComponents<Shape> = {
|
||||
ContextBar: ContextBar,
|
||||
}
|
||||
|
||||
const shapes: TLReactShapeConstructor<Shape>[] = [
|
||||
BoxShape,
|
||||
DotShape,
|
||||
EllipseShape,
|
||||
HighlighterShape,
|
||||
ImageShape,
|
||||
LineShape,
|
||||
PenShape,
|
||||
PolygonShape,
|
||||
TextShape,
|
||||
YouTubeShape,
|
||||
LogseqPortalShape,
|
||||
]
|
||||
|
||||
const tools: TLReactToolConstructor<Shape>[] = [
|
||||
BoxTool,
|
||||
DotTool,
|
||||
|
|
|
@ -4,13 +4,23 @@ import { ToolBar } from './Toolbar'
|
|||
import { StatusBar } from './StatusBar'
|
||||
import { PrimaryTools } from './PrimaryTools'
|
||||
import { DevTools } from './Devtools'
|
||||
import { useApp } from '@tldraw/react'
|
||||
import { WhiteboardPreview } from '~lib'
|
||||
|
||||
const isDev = process.env.NODE_ENV === 'development'
|
||||
|
||||
export const AppUI = observer(function AppUI() {
|
||||
const app = useApp()
|
||||
|
||||
const preview = React.useMemo(() => {
|
||||
const WP = new WhiteboardPreview(app.serialized)
|
||||
return WP.getPreview()
|
||||
}, [app.serialized])
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* <ToolBar /> */}
|
||||
{preview}
|
||||
{isDev && <StatusBar />}
|
||||
{isDev && <DevTools />}
|
||||
<PrimaryTools />
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
export * from './shapes'
|
||||
export * from './tools'
|
||||
export * from './preview'
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
import type { TLDocumentModel, TLShapeConstructor } from '@tldraw/core'
|
||||
import React from 'react'
|
||||
import ReactDOMServer from 'react-dom/server'
|
||||
import { Shape, shapes } from './shapes'
|
||||
|
||||
const ShapesMap = new Map(shapes.map(shape => [shape.id, shape]))
|
||||
|
||||
const getShapeClass = (type: string): TLShapeConstructor<Shape> => {
|
||||
if (!type) throw Error('No shape type provided.')
|
||||
const Shape = ShapesMap.get(type)
|
||||
if (!Shape) throw Error(`Could not find shape class for ${type}`)
|
||||
return Shape
|
||||
}
|
||||
|
||||
export class WhiteboardPreview {
|
||||
shapes: Shape[] | undefined
|
||||
constructor(serializedApp?: TLDocumentModel<Shape>) {
|
||||
if (serializedApp) {
|
||||
this.deserialize(serializedApp)
|
||||
}
|
||||
}
|
||||
|
||||
deserialize(snapshot: TLDocumentModel) {
|
||||
const page = snapshot.pages.find(p => snapshot.currentPageId === p.id)
|
||||
this.shapes = page?.shapes.map(s => {
|
||||
const ShapeClass = getShapeClass(s.type)
|
||||
return new ShapeClass(s)
|
||||
})
|
||||
}
|
||||
|
||||
getPreview() {
|
||||
// TODO: translate each shape to where it should be
|
||||
return (
|
||||
<svg
|
||||
style={{
|
||||
pointerEvents: 'none',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
position: 'fixed',
|
||||
border: '1px solid black',
|
||||
transformOrigin: 'bottom right',
|
||||
transform: 'scale(0.2)',
|
||||
}}
|
||||
>
|
||||
{this.shapes?.map(s => {
|
||||
const {
|
||||
bounds,
|
||||
props: { rotation },
|
||||
} = s
|
||||
const transformStr = `translate(${bounds.minX}px, ${bounds.minY}px)
|
||||
rotate(${(rotation ?? 0) + (bounds.rotation ?? 0)}rad)`
|
||||
return (
|
||||
<g style={{ transform: transformStr }} key={s.id}>
|
||||
{s.getShapeSVGJsx()}
|
||||
</g>
|
||||
)
|
||||
})}
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -128,7 +128,7 @@ export class LineShape extends TLLineShape<LineShapeProps> {
|
|||
return offset
|
||||
}, [bounds, scale, midPoint])
|
||||
return (
|
||||
<>
|
||||
<g>
|
||||
<LabelMask id={id} bounds={bounds} labelSize={labelSize} offset={offset} scale={scale} />
|
||||
<path
|
||||
mask={label ? `url(#${id}_clip)` : ``}
|
||||
|
@ -151,7 +151,7 @@ export class LineShape extends TLLineShape<LineShapeProps> {
|
|||
fill="transparent"
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</g>
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
@ -32,9 +32,11 @@ export class LogseqPortalShape extends TLBoxShape<LogseqPortalShapeProps> {
|
|||
pageId: '',
|
||||
}
|
||||
|
||||
hideRotateHandle = true
|
||||
canChangeAspectRatio = true
|
||||
canFlip = true
|
||||
canActivate = true
|
||||
canEdit = true
|
||||
|
||||
ReactContextBar = observer(() => {
|
||||
const { pageId } = this.props
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import type { BoxShape } from './BoxShape'
|
||||
import type { DotShape } from './DotShape'
|
||||
import type { EllipseShape } from './EllipseShape'
|
||||
import type { HighlighterShape } from './HighlighterShape'
|
||||
import type { ImageShape } from './ImageShape'
|
||||
import type { LineShape } from './LineShape'
|
||||
import type { PenShape } from './PenShape'
|
||||
import type { PolygonShape } from './PolygonShape'
|
||||
import type { TextShape } from './TextShape'
|
||||
import type { YouTubeShape } from './YouTubeShape'
|
||||
import type { LogseqPortalShape } from './LogseqPortalShape';
|
||||
import { BoxShape } from './BoxShape'
|
||||
import { DotShape } from './DotShape'
|
||||
import { EllipseShape } from './EllipseShape'
|
||||
import { HighlighterShape } from './HighlighterShape'
|
||||
import { ImageShape } from './ImageShape'
|
||||
import { LineShape } from './LineShape'
|
||||
import { PenShape } from './PenShape'
|
||||
import { PolygonShape } from './PolygonShape'
|
||||
import { TextShape } from './TextShape'
|
||||
import { YouTubeShape } from './YouTubeShape'
|
||||
import { LogseqPortalShape } from './LogseqPortalShape'
|
||||
import type { TLReactShapeConstructor } from '@tldraw/react'
|
||||
|
||||
export type Shape =
|
||||
| BoxShape
|
||||
|
@ -35,3 +36,17 @@ export * from './PolygonShape'
|
|||
export * from './TextShape'
|
||||
export * from './YouTubeShape'
|
||||
export * from './LogseqPortalShape'
|
||||
|
||||
export const shapes: TLReactShapeConstructor<Shape>[] = [
|
||||
BoxShape,
|
||||
DotShape,
|
||||
EllipseShape,
|
||||
HighlighterShape,
|
||||
ImageShape,
|
||||
LineShape,
|
||||
PenShape,
|
||||
PolygonShape,
|
||||
TextShape,
|
||||
YouTubeShape,
|
||||
LogseqPortalShape,
|
||||
]
|
|
@ -67,6 +67,7 @@
|
|||
.logseq-tldraw .statusbar {
|
||||
position: absolute;
|
||||
font-family: monospace;
|
||||
font-size: 10px;
|
||||
bottom: 0;
|
||||
grid-row: 3;
|
||||
width: 100%;
|
||||
|
|
|
@ -31,8 +31,6 @@ export interface TLShapeProps {
|
|||
scale?: number[]
|
||||
rotation?: number
|
||||
handles?: Record<string, TLHandle>
|
||||
label?: string
|
||||
labelPosition?: number[]
|
||||
clipping?: number | number[]
|
||||
assetId?: string
|
||||
children?: string[]
|
||||
|
@ -330,4 +328,21 @@ export abstract class TLShape<P extends TLShapeProps = TLShapeProps, M = any> {
|
|||
handles: nextHandles,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a svg group element that can be used to render the shape with only the props data. In the
|
||||
* base, draw any shape as a box. Can be overridden by subclasses.
|
||||
*/
|
||||
getShapeSVGJsx() {
|
||||
// Do not need to consider the original point here
|
||||
const bounds = this.getBounds()
|
||||
return (
|
||||
<rect
|
||||
fill="var(--tl-foreground)"
|
||||
fillOpacity={0.2}
|
||||
width={bounds.width}
|
||||
height={bounds.height}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -114,6 +114,7 @@ export class TranslatingState<
|
|||
onExit = () => {
|
||||
// Resume the history when we exit
|
||||
this.app.history.resume()
|
||||
this.app.persist()
|
||||
|
||||
// Reset initial data
|
||||
this.didClone = false
|
||||
|
|
Loading…
Reference in New Issue