diff --git a/CHANGES.md b/CHANGES.md index 410eb0f4f..85b8eaf72 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,8 +3,10 @@ ## :rocket: Next ### :boom: Breaking changes + ### :sparkles: New features +- Add new options for zoom widget in workspace and viewer mode [Taiga #896](https://tree.taiga.io/project/penpot/us/896). - Allow decimals on stroke width and positions [Taiga #2035](https://tree.taiga.io/project/penpot/issue/2035). - Ability to ignore background when exporting an artboard [Taiga #1395](https://tree.taiga.io/project/penpot/us/1395). - Show color hex or name on hover [Taiga #2413](https://tree.taiga.io/project/penpot/us/2413). @@ -72,7 +74,6 @@ - Explain folders in components (by @candideu) [Penpot-docs #42](https://github.com/penpot/penpot-docs/pull/42). - Readability improvements of user guide (by @PaulSchulz) [Penpot-docs #50](https://github.com/penpot/penpot-docs/pull/50). - # 1.10.4-beta ### :sparkles: Enhacements @@ -85,7 +86,6 @@ - Minor fix on how file changes log is persisted. - Fix many issues on error reporting. - # 1.10.3-beta ### :sparkles: Enhacements @@ -120,14 +120,12 @@ - Update log4j2 dependency. - # 1.10.1-beta ### :bug: Bugs fixed - Fix problems with team management [#1353](https://github.com/penpot/penpot/issues/1353) - ## 1.10.0-beta ### :boom: Breaking changes @@ -232,8 +230,6 @@ - To the translation community for the hard work on making penpot available on so many languages. - - ## 1.8.4-alpha ### :bug: Bugs fixed @@ -261,7 +257,6 @@ - Set proper environment variable on docker images for chrome executable. - Fix internal metrics on websocket connections. - ## 1.8.0-alpha ### :boom: Breaking changes @@ -303,12 +298,13 @@ - Fix problem while moving imported SVG's [#1199](https://github.com/penpot/penpot/issues/1199) ### :arrow_up: Deps updates + ### :boom: Breaking changes + ### :heart: Community contributions by (Thank you!) - eduayme [#1129](https://github.com/penpot/penpot/pull/1129). - ## 1.7.4-alpha ### :bug: Bugs fixed @@ -316,14 +312,12 @@ - Fix demo user creation (self-hosted only) - Add better ldap response validation and reporting (self-hosted only) - ## 1.7.3-alpha ### :bug: Bugs fixed - Fix font uploading issue on Windows. - ## 1.7.2-alpha ### :sparkles: New features @@ -347,7 +341,6 @@ - soultipsy [#1100](https://github.com/penpot/penpot/pull/1100) - ## 1.7.1-alpha ### :bug: Bugs fixed @@ -357,7 +350,6 @@ - Fix issue on undo page deletion. - Fix some issues related to constraints. - ## 1.7.0-alpha ### :sparkles: New features @@ -392,7 +384,6 @@ - Fix header partially visible on fullscreen viewer mode [Taiga #1875](https://tree.taiga.io/project/penpot/issue/1875) - Fix dynamic alignment enabled with hidden objects [#1063](https://github.com/penpot/penpot/issues/1063) - ## 1.6.5-alpha ### :bug: Bugs fixed @@ -403,8 +394,8 @@ ### :sparkles: Minor improvements -- Decrease default bulk buffers on storage tasks. -- Reduce file_change preserve interval to 24h. +- Decrease default bulk buffers on storage tasks. +- Reduce file_change preserve interval to 24h. ### :bug: Bugs fixed @@ -417,7 +408,6 @@ - Properly handle nil values on `update-shapes` function. - Replace frame term usage by artboard on viewer app. - ## 1.6.3-alpha ### :bug: Bugs fixed @@ -444,7 +434,6 @@ - Minor fix on previous commit. - Minor improvements on svg uploading on libraries. - ## 1.6.1-alpha ### :bug: Bugs fixed @@ -460,7 +449,6 @@ - Improve editor lifecycle management. - Make the navigation async by default. - ## 1.6.0-alpha ### :sparkles: New features @@ -475,7 +463,6 @@ - Use shift instead of ctrl/cmd to keep aspect ratio [Taiga 1697](https://tree.taiga.io/project/penpot/issue/1697). - New translations: Portuguese (Brazil) and Romanias. - ### :bug: Bugs fixed - Remove interactions when the destination artboard is deleted [Taiga #1656](https://tree.taiga.io/project/penpot/issue/1656). @@ -493,7 +480,6 @@ - Update exporter dependencies (puppeteer), that fixes some unexpected exceptions. - Update string manipulation library. - ### :boom: Breaking changes - The OIDC setting `PENPOT_OIDC_SCOPES` has changed the default semantics. Before this @@ -504,7 +490,6 @@ - Translations: Portuguese (Brazil) and Romanias. - ## 1.5.4-alpha ### :bug: Bugs fixed @@ -512,7 +497,6 @@ - Fix issues on group rendering. - Fix problem with text editing auto-height [Taiga #1683](https://tree.taiga.io/project/penpot/issue/1683) - ## 1.5.3-alpha ### :bug: Bugs fixed @@ -536,7 +520,6 @@ - Increase default team invitation token expiration to 48h. - Fix wrong error message when an expired token is used. - ## 1.5.0-alpha ### :sparkles: New features @@ -582,7 +565,6 @@ - madmath03 (by [Monogramm](https://github.com/Monogramm)) [#807](https://github.com/penpot/penpot/pull/807) - zzkt [#814](https://github.com/penpot/penpot/pull/814) - ## 1.4.1-alpha ### :bug: Bugs fixed @@ -594,7 +576,6 @@ - Fix incorrect state management of user lang selection. - Fix email validation usability issue on team invitation lightbox. - ## 1.4.0-alpha ### :sparkles: New features @@ -617,7 +598,6 @@ - Replace Slate-Editor with DraftJS [Taiga #1346](https://tree.taiga.io/project/penpot/us/1346) - Set proper page title [Taiga #1377](https://tree.taiga.io/project/penpot/us/1377) - ### :bug: Bugs fixed - Disable buttons in view mode for users without permissions [Taiga #1328](https://tree.taiga.io/project/penpot/issue/1328) @@ -660,13 +640,11 @@ (example `:username`) instead of `$`. The main reason is avoid unnecessary conflict with bash interpolation. - ### :arrow_up: Deps updates - Update backend to JDK16. - Update exporter nodejs to v14.16.0 - ### :heart: Community contributions by (Thank you!) - iblueer [#726](https://github.com/penpot/penpot/pull/726) @@ -674,7 +652,6 @@ - girafic [#748](https://github.com/penpot/penpot/pull/748) - mbrksntrk [#794](https://github.com/penpot/penpot/pull/794) - ## 1.3.0-alpha ### :sparkles: New features @@ -690,7 +667,6 @@ - Disable groups interactions when holding "Ctrl" key (deep selection) - New action in context menu to "edit" some shapes (bound to key "Enter") - ### :bug: Bugs fixed - Add more improvements to french translation strings [#591](https://github.com/penpot/penpot/pull/591) @@ -711,13 +687,11 @@ - Properly mark profile auth backend (on first register/ auth with 3rd party auth provider). - Refactor LDAP auth backend. - ### :heart: Community contributions by (Thank you!) - girafic [#538](https://github.com/penpot/penpot/pull/654) - arkhi [#591](https://github.com/penpot/penpot/pull/591) - ## 1.2.0-alpha ### :sparkles: New features @@ -732,7 +706,6 @@ - Show a pixel grid when zoom greater than 800% [#519](https://github.com/penpot/penpot/discussions/519) - Fix behavior of select all command when there are objects outside frames [Taiga #1209](https://tree.taiga.io/project/penpot/issue/1209) - ### :bug: Bugs fixed - Fix 404 when access shared link [#615](https://github.com/penpot/penpot/issues/615) @@ -768,7 +741,6 @@ - Improved MacOS shortcuts and helpers - Small changes to shape creation - ## 1.0.0-alpha Initial release diff --git a/frontend/resources/styles/main/partials/zoom-widget.scss b/frontend/resources/styles/main/partials/zoom-widget.scss index be6d3a704..c6ea675ba 100644 --- a/frontend/resources/styles/main/partials/zoom-widget.scss +++ b/frontend/resources/styles/main/partials/zoom-widget.scss @@ -33,15 +33,62 @@ display: flex; padding: $size-2; + &.separator { + border-top: 1px solid $color-gray-10; + padding: 0px; + margin: 2px; + height: 0; + } + + &.basic-zoom-bar { + cursor: auto; + display: flex; + justify-content: space-between; + + &:hover { + background-color: $color-white; + } + } + span { color: $color-gray-20; font-size: $fs14; margin-left: auto; + + &.zoom-btns { + display: flex; + margin-left: 2px; + color: $color-gray-60; + + p { + margin-bottom: 0; + font-size: $fs14; + padding: 0 3px; + } + } } &:hover { background-color: $color-primary-lighter; } + + button { + cursor: pointer; + background-color: $color-white; + border: none; + &:hover { + color: $color-primary; + } + } + .reset-btn { + color: $color-primary-dark; + } + .zoom-size { + min-width: 40px; + display: flex; + align-items: center; + justify-content: flex-end; + } } } } diff --git a/frontend/src/app/main/data/viewer.cljs b/frontend/src/app/main/data/viewer.cljs index 0503dc5f1..33690b5c0 100644 --- a/frontend/src/app/main/data/viewer.cljs +++ b/frontend/src/app/main/data/viewer.cljs @@ -26,6 +26,7 @@ (def ^:private default-local-state {:zoom 1 + :fullscreen? false :interactions-mode :hide :interactions-show? false :comments-mode :all @@ -209,17 +210,57 @@ (update [_ state] (assoc-in state [:viewer-local :zoom] 1)))) -(def zoom-to-50 - (ptk/reify ::zoom-to-50 +(def zoom-to-fit + (ptk/reify ::zoom-to-fit ptk/UpdateEvent (update [_ state] - (assoc-in state [:viewer-local :zoom] 0.5)))) + (let [page-id (get-in state [:route :query-params :page-id]) + frame-idx (get-in state [:route :query-params :index]) + srect (get (nth (get-in state [:viewer :pages page-id :frames]) frame-idx) :selrect) + original-size (get-in state [:viewer-local :viewport-size]) + wdiff (/ (:width original-size) (:width srect)) + hdiff (/ (:height original-size) (:height srect)) + minzoom (min wdiff hdiff)] + (-> state + (assoc-in [:viewer-local :zoom] minzoom) + (assoc-in [:viewer-local :zoom-type] :fit)))))) -(def zoom-to-200 - (ptk/reify ::zoom-to-200 +(def zoom-to-fill + (ptk/reify ::zoom-to-fill ptk/UpdateEvent (update [_ state] - (assoc-in state [:viewer-local :zoom] 2)))) + (let [page-id (get-in state [:route :query-params :page-id]) + frame-idx (get-in state [:route :query-params :index]) + srect (get (nth (get-in state [:viewer :pages page-id :frames]) frame-idx) :selrect) + original-size (get-in state [:viewer-local :viewport-size]) + wdiff (/ (:width original-size) (:width srect)) + hdiff (/ (:height original-size) (:height srect)) + maxzoom (max wdiff hdiff)] + (-> state + (assoc-in [:viewer-local :zoom] maxzoom) + (assoc-in [:viewer-local :zoom-type] :fill)))))) + +(def toggle-zoom-style + (ptk/reify ::toggle-zoom-style + ptk/WatchEvent + (watch [_ state _] + (let [zoom-type (get-in state [:viewer-local :zoom-type])] + (if (= zoom-type :fit) + (rx/of zoom-to-fill) + (rx/of zoom-to-fit)))))) + +(def toggle-fullscreen + (ptk/reify ::toggle-fullscreen + ptk/UpdateEvent + (update [_ state] + (update-in state [:viewer-local :fullscreen?] not)))) + +(defn set-viewport-size + [{:keys [size]}] + (ptk/reify ::set-viewport-size + ptk/UpdateEvent + (update [_ state] + (assoc-in state [:viewer-local :viewport-size] size)))) ;; --- Local State Management @@ -408,7 +449,7 @@ [state frame position close-click-outside background-overlay animation] (cond-> state :always - (update-in [:viewer-local :overlays] conj + (update-in [:viewer-local :overlays] conj {:frame frame :position position :close-click-outside close-click-outside @@ -588,14 +629,14 @@ (assoc-in state [:viewer-local :overlays] [])) ptk/WatchEvent - (watch [_ state _] - (let [route (:route state) - pparams (:path-params route) - qparams (-> (:query-params route) - (assoc :index 0) - (assoc :page-id page-id)) - rname (get-in route [:data :name])] - (rx/of (rt/nav rname pparams qparams)))))) + (watch [_ state _] + (let [route (:route state) + pparams (:path-params route) + qparams (-> (:query-params route) + (assoc :index 0) + (assoc :page-id page-id)) + rname (get-in route [:data :name])] + (rx/of (rt/nav rname pparams qparams)))))) (defn go-to-workspace ([] (go-to-workspace nil)) diff --git a/frontend/src/app/main/data/viewer/shortcuts.cljs b/frontend/src/app/main/data/viewer/shortcuts.cljs index 87e714d61..1a4dab457 100644 --- a/frontend/src/app/main/data/viewer/shortcuts.cljs +++ b/frontend/src/app/main/data/viewer/shortcuts.cljs @@ -23,17 +23,17 @@ :command (ds/c-mod "a") :fn (st/emitf (dv/select-all))} - :zoom-50 {:tooltip (ds/shift "0") + :reset-zoom {:tooltip (ds/shift "0") :command "shift+0" - :fn (st/emitf dv/zoom-to-50)} - - :reset-zoom {:tooltip (ds/shift "1") - :command "shift+1" :fn (st/emitf dv/reset-zoom)} - :zoom-200 {:tooltip (ds/shift "2") - :command "shift+2" - :fn (st/emitf dv/zoom-to-200)} + :toggle-zoom-style {:tooltip "F" + :command "f" + :fn (st/emitf dv/toggle-zoom-style)} + + :toogle-fullscreen {:tooltip (ds/shift "F") + :command "shift+f" + :fn (st/emitf dv/toggle-fullscreen)} :next-frame {:tooltip ds/left-arrow :command "left" diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index bcf1bb0ac..fce8352a2 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -263,7 +263,7 @@ (when-not (contains? (get-in state [:workspace-data :pages-index]) page-id) (let [default-page-id (get-in state [:workspace-data :pages 0])] (rx/of (go-to-page default-page-id))))) - + ptk/UpdateEvent (update [_ state] (let [;; we maintain a cache of page state for user convenience @@ -455,7 +455,7 @@ :y (+ (:y srect) (/ (- (:height srect) height) 2))))))) (setup [state local] - (if (and (:vport local) (:vbox local)) + (if (and (:vbox local) (:vport local)) (update* local) (initialize state local)))] @@ -755,7 +755,7 @@ :shapes [id]})) selected) - uchanges (mapv (fn [id] + uchanges (mapv (fn [id] (let [obj (get objects id)] {:type :mov-objects :parent-id (:parent-id obj) @@ -763,7 +763,7 @@ :page-id page-id :shapes [id] :index (cp/position-on-parent id objects)})) - selected)] + selected)] ;; TODO: maybe missing the :reg-objects event? (rx/of (dch/commit-changes {:redo-changes rchanges :undo-changes uchanges @@ -1223,15 +1223,15 @@ (ptk/reify ::toggle-propotion-lock ptk/WatchEvent (watch [_ state _] - (let [page-id (:current-page-id state) - objects (wsh/lookup-page-objects state page-id) - selected (wsh/lookup-selected state) - selected-obj (-> (map #(get objects %) selected)) - multi (attrs/get-attrs-multi selected-obj [:proportion-lock]) - multi? (= :multiple (:proportion-lock multi))] - (if multi? - (rx/of (dch/update-shapes selected #(assoc % :proportion-lock true))) - (rx/of (dch/update-shapes selected #(update % :proportion-lock not)))))))) + (let [page-id (:current-page-id state) + objects (wsh/lookup-page-objects state page-id) + selected (wsh/lookup-selected state) + selected-obj (-> (map #(get objects %) selected)) + multi (attrs/get-attrs-multi selected-obj [:proportion-lock]) + multi? (= :multiple (:proportion-lock multi))] + (if multi? + (rx/of (dch/update-shapes selected #(assoc % :proportion-lock true))) + (rx/of (dch/update-shapes selected #(update % :proportion-lock not)))))))) ;; --- Update Shape Flags @@ -1256,8 +1256,8 @@ (ptk/reify ::toggle-visibility-selected ptk/WatchEvent (watch [_ state _] - (let [selected (wsh/lookup-selected state)] - (rx/of (dch/update-shapes selected #(update % :hidden not))))))) + (let [selected (wsh/lookup-selected state)] + (rx/of (dch/update-shapes selected #(update % :hidden not))))))) (defn toggle-lock-selected [] @@ -1417,12 +1417,12 @@ (defn go-to-dashboard-fonts [] - (ptk/reify ::go-to-dashboard-fonts - ptk/WatchEvent - (watch [_ state _] - (let [team-id (:current-team-id state)] - (rx/of ::dwp/force-persist - (rt/nav :dashboard-fonts {:team-id team-id})))))) + (ptk/reify ::go-to-dashboard-fonts + ptk/WatchEvent + (watch [_ state _] + (let [team-id (:current-team-id state)] + (rx/of ::dwp/force-persist + (rt/nav :dashboard-fonts {:team-id team-id})))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Context Menu @@ -1601,9 +1601,9 @@ paste-image-str) (rx/first) (rx/catch - (fn [err] - (js/console.error "Clipboard error:" err) - (rx/empty))))) + (fn [err] + (js/console.error "Clipboard error:" err) + (rx/empty))))) (catch :default e (let [data (ex-data e)] (if (:not-implemented data) @@ -1760,7 +1760,7 @@ (cond-> ;; if foreign instance, detach the shape - (foreign-instance? shape paste-objects state) + (foreign-instance? shape paste-objects state) (dissoc :component-id :component-file :component-root? @@ -1933,10 +1933,10 @@ (assoc :frame-id frame-id) (gsh/setup-selrect))] (rx/of - (dwu/start-undo-transaction) - (dwc/add-shape shape) - (dwc/move-shapes-into-frame (:id shape) selected) - (dwu/commit-undo-transaction)))))))) + (dwu/start-undo-transaction) + (dwc/add-shape shape) + (dwc/move-shapes-into-frame (:id shape) selected) + (dwu/commit-undo-transaction)))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index a456a1477..f93a53ae9 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -322,3 +322,7 @@ (def users (l/derived :users st/state)) +(def fullscreen? + (l/derived (fn [state] + (get-in state [:viewer-local :fullscreen?] [])) + st/state)) diff --git a/frontend/src/app/main/ui.cljs b/frontend/src/app/main/ui.cljs index ab8dbeef4..c6392cc26 100644 --- a/frontend/src/app/main/ui.cljs +++ b/frontend/src/app/main/ui.cljs @@ -11,7 +11,6 @@ [app.main.store :as st] [app.main.ui.auth :refer [auth]] [app.main.ui.auth.verify-token :refer [verify-token]] - [app.main.ui.components.fullscreen :as fs] [app.main.ui.context :as ctx] [app.main.ui.cursors :as c] [app.main.ui.dashboard :refer [dashboard]] @@ -62,8 +61,7 @@ [:h1 "Cursors"] [:& c/debug-preview] [:h1 "Icons"] - [:& i/debug-icons-preview] - ]) + [:& i/debug-icons-preview]]) (:dashboard-search :dashboard-projects @@ -78,8 +76,7 @@ #_[:div.modal-wrapper #_[:& app.main.ui.onboarding/onboarding-templates-modal] #_[:& app.main.ui.onboarding/onboarding-modal] - #_[:& app.main.ui.onboarding/onboarding-team-modal] - ] + #_[:& app.main.ui.onboarding/onboarding-team-modal]] (when-let [props (some-> profile (get :props {}))] (cond (and cf/onboarding-form-id @@ -104,14 +101,13 @@ (let [{:keys [query-params path-params]} route {:keys [index share-id section page-id] :or {section :interactions}} query-params {:keys [file-id]} path-params] - [:& fs/fullscreen-wrapper {} - (if (:token query-params) - [:& viewer/breaking-change-notice] - [:& viewer/viewer-page {:page-id page-id - :file-id file-id - :section section - :index index - :share-id share-id}])]) + (if (:token query-params) + [:& viewer/breaking-change-notice] + [:& viewer/viewer-page {:page-id page-id + :file-id file-id + :section section + :index index + :share-id share-id}])) :render-object (do diff --git a/frontend/src/app/main/ui/components/fullscreen.cljs b/frontend/src/app/main/ui/components/fullscreen.cljs deleted file mode 100644 index ff06bcbbe..000000000 --- a/frontend/src/app/main/ui/components/fullscreen.cljs +++ /dev/null @@ -1,53 +0,0 @@ -;; This Source Code Form is subject to the terms of the Mozilla Public -;; License, v. 2.0. If a copy of the MPL was not distributed with this -;; file, You can obtain one at http://mozilla.org/MPL/2.0/. -;; -;; Copyright (c) UXBOX Labs SL - -(ns app.main.ui.components.fullscreen - (:require - [app.util.dom :as dom] - [app.util.webapi :as wapi] - [rumext.alpha :as mf])) - -(def fullscreen-context - (mf/create-context)) - -(mf/defc fullscreen-wrapper - [{:keys [children] :as props}] - (let [container (mf/use-ref) - state (mf/use-state (dom/fullscreen?)) - - change - (mf/use-callback - (fn [_] - (let [val (dom/fullscreen?)] - (reset! state val)))) - - manager - (mf/use-memo - (mf/deps @state) - (fn [] - (specify! state - cljs.core/IFn - (-invoke - ([_ val] - (if val - (wapi/request-fullscreen (mf/ref-val container)) - (wapi/exit-fullscreen)))))))] - - ;; NOTE: the user interaction with F11 keyboard hot-key does not - ;; emits the `fullscreenchange` event; that event is emitted only - ;; when API is used. There are no way to detect the F11 behavior - ;; in a uniform cross browser way. - - (mf/use-effect - (fn [] - (.addEventListener js/document "fullscreenchange" change) - (fn [] - (.removeEventListener js/document "fullscreenchange" change)))) - - [:div.fullscreen-wrapper {:ref container :class (dom/classnames :fullscreen @state)} - [:& (mf/provider fullscreen-context) {:value manager} - children]])) - diff --git a/frontend/src/app/main/ui/hooks.cljs b/frontend/src/app/main/ui/hooks.cljs index 683e27c6c..9280fc14a 100644 --- a/frontend/src/app/main/ui/hooks.cljs +++ b/frontend/src/app/main/ui/hooks.cljs @@ -91,9 +91,9 @@ (fn [] (some-> (:subscr @state) rx/unsub!) (swap! state (fn [state] - (-> state - (cancel-timer) - (dissoc :over :subscr))))) + (-> state + (cancel-timer) + (dissoc :over :subscr))))) subscribe-to-drag-end (fn [] diff --git a/frontend/src/app/main/ui/viewer.cljs b/frontend/src/app/main/ui/viewer.cljs index 752c06262..ea1fedd09 100644 --- a/frontend/src/app/main/ui/viewer.cljs +++ b/frontend/src/app/main/ui/viewer.cljs @@ -26,6 +26,7 @@ [app.main.ui.viewer.thumbnails :refer [thumbnails-panel]] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] + [app.util.webapi :as wapi] [goog.events :as events] [rumext.alpha :as mf])) @@ -72,7 +73,7 @@ zoom (:zoom local) frames (:frames page) frame (get frames index) - + fullscreen? (mf/deref refs/fullscreen?) overlays (:overlays local) orig-frame @@ -84,12 +85,12 @@ (fn [] (calculate-size frame zoom))) orig-size (mf/use-memo - (mf/deps orig-frame zoom) - (fn [] (when orig-frame (calculate-size orig-frame zoom)))) + (mf/deps orig-frame zoom) + (fn [] (when orig-frame (calculate-size orig-frame zoom)))) wrapper-size (mf/use-memo - (mf/deps size orig-size zoom) - (fn [] (calculate-wrapper size orig-size zoom))) + (mf/deps size orig-size zoom) + (fn [] (calculate-wrapper size orig-size zoom))) interactions-mode (:interactions-mode local) @@ -103,8 +104,15 @@ close-overlay (mf/use-callback - (fn [frame] - (st/emit! (dv/close-overlay (:id frame)))))] + (fn [frame] + (st/emit! (dv/close-overlay (:id frame))))) + + set-up-new-size + (mf/use-callback + (fn [_] + (let [viewer-section (dom/get-element "viewer-section") + size (dom/get-client-size viewer-section)] + (st/emit! (dv/set-viewport-size {:size size})))))] (hooks/use-shortcuts ::viewer sc/shortcuts) @@ -125,73 +133,92 @@ (events/unlistenByKey key1))))) (mf/use-layout-effect - (mf/deps nav-scroll) - (fn [] - ;; Set scroll position after navigate - (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))))) + (fn [] + (set-up-new-size) + (.addEventListener js/window "resize" set-up-new-size) + (fn [] + (.removeEventListener js/window "resize" set-up-new-size)))) (mf/use-layout-effect - (mf/deps index) - (fn [] + (mf/deps nav-scroll) + (fn [] + ;; Set scroll position after navigate + (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))))) + + (mf/use-layout-effect + (mf/deps fullscreen?) + (fn [] + ;; Trigger dom fullscreen depending on our state + (let [wrapper (dom/get-element "viewer-layout") + fullscreen-dom? (dom/fullscreen?)] + (when (not= fullscreen? fullscreen-dom?) + (if fullscreen? + (wapi/request-fullscreen wrapper) + (wapi/exit-fullscreen)))))) + + (mf/use-layout-effect + (mf/deps index) + (fn [] ;; Navigate animation needs to be started after navigation ;; is complete, and we have the next page index. - (when (and current-animation - (= (:kind current-animation) :go-to-frame)) - (let [orig-viewport (mf/ref-val orig-viewport-ref) - current-viewport (mf/ref-val current-viewport-ref)] - (interactions/animate-go-to-frame - (:animation current-animation) - current-viewport - orig-viewport - size - orig-size - wrapper-size))))) + (when (and current-animation + (= (:kind current-animation) :go-to-frame)) + (let [orig-viewport (mf/ref-val orig-viewport-ref) + current-viewport (mf/ref-val current-viewport-ref)] + (interactions/animate-go-to-frame + (:animation current-animation) + current-viewport + orig-viewport + size + orig-size + wrapper-size))))) (mf/use-layout-effect - (mf/deps current-animation) - (fn [] + (mf/deps current-animation) + (fn [] ;; Overlay animations may be started when needed. - (when current-animation - (case (:kind current-animation) + (when current-animation + (case (:kind current-animation) - :open-overlay - (let [overlay-viewport (dom/get-element (str "overlay-" (str (:overlay-id current-animation)))) - overlay (d/seek #(= (:id (:frame %)) (:overlay-id current-animation)) - overlays) - overlay-size (calculate-size (:frame overlay) zoom) - overlay-position {:x (* (:x (:position overlay)) zoom) - :y (* (:y (:position overlay)) zoom)}] - (interactions/animate-open-overlay - (:animation current-animation) - overlay-viewport - wrapper-size - overlay-size - overlay-position)) + :open-overlay + (let [overlay-viewport (dom/get-element (str "overlay-" (str (:overlay-id current-animation)))) + overlay (d/seek #(= (:id (:frame %)) (:overlay-id current-animation)) + overlays) + overlay-size (calculate-size (:frame overlay) zoom) + overlay-position {:x (* (:x (:position overlay)) zoom) + :y (* (:y (:position overlay)) zoom)}] + (interactions/animate-open-overlay + (:animation current-animation) + overlay-viewport + wrapper-size + overlay-size + overlay-position)) - :close-overlay - (let [overlay-viewport (dom/get-element (str "overlay-" (str (:overlay-id current-animation)))) - overlay (d/seek #(= (:id (:frame %)) (:overlay-id current-animation)) - overlays) - overlay-size (calculate-size (:frame overlay) zoom) - overlay-position {:x (* (:x (:position overlay)) zoom) - :y (* (:y (:position overlay)) zoom)}] - (interactions/animate-close-overlay - (:animation current-animation) - overlay-viewport - wrapper-size - overlay-size - overlay-position - (:id (:frame overlay)))) + :close-overlay + (let [overlay-viewport (dom/get-element (str "overlay-" (str (:overlay-id current-animation)))) + overlay (d/seek #(= (:id (:frame %)) (:overlay-id current-animation)) + overlays) + overlay-size (calculate-size (:frame overlay) zoom) + overlay-position {:x (* (:x (:position overlay)) zoom) + :y (* (:y (:position overlay)) zoom)}] + (interactions/animate-close-overlay + (:animation current-animation) + overlay-viewport + wrapper-size + overlay-size + overlay-position + (:id (:frame overlay)))) - nil)))) + nil)))) - [:div {:class (dom/classnames - :force-visible (:show-thumbnails local) - :viewer-layout (not= section :handoff) - :handoff-layout (= section :handoff))} + [:div#viewer-layout {:class (dom/classnames + :force-visible (:show-thumbnails local) + :viewer-layout (not= section :handoff) + :handoff-layout (= section :handoff) + :fullscreen fullscreen?)} [:& header {:project project :index index @@ -273,7 +300,7 @@ (:background-overlay overlay)) [:div.viewer-overlay-background {:class (dom/classnames - :visible (:background-overlay overlay)) + :visible (:background-overlay overlay)) :style {:width (:width wrapper-size) :height (:height wrapper-size) :position "absolute" diff --git a/frontend/src/app/main/ui/viewer/header.cljs b/frontend/src/app/main/ui/viewer/header.cljs index 3bdffd1ba..c96bed906 100644 --- a/frontend/src/app/main/ui/viewer/header.cljs +++ b/frontend/src/app/main/ui/viewer/header.cljs @@ -6,29 +6,65 @@ (ns app.main.ui.viewer.header (:require + [app.common.math :as mth] [app.main.data.modal :as modal] [app.main.data.viewer :as dv] [app.main.data.viewer.shortcuts :as sc] + [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.dropdown :refer [dropdown]] - [app.main.ui.components.fullscreen :as fs] [app.main.ui.icons :as i] [app.main.ui.viewer.comments :refer [comments-menu]] [app.main.ui.viewer.interactions :refer [flows-menu interactions-menu]] - [app.main.ui.workspace.header :refer [zoom-widget]] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [rumext.alpha :as mf])) +(mf/defc zoom-widget + {::mf/wrap [mf/memo]} + [{:keys [zoom + on-increase + on-decrease + on-zoom-reset + on-fullscreen + on-zoom-fit + on-zoom-fill] + :as props}] + (let [show-dropdown? (mf/use-state false)] + [:div.zoom-widget {:on-click #(reset! show-dropdown? true)} + [:span.label {} (str (mth/round (* 100 zoom)) "%")] + [:span.icon i/arrow-down] + [:& dropdown {:show @show-dropdown? + :on-close #(reset! show-dropdown? false)} + [:ul.dropdown + [:li.basic-zoom-bar + [:span.zoom-btns + [:button {:on-click (fn [event] + (dom/stop-propagation event) + (dom/prevent-default event) + (on-decrease))} "-"] + [:p.zoom-size {} (str (mth/round (* 100 zoom)) "%")] + [:button {:on-click (fn [event] + (dom/stop-propagation event) + (dom/prevent-default event) + (on-increase))} "+"]] + [:button.reset-btn {:on-click on-zoom-reset} (tr "workspace.header.reset-zoom")]] + [:li.separator] + [:li {:on-click on-zoom-fit} + (tr "workspace.header.zoom-fit") [:span (sc/get-tooltip :toggle-zoom-style)]] + [:li {:on-click on-zoom-fill} + (tr "workspace.header.zoom-fill") [:span (sc/get-tooltip :toggle-zoom-style)]] + [:li {:on-click on-fullscreen} + (tr "workspace.header.zoom-full-screen") [:span (sc/get-tooltip :toogle-fullscreen)]]]]])) + + (mf/defc header-options [{:keys [section zoom page file index permissions]}] - (let [fullscreen (mf/use-ctx fs/fullscreen-context) + (let [fullscreen? (mf/deref refs/fullscreen?) toggle-fullscreen (mf/use-callback - (mf/deps fullscreen) - (fn [] - (if @fullscreen (fullscreen false) (fullscreen true)))) + (fn [] (st/emit! dv/toggle-fullscreen))) go-to-workspace (mf/use-callback @@ -56,15 +92,15 @@ {:zoom zoom :on-increase (st/emitf dv/increase-zoom) :on-decrease (st/emitf dv/decrease-zoom) - :on-zoom-to-50 (st/emitf dv/zoom-to-50) - :on-zoom-to-100 (st/emitf dv/reset-zoom) - :on-zoom-to-200 (st/emitf dv/zoom-to-200) + :on-zoom-reset (st/emitf dv/reset-zoom) + :on-zoom-fill (st/emitf dv/zoom-to-fill) + :on-zoom-fit (st/emitf dv/zoom-to-fit) :on-fullscreen toggle-fullscreen}] [:span.btn-icon-dark.btn-small.tooltip.tooltip-bottom-left {:alt (tr "viewer.header.fullscreen") :on-click toggle-fullscreen} - (if @fullscreen + (if fullscreen? i/full-screen-off i/full-screen)] @@ -104,31 +140,31 @@ (st/emit! (dv/go-to-page page-id)) (reset! show-dropdown? false)))] - [:div.sitemap-zone {:alt (tr "viewer.header.sitemap")} - [:div.breadcrumb - {:on-click open-dropdown} - [:span.project-name project-name] - [:span "/"] - [:span.file-name file-name] - [:span "/"] + [:div.sitemap-zone {:alt (tr "viewer.header.sitemap")} + [:div.breadcrumb + {:on-click open-dropdown} + [:span.project-name project-name] + [:span "/"] + [:span.file-name file-name] + [:span "/"] - [:span.page-name page-name] - [:span.icon i/arrow-down] + [:span.page-name page-name] + [:span.icon i/arrow-down] - [:& dropdown {:show @show-dropdown? - :on-close close-dropdown} - [:ul.dropdown - (for [id (get-in file [:data :pages])] - [:li {:id (str id) - :on-click (partial navigate-to id)} - (get-in file [:data :pages-index id :name])])]]] + [:& dropdown {:show @show-dropdown? + :on-close close-dropdown} + [:ul.dropdown + (for [id (get-in file [:data :pages])] + [:li {:id (str id) + :on-click (partial navigate-to id)} + (get-in file [:data :pages-index id :name])])]]] - [:div.current-frame - {:on-click toggle-thumbnails} - [:span.label "/"] - [:span.label frame-name] - [:span.icon i/arrow-down] - [:span.counters (str (inc index) " / " total)]]])) + [:div.current-frame + {:on-click toggle-thumbnails} + [:span.label "/"] + [:span.label frame-name] + [:span.icon i/arrow-down] + [:span.counters (str (inc index) " / " total)]]])) (mf/defc header diff --git a/frontend/src/app/main/ui/workspace/header.cljs b/frontend/src/app/main/ui/workspace/header.cljs index 85de2ea77..f64be2ec5 100644 --- a/frontend/src/app/main/ui/workspace/header.cljs +++ b/frontend/src/app/main/ui/workspace/header.cljs @@ -57,16 +57,14 @@ [:span.icon i/msg-warning] [:span.label (tr "workspace.header.save-error")]])])) - -(mf/defc zoom-widget +(mf/defc zoom-widget-workspace {::mf/wrap [mf/memo]} [{:keys [zoom on-increase on-decrease on-zoom-reset on-zoom-fit - on-zoom-selected - on-fullscreen] + on-zoom-selected] :as props}] (let [show-dropdown? (mf/use-state false)] [:div.zoom-widget {:on-click #(reset! show-dropdown? true)} @@ -75,19 +73,24 @@ [:& dropdown {:show @show-dropdown? :on-close #(reset! show-dropdown? false)} [:ul.dropdown - [:li {:on-click on-increase} - "Zoom in" [:span (sc/get-tooltip :increase-zoom)]] - [:li {:on-click on-decrease} - "Zoom out" [:span (sc/get-tooltip :decrease-zoom)]] - [:li {:on-click on-zoom-reset} - "Zoom to 100%" [:span (sc/get-tooltip :reset-zoom)]] + [:li.basic-zoom-bar + [:span.zoom-btns + [:button {:on-click (fn [event] + (dom/stop-propagation event) + (dom/prevent-default event) + (on-decrease))} "-"] + [:p.zoom-size {} (str (mth/round (* 100 zoom)) "%")] + [:button {:on-click (fn [event] + (dom/stop-propagation event) + (dom/prevent-default event) + (on-increase))} "+"]] + [:button.reset-btn {:on-click on-zoom-reset} (tr "workspace.header.reset-zoom")]] + [:li.separator] [:li {:on-click on-zoom-fit} - "Zoom to fit all" [:span (sc/get-tooltip :fit-all)]] + (tr "workspace.header.zoom-fit-all") [:span (sc/get-tooltip :fit-all)]] [:li {:on-click on-zoom-selected} - "Zoom to selected" [:span (sc/get-tooltip :zoom-selected)]] - (when on-fullscreen - [:li {:on-click on-fullscreen} - "Full screen"])]]])) + (tr "workspace.header.zoom-selected") [:span (sc/get-tooltip :zoom-selected)]]]]])) + ;; --- Header Users @@ -166,24 +169,24 @@ on-export-frames (mf/use-callback - (mf/deps file frames) - (fn [_] - (when (seq frames) - (let [filename (str (:name file) ".pdf") - frame-ids (mapv :id frames)] - (st/emit! (dm/info (tr "workspace.options.exporting-object") - {:timeout nil})) - (->> (rp/query! :export-frames - {:name (:name file) - :file-id (:id file) - :page-id page-id - :frame-ids frame-ids}) - (rx/subs - (fn [body] - (dom/trigger-download filename body)) - (fn [_error] - (st/emit! (dm/error (tr "errors.unexpected-error")))) - (st/emitf dm/hide)))))))] + (mf/deps file frames) + (fn [_] + (when (seq frames) + (let [filename (str (:name file) ".pdf") + frame-ids (mapv :id frames)] + (st/emit! (dm/info (tr "workspace.options.exporting-object") + {:timeout nil})) + (->> (rp/query! :export-frames + {:name (:name file) + :file-id (:id file) + :page-id page-id + :frame-ids frame-ids}) + (rx/subs + (fn [body] + (dom/trigger-download filename body)) + (fn [_error] + (st/emit! (dm/error (tr "errors.unexpected-error")))) + (st/emitf dm/hide)))))))] (mf/use-effect (mf/deps @editing?) @@ -289,9 +292,7 @@ (when (contains? @cf/flags :user-feedback) [:li.feedback {:on-click (st/emitf (rt/nav :settings-feedback))} - [:span (tr "labels.give-feedback")]]) - - ]]])) + [:span (tr "labels.give-feedback")]])]]])) ;; --- Header Component @@ -327,7 +328,7 @@ [:div.options-section [:& persistence-state-widget] - [:& zoom-widget + [:& zoom-widget-workspace {:zoom zoom :on-increase #(st/emit! (dw/increase-zoom nil)) :on-decrease #(st/emit! (dw/decrease-zoom nil)) diff --git a/frontend/translations/en.po b/frontend/translations/en.po index e22b15257..13d7bb547 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -2087,6 +2087,30 @@ msgstr "Saving" msgid "workspace.header.unsaved" msgstr "Unsaved changes" +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.reset-zoom" +msgstr "Reset" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.zoom-fit-all" +msgstr "Zoom to fil all" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.zoom-selected" +msgstr "Zoom to selected" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.zoom-fit" +msgstr "Fit - Scale down to fit" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.zoom-fill" +msgstr "Fill -Scale to fill" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.zoom-full-screen" +msgstr "Full screen" + #: src/app/main/ui/workspace/header.cljs msgid "workspace.header.viewer" msgstr "View mode (%s)" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index ff83682a2..d1bafa541 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -2102,6 +2102,30 @@ msgstr "Guardando" msgid "workspace.header.unsaved" msgstr "Cambios sin guardar" +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.reset-zoom" +msgstr "Restablecer" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.zoom-fit-all" +msgstr "Zoom abarcar todo" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.zoom-selected" +msgstr "Zoom a selección" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.zoom-fit" +msgstr "Escalar para ajustar" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.zoom-fill" +msgstr "Escalar para rellenar" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.zoom-full-screen" +msgstr "Pantalla completa" + #: src/app/main/ui/workspace/header.cljs msgid "workspace.header.viewer" msgstr "Modo de visualización (%s)"