Browse Source

Enhance (Whiteboards): Paste and dnd behavior (also add a placeholder to shape labels) (#8753)

* fix: paste shape

* enhance: add label placeholder

* fix: don't create portal on ref click

* enhance: allow ref dragging

* fix: create line binding on drop

* enhance: allow creating url based elements on drop
Konstantinos 2 years ago
parent
commit
82e5abf9e0

+ 3 - 6
src/main/frontend/components/block.cljs

@@ -511,11 +511,6 @@
          (:db/id page-entity)
          :page))
 
-      (whiteboard-handler/inside-portal? (.-target e))
-      (whiteboard-handler/add-new-block-portal-shape!
-       page-name
-       (whiteboard-handler/closest-shape (.-target e)))
-
       whiteboard-page?
       (route-handler/redirect-to-whiteboard! page-name)
 
@@ -546,7 +541,9 @@
                (str " page-property-key block-property")
                untitled? (str " opacity-50"))
       :data-ref page-name
-      :on-mouse-down (fn [e] (open-page-ref e page-name redirect-page-name page-name-in-block contents-page? whiteboard-page?))
+      :draggable true
+      :on-drag-start (fn [e] (editor-handler/block->data-transfer! page-name e))
+      :on-mouse-up (fn [e] (open-page-ref e page-name redirect-page-name page-name-in-block contents-page? whiteboard-page?))
       :on-key-up (fn [e] (when (and e (= (.-key e) "Enter"))
                            (open-page-ref e page-name redirect-page-name page-name-in-block contents-page? whiteboard-page?)))}
 

+ 14 - 14
tldraw/apps/tldraw-logseq/src/hooks/usePaste.ts

@@ -29,8 +29,8 @@ import { LogseqContext, LogseqContextValue } from '../lib/logseq-context'
 
 const isValidURL = (url: string) => {
   try {
-    new URL(url)
-    return true
+    const parsedUrl = new URL(url)
+    return parsedUrl.host && ['http:', 'https:'].includes(parsedUrl.protocol)
   } catch {
     return false
   }
@@ -105,17 +105,17 @@ const handleCreatingShapes = async (
     const existingAsset = Object.values(app.assets).find(asset => asset.src === url)
     if (existingAsset) {
       return existingAsset as VideoImageAsset
-    } else {
-      // Create a new asset for this image
-      const asset: VideoImageAsset = {
-        id: uniqueId(),
-        type: isVideo ? 'video' : 'image',
-        src: url,
-        size: await getSizeFromSrc(handlers.makeAssetUrl(url), isVideo),
-      }
-      return asset
     }
-  }
+
+    // Create a new asset for this image
+    const asset: VideoImageAsset = {
+      id: uniqueId(),
+      type: isVideo ? 'video' : 'image',
+      src: url,
+      size: await getSizeFromSrc(handlers.makeAssetUrl(url), isVideo),
+    }
+    return asset
+}
 
   async function createAssetsFromFiles(files: File[]) {
     const tasks = files
@@ -272,7 +272,7 @@ const handleCreatingShapes = async (
   }
 
   async function tryCreateShapeFromURL(rawText: string) {
-    if (isValidURL(rawText) && !(shiftKey || fromDrop)) {
+    if (isValidURL(rawText) && !shiftKey) {
       if (YOUTUBE_REGEX.test(rawText)) {
         return [
           {
@@ -415,7 +415,7 @@ const handleCreatingShapes = async (
     }
     app.currentPage.updateBindings(Object.fromEntries(bindingsToCreate.map(b => [b.id, b])))
 
-    if (app.selectedShapesArray.length === 1 && allShapesToAdd.length === 1 && !fromDrop) {
+    if (app.selectedShapesArray.length === 1 && allShapesToAdd.length === 1 && fromDrop) {
       const source = app.selectedShapesArray[0]
       const target = app.getShapeById(allShapesToAdd[0].id!)!
       app.createNewLineBinding(source, target)

+ 1 - 1
tldraw/apps/tldraw-logseq/src/lib/shapes/LineShape.tsx

@@ -77,7 +77,7 @@ export class LineShape extends TLLineShape<LineShapeProps> {
     const labelSize =
       label || isEditing
         ? getTextLabelSize(
-            label,
+            label || "Enter text",
             { fontFamily: 'var(--ls-font-family)', fontSize, lineHeight: 1, fontWeight },
             6
           )

+ 5 - 9
tldraw/apps/tldraw-logseq/src/lib/shapes/text/TextLabel.tsx

@@ -5,6 +5,7 @@ import { TextAreaUtils } from './TextAreaUtils'
 
 const stopPropagation = (e: KeyboardEvent | React.SyntheticEvent<any, Event>) => e.stopPropagation()
 
+const placeholder = "Enter text"
 export interface TextLabelProps {
   font: string
   text: string
@@ -57,11 +58,7 @@ export const TextLabel = React.memo(function TextLabel({
       if (!(e.key === 'Meta' || e.metaKey)) {
         e.stopPropagation()
       } else if (e.key === 'z' && e.metaKey) {
-        if (e.shiftKey) {
-          document.execCommand('redo', false)
-        } else {
-          document.execCommand('undo', false)
-        }
+        document.execCommand(e.shiftKey ? 'redo' : 'undo', false)
         e.stopPropagation()
         e.preventDefault()
         return
@@ -92,8 +89,7 @@ export const TextLabel = React.memo(function TextLabel({
 
   const handleFocus = React.useCallback(
     (e: React.FocusEvent<HTMLTextAreaElement>) => {
-      if (!isEditing) return
-      if (!rIsMounted.current) return
+      if (!isEditing || !rIsMounted.current) return
 
       if (document.activeElement === e.currentTarget) {
         e.currentTarget.select()
@@ -130,7 +126,7 @@ export const TextLabel = React.memo(function TextLabel({
     const elm = rInnerWrapper.current
     if (!elm) return
     const size = getTextLabelSize(
-      text,
+      text || placeholder,
       { fontFamily: 'var(--ls-font-family)', fontSize, lineHeight: 1, fontWeight },
       4
     )
@@ -172,7 +168,7 @@ export const TextLabel = React.memo(function TextLabel({
             autoCorrect="false"
             autoSave="false"
             autoFocus
-            placeholder=""
+            placeholder={placeholder}
             spellCheck="true"
             wrap="off"
             dir="auto"

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

@@ -122,9 +122,8 @@ export class TLPage<S extends TLShape = TLShape, E extends TLEventMap = TLEventM
   private parseShapesArg<S>(shapes: S[] | string[]) {
     if (typeof shapes[0] === 'string') {
       return this.shapes.filter(shape => (shapes as string[]).includes(shape.id))
-    } else {
-      return shapes as S[]
     }
+    return shapes as S[]
   }
 
   @action removeShapes(...shapes: S[] | string[]) {