diff --git a/CHANGES.md b/CHANGES.md index ab9e7656c..62020d9d8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,7 @@ ### :sparkles: New features - Enhance corner radius behavior [Taiga #2190](https://tree.taiga.io/project/penpot/issue/2190). +- Allow preserve scroll position in interactions [Taiga task #1998](https://tree.taiga.io/project/penpot/task/1998). ### :bug: Bugs fixed diff --git a/common/src/app/common/file_builder.cljc b/common/src/app/common/file_builder.cljc index 0ae1127df..4cf883a97 100644 --- a/common/src/app/common/file_builder.cljc +++ b/common/src/app/common/file_builder.cljc @@ -392,7 +392,8 @@ (defmethod read-action-opts :navigate [interaction-src] - (select-keys interaction-src [:destination])) + (select-keys interaction-src [:destination + :preserve-scroll])) (defmethod read-action-opts :open-overlay [interaction-src] @@ -430,7 +431,8 @@ (let [{:keys [event-type action-type]} (read-classifier interaction-src) {:keys [delay]} (read-event-opts interaction-src) {:keys [destination overlay-pos-type overlay-position url - close-click-outside background-overlay]} (read-action-opts interaction-src) + close-click-outside background-overlay preserve-scroll]} + (read-action-opts interaction-src) interactions (-> (lookup-shape file from-id) :interactions @@ -443,7 +445,8 @@ :overlay-position overlay-position :url url :close-click-outside close-click-outside - :background-overlay background-overlay})))] + :background-overlay background-overlay + :preserve-scroll preserve-scroll})))] (commit-change file {:type :mod-obj diff --git a/common/src/app/common/types/interactions.cljc b/common/src/app/common/types/interactions.cljc index ebbc81e70..7c56113ad 100644 --- a/common/src/app/common/types/interactions.cljc +++ b/common/src/app/common/types/interactions.cljc @@ -64,11 +64,13 @@ (s/def ::url ::us/string) (s/def ::close-click-outside ::us/boolean) (s/def ::background-overlay ::us/boolean) +(s/def ::preserve-scroll ::us/boolean) (defmulti action-opts-spec :action-type) (defmethod action-opts-spec :navigate [_] - (s/keys :req-un [::destination])) + (s/keys :req-un [::destination] + :opt-un [::preserve-scroll])) (defmethod action-opts-spec :open-overlay [_] (s/keys :req-un [::destination @@ -151,7 +153,8 @@ :navigate (assoc interaction :action-type action-type - :destination (get interaction :destination)) + :destination (get interaction :destination) + :preserve-scroll false) (:open-overlay :toggle-overlay) (let [overlay-pos-type (get interaction :overlay-pos-type :center) @@ -196,6 +199,10 @@ (and (has-destination interaction) (some? (:destination interaction)))) +(defn has-preserve-scroll + [interaction] + (= (:action-type interaction) :navigate)) + (defn set-destination [interaction destination] (us/verify ::interaction interaction) @@ -210,6 +217,13 @@ (assoc :overlay-pos-type :center :overlay-position (gpt/point 0 0)))) +(defn set-preserve-scroll + [interaction preserve-scroll] + (us/verify ::interaction interaction) + (us/verify ::us/boolean preserve-scroll) + (assert (has-preserve-scroll interaction)) + (assoc interaction :preserve-scroll preserve-scroll)) + (defn has-url [interaction] (= (:action-type interaction) :open-url)) diff --git a/frontend/resources/styles/main/layouts/handoff.scss b/frontend/resources/styles/main/layouts/handoff.scss index 027bbac0d..917410eca 100644 --- a/frontend/resources/styles/main/layouts/handoff.scss +++ b/frontend/resources/styles/main/layouts/handoff.scss @@ -46,7 +46,7 @@ $width-settings-bar: 16rem; } .handoff-layout { - .viewer-preview { + .viewer-section { flex-wrap: nowrap; } .settings-bar { diff --git a/frontend/resources/styles/main/partials/viewer.scss b/frontend/resources/styles/main/partials/viewer.scss index 346db1c83..34e2c5779 100644 --- a/frontend/resources/styles/main/partials/viewer.scss +++ b/frontend/resources/styles/main/partials/viewer.scss @@ -6,8 +6,8 @@ grid-template-columns: 1fr; } -.viewer-preview { - height: calc(100vh - 40px); +.viewer-section { + height: calc(100vh - 48px); grid-row: 1 / span 2; grid-column: 1 / span 1; diff --git a/frontend/src/app/main/data/viewer.cljs b/frontend/src/app/main/data/viewer.cljs index 15f39ba1f..56ee0c0df 100644 --- a/frontend/src/app/main/data/viewer.cljs +++ b/frontend/src/app/main/data/viewer.cljs @@ -302,6 +302,21 @@ (update [_ state] (assoc-in state [:viewer-local :interactions-show?] false)))) +(defn set-nav-scroll + [scroll] + (ptk/reify ::set-nav-scroll + ptk/UpdateEvent + (update [_ state] + (assoc-in state [:viewer-local :nav-scroll] scroll)))) + +(defn reset-nav-scroll + [] + (ptk/reify ::reset-nav-scroll + ptk/UpdateEvent + (update [_ state] + (d/dissoc-in state [:viewer-local :nav-scroll])))) + + ;; --- Navigation inside page (defn go-to-frame-by-index diff --git a/frontend/src/app/main/ui/shapes/export.cljs b/frontend/src/app/main/ui/shapes/export.cljs index f20428298..45f4b48bd 100644 --- a/frontend/src/app/main/ui/shapes/export.cljs +++ b/frontend/src/app/main/ui/shapes/export.cljs @@ -242,7 +242,8 @@ :penpot:overlay-position-y ((d/nilf get-in) interaction [:overlay-position :y]) :penpot:url (:url interaction) :penpot:close-click-outside ((d/nilf str) (:close-click-outside interaction)) - :penpot:background-overlay ((d/nilf str) (:background-overlay interaction))}])])) + :penpot:background-overlay ((d/nilf str) (:background-overlay interaction)) + :penpot:preserve-scroll ((d/nilf str) (:preserve-scroll interaction))}])])) (mf/defc export-data [{:keys [shape]}] diff --git a/frontend/src/app/main/ui/viewer.cljs b/frontend/src/app/main/ui/viewer.cljs index a17097db4..18d2485be 100644 --- a/frontend/src/app/main/ui/viewer.cljs +++ b/frontend/src/app/main/ui/viewer.cljs @@ -43,6 +43,8 @@ local (mf/deref refs/viewer-local) + nav-scroll (:nav-scroll local) + page-id (or page-id (-> file :data :pages first)) page (mf/use-memo @@ -91,6 +93,14 @@ (fn [] (events/unlistenByKey key1))))) + (mf/use-layout-effect + (mf/deps nav-scroll) + (fn [] + (when (number? nav-scroll) + (let [viewer-section (dom/get-element "viewer-section")] + (st/emit! (dv/reset-nav-scroll)) + (dom/set-scroll-pos! viewer-section nav-scroll))))) + [:div {:class (dom/classnames :force-visible (:show-thumbnails local) :viewer-layout (not= section :handoff) @@ -110,7 +120,7 @@ :show? (:show-thumbnails local false) :page page :index index}] - [:section.viewer-preview + [:section.viewer-section {:id "viewer-section"} (cond (empty? frames) [:section.empty-state diff --git a/frontend/src/app/main/ui/viewer/shapes.cljs b/frontend/src/app/main/ui/viewer/shapes.cljs index dc10b4446..53a574ea0 100644 --- a/frontend/src/app/main/ui/viewer/shapes.cljs +++ b/frontend/src/app/main/ui/viewer/shapes.cljs @@ -44,7 +44,12 @@ (case (:action-type interaction) :navigate (when-let [frame-id (:destination interaction)] - (st/emit! (dv/go-to-frame frame-id))) + (let [viewer-section (dom/get-element "viewer-section") + scroll (if (:preserve-scroll interaction) + (dom/get-scroll-pos viewer-section) + 0)] + (st/emit! (dv/set-nav-scroll scroll) + (dv/go-to-frame frame-id)))) :open-overlay (let [dest-frame-id (:destination interaction) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs index c4e86847e..689e39d94 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs @@ -169,6 +169,7 @@ overlay-pos-type (:overlay-pos-type interaction) close-click-outside? (:close-click-outside interaction false) background-overlay? (:background-overlay interaction false) + preserve-scroll? (:preserve-scroll interaction false) extended-open? (mf/use-state false) @@ -197,6 +198,11 @@ value (when (not= value "") (uuid/uuid value))] (update-interaction index #(cti/set-destination % value)))) + change-preserve-scroll + (fn [event] + (let [value (-> event dom/get-target dom/checked?)] + (update-interaction index #(cti/set-preserve-scroll % value)))) + change-url (fn [event] (let [target (dom/get-target event) @@ -295,6 +301,17 @@ (not= (:id frame) (:frame-id shape))) ; nor a shape to its container frame [:option {:value (str (:id frame))} (:name frame)]))]]) + ; Preserve scroll + (when (cti/has-preserve-scroll interaction) + [:div.interactions-element + [:div.input-checkbox + [:input {:type "checkbox" + :id (str "preserve-" index) + :checked preserve-scroll? + :on-change change-preserve-scroll}] + [:label {:for (str "preserve-" index)} + (tr "workspace.options.interaction-preserve-scroll")]]]) + ; URL (when (cti/has-url interaction) [:div.interactions-element diff --git a/frontend/src/app/util/dom.cljs b/frontend/src/app/util/dom.cljs index a207f570d..82a94e0ed 100644 --- a/frontend/src/app/util/dom.cljs +++ b/frontend/src/app/util/dom.cljs @@ -340,6 +340,14 @@ (defn remove-attribute [^js node ^string attr] (.removeAttribute node attr)) +(defn get-scroll-pos + [element] + (.-scrollTop ^js element)) + +(defn set-scroll-pos! + [element scroll] + (obj/set! ^js element "scrollTop" scroll)) + (defn scroll-into-view! ([element] (.scrollIntoView ^js element false)) diff --git a/frontend/src/app/util/import/parser.cljs b/frontend/src/app/util/import/parser.cljs index 552ca311b..85e976167 100644 --- a/frontend/src/app/util/import/parser.cljs +++ b/frontend/src/app/util/import/parser.cljs @@ -763,7 +763,8 @@ (assoc :delay (get-meta node :delay d/parse-double)) (cti/has-destination interaction) - (assoc :destination (get-meta node :destination uuid/uuid)) + (assoc :destination (get-meta node :destination uuid/uuid) + :preserve-scroll (get-meta node :preserve-scroll str->bool)) (cti/has-url interaction) (assoc :url (get-meta node :url str)) diff --git a/frontend/translations/en.po b/frontend/translations/en.po index a79a85c8a..1414b9602 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -2469,6 +2469,10 @@ msgstr "Top right" msgid "workspace.options.interaction-position" msgstr "Position" +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.interaction-preserve-scroll" +msgstr "Preserve scroll position" + #: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs msgid "workspace.options.interaction-prev-screen" msgstr "Previous screen" @@ -3200,4 +3204,3 @@ msgstr "Update" msgid "workspace.viewport.click-to-close-path" msgstr "Click to close the path" - diff --git a/frontend/translations/es.po b/frontend/translations/es.po index f2561d82e..90d5350c2 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -2464,6 +2464,10 @@ msgstr "Arriba derecha" msgid "workspace.options.interaction-position" msgstr "PosiciĆ³n" +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.interaction-preserve-scroll" +msgstr "Conservar posiciĆ³n de desplazamiento" + #: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs msgid "workspace.options.interaction-prev-screen" msgstr "Pantalla anterior"