mirror of
https://github.com/logseq/logseq.git
synced 2026-05-22 11:44:10 +00:00
Merge branch 'master' into feat/slash-command-for-code-block
This commit is contained in:
@@ -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,
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -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)))))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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?]
|
||||
|
||||
@@ -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))))
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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"
|
||||
/>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
),
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -42,6 +42,7 @@ export interface LogseqContextValue {
|
||||
}>
|
||||
}
|
||||
handlers: {
|
||||
t: (key: string) => any
|
||||
search: (
|
||||
query: string,
|
||||
filters: { 'pages?': boolean; 'blocks?': boolean; 'files?': boolean }
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'] = () => {
|
||||
|
||||
@@ -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'] = () => {
|
||||
|
||||
@@ -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'] = () => {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import * as React from 'react'
|
||||
import { useBoundsEvents } from '../../../../hooks'
|
||||
|
||||
interface RotateHandleProps {
|
||||
|
||||
Reference in New Issue
Block a user