Merge branch 'master' into feat/slash-command-for-code-block

This commit is contained in:
charlie
2023-06-03 10:22:30 +08:00
28 changed files with 359 additions and 173 deletions

View File

@@ -85,10 +85,10 @@ test('draw a rectangle', async ({ page }) => {
await page.keyboard.type('wr')
await page.mouse.move(bounds.x + 5, bounds.y + 5)
await page.mouse.move(bounds.x + 105, bounds.y + 105)
await page.mouse.down()
await page.mouse.move(bounds.x + 50, bounds.y + 50 )
await page.mouse.move(bounds.x + 150, bounds.y + 150 )
await page.mouse.up()
await page.keyboard.press('Escape')
@@ -114,12 +114,14 @@ test('clone the rectangle', async ({ page }) => {
const canvas = await page.waitForSelector('.logseq-tldraw')
const bounds = (await canvas.boundingBox())!
await page.mouse.move(bounds.x + 20, bounds.y + 20, {steps: 5})
await page.mouse.move(bounds.x + 400, bounds.y + 400)
await page.mouse.move(bounds.x + 120, bounds.y + 120, {steps: 5})
await page.keyboard.down('Alt')
await page.mouse.down()
await page.mouse.move(bounds.x + 100, bounds.y + 100, {steps: 5})
await page.mouse.move(bounds.x + 200, bounds.y + 200, {steps: 5})
await page.mouse.up()
await page.keyboard.up('Alt')
@@ -163,10 +165,10 @@ test('connect rectangles with an arrow', async ({ page }) => {
await page.keyboard.type('wc')
await page.mouse.move(bounds.x + 20, bounds.y + 20)
await page.mouse.move(bounds.x + 120, bounds.y + 120)
await page.mouse.down()
await page.mouse.move(bounds.x + 100, bounds.y + 100, {steps: 5}) // will fail without steps
await page.mouse.move(bounds.x + 200, bounds.y + 200, {steps: 5}) // will fail without steps
await page.mouse.up()
await page.keyboard.press('Escape')
@@ -191,10 +193,15 @@ test('undo the delete action', async ({ page }) => {
})
test('convert the first rectangle to ellipse', async ({ page }) => {
const canvas = await page.waitForSelector('.logseq-tldraw')
const bounds = (await canvas.boundingBox())!
await page.keyboard.press('Escape')
await page.waitForTimeout(1000)
await page.click('.logseq-tldraw .tl-box-container:first-of-type')
await page.mouse.move(0, 0) // move mouse to trigger a rerender of the context bar
await page.mouse.move(bounds.x + 220, bounds.y + 220)
await page.mouse.down()
await page.mouse.up()
await page.mouse.move(bounds.x + 520, bounds.y + 520)
await page.click('.tl-context-bar .tl-geometry-tools-pane-anchor')
await page.click('.tl-context-bar .tl-geometry-toolbar [data-tool=ellipse]')
@@ -223,9 +230,14 @@ test('undo the shape conversion', async ({ page }) => {
})
test('locked elements should not be removed', async ({ page }) => {
const canvas = await page.waitForSelector('.logseq-tldraw')
const bounds = (await canvas.boundingBox())!
await page.keyboard.press('Escape')
await page.waitForTimeout(1000)
await page.click('.logseq-tldraw .tl-box-container:first-of-type')
await page.mouse.move(bounds.x + 220, bounds.y + 220)
await page.mouse.down()
await page.mouse.up()
await page.mouse.move(bounds.x + 520, bounds.y + 520)
await page.keyboard.press(`${modKey}+l`)
await page.keyboard.press('Delete')
await page.keyboard.press(`${modKey}+Shift+l`)
@@ -269,7 +281,7 @@ test('create a block', async ({ page }) => {
const bounds = (await canvas.boundingBox())!
await page.keyboard.type('ws')
await page.mouse.dblclick(bounds.x + 5, bounds.y + 5)
await page.mouse.dblclick(bounds.x + 105, bounds.y + 105)
await page.waitForTimeout(100)
await page.keyboard.type('a')
@@ -304,7 +316,7 @@ test('copy/paste url to create an iFrame shape', async ({ page }) => {
const bounds = (await canvas.boundingBox())!
await page.keyboard.type('wt')
await page.mouse.move(bounds.x + 5, bounds.y + 5)
await page.mouse.move(bounds.x + 105, bounds.y + 105)
await page.mouse.down()
await page.waitForTimeout(100)
@@ -323,7 +335,7 @@ test('copy/paste twitter status url to create a Tweet shape', async ({ page }) =
const bounds = (await canvas.boundingBox())!
await page.keyboard.type('wt')
await page.mouse.move(bounds.x + 5, bounds.y + 5)
await page.mouse.move(bounds.x + 105, bounds.y + 105)
await page.mouse.down()
await page.waitForTimeout(100)
@@ -342,7 +354,7 @@ test('copy/paste youtube video url to create a Youtube shape', async ({ page })
const bounds = (await canvas.boundingBox())!
await page.keyboard.type('wt')
await page.mouse.move(bounds.x + 5, bounds.y + 5)
await page.mouse.move(bounds.x + 105, bounds.y + 105)
await page.mouse.down()
await page.waitForTimeout(100)
@@ -394,8 +406,8 @@ test('quick add another whiteboard', async ({ page }) => {
const canvas = await page.waitForSelector('.logseq-tldraw')
await canvas.dblclick({
position: {
x: 100,
y: 100,
x: 200,
y: 200,
},
})

View File

@@ -115,6 +115,14 @@
"(t title" []
"(t subtitle" [:asset/physical-delete]})
(defn- whiteboard-dicts
[]
(->> (shell {:out :string}
"grep -E -oh" "\\bt\\('[^ ']+" "-r" "tldraw/apps/tldraw-logseq/src/components")
:out
string/split-lines
(map #(keyword (subs % 3)))))
(defn- validate-ui-translations-are-used
"This validation checks to see that translations done by (t ...) are equal to
the ones defined for the default :en lang. This catches translations that have
@@ -129,6 +137,7 @@
string/split-lines
(map #(keyword (subs % 4)))
(concat (mapcat val manual-ui-dicts))
(concat (whiteboard-dicts))
set)
expected-dicts (set (remove #(re-find #"^(command|shortcut)\." (str (namespace %)))
(keys (:en (get-dicts)))))

View File

@@ -30,6 +30,7 @@
(defonce angle-bracket "<")
(defonce hashtag "#")
(defonce colon ":")
(defonce command-trigger "/")
(defonce *current-command (atom nil))
(def query-doc
@@ -52,7 +53,7 @@
"."]])
(defn link-steps []
[[:editor/input (str (state/get-editor-command-trigger) "link")]
[[:editor/input (str command-trigger "link")]
[:editor/show-input [{:command :link
:id :link
:placeholder "Link"
@@ -62,7 +63,7 @@
:placeholder "Label"}]]])
(defn image-link-steps []
[[:editor/input (str (state/get-editor-command-trigger) "link")]
[[:editor/input (str command-trigger "link")]
[:editor/show-input [{:command :image-link
:id :link
:placeholder "Link"
@@ -72,7 +73,7 @@
:placeholder "Label"}]]])
(defn zotero-steps []
[[:editor/input (str (state/get-editor-command-trigger) "zotero")]
[[:editor/input (str command-trigger "zotero")]
[:editor/show-zotero]])
(def *extend-slash-commands (atom []))
@@ -96,19 +97,19 @@
[type]
(let [template (util/format "@@%s: @@"
type)]
[[:editor/input template {:last-pattern (state/get-editor-command-trigger)
[[:editor/input template {:last-pattern command-trigger
:backward-pos 2}]]))
(defn embed-page
[]
(conj
[[:editor/input "{{embed [[]]}}" {:last-pattern (state/get-editor-command-trigger)
[[:editor/input "{{embed [[]]}}" {:last-pattern command-trigger
:backward-pos 4}]]
[:editor/search-page :embed]))
(defn embed-block
[]
[[:editor/input "{{embed (())}}" {:last-pattern (state/get-editor-command-trigger)
[[:editor/input "{{embed (())}}" {:last-pattern command-trigger
:backward-pos 4}]
[:editor/search-block :embed]])
@@ -229,9 +230,9 @@
["Image link" (image-link-steps) "Create a HTTP link to a image"]
(when (state/markdown?)
["Underline" [[:editor/input "<ins></ins>"
{:last-pattern (state/get-editor-command-trigger)
{:last-pattern command-trigger
:backward-pos 6}]] "Create a underline text decoration"])
["Template" [[:editor/input (state/get-editor-command-trigger) nil]
["Template" [[:editor/input command-trigger nil]
[:editor/search-template]] "Insert a created template here"]
(cond
(and (util/electron?) (config/local-db? (state/get-current-repo)))
@@ -239,7 +240,7 @@
["Upload an asset" [[:editor/click-hidden-file-input :id]] "Upload file types like image, pdf, docx, etc.)"])]
;; ["Upload an image" [[:editor/click-hidden-file-input :id]]]
(headings)
@@ -290,12 +291,12 @@
text)) "Draw a graph with Excalidraw"]
["Embed HTML " (->inline "html")]
["Embed Video URL" [[:editor/input "{{video }}" {:last-pattern (state/get-editor-command-trigger)
["Embed Video URL" [[:editor/input "{{video }}" {:last-pattern command-trigger
:backward-pos 2}]]]
["Embed Youtube timestamp" [[:youtube/insert-timestamp]]]
["Embed Twitter tweet" [[:editor/input "{{tweet }}" {:last-pattern (state/get-editor-command-trigger)
["Embed Twitter tweet" [[:editor/input "{{tweet }}" {:last-pattern command-trigger
:backward-pos 2}]]]
["Code block" [[:editor/input "```\n```\n" {:type "block"
@@ -339,7 +340,7 @@
(when-let [input (gdom/getElement id)]
(let [last-pattern (when-not (= last-pattern :skip-check)
(when-not backward-truncate-number
(or last-pattern (state/get-editor-command-trigger))))
(or last-pattern command-trigger)))
edit-content (gobj/get input "value")
current-pos (cursor/pos input)
current-pos (or
@@ -531,7 +532,7 @@
(let [edit-content (gobj/get current-input "value")
current-pos (cursor/pos current-input)
prefix (subs edit-content 0 current-pos)
prefix (util/replace-last (state/get-editor-command-trigger) prefix "" (boolean space?))
prefix (util/replace-last command-trigger prefix "" (boolean space?))
new-value (str prefix
(subs edit-content current-pos))]
(state/set-block-content-and-last-pos! input-id

View File

@@ -385,8 +385,13 @@
(defn preferred-pasting-file [t preferred-pasting-file?]
(toggle "preferred_pasting_file"
(t :settings-page/preferred-pasting-file)
preferred-pasting-file?
[(t :settings-page/preferred-pasting-file)
(ui/tippy {:html (t :settings-page/preferred-pasting-file-hint)
:class "tippy-hover ml-2"
:interactive true
:disabled false}
(svg/info))]
preferred-pasting-file?
config-handler/toggle-preferred-pasting-file!))
(defn auto-expand-row [t auto-expand-block-refs?]

View File

@@ -5,6 +5,7 @@
[frontend.components.export :as export]
[frontend.components.page :as page]
[frontend.config :as config]
[frontend.context.i18n :refer [t]]
[frontend.db.model :as model]
[frontend.handler.editor :as editor-handler]
[frontend.handler.route :as route-handler]
@@ -93,7 +94,8 @@
(def undo (fn [] (history/undo! nil)))
(def redo (fn [] (history/redo! nil)))
(defn get-tldraw-handlers [current-whiteboard-name]
{:search search-handler
{:t (fn [key] (t (keyword key)))
:search search-handler
:queryBlockByUUID (fn [block-uuid]
(clj->js
(model/query-block-by-uuid (parse-uuid block-uuid))))

View File

@@ -92,7 +92,7 @@ nested keys or positional errors e.g. tuples"
(let [body (try (edn/read-string content)
(catch :default _ ::failed-to-detect))
warnings {:editor/command-trigger
"Will no longer be supported soon. Please use '/' and report bugs on it."}]
"is no longer supported. Please use '/' and report bugs on it."}]
(cond
(= body ::failed-to-detect)
(log/info :msg "Skip deprecation check since config is not valid edn")

View File

@@ -1536,7 +1536,7 @@
(if file-obj (.-name file-obj) (if image? "image" "asset"))
image?)
format
{:last-pattern (if drop-or-paste? "" (state/get-editor-command-trigger))
{:last-pattern (if drop-or-paste? "" commands/command-trigger)
:restore? true
:command :insert-asset})))))
(p/finally
@@ -1675,7 +1675,7 @@
last-command (and last-slash-caret-pos (subs edit-content last-slash-caret-pos pos))]
(when (> pos 0)
(or
(and (= (state/get-editor-command-trigger) (util/nth-safe edit-content (dec pos)))
(and (= commands/command-trigger (util/nth-safe edit-content (dec pos)))
@commands/*initial-commands)
(and last-command
(commands/get-matched-commands last-command)))))
@@ -1793,7 +1793,7 @@
id
(get-link format link label)
format
{:last-pattern (str (state/get-editor-command-trigger) "link")
{:last-pattern (str commands/command-trigger "link")
:command :link})))
:image-link (let [{:keys [link label]} m]
@@ -1802,7 +1802,7 @@
id
(get-image-link format link label)
format
{:last-pattern (str (state/get-editor-command-trigger) "link")
{:last-pattern (str commands/command-trigger "link")
:command :image-link})))
nil)
@@ -1879,7 +1879,7 @@
(-> (p/delay 10)
(p/then #(state/pub-event! [:editor/toggle-own-number-list edit-block]))))
(and (= last-input-char (state/get-editor-command-trigger))
(and (= last-input-char commands/command-trigger)
(or (re-find #"(?m)^/" (str (.-value input))) (start-of-new-word? input pos)))
(do
(state/set-editor-action-data! {:pos (cursor/get-caret-pos input)})
@@ -2730,7 +2730,7 @@
(delete-block! repo false))))
(and (> current-pos 1)
(= (util/nth-safe value (dec current-pos)) (state/get-editor-command-trigger)))
(= (util/nth-safe value (dec current-pos)) commands/command-trigger))
(do
(util/stop e)
(commands/restore-state)
@@ -3014,8 +3014,8 @@
(util/event-is-composing? e true)])]
(cond
;; When you type something after /
(and (= :commands (state/get-editor-action)) (not= k (state/get-editor-command-trigger)))
(if (= (state/get-editor-command-trigger) (second (re-find #"(\S+)\s+$" value)))
(and (= :commands (state/get-editor-action)) (not= k commands/command-trigger))
(if (= commands/command-trigger (second (re-find #"(\S+)\s+$" value)))
(state/clear-editor-action!)
(let [matched-commands (get-matched-commands input)]
(if (seq matched-commands)

View File

@@ -426,15 +426,6 @@ should be done through this fn in order to get global config and config defaults
(get-in @state [:me :preferred_format] "markdown")))))
;; TODO: consider adding a pane in Settings to set this through the GUI (rather
;; than having to go through the config.edn file)
(defn get-editor-command-trigger
([] (get-editor-command-trigger (get-current-repo)))
([repo-url]
(or
(:editor/command-trigger (get-config repo-url)) ;; Get from user config
"/"))) ;; Set the default
(defn markdown?
[]
(= (keyword (get-preferred-format))

View File

@@ -192,6 +192,7 @@
:settings-page/auto-expand-block-refs "Expand block references automatically when zoom-in"
:settings-page/custom-date-format "Preferred date format"
:settings-page/custom-date-format-warning "Re-index required! Existing journal references would be broken!"
:settings-page/preferred-pasting-file-hint "When enabled, pasting an image from the internet will download and insert the image. When disabled, it will paste the link to the image."
:settings-page/preferred-file-format "Preferred file format"
:settings-page/preferred-workflow "Preferred workflow"
:settings-page/preferred-pasting-file "Prefer pasting file"
@@ -249,6 +250,89 @@
:search/publishing "Search"
:search "Search or create page"
:whiteboard/link-whiteboard-or-block "Link whiteboard/page/block"
:whiteboard/align-left "Align left"
:whiteboard/align-center-horizontally "Align center horizontally"
:whiteboard/align-right "Align right"
:whiteboard/distribute-horizontally "Distribute horizontally"
:whiteboard/align-top "Align top"
:whiteboard/align-center-vertically "Align center vertically"
:whiteboard/align-bottom "Align bottom"
:whiteboard/distribute-vertically "Distribute vertically"
:whiteboard/pack-into-rectangle "Pack into rectangle"
:whiteboard/zoom-to-fit "Zoom to fit"
:whiteboard/ungroup "Ungroup"
:whiteboard/group "Group"
:whiteboard/cut "Cut"
:whiteboard/copy "Copy"
:whiteboard/paste "Paste"
:whiteboard/paste-as-link "Paste as link"
:whiteboard/export "Export"
:whiteboard/select-all "Select all"
:whiteboard/deselect-all "Deselect all"
:whiteboard/lock "Lock"
:whiteboard/unlock "Unlock"
:whiteboard/delete "Delete"
:whiteboard/flip-horizontally "Flip horizontally"
:whiteboard/flip-vertically "Flip vertically"
:whiteboard/move-to-front "Move to front"
:whiteboard/move-to-back "Move to back"
:whiteboard/dev-print-shape-props "(Dev) Print shape props"
:whiteboard/auto-resize "Auto resize"
:whiteboard/expand "Expand"
:whiteboard/collapse "Collapse"
:whiteboard/website-url "Website url"
:whiteboard/reload "Reload"
:whiteboard/open-website-url "Open website url"
:whiteboard/youtube-url "YouTube url"
:whiteboard/open-youtube-url "Open YouTube url"
:whiteboard/twitter-url "Twitter url"
:whiteboard/open-twitter-url "Open Twitter url"
:whiteboard/fill "Fill"
:whiteboard/stroke-type "Stroke type"
:whiteboard/arrow-head "Arrow head"
:whiteboard/bold "Bold"
:whiteboard/italic "Italic"
:whiteboard/undo "Undo"
:whiteboard/redo "Redo"
:whiteboard/zoom-in "Zoom in"
:whiteboard/zoom-out "Zoom out"
:whiteboard/select "Select"
:whiteboard/pan "Pan"
:whiteboard/add-block-or-page "Add block or page"
:whiteboard/draw "Draw"
:whiteboard/highlight "Highlight"
:whiteboard/eraser "Eraser"
:whiteboard/connector "Connector"
:whiteboard/text "Text"
:whiteboard/color "Color"
:whiteboard/select-custom-color "Select custom color"
:whiteboard/opacity "Opacity"
:whiteboard/extra-small "Extra Small"
:whiteboard/small "Small"
:whiteboard/medium "Medium"
:whiteboard/large "Large"
:whiteboard/extra-large "Extra Large"
:whiteboard/huge "Huge"
:whiteboard/scale-level "Scale level"
:whiteboard/rectangle "Rectangle"
:whiteboard/circle "Circle"
:whiteboard/triangle "Triangle"
:whiteboard/shape "Shape"
:whiteboard/open-page "Open page"
:whiteboard/open-page-in-sidebar "Open page in sidebar"
:whiteboard/remove-link "Remove link"
:whiteboard/link "Link"
:whiteboard/references "References"
:whiteboard/link-to-any-page-or-block "Link to any page or block"
:whiteboard/start-typing-to-search "Start typing to search..."
:whiteboard/new-block-no-colon "New block"
:whiteboard/new-block "New block:"
:whiteboard/new-page "New page:"
:whiteboard/new-whiteboard "New whiteboard"
:whiteboard/search-only-blocks "Search only blocks"
:whiteboard/search-only-pages "Search only pages"
:whiteboard/cache-outdated "Cache is outdated. Please click the 'Re-index' button in the graph's dropdown menu."
:whiteboard/shape-quick-links "Shape Quick Links"
:page-search "Search in the current page"
:graph-search "Search graph"
:home "Home"

View File

@@ -60,7 +60,7 @@
(deftest detect-deprecations
(is (re-find
#":editor/command-trigger.*Will"
#":editor/command-trigger.*is"
(deprecation-warnings-for "{:preferred-workflow :todo :editor/command-trigger \",\"}"))
"Warning when there is a deprecation")

View File

@@ -8,9 +8,13 @@ import { TablerIcon } from '../icons'
import { Button } from '../Button'
import { ZoomMenu } from '../ZoomMenu'
import * as Separator from '@radix-ui/react-separator'
import { LogseqContext } from '../../lib/logseq-context'
export const ActionBar = observer(function ActionBar(): JSX.Element {
const app = useApp<Shape>()
const {
handlers: { t },
} = React.useContext(LogseqContext)
const undo = React.useCallback(() => {
app.api.undo()
@@ -32,20 +36,20 @@ export const ActionBar = observer(function ActionBar(): JSX.Element {
<div className="tl-action-bar" data-html2canvas-ignore="true">
{!app.readOnly && (
<div className="tl-toolbar tl-history-bar">
<Button tooltip="Undo" onClick={undo}>
<Button tooltip={t('whiteboard/undo')} onClick={undo}>
<TablerIcon name="arrow-back-up" />
</Button>
<Button tooltip="Redo" onClick={redo}>
<Button tooltip={t('whiteboard/redo')} onClick={redo}>
<TablerIcon name="arrow-forward-up" />
</Button>
</div>
)}
<div className={`tl-toolbar tl-zoom-bar ${app.readOnly ? '' : 'ml-4'}`}>
<Button tooltip="Zoom in" onClick={zoomIn} id="tl-zoom-in">
<Button tooltip={t('whiteboard/zoom-in')} onClick={zoomIn} id="tl-zoom-in">
<TablerIcon name="plus" />
</Button>
<Button tooltip="Zoom out" onClick={zoomOut} id="tl-zoom-out">
<Button tooltip={t('whiteboard/zoom-out')} onClick={zoomOut} id="tl-zoom-out">
<TablerIcon name="minus" />
</Button>
<Separator.Root className="tl-toolbar-separator" orientation="vertical" />

View File

@@ -92,13 +92,16 @@ function filterShapeByAction<S extends Shape>(type: ContextBarActionType) {
const AutoResizingAction = observer(() => {
const app = useApp<Shape>()
const {
handlers: { t },
} = React.useContext(LogseqContext)
const shapes = filterShapeByAction<LogseqPortalShape | TextShape | HTMLShape>('AutoResizing')
const pressed = shapes.every(s => s.props.isAutoResizing)
return (
<ToggleInput
tooltip="Auto Resize"
tooltip={t('whiteboard/auto-resize')}
toggle={shapes.every(s => s.props.type === 'logseq-portal')}
className="tl-button"
pressed={pressed}
@@ -122,6 +125,9 @@ const AutoResizingAction = observer(() => {
const LogseqPortalViewModeAction = observer(() => {
const app = useApp<Shape>()
const {
handlers: { t },
} = React.useContext(LogseqContext)
const shapes = filterShapeByAction<LogseqPortalShape>('LogseqPortalViewMode')
const collapsed = shapes.every(s => s.collapsed)
@@ -131,7 +137,7 @@ const LogseqPortalViewModeAction = observer(() => {
const tooltip = (
<div className="flex">
{collapsed ? 'Expand' : 'Collapse'}
{collapsed ? t('whiteboard/expand') : t('whiteboard/collapse')}
<KeyboardShortcut
action={collapsed ? 'editor/expand-block-children' : 'editor/collapse-block-children'}
/>
@@ -164,6 +170,9 @@ const ScaleLevelAction = observer(() => {
const IFrameSourceAction = observer(() => {
const app = useApp<Shape>()
const {
handlers: { t },
} = React.useContext(LogseqContext)
const shape = filterShapeByAction<IFrameShape>('IFrameSource')[0]
const handleChange = React.useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
@@ -177,16 +186,20 @@ const IFrameSourceAction = observer(() => {
return (
<span className="flex gap-3">
<Button tooltip="Reload" type="button" onClick={handleReload}>
<Button tooltip={t('whiteboard/reload')} type="button" onClick={handleReload}>
<TablerIcon name="refresh" />
</Button>
<TextInput
title="Website Url"
title={t('whiteboard/website-url')}
className="tl-iframe-src"
value={`${shape.props.url}`}
onChange={handleChange}
/>
<Button tooltip="Open website url" type="button" onClick={() => window.open(shape.props.url)}>
<Button
tooltip={t('whiteboard/open-website-url')}
type="button"
onClick={() => window.open(shape.props.url)}
>
<TablerIcon name="external-link" />
</Button>
</span>
@@ -195,6 +208,9 @@ const IFrameSourceAction = observer(() => {
const YoutubeLinkAction = observer(() => {
const app = useApp<Shape>()
const {
handlers: { t },
} = React.useContext(LogseqContext)
const shape = filterShapeByAction<YouTubeShape>('YoutubeLink')[0]
const handleChange = React.useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
shape.onYoutubeLinkChange(e.target.value)
@@ -204,13 +220,13 @@ const YoutubeLinkAction = observer(() => {
return (
<span className="flex gap-3">
<TextInput
title="YouTube Link"
title={t('whiteboard/youtube-url')}
className="tl-youtube-link"
value={`${shape.props.url}`}
onChange={handleChange}
/>
<Button
tooltip="Open YouTube Link"
tooltip={t('whiteboard/open-youtube-url')}
type="button"
onClick={() => window.logseq?.api?.open_external_link?.(shape.props.url)}
>
@@ -222,6 +238,9 @@ const YoutubeLinkAction = observer(() => {
const TwitterLinkAction = observer(() => {
const app = useApp<Shape>()
const {
handlers: { t },
} = React.useContext(LogseqContext)
const shape = filterShapeByAction<TweetShape>('TwitterLink')[0]
const handleChange = React.useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
shape.onTwitterLinkChange(e.target.value)
@@ -231,13 +250,13 @@ const TwitterLinkAction = observer(() => {
return (
<span className="flex gap-3">
<TextInput
title="Twitter Link"
title={t('whiteboard/twitter-url')}
className="tl-twitter-link"
value={`${shape.props.url}`}
onChange={handleChange}
/>
<Button
tooltip="Open Twitter Link"
tooltip={t('whiteboard/open-twitter-url')}
type="button"
onClick={() => window.logseq?.api?.open_external_link?.(shape.props.url)}
>
@@ -249,6 +268,9 @@ const TwitterLinkAction = observer(() => {
const NoFillAction = observer(() => {
const app = useApp<Shape>()
const {
handlers: { t },
} = React.useContext(LogseqContext)
const shapes = filterShapeByAction<BoxShape | PolygonShape | EllipseShape>('NoFill')
const handleChange = React.useCallback((v: boolean) => {
app.selectedShapesArray.forEach(s => s.update({ noFill: v }))
@@ -259,7 +281,7 @@ const NoFillAction = observer(() => {
return (
<ToggleInput
tooltip="Fill"
tooltip={t('whiteboard/fill')}
className="tl-button"
pressed={noFill}
onPressedChange={handleChange}
@@ -315,6 +337,9 @@ const GeometryAction = observer(() => {
const StrokeTypeAction = observer(() => {
const app = useApp<Shape>()
const {
handlers: { t },
} = React.useContext(LogseqContext)
const shapes = filterShapeByAction<
BoxShape | PolygonShape | EllipseShape | LineShape | PencilShape
>('StrokeType')
@@ -340,7 +365,7 @@ const StrokeTypeAction = observer(() => {
return (
<ToggleGroupInput
title="Stroke Type"
title={t('whiteboard/stroke-type')}
options={StrokeTypeOptions}
value={value}
onValueChange={v => {
@@ -357,6 +382,9 @@ const StrokeTypeAction = observer(() => {
const ArrowModeAction = observer(() => {
const app = useApp<Shape>()
const {
handlers: { t },
} = React.useContext(LogseqContext)
const shapes = filterShapeByAction<LineShape>('ArrowMode')
const StrokeTypeOptions: ToggleGroupInputOption[] = [
@@ -384,7 +412,7 @@ const ArrowModeAction = observer(() => {
return (
<ToggleGroupMultipleInput
title="Arrow Head"
title={t('whiteboard/arrow-head')}
options={StrokeTypeOptions}
value={value}
onValueChange={v => {
@@ -401,6 +429,9 @@ const ArrowModeAction = observer(() => {
const TextStyleAction = observer(() => {
const app = useApp<Shape>()
const {
handlers: { t },
} = React.useContext(LogseqContext)
const shapes = filterShapeByAction<TextShape>('TextStyle')
const bold = shapes.every(s => s.props.fontWeight > 500)
@@ -409,7 +440,7 @@ const TextStyleAction = observer(() => {
return (
<span className="flex gap-1">
<ToggleInput
tooltip="Bold"
tooltip={t('whiteboard/bold')}
className="tl-button"
pressed={bold}
onPressedChange={v => {
@@ -425,7 +456,7 @@ const TextStyleAction = observer(() => {
<TablerIcon name="bold" />
</ToggleInput>
<ToggleInput
tooltip="Italic"
tooltip={t('whiteboard/italic')}
className="tl-button"
pressed={italic}
onPressedChange={v => {

View File

@@ -21,7 +21,9 @@ export const ContextMenu = observer(function ContextMenu({
collisionRef,
}: ContextMenuProps) {
const app = useApp()
const { handlers } = React.useContext(LogseqContext)
const {
handlers: { t },
} = React.useContext(LogseqContext)
const rContent = React.useRef<HTMLDivElement>(null)
const runAndTransition = (f: Function) => {
@@ -64,26 +66,26 @@ export const ContextMenu = observer(function ContextMenu({
<ReactContextMenu.Item>
<div className="tl-menu-button-row pb-0">
<Button
tooltip="Align left"
tooltip={t('whiteboard/align-left')}
onClick={() => runAndTransition(() => app.align(AlignType.Left))}
>
<TablerIcon name="layout-align-left" />
</Button>
<Button
tooltip="Align center horizontally"
tooltip={t('whiteboard/align-center-horizontally')}
onClick={() => runAndTransition(() => app.align(AlignType.CenterHorizontal))}
>
<TablerIcon name="layout-align-center" />
</Button>
<Button
tooltip="Align right"
tooltip={t('whiteboard/align-right')}
onClick={() => runAndTransition(() => app.align(AlignType.Right))}
>
<TablerIcon name="layout-align-right" />
</Button>
<Separator.Root className="tl-toolbar-separator" orientation="vertical" />
<Button
tooltip="Distribute horizontally"
tooltip={t('whiteboard/distribute-horizontally')}
onClick={() =>
runAndTransition(() => app.distribute(DistributeType.Horizontal))
}
@@ -93,26 +95,26 @@ export const ContextMenu = observer(function ContextMenu({
</div>
<div className="tl-menu-button-row pt-0">
<Button
tooltip="Align top"
tooltip={t('whiteboard/align-top')}
onClick={() => runAndTransition(() => app.align(AlignType.Top))}
>
<TablerIcon name="layout-align-top" />
</Button>
<Button
tooltip="Align center vertically"
tooltip={t('whiteboard/align-center-vertically')}
onClick={() => runAndTransition(() => app.align(AlignType.CenterVertical))}
>
<TablerIcon name="layout-align-middle" />
</Button>
<Button
tooltip="Align bottom"
tooltip={t('whiteboard/align-bottom')}
onClick={() => runAndTransition(() => app.align(AlignType.Bottom))}
>
<TablerIcon name="layout-align-bottom" />
</Button>
<Separator.Root className="tl-toolbar-separator" orientation="vertical" />
<Button
tooltip="Distribute vertically"
tooltip={t('whiteboard/distribute-vertically')}
onClick={() =>
runAndTransition(() => app.distribute(DistributeType.Vertical))
}
@@ -127,7 +129,7 @@ export const ContextMenu = observer(function ContextMenu({
onClick={() => runAndTransition(app.packIntoRectangle)}
>
<TablerIcon className="tl-menu-icon" name="layout-grid" />
Pack into rectangle
{t('whiteboard/pack-into-rectangle')}
</ReactContextMenu.Item>
<ReactContextMenu.Separator className="menu-separator" />
</>
@@ -138,7 +140,7 @@ export const ContextMenu = observer(function ContextMenu({
className="tl-menu-item"
onClick={() => runAndTransition(app.api.zoomToSelection)}
>
Zoom to fit
{t('whiteboard/zoom-to-fit')}
<KeyboardShortcut action="whiteboard/zoom-to-fit" />
</ReactContextMenu.Item>
<ReactContextMenu.Separator className="menu-separator" />
@@ -155,7 +157,7 @@ export const ContextMenu = observer(function ContextMenu({
onClick={() => runAndTransition(app.api.unGroup)}
>
<TablerIcon className="tl-menu-icon" name="ungroup" />
Ungroup
{t('whiteboard/ungroup')}
<KeyboardShortcut action="whiteboard/ungroup" />
</ReactContextMenu.Item>
)}
@@ -166,7 +168,7 @@ export const ContextMenu = observer(function ContextMenu({
onClick={() => runAndTransition(app.api.doGroup)}
>
<TablerIcon className="tl-menu-icon" name="group" />
Group
{t('whiteboard/group')}
<KeyboardShortcut action="whiteboard/group" />
</ReactContextMenu.Item>
)}
@@ -181,7 +183,7 @@ export const ContextMenu = observer(function ContextMenu({
onClick={() => runAndTransition(app.cut)}
>
<TablerIcon className="tl-menu-icon" name="cut" />
Cut
{t('whiteboard/cut')}
</ReactContextMenu.Item>
)}
<ReactContextMenu.Item
@@ -189,7 +191,7 @@ export const ContextMenu = observer(function ContextMenu({
onClick={() => runAndTransition(app.copy)}
>
<TablerIcon className="tl-menu-icon" name="copy" />
Copy
{t('whiteboard/copy')}
<KeyboardShortcut action="editor/copy" />
</ReactContextMenu.Item>
</>
@@ -200,7 +202,7 @@ export const ContextMenu = observer(function ContextMenu({
onClick={() => runAndTransition(app.paste)}
>
<TablerIcon className="tl-menu-icon" name="clipboard" />
Paste
{t('whiteboard/paste')}
<div className="tl-menu-right-slot">
<span className="keyboard-shortcut">
<code>{MOD_KEY}</code> <code>v</code>
@@ -213,7 +215,7 @@ export const ContextMenu = observer(function ContextMenu({
className="tl-menu-item"
onClick={() => runAndTransition(() => app.paste(undefined, true))}
>
Paste as link
{t('whiteboard/paste-as-link')}
<div className="tl-menu-right-slot">
<span className="keyboard-shortcut">
<code>{MOD_KEY}</code> <code></code> <code>v</code>
@@ -239,7 +241,7 @@ export const ContextMenu = observer(function ContextMenu({
}
>
<TablerIcon className="tl-menu-icon" name="file-export" />
Export
{t('whiteboard/export')}
<div className="tl-menu-right-slot">
<span className="keyboard-shortcut"></span>
</div>
@@ -251,7 +253,7 @@ export const ContextMenu = observer(function ContextMenu({
className="tl-menu-item"
onClick={() => runAndTransition(app.api.selectAll)}
>
Select all
{t('whiteboard/select-all')}
<KeyboardShortcut action="editor/select-parent" />
</ReactContextMenu.Item>
{app.selectedShapes?.size > 1 && (
@@ -259,29 +261,33 @@ export const ContextMenu = observer(function ContextMenu({
className="tl-menu-item"
onClick={() => runAndTransition(app.api.deselectAll)}
>
Deselect all
</ReactContextMenu.Item>
)}
{!app.readOnly && app.selectedShapes?.size > 0 && app.selectedShapesArray?.some(s => !s.props.isLocked) && (
<ReactContextMenu.Item
className="tl-menu-item"
onClick={() => runAndTransition(() => app.setLocked(true))}
>
<TablerIcon className="tl-menu-icon" name="lock" />
Lock
<KeyboardShortcut action="whiteboard/lock" />
</ReactContextMenu.Item>
)}
{!app.readOnly && app.selectedShapes?.size > 0 && app.selectedShapesArray?.some(s => s.props.isLocked) && (
<ReactContextMenu.Item
className="tl-menu-item"
onClick={() => runAndTransition(() => app.setLocked(false))}
>
<TablerIcon className="tl-menu-icon" name="lock-open" />
Unlock
<KeyboardShortcut action="whiteboard/unlock" />
{t('whiteboard/deselect-all')}
</ReactContextMenu.Item>
)}
{!app.readOnly &&
app.selectedShapes?.size > 0 &&
app.selectedShapesArray?.some(s => !s.props.isLocked) && (
<ReactContextMenu.Item
className="tl-menu-item"
onClick={() => runAndTransition(() => app.setLocked(true))}
>
<TablerIcon className="tl-menu-icon" name="lock" />
{t('whiteboard/lock')}
<KeyboardShortcut action="whiteboard/lock" />
</ReactContextMenu.Item>
)}
{!app.readOnly &&
app.selectedShapes?.size > 0 &&
app.selectedShapesArray?.some(s => s.props.isLocked) && (
<ReactContextMenu.Item
className="tl-menu-item"
onClick={() => runAndTransition(() => app.setLocked(false))}
>
<TablerIcon className="tl-menu-icon" name="lock-open" />
{t('whiteboard/unlock')}
<KeyboardShortcut action="whiteboard/unlock" />
</ReactContextMenu.Item>
)}
{app.selectedShapes?.size > 0 &&
!app.readOnly &&
app.selectedShapesArray?.some(s => !s.props.isLocked) && (
@@ -291,7 +297,7 @@ export const ContextMenu = observer(function ContextMenu({
onClick={() => runAndTransition(app.api.deleteShapes)}
>
<TablerIcon className="tl-menu-icon" name="backspace" />
Delete
{t('whiteboard/delete')}
<KeyboardShortcut action="editor/delete" />
</ReactContextMenu.Item>
{app.selectedShapes?.size > 1 && !app.readOnly && (
@@ -302,14 +308,14 @@ export const ContextMenu = observer(function ContextMenu({
onClick={() => runAndTransition(app.flipHorizontal)}
>
<TablerIcon className="tl-menu-icon" name="flip-horizontal" />
Flip horizontally
{t('whiteboard/flip-horizontally')}
</ReactContextMenu.Item>
<ReactContextMenu.Item
className="tl-menu-item"
onClick={() => runAndTransition(app.flipVertical)}
>
<TablerIcon className="tl-menu-icon" name="flip-vertical" />
Flip vertically
{t('whiteboard/flip-vertically')}
</ReactContextMenu.Item>
</>
)}
@@ -320,14 +326,14 @@ export const ContextMenu = observer(function ContextMenu({
className="tl-menu-item"
onClick={() => runAndTransition(app.bringToFront)}
>
Move to front
{t('whiteboard/move-to-front')}
<KeyboardShortcut action="whiteboard/bring-to-front" />
</ReactContextMenu.Item>
<ReactContextMenu.Item
className="tl-menu-item"
onClick={() => runAndTransition(app.sendToBack)}
>
Move to back
{t('whiteboard/move-to-back')}
<KeyboardShortcut action="whiteboard/send-to-back" />
</ReactContextMenu.Item>
</>
@@ -344,7 +350,7 @@ export const ContextMenu = observer(function ContextMenu({
}
}}
>
(Dev) Print shape props
{t('whiteboard/dev-print-shape-props')}
</ReactContextMenu.Item>
)}
</>

View File

@@ -3,6 +3,8 @@ import type { Side } from '@radix-ui/react-popper'
import { ToolButton } from '../ToolButton'
import * as Popover from '@radix-ui/react-popover'
import { TablerIcon } from '../icons'
import React from 'react'
import { LogseqContext } from '../../lib/logseq-context'
interface GeometryToolsProps extends React.HTMLAttributes<HTMLElement> {
popoverSide?: Side
@@ -12,56 +14,72 @@ interface GeometryToolsProps extends React.HTMLAttributes<HTMLElement> {
}
export const GeometryTools = observer(function GeometryTools({
popoverSide = "left",
popoverSide = 'left',
setGeometry,
activeGeometry,
chevron = true,
...rest}: GeometryToolsProps) {
...rest
}: GeometryToolsProps) {
const {
handlers: { t },
} = React.useContext(LogseqContext)
const geometries = [
{
id: 'box',
icon: 'square',
tooltip: 'Rectangle',
tooltip: t('whiteboard/rectangle'),
},
{
id: 'ellipse',
icon: 'circle',
tooltip: 'Circle',
tooltip: t('whiteboard/circle'),
},
{
id: 'polygon',
icon: 'triangle',
tooltip: 'Triangle',
tooltip: t('whiteboard/triangle'),
},
]
const shapes = {
id: 'shapes',
icon: 'triangle-square-circle',
tooltip: 'Shape',
tooltip: t('whiteboard/shape'),
}
const activeTool = activeGeometry ? geometries.find(geo => geo.id === activeGeometry) : shapes
return (
<Popover.Root>
<Popover.Trigger asChild >
<Popover.Trigger asChild>
<div {...rest} className="tl-geometry-tools-pane-anchor">
<ToolButton {...activeTool} tooltipSide={popoverSide} />
{chevron &&
{chevron && (
<TablerIcon
data-selected={activeGeometry}
className="tl-popover-indicator"
name="chevron-down-left"
/>
}
)}
</div>
</Popover.Trigger>
<Popover.Content className="tl-popover-content" side={popoverSide} sideOffset={15}>
<div className={`tl-toolbar tl-geometry-toolbar ${["left", "right"].includes(popoverSide) ? "flex-col" : "flex-row" }`}>
<div
className={`tl-toolbar tl-geometry-toolbar ${
['left', 'right'].includes(popoverSide) ? 'flex-col' : 'flex-row'
}`}
>
{geometries.map(props => (
<ToolButton key={props.id} id={props.id} icon={props.icon} tooltip={activeGeometry ? props.tooltip : ''} handleClick={setGeometry} tooltipSide={popoverSide} />
<ToolButton
key={props.id}
id={props.id}
icon={props.icon}
tooltip={activeGeometry ? props.tooltip : ''}
handleClick={setGeometry}
tooltipSide={popoverSide}
/>
))}
</div>

View File

@@ -7,9 +7,13 @@ import { GeometryTools } from '../GeometryTools'
import { ColorInput } from '../inputs/ColorInput'
import { ScaleInput } from '../inputs/ScaleInput'
import * as Separator from '@radix-ui/react-separator'
import { LogseqContext } from '../../lib/logseq-context'
export const PrimaryTools = observer(function PrimaryTools() {
const app = useApp()
const {
handlers: { t },
} = React.useContext(LogseqContext)
const handleSetColor = React.useCallback((color: string) => {
app.api.setColor(color)
@@ -37,50 +41,50 @@ export const PrimaryTools = observer(function PrimaryTools() {
<div className="tl-toolbar tl-tools-floating-panel">
<ToolButton
handleClick={() => app.selectTool('select')}
tooltip="Select"
tooltip={t('whiteboard/select')}
id="select"
icon="select-cursor"
/>
<ToolButton
handleClick={() => app.selectTool('move')}
tooltip="Pan"
tooltip={t('whiteboard/pan')}
id="move"
icon={app.isIn('move.panning') ? 'hand-grab' : 'hand-stop'}
/>
<Separator.Root className="tl-toolbar-separator" orientation="horizontal" />
<ToolButton
handleClick={() => app.selectTool('logseq-portal')}
tooltip="Add block or page"
tooltip={t('whiteboard/add-block-or-page')}
id="logseq-portal"
icon="circle-plus"
/>
<ToolButton
handleClick={() => app.selectTool('pencil')}
tooltip="Draw"
tooltip={t('whiteboard/draw')}
id="pencil"
icon="ballpen"
/>
<ToolButton
handleClick={() => app.selectTool('highlighter')}
tooltip="Highlight"
tooltip={t('whiteboard/highlight')}
id="highlighter"
icon="highlight"
/>
<ToolButton
handleClick={() => app.selectTool('erase')}
tooltip="Eraser"
tooltip={t('whiteboard/eraser')}
id="erase"
icon="eraser"
/>
<ToolButton
handleClick={() => app.selectTool('line')}
tooltip="Connector"
tooltip={t('whiteboard/connector')}
id="line"
icon="connector"
/>
<ToolButton
handleClick={() => app.selectTool('text')}
tooltip="Text"
tooltip={t('whiteboard/text')}
id="text"
icon="text"
/>

View File

@@ -8,6 +8,7 @@ import { BlockLink } from '../BlockLink'
export const QuickLinks: TLQuickLinksComponent<Shape> = observer(({ shape }) => {
const app = useApp()
const { handlers } = React.useContext(LogseqContext)
const t = handlers.t
const links = React.useMemo(() => {
const links = [...(shape.props.refs ?? [])].map<[ref: string, showReferenceContent: boolean]>(
// user added links should show the referenced block content
@@ -30,7 +31,7 @@ export const QuickLinks: TLQuickLinksComponent<Shape> = observer(({ shape }) =>
if (links.length === 0) return null
return (
<div className="tl-quick-links" title="Shape Quick Links">
<div className="tl-quick-links" title={t('whiteboard/shape-quick-links')}>
{links.map(([ref, showReferenceContent]) => {
return (
<div key={ref} className="tl-quick-links-row">

View File

@@ -101,6 +101,7 @@ export const LogseqQuickSearch = observer(
)
const rInput = React.useRef<HTMLInputElement>(null)
const { handlers, renderers } = React.useContext(LogseqContext)
const t = handlers.t
const finishSearching = React.useCallback((id: string) => {
onChange(id)
@@ -172,11 +173,11 @@ export const LogseqQuickSearch = observer(
<LogseqTypeTag active type="BA" />
{q.length > 0 ? (
<>
<strong>New block:</strong>
<strong>{t('whiteboard/new-block')}</strong>
{q}
</>
) : (
<strong>New block</strong>
<strong>{t('whiteboard/new-block-no-colon')}</strong>
)}
</div>
),
@@ -195,7 +196,7 @@ export const LogseqQuickSearch = observer(
element: (
<div className="tl-quick-search-option-row">
<LogseqTypeTag active type="PA" />
<strong>New page:</strong>
<strong>{t('whiteboard/new-page')}</strong>
{q}
</div>
),
@@ -210,7 +211,7 @@ export const LogseqQuickSearch = observer(
element: (
<div className="tl-quick-search-option-row">
<LogseqTypeTag active type="WA" />
<strong>New whiteboard:</strong>
<strong>{t('whiteboard/new-whiteboard')}</strong>
{q}
</div>
),
@@ -230,7 +231,7 @@ export const LogseqQuickSearch = observer(
element: (
<div className="tl-quick-search-option-row">
<LogseqTypeTag type="BS" />
Search only blocks
{t('whiteboard/search-only-blocks')}
</div>
),
},
@@ -243,7 +244,7 @@ export const LogseqQuickSearch = observer(
element: (
<div className="tl-quick-search-option-row">
<LogseqTypeTag type="PS" />
Search only pages
{t('whiteboard/search-only-pages')}
</div>
),
}
@@ -302,10 +303,7 @@ export const LogseqQuickSearch = observer(
</div>
</>
) : (
<div className="tl-quick-search-option-row">
Cache is outdated. Please click the 'Re-index' button in the graph's dropdown
menu.
</div>
<div className="tl-quick-search-option-row">{t('whiteboard/cache-outdated')}</div>
),
}
})

View File

@@ -5,6 +5,7 @@ import { TablerIcon } from '../icons'
import { PopoverButton } from '../PopoverButton'
import { Tooltip } from '../Tooltip'
import React from 'react'
import { LogseqContext } from '../../lib/logseq-context'
interface ColorInputProps extends React.HTMLAttributes<HTMLButtonElement> {
color?: string
@@ -22,6 +23,10 @@ export function ColorInput({
setOpacity,
...rest
}: ColorInputProps) {
const {
handlers: { t },
} = React.useContext(LogseqContext)
function renderColor(color?: string) {
return color ? (
<div className="tl-color-bg" style={{ backgroundColor: color }}>
@@ -57,7 +62,7 @@ export function ColorInput({
arrow
side={popoverSide}
label={
<Tooltip content={'Color'} side={popoverSide} sideOffset={14}>
<Tooltip content={t('whiteboard/color')} side={popoverSide} sideOffset={14}>
{renderColor(color)}
</Tooltip>
}
@@ -82,7 +87,7 @@ export function ColorInput({
className="color-input cursor-pointer"
id="tl-custom-color-input"
type="color"
value={isHexColor(color) ? color : "#000000"}
value={isHexColor(color) ? color : '#000000'}
onChange={handleChangeDebounced}
style={{ opacity: isBuiltInColor(color) ? 0 : 1 }}
{...rest}
@@ -90,7 +95,7 @@ export function ColorInput({
</div>
</div>
<label htmlFor="tl-custom-color-input" className="cursor-pointer">
Select custom color
{t('whiteboard/select-custom-color')}
</label>
</div>
@@ -101,7 +106,7 @@ export function ColorInput({
onValueCommit={value => setOpacity(value[0])}
max={1}
step={0.1}
aria-label="Opacity"
aria-label={t('whiteboard/opacity')}
className="tl-slider-root"
>
<Slider.Track className="tl-slider-track">

View File

@@ -2,6 +2,8 @@ import { SelectInput, type SelectOption } from '../inputs/SelectInput'
import type { Side } from '@radix-ui/react-popper'
import type { SizeLevel } from '../../lib'
import { useApp } from '@tldraw/react'
import React from 'react'
import { LogseqContext } from '../../lib/logseq-context'
interface ScaleInputProps extends React.HTMLAttributes<HTMLButtonElement> {
scaleLevel?: SizeLevel
@@ -11,37 +13,40 @@ interface ScaleInputProps extends React.HTMLAttributes<HTMLButtonElement> {
export function ScaleInput({ scaleLevel, compact, popoverSide, ...rest }: ScaleInputProps) {
const app = useApp<Shape>()
const {
handlers: { t },
} = React.useContext(LogseqContext)
const sizeOptions: SelectOption[] = [
{
label: compact ? 'XS' : 'Extra Small',
label: compact ? 'XS' : t('whiteboard/extra-small'),
value: 'xs',
},
{
label: compact ? 'SM' : 'Small',
label: compact ? 'SM' : t('whiteboard/small'),
value: 'sm',
},
{
label: compact ? 'MD' : 'Medium',
label: compact ? 'MD' : t('whiteboard/medium'),
value: 'md',
},
{
label: compact ? 'LG' : 'Large',
label: compact ? 'LG' : t('whiteboard/large'),
value: 'lg',
},
{
label: compact ? 'XL' : 'Extra Large',
label: compact ? 'XL' : t('whiteboard/extra-large'),
value: 'xl',
},
{
label: compact ? 'XXL' : 'Huge',
label: compact ? 'XXL' : t('whiteboard/huge'),
value: 'xxl',
},
]
return (
<SelectInput
tooltip="Scale level"
tooltip={t('whiteboard/scale-level')}
options={sizeOptions}
value={scaleLevel}
popoverSide={popoverSide}

View File

@@ -34,6 +34,7 @@ function ShapeLinkItem({
}) {
const app = useApp<Shape>()
const { handlers } = React.useContext(LogseqContext)
const t = handlers.t
return (
<div className="tl-shape-links-panel-item color-level relative">
@@ -42,12 +43,16 @@ function ShapeLinkItem({
</div>
<div className="flex-1" />
{handlers.getBlockPageName(id) !== app.currentPage.name && (
<Button tooltip="Open Page" type="button" onClick={() => handlers?.redirectToPage(id)}>
<Button
tooltip={t('whiteboard/open-page')}
type="button"
onClick={() => handlers?.redirectToPage(id)}
>
<TablerIcon name="open-as-page" />
</Button>
)}
<Button
tooltip="Open Page in Right Sidebar"
tooltip={t('whiteboard/open-page-in-sidebar')}
type="button"
onClick={() => handlers?.sidebarAddBlock(id, type === 'B' ? 'block' : 'page')}
>
@@ -56,7 +61,7 @@ function ShapeLinkItem({
{onRemove && (
<Button
className="tl-shape-links-panel-item-remove-button"
tooltip="Remove link"
tooltip={t('whiteboard/remove-link')}
type="button"
onClick={onRemove}
>
@@ -76,6 +81,10 @@ export const ShapeLinksInput = observer(function ShapeLinksInput({
onRefsChange,
...rest
}: ShapeLinksInputProps) {
const {
handlers: { t },
} = React.useContext(LogseqContext)
const noOfLinks = refs.length + (pageId ? 1 : 0)
const canAddLink = refs.length === 0
@@ -94,7 +103,7 @@ export const ShapeLinksInput = observer(function ShapeLinksInput({
align="start"
alignOffset={-6}
label={
<Tooltip content={'Link'} sideOffset={14}>
<Tooltip content={t('whiteboard/link')} sideOffset={14}>
<div className="flex gap-1 relative items-center justify-center px-1">
<TablerIcon name={noOfLinks > 0 ? 'link' : 'add-link'} />
{noOfLinks > 0 && <div className="tl-shape-links-count">{noOfLinks}</div>}
@@ -107,7 +116,7 @@ export const ShapeLinksInput = observer(function ShapeLinksInput({
<div className="tl-shape-links-reference-panel">
<div className="text-base inline-flex gap-1 items-center">
<TablerIcon className="opacity-50" name="internal-link" />
References
{t('whiteboard/references')}
</div>
<ShapeLinkItem type={portalType} id={pageId} />
</div>
@@ -115,7 +124,7 @@ export const ShapeLinksInput = observer(function ShapeLinksInput({
<div className="tl-shape-links-panel color-level">
<div className="text-base inline-flex gap-1 items-center">
<TablerIcon className="opacity-50" name="add-link" />
Link to any page or block
{t('whiteboard/link-to-any-page-or-block')}
</div>
{canAddLink && (
@@ -124,7 +133,7 @@ export const ShapeLinksInput = observer(function ShapeLinksInput({
width: 'calc(100% - 46px)',
marginLeft: '46px',
}}
placeholder="Start typing to search..."
placeholder={t('whiteboard/start-typing-to-search')}
onChange={addNewRef}
/>
)}

View File

@@ -42,6 +42,7 @@ export interface LogseqContextValue {
}>
}
handlers: {
t: (key: string) => any
search: (
query: string,
filters: { 'pages?': boolean; 'blocks?': boolean; 'files?': boolean }

View File

@@ -132,7 +132,6 @@ export class TextShape extends TLTextShape<TextShapeProps> {
const handleBlur = React.useCallback(
(e: React.FocusEvent<HTMLTextAreaElement>) => {
if (!isEditing) return
e.currentTarget.setSelectionRange(0, 0)
onEditingEnd?.()
},
[onEditingEnd]

View File

@@ -399,7 +399,6 @@ export class TLApp<
@action addAssets<T extends TLAsset>(assets: T[]): this {
assets.forEach(asset => (this.assets[asset.id] = asset))
this.persist()
return this
}

View File

@@ -19,6 +19,7 @@ export class TLViewport {
static readonly minZoom = 0.1
static readonly maxZoom = 4
static readonly panMultiplier = 0.05
static readonly panThreshold = 100
/* ------------------- Properties ------------------- */
@@ -49,9 +50,11 @@ export class TLViewport {
})
}
panToPointWhenOutOfBounds = (point: number[]) => {
const deltaMax = Vec.sub([this.currentView.maxX, this.currentView.maxY], point)
const deltaMin = Vec.sub([this.currentView.minX, this.currentView.minY], point)
panToPointWhenNearBounds = (point: number[]) => {
const threshold = [TLViewport.panThreshold, TLViewport.panThreshold]
const deltaMax = Vec.sub([this.currentView.maxX, this.currentView.maxY], Vec.add(point, threshold))
const deltaMin = Vec.sub([this.currentView.minX, this.currentView.minY], Vec.sub(point, threshold))
const deltaX = deltaMax[0] < 0 ? deltaMax[0] : deltaMin[0] > 0 ? deltaMin[0] : 0
const deltaY = deltaMax[1] < 0 ? deltaMax[1] : deltaMin[1] > 0 ? deltaMin[1] : 0

View File

@@ -69,7 +69,7 @@ export class BrushingState<
// Select hit shapes
this.app.setSelectedShapes(hits)
}
this.app.viewport.panToPointWhenOutOfBounds(currentPoint)
this.app.viewport.panToPointWhenNearBounds(currentPoint)
}
onPointerUp: TLEvents<S>['pointer'] = () => {

View File

@@ -227,7 +227,7 @@ export class ResizingState<
})
})
this.updateCursor(scaleX, scaleY)
this.app.viewport.panToPointWhenOutOfBounds(currentPoint)
this.app.viewport.panToPointWhenNearBounds(currentPoint)
}
onPointerUp: TLEvents<S>['pointer'] = () => {

View File

@@ -141,7 +141,7 @@ export class TranslatingState<
} = this.app
this.moveSelectedShapesToPointer()
this.app.viewport.panToPointWhenOutOfBounds(currentPoint)
this.app.viewport.panToPointWhenNearBounds(currentPoint)
}
onPointerDown: TLEvents<S>['pointer'] = () => {

View File

@@ -1,5 +1,4 @@
import { observer } from 'mobx-react-lite'
import * as React from 'react'
import { useBoundsEvents } from '../../../../hooks'
interface RotateHandleProps {