Browse Source

improve animations

scheinriese 1 week ago
parent
commit
9df5f8b0aa

+ 89 - 47
deps/shui/src/logseq/shui/shortcut/v2.cljs

@@ -81,32 +81,42 @@
 
 (defn- normalize-binding
   "Normalizes a shortcut binding to a string format for data attributes.
-   Examples: 'cmd+k', 'shift+cmd+k', 'cmd up'"
+   Examples: 'cmd+k', 'shift+cmd+k', 'cmd up'
+   Handles 'mod' -> 'meta' on Mac, 'ctrl' on Windows/Linux for consistency with shortcut system."
   [binding]
-  (cond
-    (string? binding)
-    (string/lower-case (string/trim binding))
-    
-    (coll? binding)
-    (let [first-item (first binding)
-          keys (flatten-keys binding)
-          normalize-key (fn [k]
-                         (cond
-                           (string? k) (string/lower-case k)
-                           (keyword? k) (name k)
-                           (symbol? k) (name k)
-                           (number? k) (str k)
-                           :else (str k)))]
-      (string/join "+" (map normalize-key keys)))
-    
-    (keyword? binding)
-    (name binding)
-    
-    (symbol? binding)
-    (name binding)
-    
-    :else
-    (str binding)))
+  (let [normalize-key (fn [k]
+                       (cond
+                         (string? k) (let [lowered (string/lower-case k)]
+                                      ;; Convert 'mod' to 'meta' on Mac, 'ctrl' on Windows/Linux
+                                      ;; to match what the shortcut system uses
+                                      (if (= lowered "mod")
+                                        (if mac? "meta" "ctrl")
+                                        lowered))
+                         (keyword? k) (name k)
+                         (symbol? k) (name k)
+                         (number? k) (str k)
+                         :else (str k)))]
+    (cond
+      (string? binding)
+      (let [trimmed (string/trim binding)
+            normalized (string/lower-case trimmed)]
+        ;; Handle 'mod' in strings like "mod+k"
+        (if (string/includes? normalized "mod")
+          (string/replace normalized "mod" (if mac? "meta" "ctrl"))
+          normalized))
+      
+      (coll? binding)
+      (let [keys (flatten-keys binding)]
+        (string/join "+" (map normalize-key keys)))
+      
+      (keyword? binding)
+      (name binding)
+      
+      (symbol? binding)
+      (name binding)
+      
+      :else
+      (str binding))))
 
 (defn- detect-style
   "Automatically detects style from shortcut format.
@@ -140,7 +150,7 @@
 
 (defn shortcut-press!
   "Central helper to trigger key press animation.
-   Finds all nodes with matching data-shortcut-binding and toggles pressed class.
+   Finds all nodes with matching data-shortcut-binding and animates individual keys.
    Optionally highlights parent row.
    
    Args:
@@ -150,24 +160,46 @@
   ([binding highlight-row?]
    (let [normalized (normalize-binding binding)
          selector (str "[data-shortcut-binding=\"" normalized "\"]")
-         elements (.querySelectorAll js/document selector)]
-     (doseq [^js el (array-seq elements)]
-       (.add (.-classList el) "shui-shortcut-key-pressed")
-       (when highlight-row?
-         (let [^js row (or (.closest el ".shui-shortcut-row")
-                            (.-parentElement el))]
-           (when row
-             (.add (.-classList row) "shui-shortcut-row--pressed"))))
-       ;; Auto-reset after animation duration
-       (js/setTimeout
-        (fn []
-          (.remove (.-classList el) "shui-shortcut-key-pressed")
-          (when highlight-row?
-            (let [^js row (or (.closest el ".shui-shortcut-row")
-                              (.-parentElement el))]
-              (when row
-                (.remove (.-classList row) "shui-shortcut-row--pressed")))))
-        160)))))
+         containers (.querySelectorAll js/document selector)]
+     (doseq [^js container (array-seq containers)]
+       ;; Find all individual keys within this container
+       (let [keys (.querySelectorAll container "kbd.shui-shortcut-key")]
+         (if (> (.-length keys) 0)
+           ;; Animate individual keys
+           (doseq [^js key-el (array-seq keys)]
+             (.add (.-classList key-el) "shui-shortcut-key-pressed")
+             (when highlight-row?
+               (let [^js row (or (.closest container ".shui-shortcut-row")
+                                 (.-parentElement container))]
+                 (when row
+                   (.add (.-classList row) "shui-shortcut-row--pressed"))))
+             ;; Auto-reset after animation duration
+             (js/setTimeout
+              (fn []
+                (.remove (.-classList key-el) "shui-shortcut-key-pressed")
+                (when highlight-row?
+                  (let [^js row (or (.closest container ".shui-shortcut-row")
+                                    (.-parentElement container))]
+                    (when row
+                      (.remove (.-classList row) "shui-shortcut-row--pressed")))))
+              160))
+           ;; Fallback: animate container if no keys found
+           (do
+             (.add (.-classList container) "shui-shortcut-key-pressed")
+             (when highlight-row?
+               (let [^js row (or (.closest container ".shui-shortcut-row")
+                                  (.-parentElement container))]
+                 (when row
+                   (.add (.-classList row) "shui-shortcut-row--pressed"))))
+             (js/setTimeout
+              (fn []
+                (.remove (.-classList container) "shui-shortcut-key-pressed")
+                (when highlight-row?
+                  (let [^js row (or (.closest container ".shui-shortcut-row")
+                                    (.-parentElement container))]
+                    (when row
+                      (.remove (.-classList row) "shui-shortcut-row--pressed")))))
+              160))))))))
 
 (rum/defc combo-keys
   "Renders combo keys (simultaneous key combinations) with separator."
@@ -252,8 +284,10 @@
    - :aria-label - accessibility label for container
    - :aria-hidden? - if true, hides from screen readers (default: false for decorative hints)
    - :animate-on-press? - if true, enables press animation (default: true)
-   - :glow? - if true, adds inner glow effect to combo/separate keys (default: true)"
-  [shortcut & {:keys [style size theme interactive? aria-label aria-hidden? animate-on-press? glow?]
+   - :glow? - if true, adds inner glow effect to combo/separate keys (default: true)
+   - :raw-binding - raw binding format for data-shortcut-binding (for animation matching).
+                    If not provided, will normalize from shortcut prop."
+  [shortcut & {:keys [style size theme interactive? aria-label aria-hidden? animate-on-press? glow? raw-binding]
                :or {style :auto
                     size :xs
                     interactive? false
@@ -293,6 +327,14 @@
                      
                      :else
                      [(str binding)])
+              ;; Use raw-binding if provided, otherwise normalize from binding
+              binding-for-data (if raw-binding
+                                 (if (coll? raw-binding)
+                                   (if (= (count raw-binding) 1)
+                                     (first raw-binding)
+                                     raw-binding)
+                                   raw-binding)
+                                 binding)
               render-fn (case detected-style
                           :combo combo-keys
                           :separate separate-keys
@@ -307,5 +349,5 @@
            (when (< 0 index)
              [:span.text-gray-11.text-sm {:key (str "sep-" index)
                                            :style {:margin "0 4px"}} "|"])
-           (render-fn keys binding opts)])))))
+           (render-fn keys binding-for-data opts)])))))
 

+ 3 - 0
src/main/frontend/components/cmdk/core.cljs

@@ -826,6 +826,9 @@
                                     (util/stop-propagation e))
       (and meta? (= keyname "o"))
       (open-current-item-link state)
+      (= keyname "/") (do
+                        (shui/shortcut-press! "/" true)
+                        nil)  ; Don't prevent default, allow typing
       :else nil)))
 
 (defn- keyup-handler

+ 4 - 4
src/main/frontend/components/settings.cljs

@@ -219,7 +219,7 @@
                 true)]]
    (when (not (or (util/mobile?) (mobile-util/native-platform?)))
      [:div {:style {:text-align "right"}}
-      (ui/render-keyboard-shortcut (shortcut-helper/gen-shortcut-seq :ui/toggle-brackets))])])
+      (ui/render-keyboard-shortcut (shortcut-helper/gen-shortcut-seq :ui/toggle-brackets) :shortcut-id :ui/toggle-brackets)])])
 
 (defn toggle-wide-mode-row [t wide-mode?]
   [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-center
@@ -233,7 +233,7 @@
                 true)]]
    (when (not (or (util/mobile?) (mobile-util/native-platform?)))
      [:div {:style {:text-align "right"}}
-      (ui/render-keyboard-shortcut (shortcut-helper/gen-shortcut-seq :ui/toggle-wide-mode))])])
+      (ui/render-keyboard-shortcut (shortcut-helper/gen-shortcut-seq :ui/toggle-wide-mode) :shortcut-id :ui/toggle-wide-mode)])])
 
 (defn editor-font-family-row [t font]
   [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4
@@ -370,7 +370,7 @@
     (row-with-button-action {:left-label (t :right-side-bar/switch-theme (string/capitalize switch-theme))
                              :-for       "toggle_theme"
                              :action     pick-theme
-                             :desc       (ui/render-keyboard-shortcut (shortcut-helper/gen-shortcut-seq :ui/toggle-theme))})))
+                             :desc       (ui/render-keyboard-shortcut (shortcut-helper/gen-shortcut-seq :ui/toggle-theme) :shortcut-id :ui/toggle-theme)})))
 
 (rum/defc accent-color-row < rum/reactive
   [_in-modal?]
@@ -426,7 +426,7 @@
    (accent-color-row true)])
 
 (defn date-format-row [t preferred-date-format]
-  [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-center
+  [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-center
    [:label.block.text-sm.font-medium.leading-5.opacity-70
     {:for "custom_date_format"}
     (t :settings-page/custom-date-format)

+ 3 - 1
src/main/frontend/components/shortcut.cljs

@@ -482,4 +482,6 @@
                                   :white-space "nowrap"}}
                          (shui/shortcut
                           (string/join " | " (map #(dh/binding-for-display id %) binding))
-                          {:size :md :interactive? true})])]]))))])])]]))
+                          {:size :md 
+                           :interactive? true
+                           :raw-binding binding})])]]))))])])]]))

+ 13 - 2
src/main/frontend/modules/shortcut/core.cljs

@@ -12,7 +12,8 @@
             [frontend.util :as util]
             [goog.events :as events]
             [goog.ui.KeyboardShortcutHandler.EventType :as EventType]
-            [lambdaisland.glogi :as log])
+            [lambdaisland.glogi :as log]
+            [logseq.shui.ui :as shui])
   (:import [goog.events KeyCodes KeyNames]
            [goog.ui KeyboardShortcutHandler]))
 
@@ -135,8 +136,18 @@
     (let [f (fn [e]
               (let [id (keyword (.-identifier e))
                     shortcut-map (dh/shortcuts-map-by-handler-id handler-id state) ;; required to get shortcut map dynamically
-                    dispatch-fn (get shortcut-map id)]
+                    dispatch-fn (get shortcut-map id)
+                    binding (dh/shortcut-binding id)]
                 (state/set-state! :editor/latest-shortcut id)
+                ;; Trigger animation for visible shortcuts
+                (when binding
+                  (let [bindings (if (coll? binding) binding [binding])]
+                    (doseq [b bindings]
+                      (when b
+                        ;; Use the raw binding, not decorated, for matching data attributes
+                        (try
+                          (shui/shortcut-press! b true)
+                          (catch :default _e nil))))))
                 ;; trigger fn
                 (when dispatch-fn
                   (plugin-handler/hook-lifecycle-fn! id dispatch-fn e))))

+ 15 - 2
src/main/frontend/ui.cljs

@@ -21,6 +21,7 @@
             [frontend.mobile.util :as mobile-util]
             [frontend.modules.shortcut.config :as shortcut-config]
             [frontend.modules.shortcut.core :as shortcut]
+            [frontend.modules.shortcut.data-helper :as shortcut-dh]
             [frontend.modules.shortcut.utils :as shortcut-utils]
             [frontend.rum :as r]
             [frontend.state :as state]
@@ -201,15 +202,27 @@
           (dropdown-content-wrapper dropdown-state close-fn modal-content modal-class {:z-index z-index}))))]))
 
 ;; `sequence` can be a list of symbols, a list of strings, or a string
-(defn render-keyboard-shortcut [sequence & {:as opts}]
+;; If `shortcut-id` is provided, uses raw binding from shortcut system for data attribute matching
+(defn render-keyboard-shortcut [sequence & {:keys [shortcut-id] :as opts}]
   (let [sequence (if (string? sequence)
                    (-> sequence ;; turn string into sequence
                        (string/trim)
                        (string/lower-case)
                        (string/split #" "))
                    sequence)
+        ;; Get raw binding for data attribute matching
+        raw-binding (if shortcut-id
+                      ;; Get raw binding from shortcut system
+                      (shortcut-dh/shortcut-binding shortcut-id)
+                      ;; Otherwise use sequence as-is (it's already the raw format)
+                      (if (and (coll? sequence) (every? string? sequence))
+                        sequence
+                        (if (string? sequence)
+                          [sequence]
+                          sequence)))
         opts (merge {:interactive? false
-                     :aria-hidden? true} opts)]
+                     :aria-hidden? true
+                     :raw-binding raw-binding} opts)]
     [:span.keyboard-shortcut
      (shui/shortcut sequence opts)]))