Browse Source

Merge branch 'whiteboards' of https://github.com/logseq/logseq into whiteboards

Peng Xiao 3 years ago
parent
commit
db51313ee1

+ 2 - 0
tldraw/apps/tldraw-logseq/src/app.tsx

@@ -28,6 +28,7 @@ import {
   shapes,
   TextTool,
   YouTubeTool,
+  IFrameTool,
   type Shape,
 } from './lib'
 import { LogseqContext, type LogseqContextValue } from './lib/logseq-context'
@@ -47,6 +48,7 @@ const tools: TLReactToolConstructor<Shape>[] = [
   PencilTool,
   TextTool,
   YouTubeTool,
+  IFrameTool,
   HTMLTool,
   LogseqPortalTool,
 ]

+ 46 - 1
tldraw/apps/tldraw-logseq/src/components/ContextBar/contextBarActionFactory.tsx

@@ -7,6 +7,7 @@ import type {
   LogseqPortalShape,
   TextShape,
   HTMLShape,
+  IFrameShape,
   YouTubeShape,
   BoxShape,
   PolygonShape,
@@ -36,13 +37,19 @@ export const contextBarActionTypes = [
   'ScaleLevel',
   'TextStyle',
   'YoutubeLink',
+  'IFrameSource',
   'LogseqPortalViewMode',
   'ArrowMode',
   'OpenPage',
 ] as const
 
 type ContextBarActionType = typeof contextBarActionTypes[number]
-const singleShapeActions: ContextBarActionType[] = ['Edit', 'YoutubeLink', 'OpenPage']
+const singleShapeActions: ContextBarActionType[] = [
+  'Edit',
+  'YoutubeLink',
+  'IFrameSource',
+  'OpenPage',
+]
 
 const contextBarActionMapping = new Map<ContextBarActionType, React.FC>()
 
@@ -51,6 +58,7 @@ type ShapeType = Shape['props']['type']
 export const shapeMapping: Partial<Record<ShapeType, ContextBarActionType[]>> = {
   'logseq-portal': ['Edit', 'LogseqPortalViewMode', 'ScaleLevel', 'OpenPage', 'AutoResizing'],
   youtube: ['YoutubeLink'],
+  iframe: ['IFrameSource'],
   box: ['Swatch', 'NoFill', 'StrokeType'],
   ellipse: ['Swatch', 'NoFill', 'StrokeType'],
   polygon: ['Swatch', 'NoFill', 'StrokeType'],
@@ -239,6 +247,42 @@ const OpenPageAction = observer(() => {
   )
 })
 
+const IFrameSourceAction = observer(() => {
+  const app = useApp<Shape>()
+  const shape = filterShapeByAction<IFrameShape>(app.selectedShapesArray, 'IFrameSource')[0]
+
+  const handleChange = React.useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
+    shape.onIFrameSourceChange(e.target.value.trim().toLowerCase())
+    app.persist()
+  }, [])
+
+  const handleReload = React.useCallback(() => {
+    shape.reload()
+  }, [])
+
+  return (
+    <span className="flex gap-3">
+      <button title="Reload" className="tl-contextbar-button" type="button" onClick={handleReload}>
+        <TablerIcon name="refresh" />
+      </button>
+      <TextInput
+        title="Website Url"
+        className="tl-iframe-src"
+        value={`${shape.props.url}`}
+        onChange={handleChange}
+      />
+      <button
+        title="Open website url"
+        className="tl-contextbar-button"
+        type="button"
+        onClick={() => window.open(shape.props.url)}
+      >
+        <TablerIcon name="external-link" />
+      </button>
+    </span>
+  )
+})
+
 const YoutubeLinkAction = observer(() => {
   const app = useApp<Shape>()
   const shape = filterShapeByAction<YouTubeShape>(app.selectedShapesArray, 'YoutubeLink')[0]
@@ -450,6 +494,7 @@ contextBarActionMapping.set('LogseqPortalViewMode', LogseqPortalViewModeAction)
 contextBarActionMapping.set('ScaleLevel', ScaleLevelAction)
 contextBarActionMapping.set('OpenPage', OpenPageAction)
 contextBarActionMapping.set('YoutubeLink', YoutubeLinkAction)
+contextBarActionMapping.set('IFrameSource', IFrameSourceAction)
 contextBarActionMapping.set('NoFill', NoFillAction)
 contextBarActionMapping.set('Swatch', SwatchAction)
 contextBarActionMapping.set('StrokeType', StrokeTypeAction)

+ 8 - 1
tldraw/apps/tldraw-logseq/src/hooks/usePaste.ts

@@ -19,6 +19,7 @@ import {
   LogseqPortalShape,
   VideoShape,
   ImageShape,
+  IFrameShape,
 } from '../lib'
 import type { LogseqContextValue } from '../lib/logseq-context'
 
@@ -243,7 +244,13 @@ export function usePaste(context: LogseqContextValue) {
           ) {
             return true
           }
-          // ??? deal with normal URLs?
+
+          shapesToCreate.push({
+            ...IFrameShape.defaultProps,
+            url: rawText,
+            point: [point[0], point[1]],
+          })
+          return true
         }
         return false
       }

+ 87 - 0
tldraw/apps/tldraw-logseq/src/lib/shapes/IFrameShape.tsx

@@ -0,0 +1,87 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import * as React from 'react'
+import { TLBoxShape, TLBoxShapeProps } from '@tldraw/core'
+import { HTMLContainer, TLComponentProps } from '@tldraw/react'
+import { action, computed } from 'mobx'
+import { observer } from 'mobx-react-lite'
+
+export interface IFrameShapeProps extends TLBoxShapeProps {
+  type: 'iframe'
+  url: string
+}
+
+export class IFrameShape extends TLBoxShape<IFrameShapeProps> {
+  static id = 'iframe'
+  frameRef = React.createRef<HTMLIFrameElement>()
+
+  static defaultProps: IFrameShapeProps = {
+    id: 'iframe',
+    type: 'iframe',
+    parentId: 'page',
+    point: [0, 0],
+    size: [853, 480],
+    url: '',
+  }
+
+  canEdit = true
+
+  @action onIFrameSourceChange = (url: string) => {
+    this.update({ url })
+  }
+
+  @action reload = () => {
+    this.frameRef.current.src = this.frameRef?.current?.src
+  }
+
+  ReactComponent = observer(({ events, isErasing, isEditing }: TLComponentProps) => {
+    const ref = React.useRef<HTMLIFrameElement>(null)
+
+    return (
+      <HTMLContainer
+        style={{
+          overflow: 'hidden',
+          pointerEvents: 'all',
+          opacity: isErasing ? 0.2 : 1,
+        }}
+        {...events}
+      >
+        <div
+          className="rounded-lg w-full h-full relative overflow-hidden shadow-xl"
+          style={{
+            pointerEvents: isEditing ? 'all' : 'none',
+            userSelect: 'none',
+          }}
+        >
+          {this.props.url && (
+            <div
+              style={{
+                overflow: 'hidden',
+                position: 'relative',
+                height: '100%',
+              }}
+            >
+              <iframe
+                ref={this.frameRef}
+                className="absolute inset-0 w-full h-full m-0"
+                width="100%"
+                height="100%"
+                src={`${this.props.url}`}
+                frameBorder="0"
+                sandbox="allow-scripts"
+              />
+            </div>
+          )}
+        </div>
+      </HTMLContainer>
+    )
+  })
+
+  ReactIndicator = observer(() => {
+    const {
+      props: {
+        size: [w, h],
+      },
+    } = this
+    return <rect width={w} height={h} fill="transparent" rx={8} ry={8} />
+  })
+}

+ 4 - 0
tldraw/apps/tldraw-logseq/src/lib/shapes/index.ts

@@ -6,6 +6,7 @@ import { HighlighterShape } from './HighlighterShape'
 import { HTMLShape } from './HTMLShape'
 import { ImageShape } from './ImageShape'
 import { VideoShape } from './VideoShape'
+import { IFrameShape } from './IFrameShape'
 import { LineShape } from './LineShape'
 import { LogseqPortalShape } from './LogseqPortalShape'
 import { PencilShape } from './PencilShape'
@@ -27,6 +28,7 @@ export type Shape =
   | PolygonShape
   | TextShape
   | YouTubeShape
+  | IFrameShape
   | HTMLShape
   | LogseqPortalShape
 
@@ -37,6 +39,7 @@ export * from './HighlighterShape'
 export * from './HTMLShape'
 export * from './ImageShape'
 export * from './VideoShape'
+export * from './IFrameShape'
 export * from './LineShape'
 export * from './LogseqPortalShape'
 export * from './PencilShape'
@@ -56,6 +59,7 @@ export const shapes: TLReactShapeConstructor<Shape>[] = [
   PolygonShape,
   TextShape,
   YouTubeShape,
+  IFrameShape,
   HTMLShape,
   LogseqPortalShape,
 ]

+ 10 - 0
tldraw/apps/tldraw-logseq/src/lib/tools/IFrameTool.tsx

@@ -0,0 +1,10 @@
+import { TLBoxTool } from '@tldraw/core'
+import type { TLReactEventMap } from '@tldraw/react'
+import { IFrameShape, type Shape } from '../shapes'
+
+export class IFrameTool extends TLBoxTool<IFrameShape, Shape, TLReactEventMap> {
+  static id = 'iframe'
+  Shape = IFrameShape
+}
+
+export {}

+ 1 - 0
tldraw/apps/tldraw-logseq/src/lib/tools/index.ts

@@ -10,3 +10,4 @@ export * from './TextTool'
 export * from './YouTubeTool'
 export * from './LogseqPortalTool'
 export * from './HTMLTool'
+export * from './IFrameTool'

+ 3 - 3
tldraw/apps/tldraw-logseq/src/styles.css

@@ -818,11 +818,11 @@ html[data-theme='dark'] {
   width: 1px;
 }
 
-.tl-youtube-link {
-  border-radius: 8px;
+.tl-youtube-link,
+.tl-iframe-src {
+  @apply rounded-lg px-2 py-1;
   color: var(--ls-primary-text-color);
   box-shadow: 0 0 0 1px var(--ls-secondary-border-color);
-  padding: 4px 14px;
 }
 
 .tl-hitarea-stroke {

+ 0 - 1
tldraw/packages/core/src/lib/TLApp/TLApp.ts

@@ -719,7 +719,6 @@ export class TLApp<
       inputs: { ctrlKey },
     } = this
     return (
-      !ctrlKey &&
       this.isInAny('select.idle', 'select.hoveringSelectionHandle') &&
       !this.isIn('select.contextMenu') &&
       selectedShapesArray.length > 0 &&

+ 1 - 2
tldraw/packages/core/src/lib/TLViewport.ts

@@ -84,7 +84,7 @@ export class TLViewport {
 
   pinchCamera = (point: number[], delta: number[], zoom: number): this => {
     const { camera } = this
-    zoom = Math.max(TLViewport.minZoom, Math.min(TLViewport.maxZoom, zoom));
+    zoom = Math.max(TLViewport.minZoom, Math.min(TLViewport.maxZoom, zoom))
     const nextPoint = Vec.sub(camera.point, Vec.div(delta, camera.zoom))
     const p0 = Vec.sub(Vec.div(point, camera.zoom), nextPoint)
     const p1 = Vec.sub(Vec.div(point, zoom), nextPoint)
@@ -105,7 +105,6 @@ export class TLViewport {
   zoomOut = () => {
     const { camera, bounds } = this
     this.setZoom(camera.zoom * ZOOM_UPDATE_FACTOR)
-
   }
 
   resetZoom = (): this => {

+ 2 - 2
tldraw/packages/core/src/lib/tools/TLSelectTool/states/PinchingState.ts

@@ -26,8 +26,8 @@ export class PinchingState<
 
     // Normalize the value of deltaZ from raw WheelEvent
     const deltaZ = normalizeWheel(event)[2] * 0.01
-    if (deltaZ === 0) return;
-    const zoom = camera.zoom - deltaZ * camera.zoom;
+    if (deltaZ === 0) return
+    const zoom = camera.zoom - deltaZ * camera.zoom
     this.app.viewport.pinchCamera(info.point, [0, 0], zoom)
   }