mirror of
https://github.com/logseq/logseq.git
synced 2026-05-28 14:39:48 +00:00
feat: swipe to select block
Swipe also prevents scroll.
This commit is contained in:
@@ -2162,8 +2162,7 @@
|
||||
(if collapsed?
|
||||
(editor-handler/expand-block! uuid)
|
||||
(editor-handler/collapse-block! uuid)))
|
||||
(when (util/mobile?)
|
||||
(haptics/haptics :light)))
|
||||
(haptics/haptics))
|
||||
;; debug config context
|
||||
(when (and (state/developer-mode?) (.-metaKey event))
|
||||
(js/console.debug "[block config]==" config)))}
|
||||
@@ -3007,22 +3006,6 @@
|
||||
(swap! *hide-block-refs? not)))}
|
||||
[:span.text-sm block-refs-count'])]))
|
||||
|
||||
(rum/defc block-left-menu < rum/reactive
|
||||
[_config {:block/keys [uuid] :as _block}]
|
||||
[:div.block-left-menu.flex.bg-base-2.rounded-r-md.mr-1
|
||||
[:div.commands-button.w-0.rounded-r-md
|
||||
{:id (str "block-left-menu-" uuid)
|
||||
:style {:max-width 40}}
|
||||
[:div.indent (ui/icon "indent-increase" {:size 18})]]])
|
||||
|
||||
(rum/defc block-right-menu < rum/reactive
|
||||
[_config {:block/keys [uuid] :as _block}]
|
||||
[:div.block-right-menu.flex.bg-base-2.rounded-md.ml-1
|
||||
[:div.commands-button.w-0.rounded-md
|
||||
{:id (str "block-right-menu-" uuid)
|
||||
:style {:max-width 40}}
|
||||
[:div.outdent (ui/icon "indent-decrease" {:size 18})]]])
|
||||
|
||||
(rum/defc block-content-with-error
|
||||
[config block edit-input-id block-id *show-query? editor-box]
|
||||
(let [[editing? set-editing!] (hooks/use-state false)
|
||||
@@ -3482,6 +3465,12 @@
|
||||
::hide-block-refs? (atom default-hide?)
|
||||
::show-query? (atom false)
|
||||
::refs-count *refs-count)))}
|
||||
(mixins/event-mixin
|
||||
(fn [state]
|
||||
(let [*ref (::ref state)]
|
||||
;; React doesn't let us directly control passive via onTouchMove
|
||||
;; So here we listen `touchmove` on the block node
|
||||
(mixins/listen state @*ref "touchmove" block-handler/on-touch-move))))
|
||||
[state container-state repo config* block {:keys [navigating-block navigated? editing? selected?] :as opts}]
|
||||
(let [*ref (::ref state)
|
||||
*hide-block-refs? (get state ::hide-block-refs?)
|
||||
@@ -3524,8 +3513,6 @@
|
||||
db-collapsed?)
|
||||
config (assoc config :collapsed? collapsed?)
|
||||
breadcrumb-show? (:breadcrumb-show? config)
|
||||
*show-left-menu? (::show-block-left-menu? container-state)
|
||||
*show-right-menu? (::show-block-right-menu? container-state)
|
||||
doc-mode? (:document/mode? config)
|
||||
embed? (:embed? config)
|
||||
page-embed? (:page-embed? config)
|
||||
@@ -3567,7 +3554,7 @@
|
||||
:icon-props {:style {:width "1lh"
|
||||
:height "1lh"
|
||||
:font-size (if (:page-title? config) 38 18)}}})])))]
|
||||
[:div.ls-block
|
||||
[:div.ls-block.swipe-item
|
||||
(cond->
|
||||
{:id (str "ls-block-"
|
||||
;; container-id "-"
|
||||
@@ -3582,7 +3569,12 @@
|
||||
(when order-list? " is-order-list")
|
||||
(when (string/blank? title) " is-blank")
|
||||
(when original-block " embed-block"))
|
||||
:haschild (str (boolean has-child?))}
|
||||
:haschild (str (boolean has-child?))
|
||||
:on-touch-start (fn [event uuid] (block-handler/on-touch-start event uuid))
|
||||
:on-touch-end (fn [event]
|
||||
(block-handler/on-touch-end event))
|
||||
:on-touch-cancel (fn [e]
|
||||
(block-handler/on-touch-cancel e))}
|
||||
|
||||
(:property-default-value? config)
|
||||
(assoc :data-is-property-default-value (:property-default-value? config))
|
||||
@@ -3619,13 +3611,6 @@
|
||||
{:style (when (and db-based? (:page-title? config))
|
||||
{:margin-left (if page-icon -36 -30)})
|
||||
:data-has-heading (some-> block (pu/lookup :logseq.property/heading))
|
||||
:on-touch-start (fn [event uuid] (block-handler/on-touch-start event uuid))
|
||||
:on-touch-move (fn [event]
|
||||
(block-handler/on-touch-move event block uuid editing? *show-left-menu? *show-right-menu?))
|
||||
:on-touch-end (fn [event]
|
||||
(block-handler/on-touch-end event block uuid *show-left-menu? *show-right-menu?))
|
||||
:on-touch-cancel (fn [_e]
|
||||
(block-handler/on-touch-cancel *show-left-menu? *show-right-menu?))
|
||||
:on-mouse-enter (fn [e]
|
||||
(block-mouse-over e block *control-show? block-id doc-mode?))
|
||||
:on-mouse-move (fn [e]
|
||||
@@ -3645,9 +3630,6 @@
|
||||
:*control-show? *control-show?
|
||||
:edit? edit?}))))
|
||||
|
||||
(when (and @*show-left-menu? (not in-whiteboard?) (not (or table? property?)))
|
||||
(block-left-menu config block))
|
||||
|
||||
[:div.flex.flex-col.w-full
|
||||
[:div.block-main-content.flex.flex-row.gap-2
|
||||
(when page-icon
|
||||
@@ -3672,10 +3654,7 @@
|
||||
:*show-query? *show-query?}))])]
|
||||
|
||||
(when (and db-based? (not collapsed?) (not (or table? property? (:page-title? config))))
|
||||
(block-positioned-properties config block :block-below))]
|
||||
|
||||
(when (and @*show-right-menu? (not in-whiteboard?) (not (or table? property?)))
|
||||
(block-right-menu config block))])
|
||||
(block-positioned-properties config block :block-below))]])
|
||||
|
||||
(when (and db-based?
|
||||
(not collapsed?)
|
||||
|
||||
@@ -540,6 +540,11 @@
|
||||
|
||||
.ls-block {
|
||||
@apply flex-1 relative py-0.5 transition-[background-color] mx-auto;
|
||||
width: 100%;
|
||||
transform: translateX(0);
|
||||
transition: transform 0.3s ease;
|
||||
will-change: transform;
|
||||
|
||||
container-type: inline-size;
|
||||
container-name: ls-block;
|
||||
|
||||
@@ -1141,3 +1146,7 @@ html.is-mac {
|
||||
@apply mb-2;
|
||||
}
|
||||
}
|
||||
|
||||
.swipe-item {
|
||||
touch-action: pan-y; /* disables horizontal scrolling on touch */
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
[frontend.state :as state]
|
||||
[frontend.util :as util]
|
||||
[frontend.util.file-based.drawer :as drawer]
|
||||
[goog.dom :as gdom]
|
||||
[goog.object :as gobj]
|
||||
[logseq.db :as ldb]
|
||||
[logseq.db.sqlite.util :as sqlite-util]
|
||||
@@ -294,140 +293,92 @@
|
||||
|
||||
(defn on-touch-start
|
||||
[event uuid]
|
||||
(let [target (.-target event)
|
||||
input (state/get-input)
|
||||
(util/stop-propagation event)
|
||||
(let [input (state/get-input)
|
||||
input-id (state/get-edit-input-id)
|
||||
selection-type (.-type (.getSelection js/document))]
|
||||
(reset! *touch-start (js/Date.now))
|
||||
(when-not (and input
|
||||
(string/ends-with? input-id (str uuid)))
|
||||
(state/clear-edit!))
|
||||
(when-not (target-disable-swipe? target)
|
||||
(when (not= selection-type "Range")
|
||||
(when-let [touches (.-targetTouches event)]
|
||||
(when (= (.-length touches) 1)
|
||||
(let [touch (aget touches 0)
|
||||
x (.-clientX touch)
|
||||
y (.-clientY touch)]
|
||||
(reset! *swipe {:x0 x :y0 y :xi x :yi y :tx x :ty y :direction nil}))))))))
|
||||
(when (not= selection-type "Range")
|
||||
(when-let [touches (.-targetTouches event)]
|
||||
(when (= (.-length touches) 1)
|
||||
(let [touch (aget touches 0)
|
||||
x (.-clientX touch)
|
||||
y (.-clientY touch)]
|
||||
(reset! *swipe {:x0 x :y0 y :xi x :yi y :tx x :ty y :direction nil})))))))
|
||||
|
||||
;; FIXME: disable scroll
|
||||
(defn on-touch-move
|
||||
[event block uuid edit? *show-left-menu? *show-right-menu?]
|
||||
(when-let [touches (.-targetTouches event)]
|
||||
(let [selection-type (.-type (.getSelection js/document))]
|
||||
(when-not (= selection-type "Range")
|
||||
(when (or (not (state/editing?))
|
||||
(< (- (js/Date.now) @*touch-start) 600))
|
||||
(when (and (= (.-length touches) 1) @*swipe)
|
||||
(let [{:keys [x0 xi direction]} @*swipe
|
||||
touch (aget touches 0)
|
||||
tx (.-clientX touch)
|
||||
ty (.-clientY touch)
|
||||
direction (if (nil? direction)
|
||||
(if (> tx x0)
|
||||
:right
|
||||
:left)
|
||||
direction)]
|
||||
(swap! *swipe #(-> %
|
||||
(assoc :tx tx)
|
||||
(assoc :ty ty)
|
||||
(assoc :xi tx)
|
||||
(assoc :yi ty)
|
||||
(assoc :direction direction)))
|
||||
(when (< (* (- xi x0) (- tx xi)) 0)
|
||||
[^js goog-event]
|
||||
(let [event (.-event_ goog-event)]
|
||||
(when-let [touches (.-targetTouches event)]
|
||||
(let [selection-type (.-type (.getSelection js/document))
|
||||
target (.-target event)
|
||||
block-container (util/rec-get-node target "ls-block")]
|
||||
(when-not (= selection-type "Range")
|
||||
(when (or (not (state/editing?))
|
||||
(< (- (js/Date.now) @*touch-start) 600))
|
||||
(when (and (= (.-length touches) 1) @*swipe)
|
||||
(let [{:keys [x0 xi direction]} @*swipe
|
||||
touch (aget touches 0)
|
||||
tx (.-clientX touch)
|
||||
ty (.-clientY touch)
|
||||
direction (if (nil? direction)
|
||||
(if (> tx x0)
|
||||
:right
|
||||
:left)
|
||||
direction)]
|
||||
(swap! *swipe #(-> %
|
||||
(assoc :x0 tx)
|
||||
(assoc :y0 ty))))
|
||||
(let [{:keys [x0 y0]} @*swipe
|
||||
dx (- tx x0)
|
||||
dy (- ty y0)]
|
||||
(when (and (< (. js/Math abs dy) 30)
|
||||
(> (. js/Math abs dx) 30))
|
||||
(let [left (gdom/getElement (str "block-left-menu-" uuid))
|
||||
right (gdom/getElement (str "block-right-menu-" uuid))]
|
||||
|
||||
(cond
|
||||
(= direction :right)
|
||||
(do
|
||||
(reset! *show-left-menu? true)
|
||||
(when left
|
||||
(when (>= dx 0)
|
||||
(set! (.. left -style -width) (str dx "px")))
|
||||
(when (< dx 0)
|
||||
(set! (.. left -style -width) (str (max (+ 40 dx) 0) "px")))
|
||||
|
||||
(let [indent (gdom/getFirstElementChild left)]
|
||||
(when (indentable? block)
|
||||
(if (>= (.-clientWidth left) 40)
|
||||
(set! (.. indent -style -opacity) "100%")
|
||||
(set! (.. indent -style -opacity) "30%"))))))
|
||||
|
||||
(= direction :left)
|
||||
(do
|
||||
(reset! *show-right-menu? true)
|
||||
(when right
|
||||
(when (<= dx 0)
|
||||
(set! (.. right -style -width) (str (- dx) "px")))
|
||||
(when (> dx 0)
|
||||
(set! (.. right -style -width) (str (max (- 80 dx) 0) "px")))
|
||||
|
||||
(let [outdent (gdom/getFirstElementChild right)
|
||||
more (when-not edit?
|
||||
(gdom/getLastElementChild right))]
|
||||
(when (and outdent (outdentable? block))
|
||||
(if (and (>= (.-clientWidth right) 40)
|
||||
(< (.-clientWidth right) 80))
|
||||
(set! (.. outdent -style -opacity) "100%")
|
||||
(set! (.. outdent -style -opacity) "30%")))
|
||||
|
||||
(when more
|
||||
(if (>= (.-clientWidth right) 80)
|
||||
(set! (.. more -style -opacity) "100%")
|
||||
(set! (.. more -style -opacity) "30%"))))))
|
||||
:else
|
||||
nil)))))))))))
|
||||
(assoc :tx tx)
|
||||
(assoc :ty ty)
|
||||
(assoc :xi tx)
|
||||
(assoc :yi ty)
|
||||
(assoc :direction direction)))
|
||||
(when (< (* (- xi x0) (- tx xi)) 0)
|
||||
(swap! *swipe #(-> %
|
||||
(assoc :x0 tx)
|
||||
(assoc :y0 ty))))
|
||||
(let [{:keys [x0 y0]} @*swipe
|
||||
dx (- tx x0)
|
||||
dy (- ty y0)]
|
||||
(when (and (< (. js/Math abs dy) 30)
|
||||
(> (. js/Math abs dx) 10)
|
||||
direction)
|
||||
(.preventDefault goog-event)
|
||||
(let [left (if (= direction :right)
|
||||
(if (>= dx 0) (min dx 60) (max dx 0))
|
||||
(if (<= dx 0) (- (min (js/Math.abs dx) 60)) (min dx 60)))]
|
||||
(dom/set-style! block-container :transform (util/format "translateX(%dpx)" left)))))))))))))
|
||||
|
||||
(defn on-touch-end
|
||||
[_event block uuid *show-left-menu? *show-right-menu?]
|
||||
[event]
|
||||
(util/stop-propagation event)
|
||||
(when @*swipe
|
||||
(let [left-menu (gdom/getElement (str "block-left-menu-" uuid))
|
||||
right-menu (gdom/getElement (str "block-right-menu-" uuid))
|
||||
{:keys [x0 tx]} @*swipe
|
||||
dx (- tx x0)]
|
||||
(let [target (.-target event)
|
||||
{:keys [x0 y0 tx ty]} @*swipe
|
||||
dy (- ty y0)
|
||||
dx (- tx x0)
|
||||
block-container (util/rec-get-node target "ls-block")]
|
||||
(try
|
||||
(when (> (. js/Math abs dx) 10)
|
||||
(cond
|
||||
left-menu
|
||||
(when (indentable? block)
|
||||
(haptics/with-haptics-impact
|
||||
(indent-outdent-blocks! [block] true nil)
|
||||
:light))
|
||||
(when (and (> (. js/Math abs dx) (. js/Math abs dy))
|
||||
(> (. js/Math abs dx) 10))
|
||||
(dom/set-style! block-container :transform "translateX(0)")
|
||||
(state/exit-editing-and-set-selected-blocks! [block-container])
|
||||
(haptics/haptics)
|
||||
|
||||
right-menu
|
||||
(when (outdentable? block)
|
||||
(haptics/with-haptics-impact
|
||||
(indent-outdent-blocks! [block] false nil)
|
||||
:light))
|
||||
|
||||
;; TODO: long press to select
|
||||
;; (haptics/with-haptics-impact
|
||||
;; (do (state/set-state! :mobile/show-action-bar? true)
|
||||
;; (state/set-state! :mobile/actioned-block block)
|
||||
;; (select-block! uuid))
|
||||
;; :light)
|
||||
|
||||
:else
|
||||
nil))
|
||||
)
|
||||
(catch :default e
|
||||
(js/console.error e))
|
||||
(finally
|
||||
(reset! *show-left-menu? false)
|
||||
(reset! *show-right-menu? false)
|
||||
(reset! *swipe nil))))))
|
||||
|
||||
(defn on-touch-cancel
|
||||
[*show-left-menu? *show-right-menu?]
|
||||
(reset! *show-left-menu? false)
|
||||
(reset! *show-right-menu? false)
|
||||
[_e]
|
||||
(reset! *swipe nil))
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
(ns frontend.mobile.haptics
|
||||
(:require
|
||||
["@capacitor/haptics" :refer [Haptics ImpactStyle]]
|
||||
[promesa.core :as p]))
|
||||
[frontend.util :as util]))
|
||||
|
||||
(defn haptics
|
||||
[impact-style]
|
||||
(let [style (cond
|
||||
(= impact-style :light)
|
||||
{:style (.-Light ImpactStyle)}
|
||||
([]
|
||||
(haptics :light))
|
||||
([impact-style]
|
||||
(when util/capacitor-new?
|
||||
(let [style (cond
|
||||
(= impact-style :light)
|
||||
{:style (.-Light ImpactStyle)}
|
||||
|
||||
(= impact-style :medium)
|
||||
{:style (.-Medium ImpactStyle)})]
|
||||
(.impact Haptics (clj->js style))))
|
||||
|
||||
(defn with-haptics-impact
|
||||
[result impact-style]
|
||||
(p/do!
|
||||
(haptics impact-style)
|
||||
result))
|
||||
(= impact-style :medium)
|
||||
{:style (.-Medium ImpactStyle)})]
|
||||
(.impact Haptics (clj->js style))))))
|
||||
|
||||
Reference in New Issue
Block a user