From 1aca903449efd663eb9d29c3a5a3dec7669d35bc Mon Sep 17 00:00:00 2001 From: Konstantinos Date: Mon, 24 Apr 2023 16:06:51 +0300 Subject: [PATCH] Fix (Whiteboards): Portal height calculation bug (#9161) * fix: portal height * fix: remove circle button from portals * enhance: remove unnecessary dev tools * enhance: convert collapse btn-group to toggle * enhance: introduce collapse shortcut --- .../src/components/Button/CircleButton.tsx | 18 +--- .../ContextBar/contextBarActionFactory.tsx | 100 ++++------------- .../src/components/Devtools/Devtools.tsx | 63 +---------- .../src/lib/shapes/LogseqPortalShape.tsx | 101 ++++++++---------- tldraw/apps/tldraw-logseq/src/styles.css | 48 +-------- tldraw/packages/core/src/lib/TLApi/TLApi.ts | 8 ++ tldraw/packages/core/src/lib/TLApp/TLApp.ts | 8 ++ 7 files changed, 83 insertions(+), 263 deletions(-) diff --git a/tldraw/apps/tldraw-logseq/src/components/Button/CircleButton.tsx b/tldraw/apps/tldraw-logseq/src/components/Button/CircleButton.tsx index b836b75f259..31a7d99b60f 100644 --- a/tldraw/apps/tldraw-logseq/src/components/Button/CircleButton.tsx +++ b/tldraw/apps/tldraw-logseq/src/components/Button/CircleButton.tsx @@ -1,11 +1,8 @@ -import React from 'react' import { TablerIcon } from '../icons' export const CircleButton = ({ - active, style, icon, - otherIcon, onClick, }: { active?: boolean @@ -14,27 +11,14 @@ export const CircleButton = ({ otherIcon?: string onClick: () => void }) => { - const [recentlyChanged, setRecentlyChanged] = React.useState(false) - - React.useEffect(() => { - setRecentlyChanged(true) - const timer = setTimeout(() => { - setRecentlyChanged(false) - }, 500) - return () => clearTimeout(timer) - }, [active]) - return ( diff --git a/tldraw/apps/tldraw-logseq/src/components/ContextBar/contextBarActionFactory.tsx b/tldraw/apps/tldraw-logseq/src/components/ContextBar/contextBarActionFactory.tsx index 05b77ff9654..42eb0f8fe4a 100644 --- a/tldraw/apps/tldraw-logseq/src/components/ContextBar/contextBarActionFactory.tsx +++ b/tldraw/apps/tldraw-logseq/src/components/ContextBar/contextBarActionFactory.tsx @@ -33,7 +33,7 @@ import { LogseqContext } from '../../lib/logseq-context' export const contextBarActionTypes = [ // Order matters - 'Edit', + 'LogseqPortalViewMode', 'Geometry', 'AutoResizing', 'Swatch', @@ -44,14 +44,12 @@ export const contextBarActionTypes = [ 'YoutubeLink', 'TwitterLink', 'IFrameSource', - 'LogseqPortalViewMode', 'ArrowMode', 'Links', ] as const type ContextBarActionType = typeof contextBarActionTypes[number] const singleShapeActions: ContextBarActionType[] = [ - 'Edit', 'YoutubeLink', 'TwitterLink', 'IFrameSource', @@ -65,7 +63,6 @@ type ShapeType = Shape['props']['type'] export const shapeMapping: Record = { 'logseq-portal': [ 'Swatch', - 'Edit', 'LogseqPortalViewMode', 'ScaleLevel', 'AutoResizing', @@ -74,13 +71,13 @@ export const shapeMapping: Record = { youtube: ['YoutubeLink', 'Links'], tweet: ['TwitterLink', 'Links'], iframe: ['IFrameSource', 'Links'], - box: ['Edit', 'Geometry', 'TextStyle', 'Swatch', 'ScaleLevel', 'NoFill', 'StrokeType', 'Links'], - ellipse: ['Edit', 'Geometry', 'TextStyle', 'Swatch', 'ScaleLevel', 'NoFill', 'StrokeType', 'Links'], - polygon: ['Edit', 'Geometry', 'TextStyle', 'Swatch', 'ScaleLevel', 'NoFill', 'StrokeType', 'Links'], - line: ['Edit', 'TextStyle', 'Swatch', 'ScaleLevel', 'ArrowMode', 'Links'], + box: ['Geometry', 'TextStyle', 'Swatch', 'ScaleLevel', 'NoFill', 'StrokeType', 'Links'], + ellipse: ['Geometry', 'TextStyle', 'Swatch', 'ScaleLevel', 'NoFill', 'StrokeType', 'Links'], + polygon: ['Geometry', 'TextStyle', 'Swatch', 'ScaleLevel', 'NoFill', 'StrokeType', 'Links'], + line: ['TextStyle', 'Swatch', 'ScaleLevel', 'ArrowMode', 'Links'], pencil: ['Swatch', 'Links', 'ScaleLevel'], highlighter: ['Swatch', 'Links', 'ScaleLevel'], - text: ['Edit', 'TextStyle', 'Swatch', 'ScaleLevel', 'AutoResizing', 'Links'], + text: ['TextStyle', 'Swatch', 'ScaleLevel', 'AutoResizing', 'Links'], html: ['ScaleLevel', 'AutoResizing', 'Links'], image: ['Links'], video: ['Links'], @@ -98,54 +95,6 @@ function filterShapeByAction(type: ContextBarActionType) { return unlockedSelectedShapes.filter(shape => shapeMapping[shape.props.type]?.includes(type)) } -const EditAction = observer(() => { - const { - handlers: { isWhiteboardPage, redirectToPage, getRedirectPageName, insertFirstPageBlock }, - } = React.useContext(LogseqContext) - - const app = useApp() - const shape = filterShapeByAction('Edit')[0] - - const iconName = - ('label' in shape.props && shape.props.label) || ('text' in shape.props && shape.props.text) - ? 'forms' - : 'text' - - return ( - - ) -}) - const AutoResizingAction = observer(() => { const app = useApp() const shapes = filterShapeByAction('AutoResizing') @@ -181,30 +130,20 @@ const LogseqPortalViewModeAction = observer(() => { const shapes = filterShapeByAction('LogseqPortalViewMode') const collapsed = shapes.every(s => s.collapsed) - const ViewModeOptions: ToggleGroupInputOption[] = [ - { - value: '1', - icon: 'object-compact', - tooltip: 'Collapse', - }, - { - value: '0', - icon: 'object-expanded', - tooltip: 'Expand', - }, - ] + if (!collapsed && !shapes.every(s => !s.collapsed)) { + return null + } + return ( - { - shapes.forEach(shape => { - shape.toggleCollapsed() - }) - app.persist() - }} - /> + s.props.type === 'logseq-portal')} + className="tl-button" + pressed={collapsed} + onPressedChange={() => app.api.setCollapsed(!collapsed) } + > + + ) }) @@ -527,7 +466,6 @@ const LinksAction = observer(() => { ) }) -contextBarActionMapping.set('Edit', EditAction) contextBarActionMapping.set('Geometry', GeometryAction) contextBarActionMapping.set('AutoResizing', AutoResizingAction) contextBarActionMapping.set('LogseqPortalViewMode', LogseqPortalViewModeAction) diff --git a/tldraw/apps/tldraw-logseq/src/components/Devtools/Devtools.tsx b/tldraw/apps/tldraw-logseq/src/components/Devtools/Devtools.tsx index fd97e1857ca..08031fcd07b 100644 --- a/tldraw/apps/tldraw-logseq/src/components/Devtools/Devtools.tsx +++ b/tldraw/apps/tldraw-logseq/src/components/Devtools/Devtools.tsx @@ -1,59 +1,12 @@ -import { useApp, useRendererContext } from '@tldraw/react' -import { autorun } from 'mobx' +import { useRendererContext } from '@tldraw/react' import { observer } from 'mobx-react-lite' import React from 'react' import ReactDOM from 'react-dom' -import type { Shape } from '../../lib' const printPoint = (point: number[]) => { return `[${point.map(d => d?.toFixed(2) ?? '-').join(', ')}]` } -const HistoryStack = observer(function HistoryStack() { - const app = useApp() - const anchorRef = React.useRef() - const [_, setTick] = React.useState(0) - - React.useEffect(() => { - anchorRef.current = document.createElement('div') - anchorRef.current.style.display = 'contents' - document.body.append(anchorRef.current) - setTick(tick => tick + 1) - return () => { - anchorRef.current?.remove() - } - }, []) - - React.useEffect(() => { - requestAnimationFrame(() => { - anchorRef.current - ?.querySelector(`[data-item-index="${app.history.pointer}"]`) - ?.scrollIntoView() - }) - }, [app.history.pointer]) - - return anchorRef.current - ? ReactDOM.createPortal( -
- {app.history.stack.map((item, i) => ( -
app.history.setPointer(i)} - className="flex items-center rounded-lg px-2 h-[32px] whitespace-nowrap" - > - {item.pages[0].nonce} -
- ))} -
, - anchorRef.current - ) - : null -}) - export const DevTools = observer(() => { const { viewport: { @@ -63,13 +16,9 @@ export const DevTools = observer(() => { inputs, } = useRendererContext() - const canvasAnchorRef = React.useRef() const statusbarAnchorRef = React.useRef() React.useEffect(() => { - const canvasAnchor = document.getElementById('tl-dev-tools-canvas-anchor') - canvasAnchorRef.current = canvasAnchor - const statusbarAnchor = document.getElementById('tl-statusbar-anchor') statusbarAnchorRef.current = statusbarAnchor }, []) @@ -84,15 +33,6 @@ export const DevTools = observer(() => { .map(p => p.join('')) .join('|') - const originPoint = canvasAnchorRef.current - ? ReactDOM.createPortal( - - - , - canvasAnchorRef.current - ) - : null - const rendererStatus = statusbarAnchorRef.current ? ReactDOM.createPortal(
{ return ( <> - {originPoint} {rendererStatus} ) diff --git a/tldraw/apps/tldraw-logseq/src/lib/shapes/LogseqPortalShape.tsx b/tldraw/apps/tldraw-logseq/src/lib/shapes/LogseqPortalShape.tsx index 8da0395bcf2..49497de6c13 100644 --- a/tldraw/apps/tldraw-logseq/src/lib/shapes/LogseqPortalShape.tsx +++ b/tldraw/apps/tldraw-logseq/src/lib/shapes/LogseqPortalShape.tsx @@ -15,7 +15,6 @@ import { action, computed, makeObservable } from 'mobx' import { observer } from 'mobx-react-lite' import * as React from 'react' import type { Shape, SizeLevel } from '.' -import { CircleButton } from '../../components/Button' import { LogseqQuickSearch } from '../../components/QuickSearch' import { useCameraMovingRef } from '../../hooks/useCameraMoving' import { LogseqContext } from '../logseq-context' @@ -149,8 +148,7 @@ export class LogseqPortalShape extends TLBoxShape { return this.props.blockType === 'B' ? this.props.compact : this.props.collapsed } - @action toggleCollapsed = async () => { - const collapsed = !this.collapsed + @action setCollapsed = async (collapsed: boolean) => { if (this.props.blockType === 'B') { this.update({ compact: collapsed }) this.canResize[1] = !collapsed @@ -192,28 +190,30 @@ export class LogseqPortalShape extends TLBoxShape { const [size, setSize] = React.useState<[number, number]>([0, 0]) const app = useApp() React.useEffect(() => { - if (ref?.current) { - const el = selector ? ref.current.querySelector(selector) : ref.current - if (el) { - const updateSize = () => { - const { width, height } = el.getBoundingClientRect() - const bound = Vec.div([width, height], app.viewport.camera.zoom) as [number, number] - setSize(bound) - return bound - } - updateSize() - // Hacky, I know 🤨 - this.getInnerHeight = () => updateSize()[1] - const resizeObserver = new ResizeObserver(() => { + setTimeout(() => { + if (ref?.current) { + const el = selector ? ref.current.querySelector(selector) : ref.current + if (el) { + const updateSize = () => { + const { width, height } = el.getBoundingClientRect() + const bound = Vec.div([width, height], app.viewport.camera.zoom) as [number, number] + setSize(bound) + return bound + } updateSize() - }) - resizeObserver.observe(el) - return () => { - resizeObserver.disconnect() + // Hacky, I know 🤨 + this.getInnerHeight = () => updateSize()[1] + const resizeObserver = new ResizeObserver(() => { + updateSize() + }) + resizeObserver.observe(el) + return () => { + resizeObserver.disconnect() + } } } - } - return () => {} + return () => {} + }, 10); }, [ref, selector]) return size } @@ -494,41 +494,30 @@ export class LogseqPortalShape extends TLBoxShape { placeholder="Create or search your graph..." /> ) : ( - <> -
- {!this.props.compact && !targetNotFound && ( - - {this.props.blockType === 'P' ? ( - - ) : ( - - )} - - )} - {targetNotFound &&
Target not found
} - {showingPortal && } -
- {!app.readOnly && !isLocked && ( - +
+ {!this.props.compact && !targetNotFound && ( + + {this.props.blockType === 'P' ? ( + + ) : ( + + )} + )} - + {targetNotFound &&
Target not found
} + {showingPortal && } +
)}
diff --git a/tldraw/apps/tldraw-logseq/src/styles.css b/tldraw/apps/tldraw-logseq/src/styles.css index b882c530534..798d48aeb98 100644 --- a/tldraw/apps/tldraw-logseq/src/styles.css +++ b/tldraw/apps/tldraw-logseq/src/styles.css @@ -547,7 +547,7 @@ button.tl-select-input-trigger { } .tl-circle-button { - @apply absolute flex items-center justify-center transition-all rounded-full shadow; + @apply absolute flex items-center justify-center rounded-full shadow; color: var(--ls-primary-text-color); background-color: var(--ls-secondary-background-color); @@ -557,60 +557,14 @@ button.tl-select-input-trigger { width: 34px; border: 2px solid var(--ls-secondary-background-color); top: 2px; - transition-delay: 0; .tie { transform: translateY(-100%); } - &[data-active='false']:hover:not([data-recently-changed='true']) { - .tie { - transform: translateY(0); - - &:first-of-type { - opacity: 0.6; - } - } - } - - &[data-active='true'] { - background-color: var(--ls-active-primary-color); - color: var(--ls-block-highlight-color); - border: 2px solid var(--ls-active-primary-color); - - .tie { - transform: translateY(0); - - &:last-of-type { - opacity: 0.6; - } - } - - &:hover:not([data-recently-changed='true']) { - color: var(--ls-primary-text-color); - background-color: var(--ls-secondary-background-color); - - .tie { - transform: translateY(-100%); - } - } - } - .tl-circle-button-icons-wrapper { @apply flex flex-col; } - - i.tie { - transition: transform 0.2s ease-in-out; - transition-delay: 0; - } - - .tl-circle-button-icons-wrapper[data-icons-count='2'] { - position: relative; - width: 22px; - height: 22px; - overflow: hidden; - } } .tl-quick-search-input-container { diff --git a/tldraw/packages/core/src/lib/TLApi/TLApi.ts b/tldraw/packages/core/src/lib/TLApi/TLApi.ts index c53c3cc4e87..38c1f9f7e98 100644 --- a/tldraw/packages/core/src/lib/TLApi/TLApi.ts +++ b/tldraw/packages/core/src/lib/TLApi/TLApi.ts @@ -442,4 +442,12 @@ export class TLApi { + shapes.forEach(shape => { + if (shape.props.type === 'logseq-portal') + shape.setCollapsed(collapsed) + }) + this.app.persist() + } } diff --git a/tldraw/packages/core/src/lib/TLApp/TLApp.ts b/tldraw/packages/core/src/lib/TLApp/TLApp.ts index 052d6156c50..c662297dcab 100644 --- a/tldraw/packages/core/src/lib/TLApp/TLApp.ts +++ b/tldraw/packages/core/src/lib/TLApp/TLApp.ts @@ -116,6 +116,14 @@ export class TLApp< keys: 'shift+2', fn: () => this.api.zoomToSelection(), }, + { + keys: 'mod+up', + fn: () => this.api.setCollapsed(true), + }, + { + keys: 'mod+down', + fn: () => this.api.setCollapsed(false), + }, { keys: 'mod+-', fn: () => this.api.zoomOut(),