diff --git a/frontend/resources/images/icons/expand-refactor.svg b/frontend/resources/images/icons/expand-refactor.svg new file mode 100644 index 000000000..e63fd74c8 --- /dev/null +++ b/frontend/resources/images/icons/expand-refactor.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/images/icons/reload-refactor.svg b/frontend/resources/images/icons/reload-refactor.svg new file mode 100644 index 000000000..e1ac593b7 --- /dev/null +++ b/frontend/resources/images/icons/reload-refactor.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/styles/common/refactor/basic-rules.scss b/frontend/resources/styles/common/refactor/basic-rules.scss index cd3b86762..944fa8e52 100644 --- a/frontend/resources/styles/common/refactor/basic-rules.scss +++ b/frontend/resources/styles/common/refactor/basic-rules.scss @@ -148,6 +148,7 @@ stroke: var(--button-tertiary-foreground-color-active); } } + &:global(.disabled), &[disabled], &:disabled { diff --git a/frontend/resources/styles/common/refactor/design-tokens.scss b/frontend/resources/styles/common/refactor/design-tokens.scss index c1d86f5ea..559d779c9 100644 --- a/frontend/resources/styles/common/refactor/design-tokens.scss +++ b/frontend/resources/styles/common/refactor/design-tokens.scss @@ -297,4 +297,13 @@ --tag-background-color: var(--color-accent-primary); --link-foreground-color: var(--color-accent-primary); + + // VIEWER + --viewer-background-color: var(--color-background-secondary); + --viewer-paginator-background-color: var(--color-background-tertiary); + --viewer-controls-background-color: var(--color-background-primary); + --viewer-inspect-border-color: var(--color-background-tertiary); + --viewer-thumbnails-control-foreground-color: var(--color-foreground-secondary); + --viewer-thumbnail-border-color: var(--color-accent-primary); + --viewer-thumbnail-background-color-selected: var(--color-accent-primary-muted); } diff --git a/frontend/src/app/main/ui/auth/login.cljs b/frontend/src/app/main/ui/auth/login.cljs index bc64bdf75..2092a2fb5 100644 --- a/frontend/src/app/main/ui/auth/login.cljs +++ b/frontend/src/app/main/ui/auth/login.cljs @@ -93,10 +93,9 @@ (assoc :message (tr "errors.email-invalid")))))) (mf/defc login-form - [{:keys [params on-success-callback] :as props}] + [{:keys [params on-success-callback origin] :as props}] (let [new-css-system (mf/use-ctx ctx/new-css-system) initial (mf/use-memo (mf/deps params) (constantly params)) - error (mf/use-state false) form (fm/use-form :spec ::login-form :validators [handle-error-messages] @@ -152,7 +151,12 @@ (let [params (:clean-data @form)] (login-with-ldap event (with-meta params {:on-error on-error - :on-success on-success})))))] + :on-success on-success}))))) + + on-recovery-request + (mf/use-fn + #(st/emit! (rt/nav :auth-recovery-request)))] + (if new-css-system [:* (when-let [message @error] @@ -178,10 +182,11 @@ :label (tr "auth.password") :class (stl/css :form-field)}]] - (when (or (contains? cf/flags :login) - (contains? cf/flags :login-with-password)) + (when (and (not= origin :viewer) + (or (contains? cf/flags :login) + (contains? cf/flags :login-with-password))) [:div {:class (stl/css :fields-row :forgot-password)} - [:& lk/link {:action #(st/emit! (rt/nav :auth-recovery-request)) + [:& lk/link {:action on-recovery-request :data-test "forgot-password"} (tr "auth.forgot-password")]]) @@ -198,6 +203,7 @@ {:label (tr "auth.login-with-ldap-submit") :on-click on-submit-ldap}])]]] + ;; OLD [:* (when-let [message @error] @@ -271,7 +277,7 @@ :icon i/brand-openid :label (tr "auth.login-with-oidc-submit") :class (stl/css :login-btn :btn-oidc-auth)}])] - + [:div.auth-buttons (when (contains? cf/flags :login-with-google) [:& bl/button-link {:on-click login-with-google @@ -299,28 +305,36 @@ (mf/defc login-button-oidc [{:keys [params] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] + (let [new-css-system (mf/use-ctx ctx/new-css-system) + login-oidc + (mf/use-fn + (mf/deps params) + (fn [event] + (login-with-oidc event :oidc params))) + + handle-key-down + (mf/use-fn + (fn [event] + (when (k/enter? event) + (login-oidc event))))] (if new-css-system (when (contains? cf/flags :login-with-oidc) [:div {:class (stl/css :link-entry :link-oidc)} [:a {:tab-index "0" - :on-key-down (fn [event] - (when (k/enter? event) - (login-with-oidc event :oidc params))) - :on-click #(login-with-oidc % :oidc params)} + :on-key-down handle-key-down + :on-click login-oidc} (tr "auth.login-with-oidc-submit")]]) - + + ;; OLD (when (contains? cf/flags :login-with-oidc) [:div.link-entry.link-oidc [:a {:tab-index "0" - :on-key-down (fn [event] - (when (k/enter? event) - (login-with-oidc event :oidc params))) - :on-click #(login-with-oidc % :oidc params)} + :on-key-down handle-key-down + :on-click login-oidc} (tr "auth.login-with-oidc-submit")]])))) (mf/defc login-methods - [{:keys [params on-success-callback] :as props}] + [{:keys [params on-success-callback origin] :as props}] (let [new-css-system (mf/use-ctx ctx/new-css-system)] (if new-css-system [:* @@ -336,7 +350,7 @@ (when (or (contains? cf/flags :login) (contains? cf/flags :login-with-password) (contains? cf/flags :login-with-ldap)) - [:& login-form {:params params :on-success-callback on-success-callback}])] + [:& login-form {:params params :on-success-callback on-success-callback :origin origin}])] ;; OLD [:* @@ -364,7 +378,19 @@ (mf/defc login-page [{:keys [params] :as props}] - (let [new-css-system (mf/use-ctx ctx/new-css-system)] + (let [new-css-system (mf/use-ctx ctx/new-css-system) + go-register + (mf/use-fn + #(st/emit! (rt/nav :auth-register {} params))) + + on-pass-recovery + (mf/use-fn + #(st/emit! (rt/nav :auth-recovery-request))) + + on-create-demo-profile + (mf/use-fn + #(st/emit! (du/create-demo-profile)))] + (if new-css-system [:div {:class (stl/css :auth-form)} [:h1 {:class (stl/css :auth-title) @@ -375,17 +401,24 @@ [:& login-methods {:params params}] [:div {:class (stl/css :links)} + (when (or (contains? cf/flags :login) + (contains? cf/flags :login-with-password)) + [:div {:class (stl/css :link-entry :register)} + [:& lk/link {:action on-pass-recovery + :data-test "forgot-password"} + (tr "auth.forgot-password")]]) + (when (contains? cf/flags :registration) [:div {:class (stl/css :link-entry :register)} [:span (tr "auth.register") " "] - [:& lk/link {:action #(st/emit! (rt/nav :auth-register {} params)) + [:& lk/link {:action go-register :data-test "register-submit"} (tr "auth.register-submit")]])] (when (contains? cf/flags :demo-users) [:div {:class (stl/css :link-entry :demo-account)} [:span (tr "auth.create-demo-profile") " "] - [:& lk/link {:action #(st/emit! (du/create-demo-profile)) + [:& lk/link {:action on-create-demo-profile :data-test "demo-account-link"} (tr "auth.create-demo-account")]])] @@ -400,14 +433,14 @@ (when (or (contains? cf/flags :login) (contains? cf/flags :login-with-password)) [:div.link-entry - [:& lk/link {:action #(st/emit! (rt/nav :auth-recovery-request)) + [:& lk/link {:action on-pass-recovery :data-test "forgot-password"} (tr "auth.forgot-password")]]) (when (contains? cf/flags :registration) [:div.link-entry [:span (tr "auth.register") " "] - [:& lk/link {:action #(st/emit! (rt/nav :auth-register {} params)) + [:& lk/link {:action go-register :data-test "register-submit"} (tr "auth.register-submit")]])] @@ -415,7 +448,7 @@ [:div.links.demo [:div.link-entry [:span (tr "auth.create-demo-profile") " "] - [:& lk/link {:action #(st/emit! (du/create-demo-profile)) + [:& lk/link {:action on-create-demo-profile :data-test "demo-account-link"} (tr "auth.create-demo-account")]]])]]))) diff --git a/frontend/src/app/main/ui/export.cljs b/frontend/src/app/main/ui/export.cljs index 710e5efc5..436265499 100644 --- a/frontend/src/app/main/ui/export.cljs +++ b/frontend/src/app/main/ui/export.cljs @@ -55,8 +55,12 @@ :cmd cmd}))) on-toggle-enabled - (fn [index] - (swap! exports update-in [index :enabled] not)) + (mf/use-fn + (mf/deps exports) + (fn [event] + (let [index (-> (dom/get-current-target event) + (dom/get-data "value"))] + (swap! exports update-in [index :enabled] not)))) change-all (fn [_] @@ -65,9 +69,8 @@ (if new-css-system [:div {:class (stl/css :modal-overlay)} - [:div.modal-container.export-multiple-dialog - {:class (stl/css-case :modal-container true - :empty (empty? all-exports))} + [:div {:class (stl/css-case :modal-container true + :empty (empty? all-exports))} [:div {:class (stl/css :modal-header)} [:h2 {:class (stl/css :modal-title)} title] @@ -84,55 +87,58 @@ :on-click change-all} [:span {:class (stl/css :checkbox-wrapper)} (cond - all-checked? [:span {:class (stl/css-case :checkbox-icon2 true + all-checked? [:span {:class (stl/css-case :checkobox-tick true :global/checked true)} i/tick-refactor] - all-unchecked? [:span {:class (stl/css-case :checkbox-icon2 true + all-unchecked? [:span {:class (stl/css-case :checkobox-tick true :global/uncheked true)}] - :else [:span {:class (stl/css-case :checkbox-icon2 true + :else [:span {:class (stl/css-case :checkobox-tick true :global/intermediate true)} i/remove-refactor])]] - [:div {:class (stl/css :selection-title)} (tr "dashboard.export-multiple.selected" - (c (count enabled-exports)) - (c (count all-exports)))]] + [:div {:class (stl/css :selection-title)} + (tr "dashboard.export-multiple.selected" + (c (count enabled-exports)) + (c (count all-exports)))]] [:div {:class (stl/css :selection-wrapper)} [:div {:class (stl/css-case :selection-list true :selection-shadow (> (count all-exports) 8))} - (for [[index {:keys [shape suffix] :as export}] (d/enumerate @exports)] - (let [{:keys [x y width height]} (:selrect shape)] - [:div {:class (stl/css :selection-row)} - [:button {:class (stl/css :selection-btn) - :on-click #(on-toggle-enabled index)} - [:span {:class (stl/css :checkbox-wrapper)} - (if (:enabled export) - [:span {:class (stl/css-case :checkbox-icon2 true - :global/checked true)} i/tick-refactor] - [:span {:class (stl/css-case :checkbox-icon2 true - :global/uncheked true)}])] + (for [[index {:keys [shape suffix] :as export}] (d/enumerate @exports)] + (let [{:keys [x y width height]} (:selrect shape)] + [:div {:class (stl/css :selection-row) + :key (:id shape)} + [:button {:class (stl/css :selection-btn) + :data-value index + :on-click on-toggle-enabled} + [:span {:class (stl/css :checkbox-wrapper)} + (if (:enabled export) + [:span {:class (stl/css-case :checkobox-tick true + :global/checked true)} i/tick-refactor] + [:span {:class (stl/css-case :checkobox-tick true + :global/uncheked true)}])] - [:div {:class (stl/css :image-wrapper)} - (if (some? (:thumbnail shape)) - [:img {:src (:thumbnail shape)}] - [:svg {:view-box (dm/str x " " y " " width " " height) - :width 24 - :height 20 - :version "1.1" - :xmlns "http://www.w3.org/2000/svg" - :xmlnsXlink "http://www.w3.org/1999/xlink" + [:div {:class (stl/css :image-wrapper)} + (if (some? (:thumbnail shape)) + [:img {:src (:thumbnail shape)}] + [:svg {:view-box (dm/str x " " y " " width " " height) + :width 24 + :height 20 + :version "1.1" + :xmlns "http://www.w3.org/2000/svg" + :xmlnsXlink "http://www.w3.org/1999/xlink" ;; Fix Chromium bug about color of html texts ;; https://bugs.chromium.org/p/chromium/issues/detail?id=1244560#c5 - :style {:-webkit-print-color-adjust :exact} - :fill "none"} + :style {:-webkit-print-color-adjust :exact} + :fill "none"} - [:& shape-wrapper {:shape shape}]])] + [:& shape-wrapper {:shape shape}]])] - [:div {:class (stl/css :selection-name)} (cond-> (:name shape) suffix (str suffix))] - (when (:scale export) - [:div {:class (stl/css :selection-scale)} - (dm/str (ust/format-precision (* width (:scale export)) 2) "x" - (ust/format-precision (* height (:scale export)) 2) "px ")]) + [:div {:class (stl/css :selection-name)} (cond-> (:name shape) suffix (str suffix))] + (when (:scale export) + [:div {:class (stl/css :selection-scale)} + (dm/str (ust/format-precision (* width (:scale export)) 2) "px" + (ust/format-precision (* height (:scale export)) 2) "px")]) - (when (:type export) - [:div {:class (stl/css :selection-extension)} - (-> export :type d/name str/upper)])]]))]]] + (when (:type export) + [:div {:class (stl/css :selection-extension)} + (-> export :type d/name str/upper)])]]))]]] [:& no-selection])] @@ -187,7 +193,8 @@ (for [[index {:keys [shape suffix] :as export}] (d/enumerate @exports)] (let [{:keys [x y width height]} (:selrect shape)] [:div.row - [:div.field.check {:on-click #(on-toggle-enabled index)} + [:div.field.check {:data-value index + :on-click on-toggle-enabled} (if (:enabled export) [:span.checked i/checkbox-checked] [:span.unchecked i/checkbox-unchecked])] @@ -210,7 +217,7 @@ [:div.field.name (cond-> (:name shape) suffix (str suffix))] (when (:scale export) - [:div.field.scale (dm/str (ust/format-precision (* width (:scale export)) 2) "x" + [:div.field.scale (dm/str (ust/format-precision (* width (:scale export)) 2) "px" (ust/format-precision (* height (:scale export)) 2) "px ")]) (when (:type export) diff --git a/frontend/src/app/main/ui/export.scss b/frontend/src/app/main/ui/export.scss index a14f40908..a958a0dc8 100644 --- a/frontend/src/app/main/ui/export.scss +++ b/frontend/src/app/main/ui/export.scss @@ -161,7 +161,7 @@ height: $s-24; width: $s-24; padding: 0; - .checkbox-icon2 { + .checkobox-tick { @extend .checkbox-icon; } } diff --git a/frontend/src/app/main/ui/icons.cljs b/frontend/src/app/main/ui/icons.cljs index 610eec75e..96ebebf05 100644 --- a/frontend/src/app/main/ui/icons.cljs +++ b/frontend/src/app/main/ui/icons.cljs @@ -339,6 +339,7 @@ (def easing-ease-in-out-refactor (icon-xref :easing-ease-in-out-refactor)) (def effects-refactor (icon-xref :effects-refactor)) (def elipse-refactor (icon-xref :elipse-refactor)) +(def expand-refactor (icon-xref :expand-refactor)) (def fill-content-refactor (icon-xref :fill-content-refactor)) (def filter-refactor (icon-xref :filter-refactor)) (def fixed-width-refactor (icon-xref :fixed-width-refactor)) @@ -414,6 +415,7 @@ (def picker-refactor (icon-xref :picker-refactor)) (def play-refactor (icon-xref :play-refactor)) (def rectangle-refactor (icon-xref :rectangle-refactor)) +(def reload-refactor (icon-xref :reload-refactor)) (def remove-refactor (icon-xref :remove-refactor)) (def rgba-refactor (icon-xref :rgba-refactor)) (def rgba-complementary-refactor (icon-xref :rgba-complementary-refactor)) diff --git a/frontend/src/app/main/ui/viewer.cljs b/frontend/src/app/main/ui/viewer.cljs index 366453dd0..37577d56f 100644 --- a/frontend/src/app/main/ui/viewer.cljs +++ b/frontend/src/app/main/ui/viewer.cljs @@ -6,6 +6,7 @@ (ns app.main.ui.viewer (:import goog.events.EventType) + (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] [app.common.data.macros :as dm] @@ -91,29 +92,57 @@ :vbox (str "0 0 " width " " height)}))) (mf/defc viewer-pagination - [{:keys [index num-frames left-bar right-bar] :as props}] - [:* - (when (pos? index) - [:div.viewer-go-prev {:class (when left-bar "left-bar")} - [:div.arrow {:on-click #(st/emit! dv/select-prev-frame)} i/go-prev]]) - (when (< (+ index 1) num-frames) - [:div.viewer-go-next {:class (when right-bar "right-bar")} - [:div.arrow {:on-click #(st/emit! dv/select-next-frame)} i/go-next]]) - [:div.viewer-bottom {:class (when left-bar "left-bar")} - [:div.reset {:on-click #(st/emit! dv/select-first-frame)} i/reset] - [:div.counter (str/join " / " [(+ index 1) num-frames])] - [:span]]]) + [{:keys [index num-frames left-bar right-bar comment-sidebar] :as props}] + (let [new-css-system (mf/use-ctx ctx/new-css-system) + go-prev-frame (mf/use-fn #(st/emit! dv/select-prev-frame)) + go-next-frame (mf/use-fn #(st/emit! dv/select-next-frame)) + go-first-frame (mf/use-fn #(st/emit! dv/select-first-frame))] + (if new-css-system + [:* + (when (pos? index) + [:button {:class (stl/css-case :viewer-go-prev true + :left-bar left-bar) + :on-click go-prev-frame} + i/arrow-refactor]) + (when (< (+ index 1) num-frames) + [:button {:class (stl/css-case :viewer-go-next true + :comment-sidebar comment-sidebar + :right-bar right-bar) + :on-click go-next-frame} + i/arrow-refactor]) + [:div {:class (stl/css-case :viewer-bottom true + :left-bar left-bar)} + [:button {:on-click go-first-frame + :class (stl/css :reset-button)} + i/reload-refactor] + [:span {:class (stl/css :counter)} + (str/join " / " [(+ index 1) num-frames])] + [:span]]] + + + ;; OLD + [:* + (when (pos? index) + [:div.viewer-go-prev {:class (when left-bar "left-bar")} + [:div.arrow {:on-click go-prev-frame} i/go-prev]]) + (when (< (+ index 1) num-frames) + [:div.viewer-go-next {:class (when right-bar "right-bar")} + [:div.arrow {:on-click go-next-frame} i/go-next]]) + [:div.viewer-bottom {:class (when left-bar "left-bar")} + [:div.reset {:on-click go-first-frame} i/reset] + [:div.counter (str/join " / " [(+ index 1) num-frames])] + [:span]]]))) (mf/defc viewer-pagination-and-sidebar {::mf/wrap [mf/memo]} [{:keys [section index users frame page]}] (let [comments-local (mf/deref refs/comments-local) show-sidebar? (and (= section :comments) (:show-sidebar? comments-local))] - [:* + [:* [:& viewer-pagination {:index index :num-frames (count (:frames page)) - :right-bar show-sidebar?}] + :comment-sidebar show-sidebar?}] (when show-sidebar? [:& comments-sidebar @@ -172,72 +201,138 @@ :page page :interactions-mode interactions-mode}]]])) - (mf/defc viewer-wrapper {::mf/wrap-props false} [{:keys [wrapper-size orig-frame orig-viewport-ref orig-size page file users current-viewport-ref size frame interactions-mode overlays zoom section index]}] - [:* - [:& viewer-pagination-and-sidebar - {:section section - :index index - :page page - :users users - :frame frame - :interactions-mode interactions-mode}] + (let [new-css-system (mf/use-ctx ctx/new-css-system)] + (if new-css-system + [:* + [:& viewer-pagination-and-sidebar + {:section section + :index index + :page page + :users users + :frame frame + :interactions-mode interactions-mode}] - [:div.viewer-wrapper - {:style {:width (:width wrapper-size) - :height (:height wrapper-size)}} - [:div.viewer-clipper - (when orig-frame - [:div.viewport-container - {:ref orig-viewport-ref - :style {:width (:width orig-size) - :height (:height orig-size) - :position "relative"}} + [:div {:class (stl/css :viewer-wrapper) + :style {:width (:width wrapper-size) + :height (:height wrapper-size)}} + [:div {:class (stl/css :viewer-clipper)} - [:& interactions/viewport - {:frame orig-frame - :base-frame orig-frame - :frame-offset (gpt/point 0 0) - :size orig-size - :page page - :users users - :interactions-mode interactions-mode}]]) + (when orig-frame + [:div {:class (stl/css :viewport-container) + :ref orig-viewport-ref + :style {:width (:width orig-size) + :height (:height orig-size) + :position "relative"}} - [:div.viewport-container - {:ref current-viewport-ref - :style {:width (:width size) - :height (:height size) - :position "relative"}} + [:& interactions/viewport + {:frame orig-frame + :base-frame orig-frame + :frame-offset (gpt/point 0 0) + :size orig-size + :page page + :users users + :interactions-mode interactions-mode}]]) - [:& interactions/viewport - {:frame frame - :base-frame frame - :frame-offset (gpt/point 0 0) - :size size - :page page - :interactions-mode interactions-mode}] + [:div {:class (stl/css :viewport-container) + :ref current-viewport-ref + :style {:width (:width size) + :height (:height size) + :position "relative"}} - (for [overlay overlays] - [:& viewer-overlay - {:overlay overlay - :key (dm/str (:id overlay)) - :page page - :frame frame - :zoom zoom - :wrapper-size wrapper-size - :interactions-mode interactions-mode}])]] + [:& interactions/viewport + {:frame frame + :base-frame frame + :frame-offset (gpt/point 0 0) + :size size + :page page + :interactions-mode interactions-mode}] + + (for [overlay overlays] + [:& viewer-overlay + {:overlay overlay + :key (dm/str (:id overlay)) + :page page + :frame frame + :zoom zoom + :wrapper-size wrapper-size + :interactions-mode interactions-mode}])]] - (when (= section :comments) - [:& comments-layer {:file file - :users users - :frame frame - :page page - :zoom zoom}])]]) + (when (= section :comments) + [:& comments-layer {:file file + :users users + :frame frame + :page page + :zoom zoom}])]] + + + + ;; OLD + [:* + [:& viewer-pagination-and-sidebar + {:section section + :index index + :page page + :users users + :frame frame + :interactions-mode interactions-mode}] + + [:div.viewer-wrapper + {:style {:width (:width wrapper-size) + :height (:height wrapper-size)}} + [:div.viewer-clipper + (when orig-frame + [:div.viewport-container + {:ref orig-viewport-ref + :style {:width (:width orig-size) + :height (:height orig-size) + :position "relative"}} + + [:& interactions/viewport + {:frame orig-frame + :base-frame orig-frame + :frame-offset (gpt/point 0 0) + :size orig-size + :page page + :users users + :interactions-mode interactions-mode}]]) + + [:div.viewport-container + {:ref current-viewport-ref + :style {:width (:width size) + :height (:height size) + :position "relative"}} + + [:& interactions/viewport + {:frame frame + :base-frame frame + :frame-offset (gpt/point 0 0) + :size size + :page page + :interactions-mode interactions-mode}] + + (for [overlay overlays] + [:& viewer-overlay + {:overlay overlay + :key (dm/str (:id overlay)) + :page page + :frame frame + :zoom zoom + :wrapper-size wrapper-size + :interactions-mode interactions-mode}])]] + + + (when (= section :comments) + [:& comments-layer {:file file + :users users + :frame frame + :page page + :zoom zoom}])]]))) (mf/defc viewer [{:keys [params data]}] @@ -245,6 +340,8 @@ (let [{:keys [page-id share-id section index interactions-mode]} params {:keys [file users project permissions]} data + new-css-system (mf/use-ctx ctx/new-css-system) + allowed (or (= section :interactions) (and (= section :comments) @@ -311,7 +408,7 @@ (calculate-wrapper size orig-size zoom)) click-on-screen - (mf/use-callback + (mf/use-fn (fn [event] (let [origin (dom/get-target event) over-section? (dom/class? origin "viewer-section") @@ -360,9 +457,13 @@ (if shift? (dom/set-h-scroll-pos! section new-scroll-pos) (dom/set-scroll-pos! section new-scroll-pos))))))))) + on-thumbnails-close + (mf/use-fn + #(st/emit! dv/close-thumbnails-panel)) + on-exit-fullscreen - (mf/use-callback + (mf/use-fn (fn [] (when (not (dom/fullscreen?)) (st/emit! (dv/exit-fullscreen)))))] @@ -441,7 +542,7 @@ nil) ;; Navigate animation needs to be started after navigation ;; is complete, and we have the next page index. - (let [nav-animation (d/seek #(= (:kind %) :go-to-frame) (vals current-animations))] + (let [nav-animation (d/seek #(= (:kind %) :go-to-frame) (vals current-animations))] (when nav-animation (let [orig-viewport (mf/ref-val orig-viewport-ref) current-viewport (mf/ref-val current-viewport-ref)] @@ -498,76 +599,153 @@ fonts (into #{} (keep :font-id) text-nodes)] (run! fonts/ensure-loaded! fonts)))) - [:div#viewer-layout - {:class (dom/classnames - :force-visible (:show-thumbnails local) - :viewer-layout (not= section :inspect) - :inspect-layout (= section :inspect) - :fullscreen fullscreen?)} + (if new-css-system + [:div#viewer-layout + {:class (stl/css-case + :force-visible (:show-thumbnails local) + :viewer-layout (not= section :inspect) + :inspect-layout (= section :inspect) + :fullscreen fullscreen?)} - [:div.viewer-content - [:& header/header {:project project - :index index - :file file - :page page - :frame frame - :permissions permissions - :zoom zoom - :section section - :interactions-mode interactions-mode}] - [:div.thumbnail-close {:on-click #(st/emit! dv/close-thumbnails-panel) - :class (dom/classnames :invisible (not (:show-thumbnails local false)))}] - [:& thumbnails-panel {:frames frames - :show? (:show-thumbnails local false) - :page page - :index index - :thumbnail-data (:thumbnails file)}] - [:section.viewer-section {:id "viewer-section" - :ref viewer-section-ref - :class (if fullscreen? "fullscreen" "") - :on-click click-on-screen} - (cond - (empty? frames) - [:section.empty-state - [:span (tr "viewer.empty-state")]] + [:div {:class (stl/css :viewer-content)} + [:& header/header {:project project + :index index + :file file + :page page + :frame frame + :permissions permissions + :zoom zoom + :section section + :interactions-mode interactions-mode}] - (nil? frame) - [:section.empty-state - (when (some? index) - [:span (tr "viewer.frame-not-found")])] + [:button {:on-click on-thumbnails-close + :class (stl/css-case :thumbnails-close true + :invisible (not (:show-thumbnails local false)))}] - (some? frame) - (if (= :inspect section) - [:& inspect/viewport - {:frame frame - :page page - :file file - :section section - :local local - :size size - :index index - :viewer-pagination viewer-pagination - :interactions-mode interactions-mode - :share-id share-id}] + [:& thumbnails-panel {:frames frames + :show? (:show-thumbnails local false) + :page page + :index index + :thumbnail-data (:thumbnails file)}] + + [:section {:id "viewer-section" + :ref viewer-section-ref + :class (stl/css-case :viewer-section true + :fulscreen fullscreen?) + :on-click click-on-screen} + (cond + (empty? frames) + [:section {:class (stl/css :empty-state)} + [:span (tr "viewer.empty-state")]] + + (nil? frame) + [:section {:class (stl/css :empty-state)} + (when (some? index) + [:span (tr "viewer.frame-not-found")])] + + (some? frame) + (if (= :inspect section) + [:& inspect/viewport + {:frame frame + :page page + :file file + :section section + :local local + :size size + :index index + :viewer-pagination viewer-pagination + :interactions-mode interactions-mode + :share-id share-id}] + + [:& (mf/provider ctx/current-zoom) {:value zoom} + [:& viewer-wrapper + {:wrapper-size wrapper-size + :orig-frame orig-frame + :orig-viewport-ref orig-viewport-ref + :orig-size orig-size + :page page + :file file + :users users + :current-viewport-ref current-viewport-ref + :size size + :frame frame + :interactions-mode interactions-mode + :overlays overlays + :zoom zoom + :section section + :index index}]]))]]] + + ;; OLD + [:div#viewer-layout + {:class (dom/classnames + :force-visible (:show-thumbnails local) + :viewer-layout (not= section :inspect) + :inspect-layout (= section :inspect) + :fullscreen fullscreen?)} + + [:div.viewer-content + [:& header/header {:project project + :index index + :file file + :page page + :frame frame + :permissions permissions + :zoom zoom + :section section + :interactions-mode interactions-mode}] + [:div.thumbnail-close {:on-click on-thumbnails-close + :class (dom/classnames :invisible (not (:show-thumbnails local false)))}] + [:& thumbnails-panel {:frames frames + :show? (:show-thumbnails local false) + :page page + :index index + :thumbnail-data (:thumbnails file)}] + [:section.viewer-section {:id "viewer-section" + :ref viewer-section-ref + :class (if fullscreen? "fullscreen" "") + :on-click click-on-screen} + (cond + (empty? frames) + [:section.empty-state + [:span (tr "viewer.empty-state")]] + + (nil? frame) + [:section.empty-state + (when (some? index) + [:span (tr "viewer.frame-not-found")])] + + (some? frame) + (if (= :inspect section) + [:& inspect/viewport + {:frame frame + :page page + :file file + :section section + :local local + :size size + :index index + :viewer-pagination viewer-pagination + :interactions-mode interactions-mode + :share-id share-id}] - [:& (mf/provider ctx/current-zoom) {:value zoom} - [:& viewer-wrapper - {:wrapper-size wrapper-size - :orig-frame orig-frame - :orig-viewport-ref orig-viewport-ref - :orig-size orig-size - :page page - :file file - :users users - :current-viewport-ref current-viewport-ref - :size size - :frame frame - :interactions-mode interactions-mode - :overlays overlays - :zoom zoom - :section section - :index index}]]))]]])) + [:& (mf/provider ctx/current-zoom) {:value zoom} + [:& viewer-wrapper + {:wrapper-size wrapper-size + :orig-frame orig-frame + :orig-viewport-ref orig-viewport-ref + :orig-size orig-size + :page page + :file file + :users users + :current-viewport-ref current-viewport-ref + :size size + :frame frame + :interactions-mode interactions-mode + :overlays overlays + :zoom zoom + :section section + :index index}]]))]]]))) ;; --- Component: Viewer Page diff --git a/frontend/src/app/main/ui/viewer.scss b/frontend/src/app/main/ui/viewer.scss new file mode 100644 index 000000000..7825fe3a4 --- /dev/null +++ b/frontend/src/app/main/ui/viewer.scss @@ -0,0 +1,142 @@ +// 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) KALEIDOS INC + +@use "common/refactor/common-refactor.scss" as *; + +.viewer-layout { + height: 100vh; + display: grid; + grid-template-rows: $s-48 auto; + grid-template-columns: 1fr; + user-select: none; +} + +.viewer-content { + grid-row: 2 / span 1; + display: grid; + grid-template-rows: $s-252 auto; + grid-template-columns: 1fr; + background-color: var(--viewer-background-color); +} + +.viewer-header { + grid-row: 1 / span 1; +} + +.inspect-layout { + display: grid; + grid-template-rows: $s-48 auto; + grid-template-columns: 1fr; + height: 100vh; + margin-top: 0; + user-select: none; +} + +.thumbnails-close { + @include buttonStyle; + grid-row: 1 / span 2; + grid-column: 1 / span 1; + z-index: $z-index-10; + background-color: var(--overlay-color); +} + +.thumbnails-close.invisible { + display: none; +} + +.viewer-section { + grid-row: 1 / span 2; + grid-column: 1 / span 1; + display: flex; + align-items: center; + flex-wrap: nowrap; + height: calc(100vh - $s-48); + flex-flow: wrap; + overflow: auto; +} +.inspect-layout .viewer-section { + flex-wrap: nowrap; + margin-top: 0; + height: 100%; +} + +.viewer-go-prev, +.viewer-go-next { + @extend .button-secondary; + @include flexCenter; + position: absolute; + right: $s-8; + height: $s-64; + width: $s-32; + top: calc(50vh - $s-32); + z-index: $z-index-2; + background-color: var(--viewer-controls-background-color); + svg { + @extend .button-icon; + stroke: var(--icon-foreground); + } +} + +.viewer-go-next.comment-sidebar { + right: $s-264; +} + +.viewer-go-prev { + left: $s-8; + right: unset; + svg { + transform: rotate(180deg); + } +} + +.viewer-bottom { + position: fixed; + bottom: 0; + display: flex; + align-items: flex-start; + justify-content: space-between; + width: 100%; + height: $s-40; + padding-right: 0 $s-8 $s-40 $s-8; + transition: bottom 400ms ease 300ms; + z-index: $z-index-2; +} + +.reset-button { + @extend .button-secondary; + @include flexCenter; + height: $s-32; + width: $s-28; + margin-left: $s-8; + background-color: var(--viewer-controls-background-color); + svg { + @extend .button-icon; + stroke: var(--icon-foreground); + } +} + +.counter { + @include flexCenter; + @include titleTipography; + border-radius: $br-8; + width: $s-64; + height: $s-32; + background-color: var(--viewer-controls-background-color); +} + +.viewer-wrapper { + position: relative; + margin: 0 auto; +} + +.viewer-clipper { + display: grid; + grid-template-rows: 1fr; + grid-template-columns: 1fr; + justify-items: center; + align-items: center; + overflow: hidden; +} diff --git a/frontend/src/app/main/ui/viewer/comments.cljs b/frontend/src/app/main/ui/viewer/comments.cljs index 242da7cba..b9eed98bf 100644 --- a/frontend/src/app/main/ui/viewer/comments.cljs +++ b/frontend/src/app/main/ui/viewer/comments.cljs @@ -5,7 +5,9 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.viewer.comments + (:require-macros [app.main.style :as stl]) (:require + [app.common.data :as d] [app.common.geom.matrix :as gmt] [app.common.geom.point :as gpt] [app.common.geom.rect :as grc] @@ -16,6 +18,7 @@ [app.main.store :as st] [app.main.ui.comments :as cmt] [app.main.ui.components.dropdown :refer [dropdown]] + [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.main.ui.workspace.comments :as wc] [app.util.dom :as dom] @@ -28,55 +31,126 @@ ::mf/wrap-props false} [] (let [{cmode :mode cshow :show show-sidebar? :show-sidebar?} (mf/deref refs/comments-local) - + new-css-system (mf/use-ctx ctx/new-css-system) show-dropdown? (mf/use-state false) toggle-dropdown (mf/use-fn #(swap! show-dropdown? not)) hide-dropdown (mf/use-fn #(reset! show-dropdown? false)) update-mode (mf/use-callback - (fn [mode] - (st/emit! (dcm/update-filters {:mode mode})))) + (fn [event] + (let [mode (-> (dom/get-current-target event) + (dom/get-data "value") + (keyword))] + (st/emit! (dcm/update-filters {:mode mode}))))) update-show (mf/use-callback - (fn [mode] - (st/emit! (dcm/update-filters {:show mode})))) + (fn [event] + (let [mode (-> (dom/get-current-target event) + (dom/get-data "value") + (d/read-string))] + (st/emit! (dcm/update-filters {:show mode}))))) update-options (mf/use-callback - (fn [mode] - (st/emit! (dcm/update-options {:show-sidebar? mode}))))] + (fn [event] + (let [mode (-> (dom/get-target event) + (dom/get-data "value") + (boolean))] + (st/emit! (dcm/update-options {:show-sidebar? mode})))))] - [:div.view-options {:on-click toggle-dropdown} - [:span.label (tr "labels.comments")] - [:span.icon i/arrow-down] - [:& dropdown {:show @show-dropdown? - :on-close hide-dropdown} + (if new-css-system + [:div {:class (stl/css :view-options) + :on-click toggle-dropdown} + [:span {:class (stl/css :dropdown-title)} + (tr "labels.comments")] + [:span {:class (stl/css :icon-dropdown)} + i/arrow-refactor] + [:& dropdown {:show @show-dropdown? + :on-close hide-dropdown} + [:ul {:class (stl/css :dropdown)} + [:li {:class (stl/css-case :dropdown-element true + :selected (or (= :all cmode) (nil? cmode))) + :data-value :all + :on-click update-mode} - [:ul.dropdown.with-check - [:li {:class (dom/classnames :selected (or (= :all cmode) (nil? cmode))) - :on-click #(update-mode :all)} - [:span.icon i/tick] - [:span.label (tr "labels.show-all-comments")]] + [:span {:class (stl/css :label)} (tr "labels.show-all-comments")] + (when (or (= :all cmode) (nil? cmode)) + [:span {:class (stl/css :icon)} i/tick-refactor])] - [:li {:class (dom/classnames :selected (= :yours cmode)) - :on-click #(update-mode :yours)} - [:span.icon i/tick] - [:span.label (tr "labels.show-your-comments")]] + [:li {:class (stl/css-case :dropdown-element true + :selected (= :yours cmode)) + :data-value :yours + :on-click update-mode} - [:hr] + [:span {:class (stl/css :label)} + (tr "labels.show-your-comments")] - [:li {:class (dom/classnames :selected (= :pending cshow)) - :on-click #(update-show (if (= :pending cshow) :all :pending))} - [:span.icon i/tick] - [:span.label (tr "labels.hide-resolved-comments")]] + (when (= :yours cmode) + [:span {:class (stl/css :icon)} + i/tick-refactor])] - [:hr] - [:li {:class (dom/classnames :selected show-sidebar?) - :on-click #(update-options (not show-sidebar?))} - [:span.icon i/tick] - [:span.label (tr "labels.show-comments-list")]]]]])) + [:li {:class (stl/css :separator)}] + + [:li {:class (stl/css-case :dropdown-element true + :selected (= :pending cshow)) + :data-value (if (= :pending cshow) :all :pending) + :on-click update-show} + + [:span {:class (stl/css :label)} + (tr "labels.hide-resolved-comments")] + (when (= :pending cshow) + [:span {:class (stl/css :icon)} + i/tick-refactor])] + + [:li {:class (stl/css :separator)}] + + [:li {:class (stl/css-case :dropdown-element true + :selected show-sidebar?) + :data-value (not show-sidebar?) + :on-click update-options} + + [:span {:class (stl/css :label)} (tr "labels.show-comments-list")] + (when show-sidebar? + [:span {:class (stl/css :icon)} i/tick-refactor])]]]] + + + + ; OLD + [:div.view-options {:on-click toggle-dropdown} + [:span.label (tr "labels.comments")] + [:span.icon i/arrow-down] + [:& dropdown {:show @show-dropdown? + :on-close hide-dropdown} + + [:ul.dropdown.with-check + [:li {:class (dom/classnames :selected (or (= :all cmode) (nil? cmode))) + :data-value :all + :on-click update-mode} + [:span.icon i/tick] + [:span.label (tr "labels.show-all-comments")]] + + [:li {:class (dom/classnames :selected (= :yours cmode)) + :data-value :yours + :on-click update-mode} + [:span.icon i/tick] + [:span.label (tr "labels.show-your-comments")]] + + [:hr] + + [:li {:class (dom/classnames :selected (= :pending cshow)) + :data-value (if (= :pending cshow) :all :pending) + :on-click update-show} + [:span.icon i/tick] + [:span.label (tr "labels.hide-resolved-comments")]] + + [:hr] + [:li {:class (dom/classnames :selected show-sidebar?) + :data-value (not show-sidebar?) + :on-click update-options} + [:span.icon i/tick] + [:span.label (tr "labels.show-comments-list")]]]]]))) (defn- update-thread-position [positions {:keys [id] :as thread}] @@ -88,7 +162,8 @@ (mf/defc comments-layer [{:keys [zoom file users frame page] :as props}] - (let [profile (mf/deref refs/profile) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + profile (mf/deref refs/profile) local (mf/deref refs/comments-local) open-thread-id (:open local) @@ -159,43 +234,81 @@ (st/emit! (dcm/create-thread-on-viewer params) (dcm/close-thread)))))] - [:div.comments-section {:on-click on-click} - [:div.viewer-comments-container - [:div.threads - (for [item threads] - [:& cmt/thread-bubble - {:thread item - :position-modifier modifier1 - :zoom zoom - :on-click on-bubble-click - :open? (= (:id item) (:open local)) - :key (:seqn item) - :origin :viewer}]) + (if new-css-system + [:div {:class (stl/css :comments-section) + :on-click on-click} + [:div {:class (stl/css :viewer-comments-container)} + [:div {:class (stl/css :threads)} + (for [item threads] + [:& cmt/thread-bubble + {:thread item + :position-modifier modifier1 + :zoom zoom + :on-click on-bubble-click + :open? (= (:id item) (:open local)) + :key (:seqn item) + :origin :viewer}]) - (when-let [thread (get threads-map open-thread-id)] - [:& cmt/thread-comments - {:thread thread - :position-modifier modifier1 - :users users - :zoom zoom}]) + (when-let [thread (get threads-map open-thread-id)] + [:& cmt/thread-comments + {:thread thread + :position-modifier modifier1 + :users users + :zoom zoom}]) - (when-let [draft (:draft local)] - [:& cmt/draft-thread - {:draft draft - :position-modifier modifier1 - :on-cancel on-draft-cancel - :on-submit on-draft-submit - :zoom zoom}])]]])) + (when-let [draft (:draft local)] + [:& cmt/draft-thread + {:draft draft + :position-modifier modifier1 + :on-cancel on-draft-cancel + :on-submit on-draft-submit + :zoom zoom}])]]] + + + [:div.comments-section {:on-click on-click} + [:div.viewer-comments-container + [:div.threads + (for [item threads] + [:& cmt/thread-bubble + {:thread item + :position-modifier modifier1 + :zoom zoom + :on-click on-bubble-click + :open? (= (:id item) (:open local)) + :key (:seqn item) + :origin :viewer}]) + + (when-let [thread (get threads-map open-thread-id)] + [:& cmt/thread-comments + {:thread thread + :position-modifier modifier1 + :users users + :zoom zoom}]) + + (when-let [draft (:draft local)] + [:& cmt/draft-thread + {:draft draft + :position-modifier modifier1 + :on-cancel on-draft-cancel + :on-submit on-draft-submit + :zoom zoom}])]]]))) (mf/defc comments-sidebar [{:keys [users frame page]}] - (let [profile (mf/deref refs/profile) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + profile (mf/deref refs/profile) local (mf/deref refs/comments-local) threads-map (mf/deref refs/comment-threads) threads (->> (vals threads-map) (dcm/apply-filters local profile) (filter (fn [{:keys [position]}] (gsh/has-point? frame position))))] - [:aside.settings-bar.settings-bar-right.comments-right-sidebar - [:div.settings-bar-inside - [:& wc/comments-sidebar {:users users :threads threads :page-id (:id page)}]]])) + (if new-css-system + [:aside {:class (stl/css :comments-sidebar)} + [:div {:class (stl/css :settings-bar-inside)} + [:& wc/comments-sidebar {:from-viewer true :users users :threads threads :page-id (:id page)}]]] + + + [:aside.settings-bar.settings-bar-right.comments-right-sidebar + [:div.settings-bar-inside + [:& wc/comments-sidebar {:users users :threads threads :page-id (:id page)}]]]))) diff --git a/frontend/src/app/main/ui/viewer/comments.scss b/frontend/src/app/main/ui/viewer/comments.scss new file mode 100644 index 000000000..7dbbfcc83 --- /dev/null +++ b/frontend/src/app/main/ui/viewer/comments.scss @@ -0,0 +1,119 @@ +// 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) KALEIDOS INC + +@use "common/refactor/common-refactor.scss" as *; + +// COMMENT DROPDOWN ON HEADER +.view-options { + @include titleTipography; + display: flex; + align-items: center; + position: relative; + gap: $s-4; + height: $s-32; + padding: $s-8; + border-radius: $br-8; + background-color: var(--input-background-color); + cursor: pointer; +} + +.dropdown { + @extend .menu-dropdown; + right: $s-2; + top: calc($s-2 + $s-48); + width: $s-272; + padding: $s-6; +} + +.dropdown-title { + @include titleTipography; + flex-grow: 1; + color: var(--input-foreground-color-active); +} + +.label { + flex-grow: 1; + color: var(--input-foreground-color); +} + +.icon, +.icon-dropdown { + @include flexCenter; + height: 100%; + width: $s-16; + svg { + @extend .button-icon-small; + stroke: var(--icon-foreground); + } +} + +.icon-dropdown svg { + transform: rotate(90deg); +} + +.dropdown-element { + @extend .dropdown-element-base; + .icon { + @include flexCenter; + height: 100%; + width: $s-16; + svg { + @extend .button-icon-small; + stroke: var(--icon-foreground); + } + } + &:hover .label { + color: var(--input-foreground-color-active); + } +} + +.dropdown-element.selected { + .label { + color: var(--input-foreground-color-active); + } + .icon svg { + stroke: var(--input-foreground-color); + } +} + +.separator { + height: $s-8; +} + +// FLOATING COMMENT +.viewer-comments-container { + position: absolute; + top: 0px; + left: 0px; + width: 100%; + height: 100%; + z-index: $z-index-1; +} + +.threads { + position: absolute; + top: 0px; + left: 0px; +} + +//COMMENT SIDEBAR +.comments-sidebar { + position: absolute; + right: 0; + top: $s-44; + width: $s-256; + height: calc(100vh - $s-48); + z-index: $z-index-10; + background-color: var(--panel-background-color); +} + +.settings-bar-inside { + overflow-y: auto; +} + +.comments-section { + background-color: var(--panel-background-color); +} diff --git a/frontend/src/app/main/ui/viewer/header.cljs b/frontend/src/app/main/ui/viewer/header.cljs index 156e21d95..83bf05914 100644 --- a/frontend/src/app/main/ui/viewer/header.cljs +++ b/frontend/src/app/main/ui/viewer/header.cljs @@ -5,13 +5,16 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.viewer.header + (:require-macros [app.main.style :as stl]) (:require [app.common.data.macros :as dm] [app.main.data.modal :as modal] + [app.main.data.shortcuts :as scd] [app.main.data.viewer :as dv] [app.main.data.viewer.shortcuts :as sc] [app.main.store :as st] [app.main.ui.components.dropdown :refer [dropdown]] + [app.main.ui.context :as ctx] [app.main.ui.export :refer [export-progress-widget]] [app.main.ui.formats :as fmt] [app.main.ui.icons :as i] @@ -41,40 +44,120 @@ on-zoom-fit on-zoom-fill] :as props}] - (let [show-dropdown? (mf/use-state false)] - [:div.zoom-widget {:on-click - (fn [event] - (dom/stop-propagation event) - (reset! show-dropdown? true))} - [:span.label (fmt/format-percent 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 (fmt/format-percent 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 :toggle-fullscreen)]]]]])) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + open* (mf/use-state false) + open? (deref open*) + open-dropdown + (mf/use-fn + (fn [event] + (dom/stop-propagation event) + (reset! open* true))) + + close-dropdown + (mf/use-fn + (fn [event] + (dom/stop-propagation event) + (reset! open* false))) + + on-increase + (mf/use-fn + (mf/deps on-increase) + (fn [event] + (dom/stop-propagation event) + (on-increase))) + + on-decrease + (mf/use-fn + (mf/deps on-decrease) + (fn [event] + (dom/stop-propagation event) + (on-decrease))) + + show-dropdown? (mf/use-state false)] + + (if new-css-system + [:div {:class (stl/css-case :zoom-widget true + :selected open?) + :on-click open-dropdown + :title (tr "workspace.header.zoom")} + [:span {:class (stl/css :label)} (fmt/format-percent zoom)] + [:& dropdown {:show open? + :on-close close-dropdown} + [:ul {:class (stl/css :dropdown)} + [:li {:class (stl/css :basic-zoom-bar)} + [:span {:class (stl/css :zoom-btns)} + [:button {:class (stl/css :zoom-btn) + :on-click on-decrease} + [:span {:class (stl/css :zoom-icon)} + i/remove-refactor]] + [:p {:class (stl/css :zoom-text)} + (fmt/format-percent zoom)] + [:button {:class (stl/css :zoom-btn) + :on-click on-increase} + [:span {:class (stl/css :zoom-icon)} + i/add-refactor]]] + [:button {:class (stl/css :reset-btn) + :on-click on-zoom-reset} + (tr "workspace.header.reset-zoom")]] + + [:li {:class (stl/css :zoom-option) + :on-click on-zoom-fit} + (tr "workspace.header.zoom-fit") + [:span {:class (stl/css :shortcuts)} + (for [sc (scd/split-sc (sc/get-tooltip :toggle-zoom-style))] + [:span {:class (stl/css :shortcut-key) + :key (str "zoom-fit-" sc)} sc])]] + [:li {:class (stl/css :zoom-option) + :on-click on-zoom-fill} + (tr "workspace.header.zoom-fill") + [:span {:class (stl/css :shortcuts)} + (for [sc (scd/split-sc (sc/get-tooltip :toggle-zoom-style))] + [:span {:class (stl/css :shortcut-key) + :key (str "zoom-fill-" sc)} sc])]] + [:li {:class (stl/css :zoom-option) + :on-click on-fullscreen} + (tr "workspace.header.zoom-full-screen") + [:span {:class (stl/css :shortcuts)} + (for [sc (scd/split-sc (sc/get-tooltip :toggle-fullscreen))] + [:span {:class (stl/css :shortcut-key) + :key (str "zoom-fullscreen-" sc)} sc])]]]]] + + + ;; OLD + [:div.zoom-widget {:on-click + (fn [event] + (dom/stop-propagation event) + (reset! show-dropdown? true))} + [:span.label (fmt/format-percent 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 (fmt/format-percent 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 :toggle-fullscreen)]]]]]))) (mf/defc header-options [{:keys [section zoom page file index permissions interactions-mode]}] - (let [fullscreen? (mf/deref fullscreen-ref) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + fullscreen? (mf/deref fullscreen-ref) toggle-fullscreen (mf/use-callback @@ -91,49 +174,114 @@ (mf/deps page) (fn [] (modal/show! :share-link {:page page :file file}) - (modal/allow-click-outside!)))] + (modal/allow-click-outside!))) - [:div.options-zone - (case section - :interactions [:* - (when index - [:& flows-menu {:page page :index index}]) - [:& interactions-menu {:interactions-mode interactions-mode}]] - :comments [:& comments-menu] + handle-increase + (mf/use-fn + #(st/emit! dv/increase-zoom)) - [:div.view-options]) + handle-decrease + (mf/use-fn + #(st/emit! dv/decrease-zoom)) - [:& export-progress-widget] - [:& zoom-widget - {:zoom zoom - :on-increase #(st/emit! dv/increase-zoom) - :on-decrease #(st/emit! dv/decrease-zoom) - :on-zoom-reset #(st/emit! dv/reset-zoom) - :on-zoom-fill #(st/emit! dv/zoom-to-fill) - :on-zoom-fit #(st/emit! dv/zoom-to-fit) - :on-fullscreen toggle-fullscreen}] + handle-zoom-reset + (mf/use-fn + #(st/emit! dv/reset-zoom)) - [:span.btn-icon-dark.btn-small.tooltip.tooltip-bottom-left - {:alt (tr "viewer.header.fullscreen") - :on-click toggle-fullscreen} - (if fullscreen? - i/full-screen-off - i/full-screen)] + handle-zoom-fill + (mf/use-fn + #(st/emit! dv/zoom-to-fill)) - (when (:is-admin permissions) - [:span.btn-primary.tooltip.tooltip-bottom-left {:on-click open-share-dialog :alt (tr "labels.share-prototype")} i/export [:span (tr "labels.share-prototype")]]) + handle-zoom-fit + (mf/use-fn + #(st/emit! dv/zoom-to-fit))] - (when (:can-edit permissions) - [:span.btn-text-dark {:on-click go-to-workspace} (tr "labels.edit-file")]) + (if new-css-system + [:div {:class (stl/css :options-zone)} + (case section + :interactions [:* + (when index + [:& flows-menu {:page page :index index}]) + [:& interactions-menu {:interactions-mode interactions-mode}]] + :comments [:& comments-menu] + [:div {:class (stl/css :view-options)}]) - (when-not (:is-logged permissions) - [:span.btn-text-dark {:on-click open-login-dialog} (tr "labels.log-or-sign")])])) + [:& export-progress-widget] + + [:& zoom-widget + {:zoom zoom + :on-increase handle-increase + :on-decrease handle-decrease + :on-zoom-reset handle-zoom-reset + :on-zoom-fill handle-zoom-fill + :on-zoom-fit handle-zoom-fit + :on-fullscreen toggle-fullscreen}] + + (when (:can-edit permissions) + [:span {:on-click go-to-workspace + :class (stl/css :edit-btn)} + i/curve-refactor]) + + [:span {:title (tr "viewer.header.fullscreen") + :class (stl/css-case :fullscreen-btn true + :selected fullscreen?) + :on-click toggle-fullscreen} + i/expand-refactor] + + (when (:is-admin permissions) + [:button {:on-click open-share-dialog + :class (stl/css :share-btn)} + (tr "labels.share")]) + + (when-not (:is-logged permissions) + [:span {:on-click open-login-dialog + :class (stl/css :go-log-btn)} (tr "labels.log-or-sign")])] + + + ;; OLD + [:div.options-zone + (case section + :interactions [:* + (when index + [:& flows-menu {:page page :index index}]) + [:& interactions-menu {:interactions-mode interactions-mode}]] + :comments [:& comments-menu] + + [:div.view-options]) + + [:& export-progress-widget] + [:& zoom-widget + {:zoom zoom + :on-increase handle-increase + :on-decrease handle-decrease + :on-zoom-reset handle-zoom-reset + :on-zoom-fill handle-zoom-fill + :on-zoom-fit handle-zoom-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? + i/full-screen-off + i/full-screen)] + + (when (:is-admin permissions) + [:span.btn-primary.tooltip.tooltip-bottom-left {:on-click open-share-dialog :alt (tr "labels.share-prototype")} i/export [:span (tr "labels.share-prototype")]]) + + (when (:can-edit permissions) + [:span.btn-text-dark {:on-click go-to-workspace} (tr "labels.edit-file")]) + + (when-not (:is-logged permissions) + [:span.btn-text-dark {:on-click open-login-dialog} (tr "labels.log-or-sign")])]))) (mf/defc header-sitemap [{:keys [project file page frame] :as props}] - (let [project-name (:name project) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + project-name (:name project) file-name (:name file) page-name (:name page) + page-id (:id page) frame-name (:name frame) show-dropdown? (mf/use-state false) @@ -158,88 +306,181 @@ (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 "/"] - - [:span.page-name page-name] + (if new-css-system + [:div {:class (stl/css :sitemap-zone) + :title (tr "viewer.header.sitemap")} + [:span {:class (stl/css :project-name)} project-name] + [:div {:class (stl/css :sitemap-text)} + [:div {:class (stl/css :breadcrumb) + :on-click open-dropdown} + [:span {:class (stl/css :breadcrumb-text)} + (dm/str file-name " / " page-name)] + [:span {:class (stl/css :icon)} i/arrow-refactor] + [:span "/"] + [:& dropdown {:show @show-dropdown? + :on-close close-dropdown} + [:ul {:class (stl/css :dropdown-sitemap)} + (for [id (get-in file [:data :pages])] + [:li {:class (stl/css-case :dropdown-element true + :selected (= page-id id)) + :id (str id) + :key (str id) + :on-click (partial navigate-to id)} + [:span {:class (stl/css :label)} + (get-in file [:data :pages-index id :name])] + (when (= page-id id) + [:span {:class (stl/css :icon-check)} i/tick-refactor])])]]] + [:div {:class (stl/css :current-frame) + :on-click toggle-thumbnails} + [:span {:class (stl/css :frame-name)} frame-name] + [:span {:class (stl/css :icon)} i/arrow-refactor]]]] - [:& dropdown {:show @show-dropdown? - :on-close close-dropdown} - [:ul.dropdown - (for [id (get-in file [:data :pages])] - [:li {:id (str id) - :key (str id) - :on-click (partial navigate-to id)} - (get-in file [:data :pages-index id :name])])]]] + ;; OLD + [: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.icon {:on-click open-dropdown} i/arrow-down] - [:div.current-frame - {:on-click toggle-thumbnails} - [:span.label "/"] - [:span.label frame-name]] - [:span.icon {:on-click toggle-thumbnails} i/arrow-down]])) + [:span.page-name page-name] + [:& dropdown {:show @show-dropdown? + :on-close close-dropdown} + [:ul.dropdown + (for [id (get-in file [:data :pages])] + [:li {:id (str id) + :key (str id) + :on-click (partial navigate-to id)} + (get-in file [:data :pages-index id :name])])]]] + [:span.icon {:on-click open-dropdown} i/arrow-down] + [:div.current-frame + {:on-click toggle-thumbnails} + [:span.label "/"] + [:span.label frame-name]] + [:span.icon {:on-click toggle-thumbnails} i/arrow-down]]))) (mf/defc header [{:keys [project file page frame zoom section permissions index interactions-mode]}] - (let [go-to-dashboard - #(st/emit! (dv/go-to-dashboard)) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + go-to-dashboard + (mf/use-fn + #(st/emit! (dv/go-to-dashboard))) go-to-inspect - (fn[] - (if (:is-logged permissions) - (st/emit! dv/close-thumbnails-panel (dv/go-to-section :inspect)) - (open-login-dialog))) + (mf/use-fn + (mf/deps permissions) + (fn [] + (if (:is-logged permissions) + (st/emit! dv/close-thumbnails-panel (dv/go-to-section :inspect)) + (open-login-dialog)))) navigate - (fn [section] - (if (or (= section :interactions) (:is-logged permissions)) - (st/emit! (dv/go-to-section section)) - (open-login-dialog)))] + (mf/use-fn + (mf/deps permissions) + (fn [event] + (let [section (-> (dom/get-current-target event) + (dom/get-data "value") + (keyword))] - [:header.viewer-header - [:div.nav-zone - ;; If the user doesn't have permission we disable the link - [:div.main-icon {:style {:cursor (when-not (:can-edit permissions) "auto")}} - [:a {:on-click go-to-dashboard - :style {:pointer-events (when-not (:can-edit permissions) "none")}} i/logo-icon]] + (if (or (= section :interactions) (:is-logged permissions)) + (st/emit! (dv/go-to-section section)) + (open-login-dialog)))))] - [:& header-sitemap {:project project :file file :page page :frame frame :index index}]] + (if new-css-system + [:header {:class (stl/css :viewer-header)} + [:div {:class (stl/css :nav-zone)} + ;; If the user doesn't have permission we disable the link + [:a {:class (stl/css :home-link) + :on-click go-to-dashboard + :style {:cursor (when-not (:can-edit permissions) "auto") + :pointer-events (when-not (:can-edit permissions) "none")}} + [:span {:class (stl/css :logo-icon)} + i/logo-icon]] - [:div.mode-zone - [:button.mode-zone-button.tooltip.tooltip-bottom - {:on-click #(navigate :interactions) - :class (dom/classnames :active (= section :interactions)) - :alt (tr "viewer.header.interactions-section" (sc/get-tooltip :open-interactions))} - i/play] + [:& header-sitemap {:project project + :file file + :page page + :frame frame + :index index}]] - (when (or (:can-edit permissions) - (= (:who-comment permissions) "all")) + [:div {:class (stl/css :mode-zone)} + [:button {:on-click navigate + :data-value :interactions + :class (stl/css-case :mode-zone-btn true + :selected (= section :interactions)) + :title (tr "viewer.header.interactions-section" (sc/get-tooltip :open-interactions))} + i/play-refactor] + + (when (or (:can-edit permissions) + (= (:who-comment permissions) "all")) + [:button {:on-click navigate + :data-value :comments + :class (stl/css-case :mode-zone-btn true + :selected (= section :comments)) + :title (tr "viewer.header.comments-section" (sc/get-tooltip :open-comments))} + i/comments-refactor]) + + (when (or (= (:type permissions) :membership) + (and (= (:type permissions) :share-link) + (= (:who-inspect permissions) "all"))) + [:button {:on-click go-to-inspect + :class (stl/css-case :mode-zone-btn true + :selected (= section :inspect)) + :title (tr "viewer.header.inspect-section" (sc/get-tooltip :open-inspect))} + i/code-refactor])] + + [:& header-options {:section section + :permissions permissions + :page page + :file file + :index index + :zoom zoom + :interactions-mode interactions-mode}]] + + + ;; OLD + [:header.viewer-header + [:div.nav-zone + ;; If the user doesn't have permission we disable the link + [:div.main-icon {:style {:cursor (when-not (:can-edit permissions) "auto")}} + [:a {:on-click go-to-dashboard + :style {:pointer-events (when-not (:can-edit permissions) "none")}} i/logo-icon]] + + [:& header-sitemap {:project project :file file :page page :frame frame :index index}]] + + [:div.mode-zone [:button.mode-zone-button.tooltip.tooltip-bottom - {:on-click #(navigate :comments) - :class (dom/classnames :active (= section :comments)) - :alt (tr "viewer.header.comments-section" (sc/get-tooltip :open-comments))} - i/chat]) + {:on-click navigate + :data-value :interactions + :class (dom/classnames :active (= section :interactions)) + :alt (tr "viewer.header.interactions-section" (sc/get-tooltip :open-interactions))} + i/play] - (when (or (= (:type permissions) :membership) - (and (= (:type permissions) :share-link) - (= (:who-inspect permissions) "all"))) - [:button.mode-zone-button.tooltip.tooltip-bottom - {:on-click go-to-inspect - :class (dom/classnames :active (= section :inspect)) - :alt (tr "viewer.header.inspect-section" (sc/get-tooltip :open-inspect))} - i/code])] + (when (or (:can-edit permissions) + (= (:who-comment permissions) "all")) + [:button.mode-zone-button.tooltip.tooltip-bottom + {:on-click navigate + :data-value :comments + :class (dom/classnames :active (= section :comments)) + :alt (tr "viewer.header.comments-section" (sc/get-tooltip :open-comments))} + i/chat]) - [:& header-options {:section section - :permissions permissions - :page page - :file file - :index index - :zoom zoom - :interactions-mode interactions-mode}]])) + (when (or (= (:type permissions) :membership) + (and (= (:type permissions) :share-link) + (= (:who-inspect permissions) "all"))) + [:button.mode-zone-button.tooltip.tooltip-bottom + {:on-click go-to-inspect + :class (dom/classnames :active (= section :inspect)) + :alt (tr "viewer.header.inspect-section" (sc/get-tooltip :open-inspect))} + i/code])] + + [:& header-options {:section section + :permissions permissions + :page page + :file file + :index index + :zoom zoom + :interactions-mode interactions-mode}]]))) diff --git a/frontend/src/app/main/ui/viewer/header.scss b/frontend/src/app/main/ui/viewer/header.scss new file mode 100644 index 000000000..a0dde598d --- /dev/null +++ b/frontend/src/app/main/ui/viewer/header.scss @@ -0,0 +1,301 @@ +// 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) KALEIDOS INC + +@use "common/refactor/common-refactor.scss" as *; + +.viewer-header { + position: absolute; + top: 0; + grid-column: 1 / span 1; + grid-row: 1 / span 1; + display: grid; + grid-template-columns: 1fr $s-92 1fr; + justify-content: space-between; + align-items: center; + height: $s-48; + width: 100vw; + padding: $s-8 $s-12; + background-color: var(--panel-background-color); +} + +// FILE NAVIGATION + +.nav-zone { + display: flex; + justify-content: flex-start; + flex-basis: min-content; + width: 100%; + gap: $s-12; +} + +.home-link { + padding: 0; +} + +.logo-icon { + @include flexCenter; + width: $s-32; + height: $s-32; + svg { + width: $s-28; + fill: var(--icon-foreground-hover); + } +} + +.sitemap-zone { + @include flexColumn; + position: relative; + width: 100%; +} + +.project-name { + @include tabTitleTipography; +} + +.sitemap-text { + @include flexRow; +} + +.breadcrumb { + @include titleTipography; + @include flexRow; + color: var(--title-foreground-color); + cursor: pointer; +} + +.breadcrumb-text { + @include textEllipsis; + max-width: 12vw; // This is a fallback + max-width: 12cqw; // This is a unit refered to container +} + +.icon { + @include flexCenter; + height: $s-16; + width: $s-16; + svg { + @extend .button-icon-small; + transform: rotate(90deg); + stroke: var(--icon-foreground); + } +} + +.dropdown { + position: absolute; +} + +.dropdown-sitemap { + @extend .menu-dropdown; + left: 0; + top: calc($s-2 + $s-48); + width: $s-272; + padding: $s-6; +} + +.dropdown-element { + @extend .dropdown-element-base; + .icon-check { + @include flexCenter; + height: 100%; + width: $s-16; + svg { + @extend .button-icon-small; + stroke: var(--icon-foreground); + } + } + &:hover .label { + color: var(--input-foreground-color-active); + } +} + +.current-frame { + @include titleTipography; + @include flexRow; + flex-grow: 1; + color: var(--title-foreground-color-hover); + cursor: pointer; + .icon svg { + stroke: var(--title-foreground-color-hover); + } +} + +.frame-name { + @include textEllipsis; + max-width: 17vw; // This is a fallback + max-width: 17cqw; // This is a unit refered to container +} + +// SECTION BUTTONS +.mode-zone { + @include flexRow; + height: 100%; +} + +.mode-zone-btn { + @extend .button-tertiary; + @include flexCenter; + height: $s-32; + width: $s-28; + padding: 0; + svg { + @extend .button-icon; + } +} + +.selected { + @extend .button-icon-selected; +} + +// OPTION AREA +.options-zone { + @include flexRow; + position: relative; + justify-content: flex-end; + gap: $s-8; + z-index: $z-index-10; +} + +.view-options { + position: relative; + display: flex; + align-items: center; + cursor: pointer; +} + +.fullscreen-btn { + @extend .button-tertiary; + @include flexCenter; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; + stroke: var(--icon-foreground); + } +} + +.share-btn { + @extend .button-primary; + height: $s-32; + min-width: $s-72; + margin-left: $s-4; +} + +.edit-btn { + @extend .button-tertiary; + @include flexCenter; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; + stroke: var(--icon-foreground); + } +} + +.go-log-btn { + @extend .button-tertiary; + @include titleTipography; + height: $s-32; + padding: 0 $s-8; + border-radius: $br-8; + color: var(--button-tertiary-foreground-color-rest); +} + +// ZOOM WIDGET +.zoom-widget { + @include buttonStyle; + @include flexCenter; + height: $s-28; + min-width: $s-64; + border-radius: $br-8; + .label { + @include titleTipography; + color: var(--button-tertiary-foreground-color-rest); + } + + &:hover { + .label { + color: var(--button-tertiary-foreground-color-focus); + } + } + &.selected { + .label { + color: var(--button-tertiary-foreground-color-focus); + } + } +} + +.dropdown { + @extend .menu-dropdown; + right: $s-2; + top: calc($s-2 + $s-48); + width: $s-272; +} + +.basic-zoom-bar { + display: flex; + justify-content: space-between; + padding: $s-6; + cursor: auto; +} + +.zoom-btns { + display: flex; +} + +.zoom-btn { + @extend .button-tertiary; + height: $s-28; + width: $s-28; + border-radius: $br-8; + .zoom-icon { + @include flexCenter; + width: $s-24; + height: $s-32; + svg { + @extend .button-icon; + stroke: var(--icon-foreground); + } + } + &:hover { + .zoom-icon svg { + stroke: var(--button-tertiary-foreground-color-hover); + } + } +} + +.zoom-text { + @include flexCenter; + height: 100%; + min-width: $s-64; + padding: 0; + margin: 0 $s-2; + color: var(--modal-title-foreground-color); +} + +.reset-btn { + @extend .button-tertiary; + color: var(--button-tertiary-foreground-color-hover); + height: $s-28; + border-radius: $br-8; +} + +.zoom-option { + @extend .menu-item; + .shortcuts { + @extend .shortcut; + .shortcut-key { + @extend .shortcut-key; + } + } + &:hover { + color: var(--menu-foreground-color-hover); + .shortcuts { + .shortcut-key { + color: var(--menu-foreground-color-hover); + } + } + } +} diff --git a/frontend/src/app/main/ui/viewer/inspect.cljs b/frontend/src/app/main/ui/viewer/inspect.cljs index 576cbe06b..9624d480d 100644 --- a/frontend/src/app/main/ui/viewer/inspect.cljs +++ b/frontend/src/app/main/ui/viewer/inspect.cljs @@ -5,10 +5,13 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.viewer.inspect + (:require-macros [app.main.style :as stl]) (:require + [app.common.data :as d] [app.common.data.macros :as dm] [app.main.data.viewer :as dv] [app.main.store :as st] + [app.main.ui.context :as ctx] [app.main.ui.hooks.resize :refer [use-resize-hook]] [app.main.ui.viewer.inspect.left-sidebar :refer [left-sidebar]] [app.main.ui.viewer.inspect.render :refer [render-frame-svg]] @@ -20,25 +23,27 @@ (:import goog.events.EventType)) (defn handle-select-frame - [frame] - (fn [event] + [event] + (let [frame-id (-> (dom/get-current-target event) + (dom/get-data "value") + (d/read-string)) + origin (dom/get-target event) + over-section? (dom/class? origin "inspect-svg-container") + layout (dom/get-element "viewer-layout") + has-force? (dom/class? layout "force-visible")] + (dom/prevent-default event) (dom/stop-propagation event) - (st/emit! (dv/select-shape (:id frame))) - - (let [origin (dom/get-target event) - over-section? (dom/class? origin "inspect-svg-container") - layout (dom/get-element "viewer-layout") - has-force? (dom/class? layout "force-visible")] - - (when over-section? - (if has-force? - (dom/remove-class! layout "force-visible") - (dom/add-class! layout "force-visible")))))) + (st/emit! (dv/select-shape frame-id)) + (when over-section? + (if has-force? + (dom/remove-class! layout "force-visible") + (dom/add-class! layout "force-visible"))))) (mf/defc viewport [{:keys [local file page frame index viewer-pagination size share-id]}] - (let [inspect-svg-container-ref (mf/use-ref nil) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + inspect-svg-container-ref (mf/use-ref nil) current-section* (mf/use-state :info) current-section (deref current-section*) @@ -77,7 +82,8 @@ handle-expand (mf/use-callback (mf/deps right-size) - #(set-right-size (if (> right-size 276) 276 768)))] + (fn[] + (set-right-size (if (> right-size 276) 276 768))))] (mf/use-effect on-mount) @@ -86,26 +92,60 @@ (fn [] (st/emit! (dv/select-shape (:id frame))))) - [:* - [:& left-sidebar {:frame frame - :local local - :page page}] - [:div.inspect-svg-wrapper {:on-click (handle-select-frame frame)} - [:& viewer-pagination {:index index :num-frames (count (:frames page)) :left-bar true :right-bar true}] - [:div.inspect-svg-container {:ref inspect-svg-container-ref} - [:& render-frame-svg {:frame frame :page page :local local :size size}]]] + (if new-css-system + [:* + [:& left-sidebar {:frame frame + :local local + :page page}] + [:div {:class (stl/css :inspect-svg-wrapper) + :data-value (pr-str (:id frame)) + :on-click handle-select-frame} + [:& viewer-pagination {:index index :num-frames (count (:frames page)) :left-bar true :right-bar true}] + [:div {:class (stl/css :inspect-svg-container) + :ref inspect-svg-container-ref} + [:& render-frame-svg {:frame frame :page page :local local :size size}]]] - [:div.sidebar-container - {:class (when (not can-be-expanded?) "not-expand") - :style #js {"--width" (when can-be-expanded? (dm/str right-size "px"))}} - [:div.resize-area - {:on-pointer-down on-pointer-down - :on-lost-pointer-capture on-lost-pointer-capture - :on-pointer-move on-pointer-move}] - [:& right-sidebar {:frame frame - :selected (:selected local) - :page page - :file file - :on-change-section handle-change-section - :on-expand handle-expand - :share-id share-id}]]])) + [:div {:class (stl/css-case :sidebar-container true + :not-expand (not can-be-expanded?) + :expanded can-be-expanded?) + + :style #js {"--width" (when can-be-expanded? (dm/str right-size "px"))}} + (when can-be-expanded? + [:div {:class (stl/css :resize-area) + :on-pointer-down on-pointer-down + :on-lost-pointer-capture on-lost-pointer-capture + :on-pointer-move on-pointer-move}]) + [:& right-sidebar {:frame frame + :selected (:selected local) + :page page + :file file + :on-change-section handle-change-section + :on-expand handle-expand + :share-id share-id}]]] + + + ;;OLD + [:* + [:& left-sidebar {:frame frame + :local local + :page page}] + [:div.inspect-svg-wrapper {:data-value (pr-str (:id frame)) + :on-click handle-select-frame} + [:& viewer-pagination {:index index :num-frames (count (:frames page)) :left-bar true :right-bar true}] + [:div.inspect-svg-container {:ref inspect-svg-container-ref} + [:& render-frame-svg {:frame frame :page page :local local :size size}]]] + + [:div.sidebar-container + {:class (when (not can-be-expanded?) "not-expand") + :style #js {"--width" (when can-be-expanded? (dm/str right-size "px"))}} + [:div.resize-area + {:on-pointer-down on-pointer-down + :on-lost-pointer-capture on-lost-pointer-capture + :on-pointer-move on-pointer-move}] + [:& right-sidebar {:frame frame + :selected (:selected local) + :page page + :file file + :on-change-section handle-change-section + :on-expand handle-expand + :share-id share-id}]]]))) diff --git a/frontend/src/app/main/ui/viewer/inspect.scss b/frontend/src/app/main/ui/viewer/inspect.scss new file mode 100644 index 000000000..340003ce9 --- /dev/null +++ b/frontend/src/app/main/ui/viewer/inspect.scss @@ -0,0 +1,56 @@ +// 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) KALEIDOS INC + +@use "common/refactor/common-refactor.scss" as *; + +$width-settings-bar: $s-276; +$width-settings-bar-max: $s-500; + +.inspect-svg-wrapper { + @include flexCenter; + position: relative; + flex-direction: column; + flex: 1; + width: 100%; + height: 100%; + overflow: hidden; +} + +.inspect-svg-container { + display: grid; + align-items: center; + justify-content: safe center; + width: 100%; + height: 100%; + margin: 0 auto; + overflow: auto; +} + +.sidebar-container { + position: relative; + align-self: flex-start; + width: $width-settings-bar; + + background-color: var(--panel-background-color); + border-top: $s-1 solid var(--search-bar-input-border-color); +} + +.not-expand { + max-width: $width-settings-bar; +} + +.expanded { + width: var(--width, $width-settings-bar); +} + +.resize-area { + position: absolute; + left: 0; + width: $s-8; + height: 100%; + z-index: $z-index-10; + cursor: ew-resize; +} diff --git a/frontend/src/app/main/ui/viewer/inspect/right_sidebar.cljs b/frontend/src/app/main/ui/viewer/inspect/right_sidebar.cljs index bf42b3e7b..d5fe584b7 100644 --- a/frontend/src/app/main/ui/viewer/inspect/right_sidebar.cljs +++ b/frontend/src/app/main/ui/viewer/inspect/right_sidebar.cljs @@ -87,8 +87,8 @@ (handle-change-tab :info)))) (if new-css-system - [:aside {:class (stl/css :settings-bar-right)} - + [:aside {:class (stl/css-case :settings-bar-right true + :viewer-code (= from :inspect))} (if (seq shapes) [:div {:class (stl/css :tool-windows)} [:div {:class (stl/css :shape-row)} diff --git a/frontend/src/app/main/ui/viewer/inspect/right_sidebar.scss b/frontend/src/app/main/ui/viewer/inspect/right_sidebar.scss index de3e6d54c..13bf37fac 100644 --- a/frontend/src/app/main/ui/viewer/inspect/right_sidebar.scss +++ b/frontend/src/app/main/ui/viewer/inspect/right_sidebar.scss @@ -9,11 +9,13 @@ .settings-bar-right { min-width: $s-252; width: 100%; - height: 100%; + height: 100vh; position: relative; left: unset; right: unset; grid-area: right-sidebar; + padding-top: $s-8; + padding-left: $s-12; .tool-windows { height: 100%; display: flex; @@ -64,9 +66,11 @@ padding: $s-8 $s-24; } } - .inspect-content { flex: 1; overflow: hidden; } + &.viewer-code { + height: calc(100vh - $s-48); + } } diff --git a/frontend/src/app/main/ui/viewer/interactions.cljs b/frontend/src/app/main/ui/viewer/interactions.cljs index 6e5645538..c570f091a 100644 --- a/frontend/src/app/main/ui/viewer/interactions.cljs +++ b/frontend/src/app/main/ui/viewer/interactions.cljs @@ -5,6 +5,7 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.viewer.interactions + (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] [app.common.data.macros :as dm] @@ -18,6 +19,7 @@ [app.main.data.viewer :as dv] [app.main.store :as st] [app.main.ui.components.dropdown :refer [dropdown]] + [app.main.ui.context :as ctx] [app.main.ui.hooks :as h] [app.main.ui.icons :as i] [app.main.ui.viewer.shapes :as shapes] @@ -191,77 +193,144 @@ (mf/defc flows-menu {::mf/wrap [mf/memo]} [{:keys [page index]}] - (let [flows (dm/get-in page [:options :flows]) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + flows (dm/get-in page [:options :flows]) frames (:frames page) frame (get frames index) - current-flow (mf/use-state - (ctp/get-frame-flow flows (:id frame))) + current-flow* (mf/use-state + #(ctp/get-frame-flow flows (:id frame))) - show-dropdown? (mf/use-state false) - toggle-dropdown (mf/use-fn #(swap! show-dropdown? not)) - hide-dropdown (mf/use-fn #(reset! show-dropdown? false)) + current-flow (deref current-flow*) + + show-dropdown?* (mf/use-state false) + show-dropdown? (deref show-dropdown?*) + toggle-dropdown (mf/use-fn #(swap! show-dropdown?* not)) + hide-dropdown (mf/use-fn #(reset! show-dropdown?* false)) select-flow (mf/use-callback - (fn [flow] - (reset! current-flow flow) - (st/emit! (dv/go-to-frame (:starting-frame flow)))))] + (fn [event] + (let [flow (-> (dom/get-current-target event) + (dom/get-data "value") + (d/read-string))] + (reset! current-flow* flow) + (st/emit! (dv/go-to-frame (:starting-frame flow))))))] (when (seq flows) - [:div.view-options {:on-click toggle-dropdown} - [:span.icon i/play] - [:span.label (:name @current-flow)] - [:span.icon i/arrow-down] - [:& dropdown {:show @show-dropdown? - :on-close hide-dropdown} - [:ul.dropdown.with-check - (for [[index flow] (d/enumerate flows)] - [:li {:key (dm/str "flow-" (:id flow) "-" index) - :class (dom/classnames :selected (= (:id flow) (:id @current-flow))) - :on-click #(select-flow flow)} - [:span.icon i/tick] - [:span.label (:name flow)]])]]]))) + (if new-css-system + [:div {:on-click toggle-dropdown + :class (stl/css :view-options)} + [:span {:class (stl/css :icon)} i/play-refactor] + [:span {:class (stl/css :dropdown-title)} (:name current-flow)] + [:span {:class (stl/css :icon-dropdown)} i/arrow-refactor] + [:& dropdown {:show show-dropdown? + :on-close hide-dropdown} + [:ul {:class (stl/css :dropdown)} + (for [[index flow] (d/enumerate flows)] + [:li {:key (dm/str "flow-" (:id flow) "-" index) + :class (stl/css-case :dropdown-element true + :selected (= (:id flow) (:id current-flow))) + ;; This is not a best practise, is not very performant Do not reproduce + :data-value (pr-str flow) + :on-click select-flow} + [:span {:class (stl/css :label)} (:name flow)] + (when (= (:id flow) (:id current-flow)) + [:span {:class (stl/css :icon)} i/tick-refactor])])]]] + + ;; OLD + [:div.view-options {:on-click toggle-dropdown} + [:span.icon i/play] + [:span.label (:name current-flow)] + [:span.icon i/arrow-down] + [:& dropdown {:show show-dropdown? + :on-close hide-dropdown} + [:ul.dropdown.with-check + (for [[index flow] (d/enumerate flows)] + [:li {:key (dm/str "flow-" (:id flow) "-" index) + :class (dom/classnames :selected (= (:id flow) (:id current-flow))) + ;; This is not a best practise, is not very performant Do not reproduce + :data-value (pr-str flow) + :on-click select-flow} + [:span.icon i/tick] + [:span.label (:name flow)]])]]])))) (mf/defc interactions-menu [{:keys [interactions-mode]}] - (let [show-dropdown? (mf/use-state false) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + show-dropdown? (mf/use-state false) toggle-dropdown (mf/use-fn #(swap! show-dropdown? not)) hide-dropdown (mf/use-fn #(reset! show-dropdown? false)) select-mode (mf/use-fn - (fn [event] - (let [mode (some-> (dom/get-current-target event) - (dom/get-data "mode") - (keyword))] - (dom/stop-propagation event) - (st/emit! (dv/set-interactions-mode mode)))))] + (fn [event] + (let [mode (some-> (dom/get-current-target event) + (dom/get-data "mode") + (keyword))] + (dom/stop-propagation event) + (st/emit! (dv/set-interactions-mode mode)))))] + (if new-css-system + [:div {:on-click toggle-dropdown + :class (stl/css :view-options)} + [:span {:class (stl/css :dropdown-title)} (tr "viewer.header.interactions")] + [:span {:class (stl/css :icon-dropdown)} i/arrow-refactor] + [:& dropdown {:show @show-dropdown? + :on-close hide-dropdown} + [:ul {:class (stl/css :dropdown)} + [:li {:class (stl/css-case :dropdown-element true + :selected (= interactions-mode :hide)) + :on-click select-mode + :data-mode :hide} - [:div.view-options {:on-click toggle-dropdown} - [:span.label (tr "viewer.header.interactions")] - [:span.icon i/arrow-down] - [:& dropdown {:show @show-dropdown? - :on-close hide-dropdown} - [:ul.dropdown.with-check - [:li {:class (dom/classnames :selected (= interactions-mode :hide)) - :on-click select-mode - :data-mode :hide} - [:span.icon i/tick] - [:span.label (tr "viewer.header.dont-show-interactions")]] + [:span {:class (stl/css :label)} (tr "viewer.header.dont-show-interactions")] + (when (= interactions-mode :hide) + [:span {:class (stl/css :icon)} i/tick-refactor])] - [:li {:class (dom/classnames :selected (= interactions-mode :show)) - :on-click select-mode - :data-mode :show} - [:span.icon i/tick] - [:span.label (tr "viewer.header.show-interactions")]] + [:li {:class (stl/css-case :dropdown-element true + :selected (= interactions-mode :show)) + :on-click select-mode + :data-mode :show} + [:span {:class (stl/css :label)} (tr "viewer.header.show-interactions")] + (when (= interactions-mode :show) + [:span {:class (stl/css :icon)} i/tick-refactor])] - [:li {:class (dom/classnames :selected (= interactions-mode :show-on-click)) - :on-click select-mode - :data-mode :show-on-click} - [:span.icon i/tick] - [:span.label (tr "viewer.header.show-interactions-on-click")]]]]])) + [:li {:class (stl/css-case :dropdown-element true + :selected (= interactions-mode :show-on-click)) + :on-click select-mode + :data-mode :show-on-click} + + [:span {:class (stl/css :label)} (tr "viewer.header.show-interactions-on-click")] + (when (= interactions-mode :show-on-click) + [:span {:class (stl/css :icon)} i/tick-refactor])]]]] + + + + [:div.view-options {:on-click toggle-dropdown} + [:span.label (tr "viewer.header.interactions")] + [:span.icon i/arrow-down] + [:& dropdown {:show @show-dropdown? + :on-close hide-dropdown} + [:ul.dropdown.with-check + [:li {:class (dom/classnames :selected (= interactions-mode :hide)) + :on-click select-mode + :data-mode :hide} + [:span.icon i/tick] + [:span.label (tr "viewer.header.dont-show-interactions")]] + + [:li {:class (dom/classnames :selected (= interactions-mode :show)) + :on-click select-mode + :data-mode :show} + [:span.icon i/tick] + [:span.label (tr "viewer.header.show-interactions")]] + + [:li {:class (dom/classnames :selected (= interactions-mode :show-on-click)) + :on-click select-mode + :data-mode :show-on-click} + [:span.icon i/tick] + [:span.label (tr "viewer.header.show-interactions-on-click")]]]]]))) + (defn animate-go-to-frame [animation current-viewport orig-viewport current-size orig-size wrapper-size] (case (:animation-type animation) diff --git a/frontend/src/app/main/ui/viewer/interactions.scss b/frontend/src/app/main/ui/viewer/interactions.scss new file mode 100644 index 000000000..07b12e3af --- /dev/null +++ b/frontend/src/app/main/ui/viewer/interactions.scss @@ -0,0 +1,80 @@ +// 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) KALEIDOS INC + +@use "common/refactor/common-refactor.scss" as *; + +.view-options { + @include titleTipography; + display: flex; + align-items: center; + position: relative; + gap: $s-4; + height: $s-32; + border-radius: $br-8; + background-color: var(--input-background-color); + padding: $s-8; + cursor: pointer; +} +.dropdown-title { + @include titleTipography; + flex-grow: 1; + color: var(--input-foreground-color-active); +} + +.label { + flex-grow: 1; + color: var(--input-foreground-color); +} + +.dropdown { + @extend .menu-dropdown; + right: $s-2; + top: calc($s-2 + $s-48); + width: $s-272; + padding: $s-6; +} + +.dropdown-element { + @extend .dropdown-element-base; + .icon { + @include flexCenter; + height: 100%; + width: $s-16; + svg { + @extend .button-icon-small; + stroke: var(--icon-foreground); + } + } + &:hover .label { + color: var(--input-foreground-color-active); + } +} + +.dropdown-element.selected { + .label { + color: var(--input-foreground-color-active); + } + .icon svg { + stroke: var(--input-foreground-color); + } +} + +.icon, +.icon-dropdown { + @include flexCenter; + height: 100%; + width: $s-16; + svg { + @extend .button-icon-small; + stroke: var(--icon-foreground); + } +} + +.icon-dropdown svg { + transform: rotate(90deg); +} + +// breakpoint 1013px diff --git a/frontend/src/app/main/ui/viewer/login.cljs b/frontend/src/app/main/ui/viewer/login.cljs index 46054f8f8..375066e13 100644 --- a/frontend/src/app/main/ui/viewer/login.cljs +++ b/frontend/src/app/main/ui/viewer/login.cljs @@ -5,6 +5,7 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.viewer.login + (:require-macros [app.main.style :as stl]) (:require [app.common.logging :as log] [app.main.data.modal :as modal] @@ -13,6 +14,7 @@ [app.main.ui.auth.login :refer [login-methods]] [app.main.ui.auth.recovery-request :refer [recovery-request-page]] [app.main.ui.auth.register :refer [register-methods register-validate-form register-success-page]] + [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -25,15 +27,31 @@ {::mf/register modal/components ::mf/register-as :login-register} [_] - (let [uri (. (. js/document -location) -href) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + uri (. (. js/document -location) -href) user-email (mf/use-state "") register-token (mf/use-state "") - current-section (mf/use-state :login) - set-current-section (mf/use-fn #(reset! current-section %)) + + current-section* (mf/use-state :login) + current-section (deref current-section*) + + set-current-section + (mf/use-fn #(reset! current-section* %)) + + set-section + (mf/use-fn + (fn [event] + (let [section (-> (dom/get-current-target event) + (dom/get-data "value") + (keyword))] + (set-current-section section)))) + + go-back-to-login (mf/use-fn #(set-current-section :login)) + main-section (or - (= @current-section :login) - (= @current-section :register) - (= @current-section :register-validate)) + (= current-section :login) + (= current-section :register) + (= current-section :register-validate)) close (fn [event] (dom/prevent-default event) @@ -49,57 +67,118 @@ (fn [data] (reset! register-token (:token data)) (set-current-section :register-validate))] + (mf/with-effect [] (swap! storage assoc :redirect-url uri)) - [:div.modal-overlay - [:div.modal-container.login-register - [:div.title - [:div.modal-close-button {:on-click close :title (tr "labels.close")} - i/close] - (when main-section - [:h2 (tr "labels.continue-with-penpot")])] - [:div.modal-bottom.auth-content + (if new-css-system + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-container)} + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-title)} (tr "labels.continue-with-penpot")] + [:button {:class (stl/css :modal-close-btn) + :title (tr "labels.close") + :on-click close} i/close-refactor]] - (case @current-section - :login - [:div.generic-form.login-form - [:div.form-container - [:& login-methods {:on-success-callback success-login}] - [:div.links - [:div.link-entry - [:a {:on-click #(set-current-section :recovery-request)} - (tr "auth.forgot-password")]] - [:div.link-entry - [:span (tr "auth.register") " "] - [:a {:on-click #(set-current-section :register)} - (tr "auth.register-submit")]]]]] + [:div {:class (stl/css :modal-content)} - :register - [:div.form-container - [:& register-methods {:on-success-callback success-register}] - [:div.links - [:div.link-entry - [:span (tr "auth.already-have-account") " "] - [:a {:on-click #(set-current-section :login)} - (tr "auth.login-here")]]]] + (case current-section + :login + [:div {:class (stl/css :form-container)} + [:& login-methods {:on-success-callback success-login :origin :viewer}] + [:div {:class (stl/css :links)} + [:div {:class (stl/css :link-entry)} + [:a {:on-click set-section + :data-value :recovery-request} + (tr "auth.forgot-password")]] + [:div {:class (stl/css :link-entry)} + [:span (tr "auth.register") " "] + [:a {:on-click set-section + :data-value :register} + (tr "auth.register-submit")]]]] - :register-validate - [:div.form-container - [:& register-validate-form {:params {:token @register-token} + :register + [:div {:class (stl/css :form-container)} + [:& register-methods {:on-success-callback success-register}] + [:div {:class (stl/css :links)} + [:div {:class (stl/css :link-entry)} + [:span (tr "auth.already-have-account") " "] + [:a {:on-click set-section + :data-value :login} + (tr "auth.login-here")]]]] + + :register-validate + [:div {:class (stl/css :form-container)} + [:& register-validate-form {:params {:token @register-token} + :on-success-callback success-email-sent}] + [:div {:class (stl/css :links)} + [:div {:class (stl/css :link-entry)} + [:a {:on-click set-section + :data-value :register} + (tr "labels.go-back")]]]] + + :recovery-request + [:& recovery-request-page {:go-back-callback go-back-to-login :on-success-callback success-email-sent}] - [:div.links - [:div.link-entry - [:a {:on-click #(set-current-section :register)} - (tr "labels.go-back")]]]] + :email-sent + [:div {:class (stl/css :form-container)} + [:& register-success-page {:params {:email @user-email}}]]) - :recovery-request - [:& recovery-request-page {:go-back-callback #(set-current-section :login) - :on-success-callback success-email-sent}] - :email-sent - [:div.form-container - [:& register-success-page {:params {:email @user-email}}]])] + (when main-section + [:div {:class (stl/css :links)} + [:& terms-login]])]]] - (when main-section - [:div.modal-footer.links - [:& terms-login]])]])) + + ;;OLD + [:div.modal-overlay + [:div.modal-container.login-register + [:div.title + [:div.modal-close-button {:on-click close :title (tr "labels.close")} + i/close] + (when main-section + [:h2 (tr "labels.continue-with-penpot")])] + + [:div.modal-bottom.auth-content + + (case current-section + :login + [:div.generic-form.login-form + [:div.form-container + [:& login-methods {:on-success-callback success-login}] + [:div.links + [:div.link-entry + [:a {:on-click #(set-current-section :recovery-request)} + (tr "auth.forgot-password")]] + [:div.link-entry + [:span (tr "auth.register") " "] + [:a {:on-click #(set-current-section :register)} + (tr "auth.register-submit")]]]]] + + :register + [:div.form-container + [:& register-methods {:on-success-callback success-register}] + [:div.links + [:div.link-entry + [:span (tr "auth.already-have-account") " "] + [:a {:on-click #(set-current-section :login)} + (tr "auth.login-here")]]]] + + :register-validate + [:div.form-container + [:& register-validate-form {:params {:token @register-token} + :on-success-callback success-email-sent}] + [:div.links + [:div.link-entry + [:a {:on-click #(set-current-section :register)} + (tr "labels.go-back")]]]] + + :recovery-request + [:& recovery-request-page {:go-back-callback #(set-current-section :login) + :on-success-callback success-email-sent}] + :email-sent + [:div.form-container + [:& register-success-page {:params {:email @user-email}}]])] + + (when main-section + [:div.modal-footer.links + [:& terms-login]])]]))) diff --git a/frontend/src/app/main/ui/viewer/login.scss b/frontend/src/app/main/ui/viewer/login.scss new file mode 100644 index 000000000..6e39b53f6 --- /dev/null +++ b/frontend/src/app/main/ui/viewer/login.scss @@ -0,0 +1,73 @@ +// 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) KALEIDOS INC + +@import "refactor/common-refactor.scss"; + +.modal-overlay { + @extend .modal-overlay-base; +} + +.modal-container { + @extend .modal-container-base; +} + +.modal-header { + margin-bottom: $s-24; +} + +.modal-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); +} + +.modal-close-btn { + @extend .modal-close-btn-base; +} + +.modal-content { + @include flexColumn; + @include titleTipography; + gap: $s-24; + max-height: $s-400; + width: $s-368; + overflow: hidden auto; + form { + display: flex; + flex-direction: column; + margin-bottom: 1.5rem; + gap: 0.75rem; + } +} + +.form-container { + display: flex; + justify-content: center; + flex-direction: column; + max-width: $s-368; +} + +.links { + position: relative; +} + +.link-entry { + display: flex; + flex-direction: column; + gap: $s-12; + + span { + text-align: center; + font-size: $fs-14; + color: var(--modal-text-foreground-color); + margin-top: $s-12; + } + a { + @extend .button-secondary; + height: $s-40; + text-transform: uppercase; + font-size: $fs-11; + } +} diff --git a/frontend/src/app/main/ui/viewer/share_link.cljs b/frontend/src/app/main/ui/viewer/share_link.cljs index a6662fa16..a12e30da8 100644 --- a/frontend/src/app/main/ui/viewer/share_link.cljs +++ b/frontend/src/app/main/ui/viewer/share_link.cljs @@ -5,6 +5,7 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.viewer.share-link + (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] [app.common.data.macros :as dm] @@ -16,6 +17,8 @@ [app.main.data.modal :as modal] [app.main.refs :as refs] [app.main.store :as st] + [app.main.ui.components.select :refer [select]] + [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -37,7 +40,8 @@ ::mf/register-as :share-link ::mf/wrap-props false} [{:keys [file page]}] - (let [current-page page + (let [new-css-system (mf/use-ctx ctx/new-css-system) + current-page page current-page-id (:id page) slinks (mf/deref refs/share-links) router (mf/deref refs/router) @@ -151,135 +155,284 @@ (fn [_] (swap! perms-visible* not)) - on-who-change - (fn [type event] - (let [target (dom/get-target event) - value (dom/get-value target) - value (keyword value)] - (reset! confirm* false) - (if (= type :comment) - (swap! options* assoc :who-comment (d/name value)) - (swap! options* assoc :who-inspect (d/name value)))))] + on-inspect-change + (fn [value] + (reset! confirm* false) + (swap! options* assoc :who-inspect value)) + + on-comment-change + (fn [value] + (reset! confirm* false) + (swap! options* assoc :who-comment value))] + + (if new-css-system + [:div {:class (stl/css :share-modal)} + [:div {:class (stl/css :share-link-dialog)} + [:div {:class (stl/css :share-link-header)} + [:h2 {:class (stl/css :share-link-title)} + (tr "common.share-link.title")] + [:button {:class (stl/css :modal-close-button) + :on-click on-close + :title (tr "labels.close")} + i/close-refactor]] + [:div {:class (stl/css :modal-content)} + [:div {:class (stl/css :share-link-section)} + (when (and (not confirm?) (some? current-link)) + [:div {:class (stl/css :custon-input-wrapper)} + [:input {:class (stl/css :input-text) + :type "text" + :value (or current-link "") + :placeholder (tr "common.share-link.placeholder") + :read-only true}] + + [:button {:class (stl/css :copy-button) + :title (tr "viewer.header.share.copy-link") + :on-click copy-link} + i/clipboard-refactor]]) + + [:div {:class (stl/css :hint-wrapper)} + (when (not ^boolean confirm?) + [:div {:class (stl/css :hint)} (tr "common.share-link.permissions-hint")]) + (cond + (true? confirm?) + [:div {:class (stl/css :confirm-dialog)} + [:div {:class (stl/css :description)} + (tr "common.share-link.confirm-deletion-link-description")] + [:div {:class (stl/css :actions)} + [:input {:type "button" + :class (stl/css :button-cancel) + :on-click #(reset! confirm* false) + :value (tr "labels.cancel")}] + [:input {:type "button" + :class (stl/css :button-danger) + :on-click delete-link + :value (tr "common.share-link.destroy-link")}]]] + + (some? current-link) + [:input + {:type "button" + :class (stl/css :button-danger) + :on-click try-delete-link + :value (tr "common.share-link.destroy-link")}] + + :else + [:input + {:type "button" + :class (stl/css :button-active) + :on-click create-link + :value (tr "common.share-link.get-link")}])]] + - [:div.modal-overlay.transparent.share-modal - [:div.modal-container.share-link-dialog - [:div.modal-content.initial - [:div.title - [:h2 (tr "common.share-link.title")] - [:div.modal-close-button - {:on-click on-close - :title (tr "labels.close")} - i/close]]] - [:div.modal-content - [:div.share-link-section - (when (and (not confirm?) (some? current-link)) - [:div.custom-input.with-icon - [:input {:type "text" - :value (or current-link "") - :placeholder (tr "common.share-link.placeholder") - :read-only true}] - [:div.help-icon {:title (tr "viewer.header.share.copy-link") - :on-click copy-link} - i/copy]]) - [:div.hint-wrapper (when (not ^boolean confirm?) - [:div.hint (tr "common.share-link.permissions-hint")]) - (cond - (true? confirm?) - [:div.confirm-dialog - [:div.description (tr "common.share-link.confirm-deletion-link-description")] - [:div.actions + [:div {:class (stl/css :permissions-section)} + [:button {:class (stl/css :manage-permissions) + :on-click toggle-perms-visibility} + [:span {:class (stl/css-case :icon true + :rotated perms-visible?)} + i/arrow-refactor] + (tr "common.share-link.manage-ops")] + + (when ^boolean perms-visible? + [:* + (let [all-selected? (:all-pages options) + pages (->> (get-in file [:data :pages]) + (map #(get-in file [:data :pages-index %]))) + selected (:pages options)] + [:div {:class (stl/css :view-mode)} + [:div {:class (stl/css :subtitle)} + (tr "common.share-link.permissions-pages")] + [:div {:class (stl/css :items)} + (if (= 1 (count pages)) + + [:div {:class (stl/css :checkbox-wrapper)} + [:input {:type "checkbox" + :id (dm/str "page-" current-page-id) + :data-page-id (dm/str current-page-id) + :on-change on-mark-checked-page + :checked true}] + [:label {:for (str "page-" current-page-id)} (:name current-page)] + [:span {:class (stl/css-case :checkobox-tick true + :global/checked true)} + i/status-tick-refactor] + [:span (str " " (tr "common.share-link.current-tag"))]] + + [:* + [:div {:class (stl/css :select-all-row)} + [:div {:class (stl/css :checkbox-wrapper)} + [:label {:for "view-all" + :class (stl/css :select-all-label)} + [:span {:class (stl/css-case :global/checked all-selected?)} + (when all-selected? + i/status-tick-refactor)] + (tr "common.share-link.view-all") + [:input {:type "checkbox" + :id "view-all" + :checked all-selected? + :name "pages-mode" + :on-change on-toggle-all}]]] + + [:span {:class (stl/css :count-pages)} + (tr "common.share-link.page-shared" (i18n/c (count selected)))]] + + [:ul {:class (stl/css :pages-selection)} + (for [{:keys [id name]} pages] + [:li {:class (stl/css :checkbox-wrapper) + :key (dm/str id)} + [:label {:for (dm/str "page-" id)} + [:span {:class (stl/css-case :global/checked (contains? selected id))} + (when (contains? selected id) + i/status-tick-refactor)] + name + (when (= current-page-id id) + [:div {:class (stl/css :current-tag)} (dm/str " " (tr "common.share-link.current-tag"))]) + [:input {:type "checkbox" + :id (dm/str "page-" id) + :data-page-id (dm/str id) + :on-change on-mark-checked-page + :checked (contains? selected id)}]]])]])]]) + + [:div {:class (stl/css :access-mode)} + [:div {:class (stl/css :subtitle)} + (tr "common.share-link.permissions-can-comment")] + [:div {:class (stl/css :items)} + [:& select + {:class (stl/css :who-comment-select) + :default-value (dm/str (:who-comment options)) + :options [{:value "team" :label (tr "common.share-link.team-members")} + {:value "all" :label (tr "common.share-link.all-users")}] + :on-change on-comment-change}]]] + [:div {:class (stl/css :inspect-mode)} + [:div {:class (stl/css :subtitle)} + (tr "common.share-link.permissions-can-inspect")] + [:div {:class (stl/css :items)} + [:& select + {:class (stl/css :who-inspect-select) + :default-value (dm/str (:who-inspect options)) + :options [{:value "team" :label (tr "common.share-link.team-members")} + {:value "all" :label (tr "common.share-link.all-users")}] + :on-change on-inspect-change}]]]])])]]] + + + ;;OLD + [:div.modal-overlay.transparent.share-modal + [:div.modal-container.share-link-dialog + [:div.modal-content.initial + [:div.title + [:h2 (tr "common.share-link.title")] + [:div.modal-close-button + {:on-click on-close + :title (tr "labels.close")} + i/close]]] + [:div.modal-content + [:div.share-link-section + (when (and (not confirm?) (some? current-link)) + [:div.custom-input.with-icon + [:input {:type "text" + :value (or current-link "") + :placeholder (tr "common.share-link.placeholder") + :read-only true}] + [:div.help-icon {:title (tr "viewer.header.share.copy-link") + :on-click copy-link} + i/copy]]) + [:div.hint-wrapper + (when (not ^boolean confirm?) + [:div.hint (tr "common.share-link.permissions-hint")]) + (cond + (true? confirm?) + [:div.confirm-dialog + [:div.description (tr "common.share-link.confirm-deletion-link-description")] + [:div.actions + [:input.btn-secondary + {:type "button" + :on-click #(reset! confirm* false) + :value (tr "labels.cancel")}] + [:input.btn-danger + {:type "button" + :on-click delete-link + :value (tr "common.share-link.destroy-link")}]]] + + (some? current-link) [:input.btn-secondary {:type "button" - :on-click #(reset! confirm* false) - :value (tr "labels.cancel")}] - [:input.btn-danger + :class "primary" + :on-click try-delete-link + :value (tr "common.share-link.destroy-link")}] + + :else + [:input.btn-primary {:type "button" - :on-click delete-link - :value (tr "common.share-link.destroy-link")}]]] - - (some? current-link) - [:input.btn-secondary - {:type "button" - :class "primary" - :on-click try-delete-link - :value (tr "common.share-link.destroy-link")}] - - :else - [:input.btn-primary - {:type "button" - :class "primary" - :on-click create-link - :value (tr "common.share-link.get-link")}])]]] - [:div.modal-content.ops-section - [:div.manage-permissions - {:on-click toggle-perms-visibility} - [:span.icon i/picker-hsv] - [:div.title (tr "common.share-link.manage-ops")]] - (when ^boolean perms-visible? - [:* - (let [all-selected? (:all-pages options) - pages (->> (get-in file [:data :pages]) - (map #(get-in file [:data :pages-index %]))) - selected (:pages options)] - [:* - [:div.view-mode - [:div.subtitle - [:span.icon i/play] - (tr "common.share-link.permissions-pages")] - [:div.items - (if (= 1 (count pages)) - [:div.input-checkbox.check-primary - [:input {:type "checkbox" - :id (dm/str "page-" current-page-id) - :data-page-id (dm/str current-page-id) - :on-change on-mark-checked-page - :checked true}] - [:label {:for (str "page-" current-page-id)} (:name current-page)] - [:span (str " " (tr "common.share-link.current-tag"))]] - - [:* - [:div.row + :class "primary" + :on-click create-link + :value (tr "common.share-link.get-link")}])]]] + [:div.modal-content.ops-section + [:div.manage-permissions + {:on-click toggle-perms-visibility} + [:span.icon i/picker-hsv] + [:div.title (tr "common.share-link.manage-ops")]] + (when ^boolean perms-visible? + [:* + (let [all-selected? (:all-pages options) + pages (->> (get-in file [:data :pages]) + (map #(get-in file [:data :pages-index %]))) + selected (:pages options)] + [:* + [:div.view-mode + [:div.subtitle + [:span.icon i/play] + (tr "common.share-link.permissions-pages")] + [:div.items + (if (= 1 (count pages)) [:div.input-checkbox.check-primary [:input {:type "checkbox" - :id "view-all" - :checked all-selected? - :name "pages-mode" - :on-change on-toggle-all}] - [:label {:for "view-all"} (tr "common.share-link.view-all")]] - [:span.count-pages (tr "common.share-link.page-shared" (i18n/c (count selected)))]] + :id (dm/str "page-" current-page-id) + :data-page-id (dm/str current-page-id) + :on-change on-mark-checked-page + :checked true}] + [:label {:for (str "page-" current-page-id)} (:name current-page)] + [:span (str " " (tr "common.share-link.current-tag"))]] - [:ul.pages-selection - (for [{:keys [id name]} pages] - [:li.input-checkbox.check-primary {:key (dm/str id)} + [:* + [:div.row + [:div.input-checkbox.check-primary [:input {:type "checkbox" - :id (dm/str "page-" id) - :data-page-id (dm/str id) - :on-change on-mark-checked-page - :checked (contains? selected id)}] - (if (= current-page-id id) - [:* - [:label {:for (dm/str "page-" id)} name] - [:span.current-tag (dm/str " " (tr "common.share-link.current-tag"))]] - [:label {:for (dm/str "page-" id)} name])])]])]]]) - [:div.access-mode - [:div.subtitle - [:span.icon i/chat] - (tr "common.share-link.permissions-can-comment")] - [:div.items - [:select.input-select {:on-change (partial on-who-change :comment) - :value (:who-comment options)} - [:option {:value "team"} (tr "common.share-link.team-members")] - [:option {:value "all"} (tr "common.share-link.all-users")]]]] - [:div.inspect-mode - [:div.subtitle - [:span.icon i/code] - (tr "common.share-link.permissions-can-inspect")] - [:div.items - [:select.input-select {:on-change (partial on-who-change :inspect) - :value (:who-inspect options)} - [:option {:value "team"} (tr "common.share-link.team-members")] - [:option {:value "all"} (tr "common.share-link.all-users")]]]]])]]])) + :id "view-all" + :checked all-selected? + :name "pages-mode" + :on-change on-toggle-all}] + [:label {:for "view-all"} (tr "common.share-link.view-all")]] + [:span.count-pages (tr "common.share-link.page-shared" (i18n/c (count selected)))]] + + [:ul.pages-selection + (for [{:keys [id name]} pages] + [:li.input-checkbox.check-primary {:key (dm/str id)} + [:input {:type "checkbox" + :id (dm/str "page-" id) + :data-page-id (dm/str id) + :on-change on-mark-checked-page + :checked (contains? selected id)}] + (if (= current-page-id id) + [:* + [:label {:for (dm/str "page-" id)} name] + [:span.current-tag (dm/str " " (tr "common.share-link.current-tag"))]] + [:label {:for (dm/str "page-" id)} name])])]])]]]) + [:div.access-mode + [:div.subtitle + [:span.icon i/chat] + (tr "common.share-link.permissions-can-comment")] + [:div.items + [:select.input-select {:on-change on-comment-change + :value (:who-comment options)} + [:option {:value "team"} (tr "common.share-link.team-members")] + [:option {:value "all"} (tr "common.share-link.all-users")]]]] + [:div.inspect-mode + [:div.subtitle + [:span.icon i/code] + (tr "common.share-link.permissions-can-inspect")] + [:div.items + [:select.input-select {:on-change on-inspect-change + :value (:who-inspect options)} + [:option {:value "team"} (tr "common.share-link.team-members")] + [:option {:value "all"} (tr "common.share-link.all-users")]]]]])]]]))) diff --git a/frontend/src/app/main/ui/viewer/share_link.scss b/frontend/src/app/main/ui/viewer/share_link.scss new file mode 100644 index 000000000..dc0fbc7b4 --- /dev/null +++ b/frontend/src/app/main/ui/viewer/share_link.scss @@ -0,0 +1,176 @@ +// 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) KALEIDOS INC + +@import "refactor/common-refactor.scss"; + +.share-modal { + display: block; + position: absolute; + top: $s-52; + right: $s-12; + left: calc(100vw - $s-512); + z-index: $z-index-modal; +} +.share-link-dialog { + @extend .modal-container-base; + min-height: unset; +} + +.share-link-header { + margin-bottom: $s-24; +} + +.share-link-title { + @include tabTitleTipography; + color: var(--modal-title-foreground-color); +} + +.modal-close-button { + @extend .modal-close-btn-base; +} + +.modal-content { + @include titleTipography; + @include flexColumn; + gap: $s-24; +} + +.share-link-section { + @include flexColumn; + gap: $s-8; +} + +.hint-wrapper { + @include flexRow; +} +.hint { + flex-grow: 1; +} +.custon-input-wrapper { + @include flexRow; + border-radius: $br-8; + height: $s-32; + width: 100%; + background-color: var(--input-background-color); +} + +.input-text { + @extend .input-element; + color: var(--input-foreground-color-active); + padding-left: $s-8; + margin: 0; + flex-grow: 1; + &:focus { + outline: none; + border: $s-1 solid var(--input-border-color-active); + } +} +.copy-button { + @extend .button-secondary; + @include flexRow; + gap: $s-8; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; + stroke: var(--icon-foreground-hover); + } +} + +.description { + @include titleTipography; + margin-bottom: $s-24; +} + +.actions { + @include flexRow; + justify-content: flex-end; +} + +.button-active { + @extend .modal-accept-btn; +} +.button-cancel { + @extend .modal-cancel-btn; +} +.button-danger { + @extend .modal-danger-btn; +} + +.permissions-section { + @include flexColumn; + gap: $s-8; +} + +.manage-permissions { + @include buttonStyle; + @include tabTitleTipography; + color: var(--menu-foreground-color-rest); + height: $s-32; + display: flex; + align-items: center; + padding: 0; +} + +.icon { + @include flexCenter; + margin-right: $s-6; + svg { + @extend .button-icon; + stroke: var(--icon-foreground); + } + &.rotated { + transform: rotate(90deg); + } +} +.view-mode, +.access-mode, +.inspect-mode { + display: flex; + width: 100%; +} +.subtitle { + color: var(--modal-text-foreground-color); + display: flex; + align-items: center; + justify-content: flex-start; + width: $s-136; + height: $s-32; +} + +.items { + flex-grow: 1; + color: var(--input-foreground-color-active); +} +.select-all-row { + @include flexRow; + justify-content: space-between; + height: $s-32; + border-bottom: $s-1 solid var(--input-border-color-disabled); +} +.select-all-label { + color: var(--input-foreground-color-active); +} +.pages-selection { + margin: 0; + li { + border-bottom: $s-1 solid var(--input-border-color-disabled); + } + li:last-child { + border-bottom: none; + } +} +.count-pages, +.current-tag { + @include titleTipography; + color: var(--input-foreground-color); +} + +.checkbox-wrapper { + @extend .input-checkbox; + height: $s-32; + padding: 0; +} diff --git a/frontend/src/app/main/ui/viewer/thumbnails.cljs b/frontend/src/app/main/ui/viewer/thumbnails.cljs index 086cbe047..8e95c5727 100644 --- a/frontend/src/app/main/ui/viewer/thumbnails.cljs +++ b/frontend/src/app/main/ui/viewer/thumbnails.cljs @@ -5,6 +5,7 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.viewer.thumbnails + (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] [app.common.data.macros :as dm] @@ -13,6 +14,7 @@ [app.main.data.viewer :as dv] [app.main.render :as render] [app.main.store :as st] + [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -22,7 +24,8 @@ (mf/defc thumbnails-content [{:keys [children expanded? total] :as props}] - (let [container (mf/use-ref) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + container (mf/use-ref) width (mf/use-var (.. js/document -documentElement -clientWidth)) element-width (mf/use-var 152) @@ -56,45 +59,96 @@ (reset! width (obj/get dom "clientWidth"))))] (mf/use-effect on-mount) - (if expanded? - [:div.thumbnails-content - [:div.thumbnails-list-expanded children]] - [:div.thumbnails-content - [:div.left-scroll-handler {:on-click on-left-arrow-click} i/arrow-slide] - [:div.right-scroll-handler {:on-click on-right-arrow-click} i/arrow-slide] - [:div.thumbnails-list {:ref container :on-wheel on-scroll} - [:div.thumbnails-list-inside {:style {:right (str (* @offset 152) "px")}} - children]]]))) + (if new-css-system + (if expanded? + [:div {:class (stl/css :thumbnails-content)} + [:div {:class (stl/css :thumbnails-list-expanded)} children]] + + [:div {:class (stl/css :thumbnails-content)} + [:button {:class (stl/css :left-scroll-handler) + :on-click on-left-arrow-click} i/arrow-refactor] + [:button {:class (stl/css :right-scroll-handler) + :on-click on-right-arrow-click} i/arrow-refactor] + + [:div {:class (stl/css :thumbnails-list) + :ref container + :on-wheel on-scroll} + [:div {:class (stl/css :thumbnails-list-inside) + :style {:right (str (* @offset 152) "px")}} + children]]]) + + + + (if expanded? + [:div.thumbnails-content + [:div.thumbnails-list-expanded children]] + + [:div.thumbnails-content + [:div.left-scroll-handler {:on-click on-left-arrow-click} i/arrow-slide] + [:div.right-scroll-handler {:on-click on-right-arrow-click} i/arrow-slide] + [:div.thumbnails-list {:ref container :on-wheel on-scroll} + [:div.thumbnails-list-inside {:style {:right (str (* @offset 152) "px")}} + children]]])))) (mf/defc thumbnails-summary [{:keys [on-toggle-expand on-close total] :as props}] - [:div.thumbnails-summary - [:span.counter (tr "labels.num-of-frames" (i18n/c total))] - [:span.buttons - [:span.btn-expand {:on-click on-toggle-expand} i/arrow-down] - [:span.btn-close {:on-click on-close} i/close]]]) + (let [new-css-system (mf/use-ctx ctx/new-css-system)] + (if new-css-system + [:div {:class (stl/css :thumbnails-summary)} + [:span {:class (stl/css :counter)} + (tr "labels.num-of-frames" (i18n/c total))] + [:span {:class (stl/css :actions)} + [:button {:class (stl/css :expand-btn) + :on-click on-toggle-expand} i/arrow-refactor] + [:button {:class (stl/css :close-btn) + :on-click on-close} i/close-refactor]]] + + + [:div.thumbnails-summary + [:span.counter (tr "labels.num-of-frames" (i18n/c total))] + [:span.buttons + [:span.btn-expand {:on-click on-toggle-expand} i/arrow-down] + [:span.btn-close {:on-click on-close} i/close]]]))) (mf/defc thumbnail-item {::mf/wrap [mf/memo #(mf/deferred % ts/idle-then-raf)]} [{:keys [selected? frame on-click index objects page-id thumbnail-data]}] - (let [children-ids (cfh/get-children-ids objects (:id frame)) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + children-ids (cfh/get-children-ids objects (:id frame)) children-bounds (gsh/shapes->rect (concat [frame] (->> children-ids (keep (d/getf objects)))))] - [:div.thumbnail-item {:on-click #(on-click % index)} - [:div.thumbnail-preview - {:class (dom/classnames :selected selected?)} - [:& render/frame-svg {:frame (-> frame - (assoc :thumbnail (get thumbnail-data (dm/str page-id (:id frame)))) - (assoc :children-bounds children-bounds)) - :objects objects - :use-thumbnails true}]] - [:div.thumbnail-info - [:span.name {:title (:name frame)} (:name frame)]]])) + + (if new-css-system + [:button {:class (stl/css :thumbnail-item) + :on-click #(on-click % index)} + [:div {:class (stl/css-case :thumbnail-preview true + :selected selected?)} + [:& render/frame-svg {:frame (-> frame + (assoc :thumbnail (get thumbnail-data (dm/str page-id (:id frame)))) + (assoc :children-bounds children-bounds)) + :objects objects + :use-thumbnails true}]] + [:div {:class (stl/css :thumbnail-info) + :title (:name frame)} + (:name frame)]] + + + [:div.thumbnail-item {:on-click #(on-click % index)} + [:div.thumbnail-preview + {:class (dom/classnames :selected selected?)} + [:& render/frame-svg {:frame (-> frame + (assoc :thumbnail (get thumbnail-data (dm/str page-id (:id frame)))) + (assoc :children-bounds children-bounds)) + :objects objects + :use-thumbnails true}]] + [:div.thumbnail-info + [:span.name {:title (:name frame)} (:name frame)]]]))) (mf/defc thumbnails-panel [{:keys [frames page index show? thumbnail-data] :as props}] - (let [expanded? (mf/use-state false) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + expanded? (mf/use-state false) container (mf/use-ref) objects (:objects page) @@ -109,24 +163,48 @@ (st/emit! (dv/go-to-frame-by-index index)) (when @expanded? (on-close))))] + (if new-css-system + [:section + {:class (stl/css-case :viewer-thumbnails true + :expanded @expanded?) + ;; This is better as an inline-style so it won't make a reflow of every frame inside + :style {:display (when (not show?) "none")} + :ref container} - [:section.viewer-thumbnails - {;; This is better as an inline-style so it won't make a reflow of every frame inside - :style {:display (when (not show?) "none")} - :class (dom/classnames :expanded @expanded?) - :ref container} + [:& thumbnails-summary {:on-toggle-expand #(swap! expanded? not) + :on-close on-close + :total (count frames)}] + [:& thumbnails-content {:expanded? @expanded? + :total (count frames)} + (for [[i frame] (d/enumerate frames)] + [:& thumbnail-item {:index i + :key (dm/str (:id frame) "-" i) + :frame frame + :page-id (:id page) + :objects objects + :on-click on-item-click + :selected? (= i index) + :thumbnail-data thumbnail-data}])]] - [:& thumbnails-summary {:on-toggle-expand #(swap! expanded? not) - :on-close on-close - :total (count frames)}] - [:& thumbnails-content {:expanded? @expanded? - :total (count frames)} - (for [[i frame] (d/enumerate frames)] - [:& thumbnail-item {:index i - :key (dm/str (:id frame) "-" i) - :frame frame - :page-id (:id page) - :objects objects - :on-click on-item-click - :selected? (= i index) - :thumbnail-data thumbnail-data}])]])) + + + [:section.viewer-thumbnails + {;; This is better as an inline-style so it won't make a reflow of every frame inside + :style {:display (when (not show?) "none")} + :class (dom/classnames :expanded @expanded?) + :ref container} + + [:& thumbnails-summary {:on-toggle-expand #(swap! expanded? not) + :on-close on-close + :total (count frames)}] + [:& thumbnails-content {:expanded? @expanded? + :total (count frames)} + (for [[i frame] (d/enumerate frames)] + [:& thumbnail-item {:index i + :key (dm/str (:id frame) "-" i) + :frame frame + :page-id (:id page) + :objects objects + :on-click on-item-click + :selected? (= i index) + :thumbnail-data thumbnail-data}])]]))) diff --git a/frontend/src/app/main/ui/viewer/thumbnails.scss b/frontend/src/app/main/ui/viewer/thumbnails.scss new file mode 100644 index 000000000..982a9b468 --- /dev/null +++ b/frontend/src/app/main/ui/viewer/thumbnails.scss @@ -0,0 +1,152 @@ +// 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) KALEIDOS INC + +@use "common/refactor/common-refactor.scss" as *; + +.viewer-thumbnails { + background-color: var(--viewer-background-color); + grid-row: 1 / span 1; + grid-column: 1 / span 1; + overflow: hidden; + display: flex; + flex-direction: column; + z-index: $z-index-10; +} + +.expanded { + grid-row: 1 / span 2; + + .expand-btn svg { + transform: rotate(-90deg); + } +} + +.thumbnails-summary { + display: flex; + justify-content: space-between; + align-items: center; + height: $s-32; + margin: $s-24 $s-24 0 $s-24; +} + +.counter { + @include titleTipography; + color: var(--viewer-thumbnails-control-foreground-color); +} + +.actions { + @include flexRow; + width: $s-60; +} + +.expand-btn, +.close-btn { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; + } +} + +.expand-btn svg { + transform: rotate(90deg); +} + +.thumbnails-content { + display: grid; + grid-template-columns: $s-40 auto $s-40; + grid-template-rows: auto; +} + +.thumbnails-list-expanded { + grid-column: 1 / span 3; + grid-row: 1 / span 1; + display: flex; + flex-wrap: wrap; + overflow: hidden; +} + +.right-scroll-handler, +.left-scroll-handler { + @extend .button-tertiary; + @include flexCenter; + grid-column: 3 / span 1; + grid-row: 1 / span 1; + width: $s-32; + height: $s-60; + margin: auto 0; + z-index: $z-index-10; + opacity: 0; + &:hover { + opacity: 1; + } + svg { + @extend .button-icon; + stroke: var(--icon-foreground); + } +} + +.left-scroll-handler { + grid-column: 1 / span 1; + grid-row: 1 / span 1; + svg { + transform: rotate(180deg); + } +} + +.thumbnails-list { + grid-column: 1 / span 3; + grid-row: 1 / span 1; + display: flex; + flex-wrap: nowrap; + overflow: hidden; +} + +.thumbnails-list-inside { + display: flex; + position: relative; +} + +.thumbnail-item { + @include buttonStyle; + display: flex; + flex-direction: column; + padding: $s-16; +} + +.thumbnail-preview { + @include flexCenter; + width: $s-132; + min-height: $s-132; + height: $s-132; + padding: $s-4; + + svg { + width: 100%; + height: 100%; + } + + &.selected { + background-color: var(--viewer-thumbnail-background-color-selected); + border-radius: $br-8; + } + + &:hover { + border: $s-1 solid var(--viewer-thumbnail-border-color); + border-radius: $br-8; + } +} + +.thumbnail-info { + @include titleTipography; + @include textEllipsis; + text-align: center; + color: var(--viewer-thumbnails-control-foreground-color); + padding: $s-8 0; + width: 100%; + max-width: $s-132; +} diff --git a/frontend/src/app/main/ui/workspace/comments.cljs b/frontend/src/app/main/ui/workspace/comments.cljs index ca7a4d4e8..121ca11d9 100644 --- a/frontend/src/app/main/ui/workspace/comments.cljs +++ b/frontend/src/app/main/ui/workspace/comments.cljs @@ -86,12 +86,10 @@ [:li {:class (dom/classnames :selected (= :pending cshow)) :on-click update-show} [:span.icon i/tick] - [:span.label (tr "labels.hide-resolved-comments")]]]) - - )) + [:span.label (tr "labels.hide-resolved-comments")]]]))) (mf/defc comments-sidebar - [{:keys [users threads page-id]}] + [{:keys [users threads page-id from-viewer]}] (let [new-css-system (mf/use-ctx ctx/new-css-system) threads-map (mf/deref refs/threads-ref) profile (mf/deref refs/profile) @@ -111,8 +109,11 @@ close-section (mf/use-fn - (mf/deps) - #(st/emit! :interrupt (dw/deselect-all true))) + (mf/deps from-viewer) + (fn [] + (if from-viewer + (st/emit! (dcm/update-options {:show-sidebar? false})) + (st/emit! :interrupt (dw/deselect-all true))))) tgroups (->> threads (dcm/group-threads-by-page)) @@ -121,7 +122,6 @@ toggle-mode-selector (mf/use-fn - (mf/deps) (fn [event] (dom/stop-propagation event) (swap! state* not))) @@ -147,18 +147,17 @@ :on-click close-section} i/close-refactor]] - (when (seq tgroups) - [:button {:class (stl/css :mode-dropdown-wrapper) - :on-click toggle-mode-selector} + [:button {:class (stl/css :mode-dropdown-wrapper) + :on-click toggle-mode-selector} - [:span {:class (stl/css :mode-label)} (case (:mode local) - (nil :all) (tr "labels.show-all-comments") - :yours (tr "labels.show-your-comments"))] - [:div {:class (stl/css :icon)} i/arrow-refactor]] + [:span {:class (stl/css :mode-label)} (case (:mode local) + (nil :all) (tr "labels.show-all-comments") + :yours (tr "labels.show-your-comments"))] + [:div {:class (stl/css :icon)} i/arrow-refactor]] - [:& dropdown {:show options? - :on-close #(reset! state* false)} - [:& sidebar-options {:local local}]]) + [:& dropdown {:show options? + :on-close #(reset! state* false)} + [:& sidebar-options {:local local}]] [:div {:class (stl/css :comments-section-content)} diff --git a/frontend/src/app/main/ui/workspace/right_header.cljs b/frontend/src/app/main/ui/workspace/right_header.cljs index 2359cf725..0e46f05ca 100644 --- a/frontend/src/app/main/ui/workspace/right_header.cljs +++ b/frontend/src/app/main/ui/workspace/right_header.cljs @@ -126,14 +126,14 @@ (tr "workspace.header.zoom-fit-all") [:span {:class (stl/css :shortcuts)} (for [sc (scd/split-sc (sc/get-tooltip :fit-all))] - [:span {:class (dom/classnames (stl/css :shortcut-key) true) + [:span {:class (stl/css :shortcut-key) :key (str "zoom-fit-" sc)} sc])]] [:li {:class (stl/css :zoom-option) :on-click on-zoom-selected} (tr "workspace.header.zoom-selected") [:span {:class (stl/css :shortcuts)} (for [sc (scd/split-sc (sc/get-tooltip :zoom-selected))] - [:span {:class (dom/classnames (stl/css :shortcut-key) true) + [:span {:class (stl/css :shortcut-key) :key (str "zoom-selected-" sc)} sc])]]]]])) ;; --- Header Component diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.cljs index 31f7faf2a..948c5228f 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.cljs @@ -198,6 +198,7 @@ :checked (not hide-fill-on-export?) :on-change on-change-show-fill-on-export}]]])])] + ;; OLD [:div.element-set [:div.element-set-title [:span label] diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 1c5e97d90..f1206c084 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -1637,6 +1637,9 @@ msgstr "Settings" msgid "labels.share-prototype" msgstr "Share prototype" +msgid "labels.share" +msgstr "Share" + #: src/app/main/ui/dashboard/sidebar.cljs msgid "labels.shared-libraries" msgstr "Libraries" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 218fdad3c..7b4e5b85e 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -1673,6 +1673,9 @@ msgstr "ConfiguraciĆ³n" msgid "labels.share-prototype" msgstr "Compartir prototipo" +msgid "labels.share" +msgstr "Compartir" + #: src/app/main/ui/dashboard/sidebar.cljs msgid "labels.shared-libraries" msgstr "Bibliotecas"