فهرست منبع

feat: finish double click to create poc

Peng Xiao 3 سال پیش
والد
کامیت
20b81fbd1e

+ 0 - 1
src/main/frontend/components/whiteboard.css

@@ -1,3 +1,2 @@
 .logseq-tldraw .tl-container {
-  background-color: var(--ls-secondary-background-color);
 }

+ 2 - 0
tldraw/apps/tldraw-logseq/src/components/Devtools/Devtools.tsx

@@ -51,6 +51,8 @@ export const DevTools = observer(() => {
         <div
           style={{
             flex: 1,
+            display: 'flex',
+            alignItems: 'center',
           }}
         >
           {rendererStatusText}

+ 1 - 1
tldraw/apps/tldraw-logseq/src/components/StatusBar/StatusBar.tsx

@@ -17,7 +17,7 @@ export const StatusBar = observer(function StatusBar() {
     <div className="statusbar">
       {app.selectedTool.id} | {app.selectedTool.currentState.id}
       <div style={{ flex: 1 }} />
-      <div id="tl-statusbar-anchor" />
+      <div id="tl-statusbar-anchor" style={{ display: 'flex' }} />
     </div>
   )
 })

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

@@ -30,7 +30,7 @@ export class LineShape extends TLLineShape<LineShapeProps> {
       start: { id: 'start', canBind: true, point: [0, 0] },
       end: { id: 'end', canBind: true, point: [1, 1] },
     },
-    stroke: '#000000',
+    stroke: 'var(--tl-foreground)',
     fill: '#ffffff',
     strokeWidth: 1,
     opacity: 1,

+ 64 - 38
tldraw/apps/tldraw-logseq/src/lib/shapes/LogseqPortalShape.tsx

@@ -1,10 +1,10 @@
 /* eslint-disable @typescript-eslint/no-explicit-any */
 import { TLBoxShape, TLBoxShapeProps } from '@tldraw/core'
 import { HTMLContainer, TLComponentProps, useApp } from '@tldraw/react'
+import { MagnifyingGlassIcon } from '@radix-ui/react-icons'
 import { makeObservable, transaction } from 'mobx'
 import { observer } from 'mobx-react-lite'
 import * as React from 'react'
-import { TextInput } from '~components/inputs/TextInput'
 import { useCameraMovingRef } from '~hooks/useCameraMoving'
 import type { Shape } from '~lib'
 import { LogseqContext } from '~lib/logseq-context'
@@ -56,28 +56,34 @@ const LogseqQuickSearch = observer(({ onChange }: LogseqQuickSearchProps) => {
   }, [])
 
   return (
-    <>
-      <TextInput
-        ref={rInput}
-        label="Page name or block UUID"
-        type="text"
-        value={q}
-        onChange={handleChange}
-        onKeyDown={e => {
-          if (e.key === 'Enter') {
-            commitChange(q)
-          }
-        }}
-        list="logseq-portal-search-results"
-      />
-      <datalist id="logseq-portal-search-results">
-        {options?.map(option => (
-          <option key={option} value={secretPrefix + option}>
-            {option}
-          </option>
+    <div className="tl-quick-search">
+      <div className="tl-quick-search-input-container">
+        <MagnifyingGlassIcon className="tl-quick-search-icon" width={24} height={24} />
+        <div className="tl-quick-search-input-sizer" data-value={q}>
+          <input
+            ref={rInput}
+            type="text"
+            value={q}
+            placeholder="Search or create page"
+            onChange={handleChange}
+            onKeyDown={e => {
+              if (e.key === 'Enter') {
+                commitChange(q)
+              }
+            }}
+            className="tl-quick-search-input text-input"
+          />
+        </div>
+      </div>
+
+      <div className="tl-quick-search-options">
+        {options?.map(name => (
+          <div key={name} className="tl-quick-search-option" onClick={() => commitChange(name)}>
+            {name}
+          </div>
         ))}
-      </datalist>
-    </>
+      </div>
+    </div>
   )
 })
 
@@ -90,7 +96,7 @@ export class LogseqPortalShape extends TLBoxShape<LogseqPortalShapeProps> {
     type: 'logseq-portal',
     parentId: 'page',
     point: [0, 0],
-    size: [180, 75],
+    size: [600, 50],
     stroke: 'transparent',
     fill: 'var(--ls-secondary-background-color)',
     strokeWidth: 2,
@@ -104,15 +110,9 @@ export class LogseqPortalShape extends TLBoxShape<LogseqPortalShapeProps> {
   canActivate = true
   canEdit = true
 
-  constructor(props = {} as Partial<LogseqPortalShapeProps>) {
-    super(props)
-    makeObservable(this)
-    this.draft = true
-  }
-
   ReactComponent = observer(({ events, isErasing, isActivated }: TLComponentProps) => {
     const {
-      props: { opacity, pageId, strokeWidth, stroke },
+      props: { opacity, pageId, strokeWidth, stroke, fill },
     } = this
 
     const app = useApp<Shape>()
@@ -141,13 +141,15 @@ export class LogseqPortalShape extends TLBoxShape<LogseqPortalShapeProps> {
       })
     }, [])
 
+    if (!Page) {
+      return null // not being correctly configured
+    }
+
     return (
       <HTMLContainer
         style={{
-          overflow: 'hidden',
           pointerEvents: 'all',
           opacity: isErasing ? 0.2 : opacity,
-          backgroundColor: 'var(--ls-primary-background-color)',
         }}
         {...events}
       >
@@ -156,6 +158,8 @@ export class LogseqPortalShape extends TLBoxShape<LogseqPortalShapeProps> {
           onPointerDown={stop}
           onPointerUp={stop}
           style={{
+            width: '100%',
+            height: '100%',
             pointerEvents: isActivated ? 'all' : 'none',
           }}
         >
@@ -166,20 +170,42 @@ export class LogseqPortalShape extends TLBoxShape<LogseqPortalShapeProps> {
               style={{
                 width: '100%',
                 overflow: 'auto',
+                borderRadius: '8px',
                 overscrollBehavior: 'none',
-                height: pageId ? 'calc(100% - 33px)' : '100%',
-                userSelect: 'none',
+                height: '100%',
+                display: 'flex',
+                flexDirection: 'column',
+                background: fill,
                 boxShadow: isActivated
                   ? '0px 0px 0 var(--tl-binding-distance) var(--tl-binding)'
                   : '',
-                opacity: isSelected ? 0.5 : 1,
+                opacity: isSelected ? 0.8 : 1,
               }}
             >
-              {pageId && Page ? (
-                <div style={{ padding: '12px', height: '100%', cursor: 'default' }}>
+              <div className="tl-logseq-portal-header">
+                <span className="text-xs rounded border mr-2 px-1">P</span>
+                {pageId}
+              </div>
+              <div
+                style={{
+                  width: '100%',
+                  overflow: 'auto',
+                  borderRadius: '8px',
+                  overscrollBehavior: 'none',
+                  // height: '100%',
+                  flex: 1,
+                }}
+              >
+                <div
+                  style={{
+                    padding: '12px',
+                    height: '100%',
+                    cursor: 'default',
+                  }}
+                >
                   <Page pageId={pageId} />
                 </div>
-              ) : null}
+              </div>
             </div>
           )}
         </div>

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

@@ -25,7 +25,7 @@ export class PencilShape extends TLDrawShape<PencilShapeProps> {
     point: [0, 0],
     points: [],
     isComplete: false,
-    stroke: '#000000',
+    stroke: 'var(--tl-foreground)',
     fill: '#ffffff',
     strokeWidth: 2,
     opacity: 1,

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

@@ -4,7 +4,6 @@ import { HTMLContainer, TLComponentProps, TLTextMeasure } from '@tldraw/react'
 import { TextUtils, TLBounds, TLResizeStartInfo, TLTextShape, TLTextShapeProps } from '@tldraw/core'
 import { observer } from 'mobx-react-lite'
 import { CustomStyleProps, withClampedStyles } from './style-props'
-import { NumberInput } from '~components/inputs/NumberInput'
 
 export interface TextShapeProps extends TLTextShapeProps, CustomStyleProps {
   borderRadius: number
@@ -33,7 +32,7 @@ export class TextShape extends TLTextShape<TextShapeProps> {
     padding: 4,
     fontFamily: "'Helvetica Neue', Helvetica, Arial, sans-serif",
     borderRadius: 0,
-    stroke: '#000000',
+    stroke: 'var(--tl-foreground)',
     fill: '#ffffff',
     strokeWidth: 2,
     opacity: 1,

+ 102 - 12
tldraw/apps/tldraw-logseq/src/styles.css

@@ -1,10 +1,10 @@
 /* TODO: move to useStylesheet */
 @import url('https://fonts.googleapis.com/css2?family=Inter:wght@500&display=swap');
 
-:root {
-  --color-panel: #ffffff;
-  --color-text: #000000;
-  --color-hover: #00000011;
+.logseq-tldraw {
+  --color-panel: var(--ls-secondary-background-color);
+  --color-text: var(--ls-primary-text-color);
+  --color-hover: var(--ls-tertiary-background-color);
   --color-selectedStroke: rgb(42, 123, 253);
   --color-selectedFill: rgba(66, 133, 244);
   --color-selectedContrast: #ffffff;
@@ -32,6 +32,7 @@
   cursor: pointer;
   border-radius: 2px;
   padding: 4px 8px;
+  opacity: 1;
 }
 
 .logseq-tldraw .toolbar {
@@ -54,6 +55,7 @@
   pointer-events: all;
   position: relative;
   background-color: var(--color-panel);
+  color: var(--color-text);
   padding: 8px 12px;
   border-radius: 8px;
   white-space: nowrap;
@@ -106,14 +108,13 @@
 
 .logseq-tldraw .text-input {
   height: 24px;
-  padding: 4px;
+  padding: 0;
   background: none;
-  border: 1px solid black;
   border-radius: 2px;
 }
 
-.logseq-tldraw .input > label {
-  font-size: 10px;
+.logseq-tldraw .text-input:focus {
+  outline: none;
 }
 
 .logseq-tldraw .primary-tools {
@@ -150,8 +151,8 @@
   flex-flow: column;
   border-radius: 8px;
   overflow: hidden;
-  padding: 4px;
-  gap: 4px;
+  padding: 8px;
+  gap: 8px;
 }
 
 .logseq-tldraw .floating-panel > button {
@@ -160,8 +161,8 @@
 
 .logseq-tldraw .primary-tools .button {
   position: relative;
-  height: 40px;
-  width: 40px;
+  height: 36px;
+  width: 36px;
   display: flex;
   align-items: center;
   justify-content: center;
@@ -170,6 +171,7 @@
   background: none;
   border: none;
   cursor: pointer;
+  color: var(--ls-secondary-text-color);
 }
 
 .logseq-tldraw .primary-tools .button:hover {
@@ -384,3 +386,91 @@
 .logseq-tldraw .preview-minimap-toggle[data-active='true'] {
   background: #eee;
 }
+
+.logseq-tldraw .tl-quick-search {
+  width: fit-content;
+  position: relative;
+}
+
+.logseq-tldraw .tl-quick-search-icon {
+  flex-shrink: 0;
+  margin-right: 12px;
+}
+
+.logseq-tldraw .tl-quick-search-input-container {
+  display: flex;
+  align-items: center;
+  font-size: 16px;
+  background-color: var(--ls-secondary-background-color);
+  padding: 4px 12px;
+  border-radius: 8px;
+}
+
+.logseq-tldraw .tl-quick-search-input-sizer {
+  display: inline-grid;
+  vertical-align: top;
+  align-items: center;
+  position: relative;
+  margin: 5px;
+}
+
+.logseq-tldraw .tl-quick-search-input {
+  grid-area: 1/2;
+  width: auto;
+}
+
+.logseq-tldraw .tl-quick-search-input-sizer::after {
+  content: attr(data-value) ' ';
+  visibility: hidden;
+  white-space: pre-wrap;
+  grid-area: 1/2;
+  width: auto;
+}
+
+.logseq-tldraw .tl-quick-search-options {
+  position: absolute;
+  top: calc(100% + 8px);
+  left: 0;
+  background-color: var(--ls-primary-background-color);
+  max-height: 300px;
+  width: 100%;
+  overflow-y: auto;
+  display: flex;
+  flex-direction: column;
+  border-radius: 8px;
+  --tw-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+  box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
+    var(--tw-shadow);
+  overscroll-behavior: none;
+}
+
+.logseq-tldraw .tl-quick-search-option {
+  padding: 8px 16px;
+  cursor: pointer;
+  display: flex;
+  font-size: 0.875rem;
+  line-height: 1.25rem;
+}
+
+.logseq-tldraw .tl-quick-search-option:hover {
+  background-color: var(--ls-menu-hover-color, #f4f5f7);
+}
+
+.logseq-tldraw .tl-logseq-portal-header {
+  height: 40px;
+  width: 100%;
+  flex-shrink: 0;
+  background: transparent;
+  display: flex;
+  color: var(--ls-title-text-color);
+  padding: 0px 1rem;
+  align-items: center;
+}
+
+html[data-theme='light'] .logseq-tldraw .tl-logseq-portal-header {
+  backdrop-filter: brightness(0.9);
+}
+
+html[data-theme='dark'] .logseq-tldraw .tl-logseq-portal-header {
+  backdrop-filter: brightness(1.2);
+}

+ 46 - 1
tldraw/demo/src/App.jsx

@@ -1,4 +1,5 @@
 import React from 'react'
+import ReactDOM from 'react-dom'
 import { App as TldrawApp } from 'tldraw-logseq'
 
 const storingKey = 'playground.index'
@@ -53,9 +54,53 @@ const Page = props => {
   )
 }
 
+const ThemeSwitcher = ({ theme, setTheme }) => {
+  const [anchor, setAnchor] = React.useState(null)
+  React.useEffect(() => {
+    if (anchor) {
+      return
+    }
+    let el = document.querySelector('#theme-switcher')
+    if (!el) {
+      el = document.createElement('div')
+      el.id = 'theme-switcher'
+      let timer = setInterval(() => {
+        const statusBarAnchor = document.querySelector('#tl-statusbar-anchor')
+        if (statusBarAnchor) {
+          statusBarAnchor.appendChild(el)
+          setAnchor(el)
+          clearInterval(timer)
+        }
+      }, 50)
+    }
+  })
+
+  React.useEffect(() => {
+    document.documentElement.setAttribute('data-theme', theme)
+  }, [theme])
+
+  if (!anchor) {
+    return null
+  }
+
+  return ReactDOM.createPortal(
+    <button
+      className="flex items-center justify-center mx-2 bg-grey"
+      style={{ fontSize: '1em' }}
+      onClick={() => setTheme(t => (t === 'dark' ? 'light' : 'dark'))}
+    >
+      {theme} theme
+    </button>,
+    anchor
+  )
+}
+
 export default function App() {
+  const [theme, setTheme] = React.useState('dark')
+
   return (
-    <div className="h-screen w-screen">
+    <div className={`h-screen w-screen`}>
+      <ThemeSwitcher theme={theme} setTheme={setTheme} />
       <TldrawApp
         PageComponent={Page}
         searchHandler={q => (q ? list : [])}

+ 1 - 1
tldraw/demo/src/main.jsx

@@ -1,7 +1,7 @@
 import React from 'react'
 import ReactDOM from 'react-dom'
 import 'tldraw-logseq/styles.css'
-import '../../../public/static/css/common.css'
+import '../../../public/static/css/style.css'
 
 import App from './App'
 

+ 7 - 2
tldraw/packages/core/src/lib/shapes/TLShape/TLShape.tsx

@@ -108,7 +108,7 @@ export abstract class TLShape<P extends TLShapeProps = TLShapeProps, M = any> {
   bindingDistance = BINDING_DISTANCE
 
   // For smart shape
-  @observable draft = false
+  @observable private _draft = false
   @observable private isDirty = false
   @observable private lastSerialized: TLShapeModel<P> | undefined
 
@@ -118,8 +118,13 @@ export abstract class TLShape<P extends TLShapeProps = TLShapeProps, M = any> {
     return this.props.id
   }
 
+  @computed
+  get draft() {
+    return this._draft
+  }
+
   @action setDraft(draft: boolean) {
-    this.draft = draft
+    this._draft = draft
   }
 
   @action setIsDirty(isDirty: boolean) {

+ 3 - 1
tldraw/packages/core/src/lib/tools/TLDotTool/states/CreatingState.tsx

@@ -20,13 +20,15 @@ export class CreatingState<
 
   onEnter = () => {
     const { Shape } = this.tool
-    this.offset = [Shape.defaultProps.size[0] / 2, Shape.defaultProps.size[1] / 2]
     const shape = new Shape({
       id: uniqueId(),
       parentId: this.app.currentPage.id,
       point: Vec.sub(this.app.inputs.originPoint, this.offset),
       size: Shape.defaultProps.size,
     } as any)
+    if (Shape.smart) {
+      shape.setDraft(true)
+    }
     this.creatingShape = shape
   }
 

+ 1 - 0
tldraw/packages/core/src/lib/tools/TLSelectTool/states/BrushingState.ts

@@ -48,6 +48,7 @@ export class BrushingState<
           ? BoundsUtils.boundsContain(brushBounds, shape.rotatedBounds)
           : shape.hitTestBounds(brushBounds)
       )
+      .filter(s => !s.draft)
 
     if (shiftKey) {
       if (hits.every(hit => this.initialSelectedShapes.includes(hit))) {

+ 4 - 1
tldraw/packages/core/src/lib/tools/TLSelectTool/states/IdleState.ts

@@ -83,8 +83,11 @@ export class IdleState<
           const { selectionBounds, inputs } = this.app
           if (selectionBounds && PointUtils.pointInBounds(inputs.currentPoint, selectionBounds)) {
             this.tool.transition('pointingShapeBehindBounds', info)
-          } else {
+          } else if (!info.shape.draft) {
             this.tool.transition('pointingShape', info)
+          } else {
+            // as if clicking the canvas
+            this.tool.transition('pointingCanvas')
           }
         }
         break

+ 1 - 0
tldraw/packages/core/src/lib/tools/TLSelectTool/states/PointingCanvasState.ts

@@ -52,6 +52,7 @@ export class PointingCanvasState<
           parentId: this.app.currentPage.id,
           point: [...this.app.inputs.originPoint],
         })
+        shape.setDraft(true)
         this.app.setActivatedShapes([shape.id])
         this.app.currentPage.addShapes(shape)
       }

+ 5 - 4
tldraw/packages/react/src/hooks/useStylesheet.ts

@@ -15,7 +15,7 @@ function makeCssTheme<T = AnyTheme>(prefix: string, theme: T) {
   }, '')
 }
 
-function useTheme<T = AnyTheme>(prefix: string, theme: T, selector = ':root') {
+function useTheme<T = AnyTheme>(prefix: string, theme: T, selector = '.logseq-tldraw') {
   React.useLayoutEffect(() => {
     const style = document.createElement('style')
     const cssTheme = makeCssTheme(prefix, theme)
@@ -72,9 +72,9 @@ const defaultTheme: TLTheme = {
   selectStroke: 'rgb(66, 133, 244)',
   selectFill: 'rgba(65, 132, 244, 0.05)',
   binding: 'rgba(65, 132, 244, 0.5)',
-  background: 'rgb(248, 249, 250)',
-  foreground: 'rgb(51, 51, 51)',
-  grid: 'rgba(144, 144, 144, .9)',
+  background: 'var(--ls-primary-background-color)',
+  foreground: 'var(--ls-secondary-text-color)',
+  grid: 'var(--ls-quaternary-background-color)',
 }
 
 const tlcss = css`
@@ -151,6 +151,7 @@ const tlcss = css`
     background-color: var(--tl-background);
     cursor: var(--tl-cursor) !important;
     box-sizing: border-box;
+    color: var(--tl-foreground);
   }
 
   .tl-overlay {