diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index c658a3c9f..fa87849ea 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -39,6 +39,7 @@ [app.main.data.workspace.changes :as dch] [app.main.data.workspace.collapse :as dwco] [app.main.data.workspace.drawing :as dwd] + [app.main.data.workspace.drawing.common :as dwdc] [app.main.data.workspace.edition :as dwe] [app.main.data.workspace.fix-bool-contents :as fbc] [app.main.data.workspace.groups :as dwg] @@ -1769,6 +1770,28 @@ (rx/of (modal/hide) (complete-remove-graphics))))))) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Read only +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +(defn set-workspace-read-only + [read-only?] + (ptk/reify ::set-workspace-read-only + ptk/UpdateEvent + (update [_ state] + (assoc-in state [:workspace-global :read-only?] read-only?)) + + ptk/WatchEvent + (watch [_ _ _] + (if read-only? + (rx/of :interrupt + (dwdc/clear-drawing) + (remove-layout-flag :colorpalette) + (remove-layout-flag :textpalette)) + (rx/empty))))) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Exports ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/frontend/src/app/main/data/workspace/shortcuts.cljs b/frontend/src/app/main/data/workspace/shortcuts.cljs index 31fc056ee..26936068d 100644 --- a/frontend/src/app/main/data/workspace/shortcuts.cljs +++ b/frontend/src/app/main/data/workspace/shortcuts.cljs @@ -20,6 +20,7 @@ [app.main.data.workspace.texts :as dwtxt] [app.main.data.workspace.transforms :as dwt] [app.main.data.workspace.undo :as dwu] + [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.hooks.resize :as r] [app.util.dom :as dom])) @@ -33,6 +34,11 @@ (-> (dw/toggle-layout-flag flag) (vary-meta assoc ::ev/origin "workspace-shortcuts"))) +(defn emit-when-no-readonly + [& events] + (when-not (deref refs/workspace-read-only?) + (run! st/emit! events))) + ;; Shortcuts format https://github.com/ccampbell/mousetrap (def base-shortcuts @@ -40,17 +46,17 @@ :undo {:tooltip (ds/meta "Z") :command (ds/c-mod "z") :subsections [:edit] - :fn #(st/emit! dwc/undo)} + :fn #(emit-when-no-readonly dwc/undo)} :redo {:tooltip (ds/meta "Y") :command [(ds/c-mod "shift+z") (ds/c-mod "y")] :subsections [:edit] - :fn #(st/emit! dwc/redo)} + :fn #(emit-when-no-readonly dwc/redo)} :clear-undo {:tooltip (ds/alt "Z") :command "alt+z" :subsections [:edit] - :fn #(st/emit! dwu/reinitialize-undo)} + :fn #(emit-when-no-readonly dwu/reinitialize-undo)} :copy {:tooltip (ds/meta "C") :command (ds/c-mod "c") @@ -60,8 +66,8 @@ :cut {:tooltip (ds/meta "X") :command (ds/c-mod "x") :subsections [:edit] - :fn #(st/emit! (dw/copy-selected) - (dw/delete-selected))} + :fn #(emit-when-no-readonly (dw/copy-selected) + (dw/delete-selected))} :paste {:tooltip (ds/meta "V") :disabled true @@ -72,29 +78,29 @@ :delete {:tooltip (ds/supr) :command ["del" "backspace"] :subsections [:edit] - :fn #(st/emit! (dw/delete-selected))} + :fn #(emit-when-no-readonly (dw/delete-selected))} :duplicate {:tooltip (ds/meta "D") :command (ds/c-mod "d") :subsections [:edit] - :fn #(st/emit! (dw/duplicate-selected true))} + :fn #(emit-when-no-readonly (dw/duplicate-selected true))} :start-editing {:tooltip (ds/enter) :command "enter" :subsections [:edit] - :fn #(st/emit! (dw/start-editing-selected))} + :fn #(emit-when-no-readonly (dw/start-editing-selected))} :start-measure {:tooltip (ds/alt "") :command ["alt" "."] :type "keydown" :subsections [:edit] - :fn #(st/emit! (dw/toggle-distances-display true))} + :fn #(emit-when-no-readonly (dw/toggle-distances-display true))} :stop-measure {:tooltip (ds/alt "") :command ["alt" "."] :type "keyup" :subsections [:edit] - :fn #(st/emit! (dw/toggle-distances-display false))} + :fn #(emit-when-no-readonly (dw/toggle-distances-display false))} :escape {:tooltip (ds/esc) :command "escape" @@ -107,149 +113,149 @@ :group {:tooltip (ds/meta "G") :command (ds/c-mod "g") :subsections [:modify-layers] - :fn #(st/emit! dw/group-selected)} + :fn #(emit-when-no-readonly dw/group-selected)} :ungroup {:tooltip (ds/shift "G") :command "shift+g" :subsections [:modify-layers] - :fn #(st/emit! dw/ungroup-selected)} + :fn #(emit-when-no-readonly dw/ungroup-selected)} :mask {:tooltip (ds/meta "M") :command (ds/c-mod "m") :subsections [:modify-layers] - :fn #(st/emit! dw/mask-group)} + :fn #(emit-when-no-readonly dw/mask-group)} :unmask {:tooltip (ds/meta-shift "M") :command (ds/c-mod "shift+m") :subsections [:modify-layers] - :fn #(st/emit! dw/unmask-group)} + :fn #(emit-when-no-readonly dw/unmask-group)} :create-component {:tooltip (ds/meta "K") :command (ds/c-mod "k") :subsections [:modify-layers] - :fn #(st/emit! (dwl/add-component))} + :fn #(emit-when-no-readonly (dwl/add-component))} :detach-component {:tooltip (ds/meta-shift "K") :command (ds/c-mod "shift+k") :subsections [:modify-layers] - :fn #(st/emit! dwl/detach-selected-components)} + :fn #(emit-when-no-readonly dwl/detach-selected-components)} :flip-vertical {:tooltip (ds/shift "V") :command "shift+v" :subsections [:modify-layers] - :fn #(st/emit! (dw/flip-vertical-selected))} + :fn #(emit-when-no-readonly (dw/flip-vertical-selected))} :flip-horizontal {:tooltip (ds/shift "H") :command "shift+h" :subsections [:modify-layers] - :fn #(st/emit! (dw/flip-horizontal-selected))} + :fn #(emit-when-no-readonly (dw/flip-horizontal-selected))} :bring-forward {:tooltip (ds/meta ds/up-arrow) :command (ds/c-mod "up") :subsections [:modify-layers] - :fn #(st/emit! (dw/vertical-order-selected :up))} + :fn #(emit-when-no-readonly (dw/vertical-order-selected :up))} :bring-backward {:tooltip (ds/meta ds/down-arrow) :command (ds/c-mod "down") :subsections [:modify-layers] - :fn #(st/emit! (dw/vertical-order-selected :down))} + :fn #(emit-when-no-readonly (dw/vertical-order-selected :down))} :bring-front {:tooltip (ds/meta-shift ds/up-arrow) :command (ds/c-mod "shift+up") :subsections [:modify-layers] - :fn #(st/emit! (dw/vertical-order-selected :top))} + :fn #(emit-when-no-readonly (dw/vertical-order-selected :top))} :bring-back {:tooltip (ds/meta-shift ds/down-arrow) :command (ds/c-mod "shift+down") :subsections [:modify-layers] - :fn #(st/emit! (dw/vertical-order-selected :bottom))} + :fn #(emit-when-no-readonly (dw/vertical-order-selected :bottom))} :move-fast-up {:tooltip (ds/shift ds/up-arrow) :command "shift+up" :subsections [:modify-layers] - :fn #(st/emit! (dwt/move-selected :up true))} + :fn #(emit-when-no-readonly (dwt/move-selected :up true))} :move-fast-down {:tooltip (ds/shift ds/down-arrow) :command "shift+down" :subsections [:modify-layers] - :fn #(st/emit! (dwt/move-selected :down true))} + :fn #(emit-when-no-readonly (dwt/move-selected :down true))} :move-fast-right {:tooltip (ds/shift ds/right-arrow) :command "shift+right" :subsections [:modify-layers] - :fn #(st/emit! (dwt/move-selected :right true))} + :fn #(emit-when-no-readonly (dwt/move-selected :right true))} :move-fast-left {:tooltip (ds/shift ds/left-arrow) :command "shift+left" :subsections [:modify-layers] - :fn #(st/emit! (dwt/move-selected :left true))} + :fn #(emit-when-no-readonly (dwt/move-selected :left true))} :move-unit-up {:tooltip ds/up-arrow :command "up" :subsections [:modify-layers] - :fn #(st/emit! (dwt/move-selected :up false))} + :fn #(emit-when-no-readonly (dwt/move-selected :up false))} :move-unit-down {:tooltip ds/down-arrow :command "down" :subsections [:modify-layers] - :fn #(st/emit! (dwt/move-selected :down false))} + :fn #(emit-when-no-readonly (dwt/move-selected :down false))} :move-unit-left {:tooltip ds/right-arrow :command "right" :subsections [:modify-layers] - :fn #(st/emit! (dwt/move-selected :right false))} + :fn #(emit-when-no-readonly (dwt/move-selected :right false))} :move-unit-right {:tooltip ds/left-arrow :command "left" :subsections [:modify-layers] - :fn #(st/emit! (dwt/move-selected :left false))} + :fn #(emit-when-no-readonly (dwt/move-selected :left false))} :artboard-selection {:tooltip (ds/meta (ds/alt "G")) :command (ds/c-mod "alt+g") :subsections [:modify-layers] - :fn #(st/emit! (dws/create-artboard-from-selection))} + :fn #(emit-when-no-readonly (dws/create-artboard-from-selection))} :toogle-layout-flex {:tooltip (ds/shift "F") :command "shift+f" :subsections [:modify-layers] - :fn #(st/emit! (dwsl/toogle-layout-flex))} + :fn #(emit-when-no-readonly (dwsl/toogle-layout-flex))} ;; TOOLS :draw-frame {:tooltip "B" :command ["b" "a"] :subsections [:tools :basics] - :fn #(st/emit! (dwd/select-for-drawing :frame))} + :fn #(emit-when-no-readonly (dwd/select-for-drawing :frame))} :move {:tooltip "V" :command "v" :subsections [:tools] - :fn #(st/emit! :interrupt)} + :fn #(emit-when-no-readonly :interrupt)} :draw-rect {:tooltip "R" :command "r" :subsections [:tools] - :fn #(st/emit! (dwd/select-for-drawing :rect))} + :fn #(emit-when-no-readonly (dwd/select-for-drawing :rect))} :draw-ellipse {:tooltip "E" :command "e" :subsections [:tools] - :fn #(st/emit! (dwd/select-for-drawing :circle))} + :fn #(emit-when-no-readonly (dwd/select-for-drawing :circle))} :draw-text {:tooltip "T" :command "t" :subsections [:tools] - :fn #(st/emit! dwtxt/start-edit-if-selected + :fn #(emit-when-no-readonly dwtxt/start-edit-if-selected (dwd/select-for-drawing :text))} :draw-path {:tooltip "P" :command "p" :subsections [:tools] - :fn #(st/emit! (dwd/select-for-drawing :path))} + :fn #(emit-when-no-readonly (dwd/select-for-drawing :path))} :draw-curve {:tooltip (ds/shift "C") :command "shift+c" :subsections [:tools] - :fn #(st/emit! (dwd/select-for-drawing :curve))} + :fn #(emit-when-no-readonly (dwd/select-for-drawing :curve))} :add-comment {:tooltip "C" :command "c" @@ -264,74 +270,74 @@ :toggle-visibility {:tooltip (ds/meta-shift "H") :command (ds/c-mod "shift+h") :subsections [:tools] - :fn #(st/emit! (dw/toggle-visibility-selected))} + :fn #(emit-when-no-readonly (dw/toggle-visibility-selected))} :toggle-lock {:tooltip (ds/meta-shift "L") :command (ds/c-mod "shift+l") :subsections [:tools] - :fn #(st/emit! (dw/toggle-lock-selected))} + :fn #(emit-when-no-readonly (dw/toggle-lock-selected))} :toggle-lock-size {:tooltip (ds/meta (ds/alt "L")) :command (ds/c-mod "alt+l") :subsections [:tools] - :fn #(st/emit! (dw/toggle-proportion-lock))} + :fn #(emit-when-no-readonly (dw/toggle-proportion-lock))} :toggle-scale-text {:tooltip "K" :command "k" :subsections [:tools] - :fn #(st/emit! (toggle-layout-flag :scale-text))} + :fn #(emit-when-no-readonly (toggle-layout-flag :scale-text))} :open-color-picker {:tooltip "I" :command "i" :subsections [:tools] - :fn #(st/emit! (mdc/picker-for-selected-shape))} + :fn #(emit-when-no-readonly (mdc/picker-for-selected-shape))} :toggle-focus-mode {:command "f" :tooltip "F" :subsections [:basics :tools] - :fn #(st/emit! (dw/toggle-focus-mode))} + :fn #(emit-when-no-readonly (dw/toggle-focus-mode))} ;; ITEM ALIGNMENT :align-left {:tooltip (ds/alt "A") :command "alt+a" :subsections [:alignment] - :fn #(st/emit! (dw/align-objects :hleft))} + :fn #(emit-when-no-readonly (dw/align-objects :hleft))} :align-right {:tooltip (ds/alt "D") :command "alt+d" :subsections [:alignment] - :fn #(st/emit! (dw/align-objects :hright))} + :fn #(emit-when-no-readonly (dw/align-objects :hright))} :align-top {:tooltip (ds/alt "W") :command "alt+w" :subsections [:alignment] - :fn #(st/emit! (dw/align-objects :vtop))} + :fn #(emit-when-no-readonly (dw/align-objects :vtop))} :align-hcenter {:tooltip (ds/alt "H") :command "alt+h" :subsections [:alignment] - :fn #(st/emit! (dw/align-objects :hcenter))} + :fn #(emit-when-no-readonly (dw/align-objects :hcenter))} :align-vcenter {:tooltip (ds/alt "V") :command "alt+v" :subsections [:alignment] - :fn #(st/emit! (dw/align-objects :vcenter))} + :fn #(emit-when-no-readonly (dw/align-objects :vcenter))} :align-bottom {:tooltip (ds/alt "S") :command "alt+s" :subsections [:alignment] - :fn #(st/emit! (dw/align-objects :vbottom))} + :fn #(emit-when-no-readonly (dw/align-objects :vbottom))} :h-distribute {:tooltip (ds/meta-shift (ds/alt "H")) :command (ds/c-mod "shift+alt+h") :subsections [:alignment] - :fn #(st/emit! (dw/distribute-objects :horizontal))} + :fn #(emit-when-no-readonly (dw/distribute-objects :horizontal))} :v-distribute {:tooltip (ds/meta-shift (ds/alt "V")) :command (ds/c-mod "shift+alt+v") :subsections [:alignment] - :fn #(st/emit! (dw/distribute-objects :vertical))} + :fn #(emit-when-no-readonly (dw/distribute-objects :vertical))} ;; MAIN MENU @@ -406,20 +412,20 @@ :toggle-history {:tooltip (ds/alt "H") :command (ds/a-mod "h") :subsections [:panels] - :fn #(st/emit! (dw/go-to-layout :document-history))} + :fn #(emit-when-no-readonly (dw/go-to-layout :document-history))} :toggle-colorpalette {:tooltip (ds/alt "P") :command (ds/a-mod "p") :subsections [:panels] :fn #(do (r/set-resize-type! :bottom) - (st/emit! (dw/remove-layout-flag :textpalette) + (emit-when-no-readonly (dw/remove-layout-flag :textpalette) (toggle-layout-flag :colorpalette)))} :toggle-textpalette {:tooltip (ds/alt "T") :command (ds/a-mod "t") :subsections [:panels] :fn #(do (r/set-resize-type! :bottom) - (st/emit! (dw/remove-layout-flag :colorpalette) + (emit-when-no-readonly (dw/remove-layout-flag :colorpalette) (toggle-layout-flag :textpalette)))} :hide-ui {:tooltip "\\" @@ -482,22 +488,22 @@ :bool-union {:tooltip (ds/meta (ds/alt "U")) :command (ds/c-mod "alt+u") :subsections [:shape] - :fn #(st/emit! (dw/create-bool :union))} + :fn #(emit-when-no-readonly (dw/create-bool :union))} :bool-difference {:tooltip (ds/meta (ds/alt "D")) :command (ds/c-mod "alt+d") :subsections [:shape] - :fn #(st/emit! (dw/create-bool :difference))} + :fn #(emit-when-no-readonly (dw/create-bool :difference))} :bool-intersection {:tooltip (ds/meta (ds/alt "I")) :command (ds/c-mod "alt+i") :subsections [:shape] - :fn #(st/emit! (dw/create-bool :intersection))} + :fn #(emit-when-no-readonly (dw/create-bool :intersection))} :bool-exclude {:tooltip (ds/meta (ds/alt "E")) :command (ds/c-mod "alt+e") :subsections [:shape] - :fn #(st/emit! (dw/create-bool :exclude))}} + :fn #(emit-when-no-readonly (dw/create-bool :exclude))}} ) (def opacity-shortcuts @@ -507,7 +513,7 @@ {:tooltip (str n) :command (str n) :subsections [:modify-layers] - :fn #(st/emit! (dwly/pressed-opacity n))}]))))) + :fn #(emit-when-no-readonly (dwly/pressed-opacity n))}]))))) (def shortcuts (merge base-shortcuts opacity-shortcuts)) diff --git a/frontend/src/app/main/data/workspace/state_helpers.cljs b/frontend/src/app/main/data/workspace/state_helpers.cljs index e8bee24ec..b6596511b 100644 --- a/frontend/src/app/main/data/workspace/state_helpers.cljs +++ b/frontend/src/app/main/data/workspace/state_helpers.cljs @@ -146,3 +146,4 @@ (let [{:keys [x y width height]} (get-in state [:workspace-local :vbox])] (gpt/point (+ x (/ width 2)) (+ y (/ height 2))))) + diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index 9711aeefb..147efe5dc 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -261,6 +261,9 @@ (def workspace-page-objects (l/derived wsh/lookup-page-objects st/state =)) +(def workspace-read-only? + (l/derived :read-only? workspace-global)) + (defn object-by-id [id] (l/derived #(get % id) workspace-page-objects)) diff --git a/frontend/src/app/main/ui/components/tab_container.cljs b/frontend/src/app/main/ui/components/tab_container.cljs index 579014fbb..1052cbe12 100644 --- a/frontend/src/app/main/ui/components/tab_container.cljs +++ b/frontend/src/app/main/ui/components/tab_container.cljs @@ -20,7 +20,9 @@ (mf/defc tab-container {::mf/wrap-props false} [props] - (let [children (unchecked-get props "children") + (let [children (->> + (unchecked-get props "children") + (filter some?)) selected (unchecked-get props "selected") on-change (unchecked-get props "on-change-tab") diff --git a/frontend/src/app/main/ui/context.cljs b/frontend/src/app/main/ui/context.cljs index 51f0bb182..3b0f84bf1 100644 --- a/frontend/src/app/main/ui/context.cljs +++ b/frontend/src/app/main/ui/context.cljs @@ -8,20 +8,22 @@ (:require [rumext.v2 :as mf])) -(def render-id (mf/create-context nil)) +(def render-id (mf/create-context nil)) -(def current-route (mf/create-context nil)) -(def current-profile (mf/create-context nil)) -(def current-team-id (mf/create-context nil)) -(def current-project-id (mf/create-context nil)) -(def current-page-id (mf/create-context nil)) -(def current-file-id (mf/create-context nil)) +(def current-route (mf/create-context nil)) +(def current-profile (mf/create-context nil)) +(def current-team-id (mf/create-context nil)) +(def current-project-id (mf/create-context nil)) +(def current-page-id (mf/create-context nil)) +(def current-file-id (mf/create-context nil)) -(def active-frames (mf/create-context nil)) -(def render-thumbnails (mf/create-context nil)) +(def active-frames (mf/create-context nil)) +(def render-thumbnails (mf/create-context nil)) -(def libraries (mf/create-context nil)) -(def components-v2 (mf/create-context nil)) +(def libraries (mf/create-context nil)) +(def components-v2 (mf/create-context nil)) -(def current-scroll (mf/create-context nil)) -(def current-zoom (mf/create-context nil)) +(def current-scroll (mf/create-context nil)) +(def current-zoom (mf/create-context nil)) + +(def workspace-read-only? (mf/create-context nil)) diff --git a/frontend/src/app/main/ui/hooks.cljs b/frontend/src/app/main/ui/hooks.cljs index 0d1238e40..9b6bdb604 100644 --- a/frontend/src/app/main/ui/hooks.cljs +++ b/frontend/src/app/main/ui/hooks.cljs @@ -84,7 +84,9 @@ ;; things go weird. (defn use-sortable - [& {:keys [data-type data on-drop on-drag on-hold disabled detect-center?] :as opts}] + [& {:keys [data-type data on-drop on-drag on-hold disabled detect-center? draggable?] + :or {draggable? true} + :as opts}] (let [ref (mf/use-ref) state (mf/use-state {:over nil :timer nil @@ -169,7 +171,7 @@ on-mount (fn [] (let [dom (mf/ref-val ref)] - (.setAttribute dom "draggable" true) + (.setAttribute dom "draggable" draggable?) ;; Register all events in the (default) bubble mode, so that they ;; are captured by the most leaf item. The handler will stop @@ -189,7 +191,7 @@ (.removeEventListener dom "dragend" on-drag-end))))] (mf/use-effect - (mf/deps data on-drop) + (mf/deps data on-drop draggable?) on-mount) [(deref state) ref])) diff --git a/frontend/src/app/main/ui/workspace.cljs b/frontend/src/app/main/ui/workspace.cljs index 05e5e5783..84de5c4a4 100644 --- a/frontend/src/app/main/ui/workspace.cljs +++ b/frontend/src/app/main/ui/workspace.cljs @@ -117,11 +117,12 @@ (mf/defc workspace {::mf/wrap [mf/memo]} [{:keys [project-id file-id page-id layout-name] :as props}] - (let [file (mf/deref refs/workspace-file) - project (mf/deref refs/workspace-project) - layout (mf/deref refs/workspace-layout) - wglobal (mf/deref refs/workspace-global) + (let [file (mf/deref refs/workspace-file) + project (mf/deref refs/workspace-project) + layout (mf/deref refs/workspace-layout) + wglobal (mf/deref refs/workspace-global) ready? (mf/deref refs/workspace-ready?) + workspace-read-only? (mf/deref refs/workspace-read-only?) components-v2 (features/use-feature :components-v2) @@ -152,22 +153,23 @@ [:& (mf/provider ctx/current-project-id) {:value (:id project)} [:& (mf/provider ctx/current-page-id) {:value page-id} [:& (mf/provider ctx/components-v2) {:value components-v2} - [:section#workspace {:style {:background-color background-color}} - (when (not (:hide-ui layout)) - [:& header {:file file - :page-id page-id - :project project - :layout layout}]) + [:& (mf/provider ctx/workspace-read-only?) {:value workspace-read-only?} + [:section#workspace {:style {:background-color background-color}} + (when (not (:hide-ui layout)) + [:& header {:file file + :page-id page-id + :project project + :layout layout}]) - [:& context-menu] + [:& context-menu] (if ready? - [:& workspace-page {:key (dm/str "page-" page-id) - :page-id page-id - :file file - :wglobal wglobal - :layout layout}] - [:& workspace-loader])]]]]]])) + [:& workspace-page {:key (dm/str "page-" page-id) + :page-id page-id + :file file + :wglobal wglobal + :layout layout}] + [:& workspace-loader])]]]]]]])) (mf/defc remove-graphics-dialog {::mf/register modal/components diff --git a/frontend/src/app/main/ui/workspace/header.cljs b/frontend/src/app/main/ui/workspace/header.cljs index 9b742fec8..f8e5729ec 100644 --- a/frontend/src/app/main/ui/workspace/header.cljs +++ b/frontend/src/app/main/ui/workspace/header.cljs @@ -21,6 +21,7 @@ [app.main.repo :as rp] [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.hooks.resize :as r] @@ -107,13 +108,14 @@ (mf/defc menu [{:keys [layout project file team-id] :as props}] - (let [show-menu? (mf/use-state false) - show-sub-menu? (mf/use-state false) - editing? (mf/use-state false) - edit-input-ref (mf/use-ref nil) - objects (mf/deref refs/workspace-page-objects) - frames (->> (cph/get-immediate-children objects uuid/zero) - (filterv cph/frame-shape?)) + (let [show-menu? (mf/use-state false) + show-sub-menu? (mf/use-state false) + editing? (mf/use-state false) + edit-input-ref (mf/use-ref nil) + objects (mf/deref refs/workspace-page-objects) + frames (->> (cph/get-immediate-children objects uuid/zero) + (filterv cph/frame-shape?)) + workspace-read-only? (mf/use-ctx ctx/workspace-read-only?) add-shared-fn #(st/emit! (dwl/set-file-shared (:id file) true)) @@ -328,25 +330,27 @@ (tr "workspace.header.menu.show-grid"))] [:span.shortcut (sc/get-tooltip :toggle-grid)]] - [:li {:on-click (fn [] - (r/set-resize-type! :bottom) - (st/emit! (dw/remove-layout-flag :textpalette) - (toggle-flag :colorpalette)))} - [:span - (if (contains? layout :colorpalette) - (tr "workspace.header.menu.hide-palette") - (tr "workspace.header.menu.show-palette"))] - [:span.shortcut (sc/get-tooltip :toggle-colorpalette)]] + (when-not workspace-read-only? + [:* + [:li {:on-click (fn [] + (r/set-resize-type! :bottom) + (st/emit! (dw/remove-layout-flag :textpalette) + (toggle-flag :colorpalette)))} + [:span + (if (contains? layout :colorpalette) + (tr "workspace.header.menu.hide-palette") + (tr "workspace.header.menu.show-palette"))] + [:span.shortcut (sc/get-tooltip :toggle-colorpalette)]] - [:li {:on-click (fn [] - (r/set-resize-type! :bottom) - (st/emit! (dw/remove-layout-flag :colorpalette) - (toggle-flag :textpalette)))} - [:span - (if (contains? layout :textpalette) - (tr "workspace.header.menu.hide-textpalette") - (tr "workspace.header.menu.show-textpalette"))] - [:span.shortcut (sc/get-tooltip :toggle-textpalette)]] + [:li {:on-click (fn [] + (r/set-resize-type! :bottom) + (st/emit! (dw/remove-layout-flag :colorpalette) + (toggle-flag :textpalette)))} + [:span + (if (contains? layout :textpalette) + (tr "workspace.header.menu.hide-textpalette") + (tr "workspace.header.menu.show-textpalette"))] + [:span.shortcut (sc/get-tooltip :toggle-textpalette)]]]) [:li {:on-click #(st/emit! (toggle-flag :display-artboard-names))} [:span @@ -436,6 +440,7 @@ (let [team-id (:team-id project) zoom (mf/deref refs/selected-zoom) params {:page-id page-id :file-id (:id file) :section "interactions"} + workspace-read-only? (mf/use-ctx ctx/workspace-read-only?) close-modals (mf/use-callback @@ -474,13 +479,14 @@ [:div.options-section [:& persistence-state-widget] [:& export-progress-widget] - [:button.document-history - {:alt (tr "workspace.sidebar.history" (sc/get-tooltip :toggle-history)) - :aria-label (tr "workspace.sidebar.history" (sc/get-tooltip :toggle-history)) - :class (when (contains? layout :document-history) "selected") - :on-click #(st/emit! (-> (dw/toggle-layout-flag :document-history) - (vary-meta assoc ::ev/origin "workspace-header")))} - i/recent]] + (when-not workspace-read-only? + [:button.document-history + {:alt (tr "workspace.sidebar.history" (sc/get-tooltip :toggle-history)) + :aria-label (tr "workspace.sidebar.history" (sc/get-tooltip :toggle-history)) + :class (when (contains? layout :document-history) "selected") + :on-click #(st/emit! (-> (dw/toggle-layout-flag :document-history) + (vary-meta assoc ::ev/origin "workspace-header")))} + i/recent])] [:div.options-section [:& zoom-widget-workspace diff --git a/frontend/src/app/main/ui/workspace/left_toolbar.cljs b/frontend/src/app/main/ui/workspace/left_toolbar.cljs index 458632e6b..bf01aa001 100644 --- a/frontend/src/app/main/ui/workspace/left_toolbar.cljs +++ b/frontend/src/app/main/ui/workspace/left_toolbar.cljs @@ -15,6 +15,7 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.file-uploader :refer [file-uploader]] + [app.main.ui.context :as ctx] [app.main.ui.hooks.resize :as r] [app.main.ui.icons :as i] [app.util.dom :as dom] @@ -64,10 +65,11 @@ {::mf/wrap [mf/memo] ::mf/wrap-props false} [props] - (let [layout (obj/get props "layout") - selected-drawtool (mf/deref refs/selected-drawing-tool) - select-drawtool #(st/emit! :interrupt (dw/select-for-drawing %)) - edition (mf/deref refs/selected-edition)] + (let [layout (obj/get props "layout") + selected-drawtool (mf/deref refs/selected-drawing-tool) + select-drawtool #(st/emit! :interrupt (dw/select-for-drawing %)) + edition (mf/deref refs/selected-edition) + workspace-read-only? (mf/use-ctx ctx/workspace-read-only?)] [:aside.left-toolbar [:ul.left-toolbar-options [:li @@ -78,56 +80,58 @@ (not edition)) "selected") :on-click #(st/emit! :interrupt)} i/pointer-inner]] - [:li - [:button.tooltip.tooltip-right - {:alt (tr "workspace.toolbar.frame" (sc/get-tooltip :draw-frame)) - :aria-label (tr "workspace.toolbar.frame" (sc/get-tooltip :draw-frame)) - :class (when (= selected-drawtool :frame) "selected") - :on-click (partial select-drawtool :frame) - :data-test "artboard-btn"} - i/artboard]] - [:li - [:button.tooltip.tooltip-right - {:alt (tr "workspace.toolbar.rect" (sc/get-tooltip :draw-rect)) - :aria-label (tr "workspace.toolbar.rect" (sc/get-tooltip :draw-rect)) - :class (when (= selected-drawtool :rect) "selected") - :on-click (partial select-drawtool :rect) - :data-test "rect-btn"} - i/box]] - [:li - [:button.tooltip.tooltip-right - {:alt (tr "workspace.toolbar.ellipse" (sc/get-tooltip :draw-ellipse)) - :aria-label (tr "workspace.toolbar.ellipse" (sc/get-tooltip :draw-ellipse)) - :class (when (= selected-drawtool :circle) "selected") - :on-click (partial select-drawtool :circle) - :data-test "ellipse-btn"} - i/circle]] - [:li - [:button.tooltip.tooltip-right - {:alt (tr "workspace.toolbar.text" (sc/get-tooltip :draw-text)) - :aria-label (tr "workspace.toolbar.text" (sc/get-tooltip :draw-text)) - :class (when (= selected-drawtool :text) "selected") - :on-click (partial select-drawtool :text)} - i/text]] + (when-not workspace-read-only? + [:* + [:li + [:button.tooltip.tooltip-right + {:alt (tr "workspace.toolbar.frame" (sc/get-tooltip :draw-frame)) + :aria-label (tr "workspace.toolbar.frame" (sc/get-tooltip :draw-frame)) + :class (when (= selected-drawtool :frame) "selected") + :on-click (partial select-drawtool :frame) + :data-test "artboard-btn"} + i/artboard]] + [:li + [:button.tooltip.tooltip-right + {:alt (tr "workspace.toolbar.rect" (sc/get-tooltip :draw-rect)) + :aria-label (tr "workspace.toolbar.rect" (sc/get-tooltip :draw-rect)) + :class (when (= selected-drawtool :rect) "selected") + :on-click (partial select-drawtool :rect) + :data-test "rect-btn"} + i/box]] + [:li + [:button.tooltip.tooltip-right + {:alt (tr "workspace.toolbar.ellipse" (sc/get-tooltip :draw-ellipse)) + :aria-label (tr "workspace.toolbar.ellipse" (sc/get-tooltip :draw-ellipse)) + :class (when (= selected-drawtool :circle) "selected") + :on-click (partial select-drawtool :circle) + :data-test "ellipse-btn"} + i/circle]] + [:li + [:button.tooltip.tooltip-right + {:alt (tr "workspace.toolbar.text" (sc/get-tooltip :draw-text)) + :aria-label (tr "workspace.toolbar.text" (sc/get-tooltip :draw-text)) + :class (when (= selected-drawtool :text) "selected") + :on-click (partial select-drawtool :text)} + i/text]] - [:& image-upload] + [:& image-upload] - [:li - [:button.tooltip.tooltip-right - {:alt (tr "workspace.toolbar.curve" (sc/get-tooltip :draw-curve)) - :aria-label (tr "workspace.toolbar.curve" (sc/get-tooltip :draw-curve)) - :class (when (= selected-drawtool :curve) "selected") - :on-click (partial select-drawtool :curve) - :data-test "curve-btn"} - i/pencil]] - [:li - [:button.tooltip.tooltip-right - {:alt (tr "workspace.toolbar.path" (sc/get-tooltip :draw-path)) - :aria-label (tr "workspace.toolbar.path" (sc/get-tooltip :draw-path)) - :class (when (= selected-drawtool :path) "selected") - :on-click (partial select-drawtool :path) - :data-test "path-btn"} - i/pen]] + [:li + [:button.tooltip.tooltip-right + {:alt (tr "workspace.toolbar.curve" (sc/get-tooltip :draw-curve)) + :aria-label (tr "workspace.toolbar.curve" (sc/get-tooltip :draw-curve)) + :class (when (= selected-drawtool :curve) "selected") + :on-click (partial select-drawtool :curve) + :data-test "curve-btn"} + i/pencil]] + [:li + [:button.tooltip.tooltip-right + {:alt (tr "workspace.toolbar.path" (sc/get-tooltip :draw-path)) + :aria-label (tr "workspace.toolbar.path" (sc/get-tooltip :draw-path)) + :class (when (= selected-drawtool :path) "selected") + :on-click (partial select-drawtool :path) + :data-test "path-btn"} + i/pen]]]) [:li [:button.tooltip.tooltip-right @@ -138,31 +142,33 @@ i/chat]]] [:ul.left-toolbar-options.panels - [:li - [:button.tooltip.tooltip-right - {:alt (tr "workspace.toolbar.text-palette" (sc/get-tooltip :toggle-textpalette)) - :aria-label (tr "workspace.toolbar.text-palette" (sc/get-tooltip :toggle-textpalette)) - :class (when (contains? layout :textpalette) "selected") - :on-click (fn [] - (r/set-resize-type! :bottom) - (dom/add-class! (dom/get-element-by-class "color-palette") "fade-out-down") - (ts/schedule 300 #(st/emit! (dw/remove-layout-flag :colorpalette) - (-> (dw/toggle-layout-flag :textpalette) - (vary-meta assoc ::ev/origin "workspace-left-toolbar")))))} - "Ag"]] + (when-not workspace-read-only? + [:* + [:li + [:button.tooltip.tooltip-right + {:alt (tr "workspace.toolbar.text-palette" (sc/get-tooltip :toggle-textpalette)) + :aria-label (tr "workspace.toolbar.text-palette" (sc/get-tooltip :toggle-textpalette)) + :class (when (contains? layout :textpalette) "selected") + :on-click (fn [] + (r/set-resize-type! :bottom) + (dom/add-class! (dom/get-element-by-class "color-palette") "fade-out-down") + (ts/schedule 300 #(st/emit! (dw/remove-layout-flag :colorpalette) + (-> (dw/toggle-layout-flag :textpalette) + (vary-meta assoc ::ev/origin "workspace-left-toolbar")))))} + "Ag"]] - [:li - [:button.tooltip.tooltip-right - {:alt (tr "workspace.toolbar.color-palette" (sc/get-tooltip :toggle-colorpalette)) - :aria-label (tr "workspace.toolbar.color-palette" (sc/get-tooltip :toggle-colorpalette)) - :class (when (contains? layout :colorpalette) "selected") - :on-click (fn [] - (r/set-resize-type! :bottom) - (dom/add-class! (dom/get-element-by-class "color-palette") "fade-out-down") - (ts/schedule 300 #(st/emit! (dw/remove-layout-flag :textpalette) - (-> (dw/toggle-layout-flag :colorpalette) - (vary-meta assoc ::ev/origin "workspace-left-toolbar")))))} - i/palette]] + [:li + [:button.tooltip.tooltip-right + {:alt (tr "workspace.toolbar.color-palette" (sc/get-tooltip :toggle-colorpalette)) + :aria-label (tr "workspace.toolbar.color-palette" (sc/get-tooltip :toggle-colorpalette)) + :class (when (contains? layout :colorpalette) "selected") + :on-click (fn [] + (r/set-resize-type! :bottom) + (dom/add-class! (dom/get-element-by-class "color-palette") "fade-out-down") + (ts/schedule 300 #(st/emit! (dw/remove-layout-flag :textpalette) + (-> (dw/toggle-layout-flag :colorpalette) + (vary-meta assoc ::ev/origin "workspace-left-toolbar")))))} + i/palette]]]) [:li [:button.tooltip.tooltip-right.separator {:alt (tr "workspace.toolbar.shortcuts" (sc/get-tooltip :show-shortcuts)) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs index d77fb28e1..99171cd79 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs @@ -364,11 +364,11 @@ (mf/defc components-item [{:keys [component renaming listing-thumbs? selected-components - on-asset-click on-context-menu on-drag-start do-rename - cancel-rename selected-components-full selected-components-paths]}] - (let [item-ref (mf/use-ref) - - dragging? (mf/use-state false) + on-asset-click on-context-menu on-drag-start do-rename cancel-rename + selected-components-full selected-components-paths]}] + (let [item-ref (mf/use-ref) + dragging? (mf/use-state false) + workspace-read-only? (mf/use-ctx ctx/workspace-read-only?) unselect-all (mf/use-fn @@ -422,7 +422,7 @@ :grid-cell @listing-thumbs? :enum-item (not @listing-thumbs?)) :id (str "component-shape-id-" (:id component)) - :draggable true + :draggable (not workspace-read-only?) :on-click on-component-click :on-context-menu (on-context-menu (:id component)) :on-drag-start on-component-drag-start @@ -552,11 +552,12 @@ (mf/defc components-box [{:keys [file-id local? components listing-thumbs? open? reverse-sort? open-groups selected-assets on-asset-click on-assets-delete on-clear-selection] :as props}] - (let [input-ref (mf/use-ref nil) - state (mf/use-state {:renaming nil - :component-id nil}) + (let [input-ref (mf/use-ref nil) + state (mf/use-state {:renaming nil + :component-id nil}) - menu-state (mf/use-state auto-pos-menu-state) + menu-state (mf/use-state auto-pos-menu-state) + workspace-read-only? (mf/use-ctx ctx/workspace-read-only?) selected-components (:components selected-assets) selected-components-full (filter #(contains? selected-components (:id %)) components) @@ -627,10 +628,10 @@ on-context-menu (mf/use-fn - (mf/deps selected-components on-clear-selection) + (mf/deps selected-components on-clear-selection workspace-read-only?) (fn [component-id] (fn [event] - (when local? + (when (and local? (not workspace-read-only?)) (when-not (contains? selected-components component-id) (on-clear-selection)) (swap! state assoc :component-id component-id) @@ -715,7 +716,7 @@ :open? open?} (when local? [:& asset-section-block {:role :title-button} - (when components-v2 + (when (and components-v2 (not workspace-read-only?)) [:div.assets-button {:on-click add-component} i/plus [:& file-uploader {:accept cm/str-image-types @@ -759,9 +760,10 @@ [{:keys [object renaming listing-thumbs? selected-objects on-asset-click on-context-menu on-drag-start do-rename cancel-rename selected-graphics-full selected-graphics-paths]}] - (let [item-ref (mf/use-ref) - visible? (h/use-visible item-ref :once? true) - dragging? (mf/use-state false) + (let [item-ref (mf/use-ref) + visible? (h/use-visible item-ref :once? true) + dragging? (mf/use-state false) + workspace-read-only? (mf/use-ctx ctx/workspace-read-only?) on-drop (mf/use-fn @@ -795,7 +797,7 @@ :selected (contains? selected-objects (:id object)) :grid-cell @listing-thumbs? :enum-item (not @listing-thumbs?)) - :draggable true + :draggable (not workspace-read-only?) :on-click #(on-asset-click % (:id object) nil) :on-context-menu (on-context-menu (:id object)) :on-drag-start on-grahic-drag-start @@ -928,18 +930,19 @@ (mf/defc graphics-box [{:keys [file-id project-id local? objects listing-thumbs? open? open-groups selected-assets reverse-sort? on-asset-click on-assets-delete on-clear-selection] :as props}] - (let [input-ref (mf/use-ref nil) - state (mf/use-state {:renaming nil - :object-id nil}) + (let [input-ref (mf/use-ref nil) + state (mf/use-state {:renaming nil + :object-id nil}) - menu-state (mf/use-state auto-pos-menu-state) + menu-state (mf/use-state auto-pos-menu-state) + workspace-read-only? (mf/use-ctx ctx/workspace-read-only?) - selected-objects (:graphics selected-assets) + selected-objects (:graphics selected-assets) selected-graphics-full (filter #(contains? selected-objects (:id %)) objects) - multi-objects? (> (count selected-objects) 1) - multi-assets? (or (seq (:components selected-assets)) - (seq (:colors selected-assets)) - (seq (:typographies selected-assets))) + multi-objects? (> (count selected-objects) 1) + multi-assets? (or (seq (:components selected-assets)) + (seq (:colors selected-assets)) + (seq (:typographies selected-assets))) objects (->> objects (map dwl/extract-path-if-missing)) @@ -996,10 +999,10 @@ on-context-menu (mf/use-fn - (mf/deps selected-objects on-clear-selection) + (mf/deps selected-objects on-clear-selection workspace-read-only?) (fn [object-id] (fn [event] - (when local? + (when (and local? (not workspace-read-only?)) (when-not (contains? selected-objects object-id) (on-clear-selection)) (swap! state assoc :object-id object-id) @@ -1084,7 +1087,7 @@ :open? open?} (when local? [:& asset-section-block {:role :title-button} - (when-not components-v2 + (when (and (not components-v2) (not workspace-read-only?)) [:div.assets-button {:on-click add-graphic} i/plus [:& file-uploader {:accept cm/str-image-types @@ -1125,13 +1128,14 @@ [{:keys [color local? file-id selected-colors multi-colors? multi-assets? on-asset-click on-assets-delete on-clear-selection on-group selected-colors-full selected-colors-paths move-color] :as props}] - (let [item-ref (mf/use-ref) - dragging? (mf/use-state false) - rename? (= (:color-for-rename @refs/workspace-local) (:id color)) - input-ref (mf/use-ref) - state (mf/use-state {:editing rename?}) + (let [item-ref (mf/use-ref) + dragging? (mf/use-state false) + rename? (= (:color-for-rename @refs/workspace-local) (:id color)) + input-ref (mf/use-ref) + state (mf/use-state {:editing rename?}) - menu-state (mf/use-state auto-pos-menu-state) + menu-state (mf/use-state auto-pos-menu-state) + workspace-read-only? (mf/use-ctx ctx/workspace-read-only?) default-name (cond (:gradient color) (bc/gradient-type->string (get-in color [:gradient :type])) @@ -1182,7 +1186,7 @@ rename-color-clicked (fn [event] - (when local? + (when (and local? (not workspace-read-only?)) (dom/prevent-default event) (swap! state assoc :editing true))) @@ -1213,9 +1217,9 @@ on-context-menu (mf/use-fn - (mf/deps color selected-colors on-clear-selection) + (mf/deps color selected-colors on-clear-selection workspace-read-only?) (fn [event] - (when local? + (when (and local? (not workspace-read-only?)) (when-not (contains? selected-colors (:id color)) (on-clear-selection)) (swap! menu-state #(open-auto-pos-menu % event))))) @@ -1265,7 +1269,7 @@ #(on-asset-click % (:id color) (partial apply-color (:id color)))) :ref item-ref - :draggable true + :draggable (not workspace-read-only?) :on-drag-start on-color-drag-start :on-drag-enter on-drag-enter :on-drag-leave on-drag-leave @@ -1400,14 +1404,15 @@ (mf/defc colors-box [{:keys [file-id local? colors open? open-groups selected-assets reverse-sort? on-asset-click on-assets-delete on-clear-selection] :as props}] - (let [selected-colors (:colors selected-assets) + (let [selected-colors (:colors selected-assets) selected-colors-full (filter #(contains? selected-colors (:id %)) colors) - multi-colors? (> (count selected-colors) 1) - multi-assets? (or (seq (:components selected-assets)) - (seq (:graphics selected-assets)) - (seq (:typographies selected-assets))) + multi-colors? (> (count selected-colors) 1) + multi-assets? (or (seq (:components selected-assets)) + (seq (:graphics selected-assets)) + (seq (:typographies selected-assets))) - groups (group-assets colors reverse-sort?) + groups (group-assets colors reverse-sort?) + workspace-read-only? (mf/use-ctx ctx/workspace-read-only?) add-color (mf/use-fn @@ -1501,8 +1506,9 @@ :open? open?} (when local? [:& asset-section-block {:role :title-button} - [:div.assets-button {:on-click add-color-clicked} - i/plus]]) + (when-not workspace-read-only? + [:div.assets-button {:on-click add-color-clicked} + i/plus])]) [:& asset-section-block {:role :content} [:& colors-group {:file-id file-id @@ -1526,10 +1532,11 @@ (mf/defc typography-item [{:keys [typography file local? handle-change selected-typographies apply-typography - editing-id local-data on-asset-click on-context-menu - selected-typographies-full selected-typographies-paths move-typography] :as props}] - (let [item-ref (mf/use-ref) - dragging? (mf/use-state false) + editing-id local-data on-asset-click on-context-menu selected-typographies-full + selected-typographies-paths move-typography] :as props}] + (let [item-ref (mf/use-ref) + dragging? (mf/use-state false) + workspace-read-only? (mf/use-ctx ctx/workspace-read-only?) on-drop (mf/use-fn (mf/deps typography dragging? selected-typographies selected-typographies-full selected-typographies-paths move-typography) @@ -1558,7 +1565,7 @@ (on-asset-drag-start event typography selected-typographies item-ref :typographies identity)))] [:div.typography-container {:ref item-ref - :draggable true + :draggable (not workspace-read-only?) :on-drag-start on-typography-drag-start :on-drag-enter on-drag-enter :on-drag-leave on-drag-leave @@ -1568,7 +1575,7 @@ {:key (:id typography) :typography typography :file file - :read-only? (not local?) + :local? local? :on-context-menu #(on-context-menu (:id typography) %) :on-change #(handle-change typography %) :selected? (contains? selected-typographies (:id typography)) @@ -1581,8 +1588,8 @@ (mf/defc typographies-group [{:keys [file-id prefix groups open-groups file local? selected-typographies local-data - editing-id on-asset-click handle-change apply-typography - on-rename-group on-ungroup on-context-menu selected-typographies-full]}] + editing-id on-asset-click handle-change apply-typography on-rename-group + on-ungroup on-context-menu selected-typographies-full]}] (let [group-open? (get open-groups prefix true) dragging? (mf/use-state false) @@ -1690,6 +1697,7 @@ multi-assets? (or (seq (:components selected-assets)) (seq (:graphics selected-assets)) (seq (:colors selected-assets))) + workspace-read-only? (mf/use-ctx ctx/workspace-read-only?) add-typography (mf/use-fn @@ -1783,9 +1791,9 @@ on-context-menu (mf/use-fn - (mf/deps selected-typographies on-clear-selection) + (mf/deps selected-typographies on-clear-selection workspace-read-only?) (fn [id event] - (when local? + (when (and local? (not workspace-read-only?)) (when-not (contains? selected-typographies id) (on-clear-selection)) (swap! state assoc :id id) @@ -1833,8 +1841,9 @@ :open? open?} (when local? [:& asset-section-block {:role :title-button} - [:div.assets-button {:on-click add-typography} - i/plus]]) + (when-not workspace-read-only? + [:div.assets-button {:on-click add-typography} + i/plus])]) [:& asset-section-block {:role :content} [:& typographies-group {:file-id file-id @@ -1929,52 +1938,52 @@ (mf/defc file-library [{:keys [file local? default-open? filters] :as props}] - (let [open-file (mf/deref (make-open-file-ref (:id file))) - open? (-> open-file - :library - (d/nilv default-open?)) - open-box? (fn [box] - (-> open-file - box - (d/nilv true))) - open-groups (fn [box] - (-> open-file - :groups - box - (d/nilv {}))) - shared? (:is-shared file) - router (mf/deref refs/router) + (let [open-file (mf/deref (make-open-file-ref (:id file))) + open? (-> open-file + :library + (d/nilv default-open?)) + open-box? (fn [box] + (-> open-file + box + (d/nilv true))) + open-groups (fn [box] + (-> open-file + :groups + box + (d/nilv {}))) + shared? (:is-shared file) + router (mf/deref refs/router) - reverse-sort? (mf/use-state false) - listing-thumbs? (mf/use-state true) + reverse-sort? (mf/use-state false) + listing-thumbs? (mf/use-state true) - selected-assets (mf/deref refs/selected-assets) + selected-assets (mf/deref refs/selected-assets) - selected-count (+ (count (:components selected-assets)) - (count (:graphics selected-assets)) - (count (:colors selected-assets)) - (count (:typographies selected-assets))) + selected-count (+ (count (:components selected-assets)) + (count (:graphics selected-assets)) + (count (:colors selected-assets)) + (count (:typographies selected-assets))) - components-v2 (mf/use-ctx ctx/components-v2) + components-v2 (mf/use-ctx ctx/components-v2) - toggle-open #(st/emit! (dwl/set-assets-box-open (:id file) :library (not open?))) + toggle-open #(st/emit! (dwl/set-assets-box-open (:id file) :library (not open?))) - url (rt/resolve router :workspace + url (rt/resolve router :workspace {:project-id (:project-id file) :file-id (:id file)} {:page-id (get-in file [:data :pages 0])}) - colors-ref (mf/use-memo (mf/deps (:id file)) #(file-colors-ref (:id file))) - colors (apply-filters (mf/deref colors-ref) filters @reverse-sort?) + colors-ref (mf/use-memo (mf/deps (:id file)) #(file-colors-ref (:id file))) + colors (apply-filters (mf/deref colors-ref) filters @reverse-sort?) - typography-ref (mf/use-memo (mf/deps (:id file)) #(file-typography-ref (:id file))) - typographies (apply-filters (mf/deref typography-ref) filters @reverse-sort?) + typography-ref (mf/use-memo (mf/deps (:id file)) #(file-typography-ref (:id file))) + typographies (apply-filters (mf/deref typography-ref) filters @reverse-sort?) - media-ref (mf/use-memo (mf/deps (:id file)) #(file-media-ref (:id file))) - media (apply-filters (mf/deref media-ref) filters @reverse-sort?) + media-ref (mf/use-memo (mf/deps (:id file)) #(file-media-ref (:id file))) + media (apply-filters (mf/deref media-ref) filters @reverse-sort?) - components-ref (mf/use-memo (mf/deps (:id file)) #(file-components-ref (:id file))) - components (apply-filters (mf/deref components-ref) filters @reverse-sort?) + components-ref (mf/use-memo (mf/deps (:id file)) #(file-components-ref (:id file))) + components (apply-filters (mf/deref components-ref) filters @reverse-sort?) toggle-sort (mf/use-fn @@ -2171,12 +2180,13 @@ (mf/defc assets-toolbox [] - (let [libraries (->> (mf/deref refs/workspace-libraries) - (vals) - (remove :is-indirect)) - file (mf/deref refs/workspace-file) - team-id (mf/use-ctx ctx/current-team-id) - filters (mf/use-state {:term "" :box :all}) + (let [libraries (->> (mf/deref refs/workspace-libraries) + (vals) + (remove :is-indirect)) + file (mf/deref refs/workspace-file) + team-id (mf/use-ctx ctx/current-team-id) + filters (mf/use-state {:term "" :box :all}) + workspace-read-only? (mf/use-ctx ctx/workspace-read-only?) on-search-term-change (mf/use-fn @@ -2205,9 +2215,10 @@ [:div.tool-window-content [:div.assets-bar-title (tr "workspace.assets.assets") - [:div.libraries-button {:on-click #(modal/show! :libraries-dialog {})} - i/text-align-justify - (tr "workspace.assets.libraries")]] + (when-not workspace-read-only? + [:div.libraries-button {:on-click #(modal/show! :libraries-dialog {})} + i/text-align-justify + (tr "workspace.assets.libraries")])] [:div.search-block [:input.search-input diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs index 52bce124f..caff9ca99 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs @@ -87,25 +87,26 @@ (mf/defc layer-item [{:keys [index item selected objects sortable? filtered?] :as props}] - (let [id (:id item) - blocked? (:blocked item) - hidden? (:hidden item) + (let [id (:id item) + blocked? (:blocked item) + hidden? (:hidden item) - disable-drag (mf/use-state false) - scroll-to-middle? (mf/use-var true) - expanded-iref (mf/with-memo [id] - (-> (l/in [:expanded id]) - (l/derived refs/workspace-local))) + disable-drag (mf/use-state false) + scroll-to-middle? (mf/use-var true) + expanded-iref (mf/with-memo [id] + (-> (l/in [:expanded id]) + (l/derived refs/workspace-local))) - expanded? (mf/deref expanded-iref) - selected? (contains? selected id) - container? (or (cph/frame-shape? item) - (cph/group-shape? item)) + expanded? (mf/deref expanded-iref) + selected? (contains? selected id) + container? (or (cph/frame-shape? item) + (cph/group-shape? item)) - components-v2 (mf/use-ctx ctx/components-v2) - main-instance? (if components-v2 - (:main-instance? item) - true) + components-v2 (mf/use-ctx ctx/components-v2) + workspace-read-only? (mf/use-ctx ctx/workspace-read-only?) + main-instance? (if components-v2 + (:main-instance? item) + true) toggle-collapse (mf/use-fn @@ -170,12 +171,13 @@ on-context-menu (mf/use-fn - (mf/deps item) + (mf/deps item workspace-read-only?) (fn [event] (dom/prevent-default event) (dom/stop-propagation event) - (let [pos (dom/get-client-position event)] - (st/emit! (dw/show-shape-context-menu {:position pos :shape item}))))) + (when-not workspace-read-only? + (let [pos (dom/get-client-position event)] + (st/emit! (dw/show-shape-context-menu {:position pos :shape item})))))) on-drag (mf/use-fn @@ -192,7 +194,7 @@ (st/emit! (dw/relocate-selected-shapes id 0)) (let [to-index (if (= side :top) (inc index) index) parent-id (cph/get-parent-id objects id)] - (st/emit! (dw/relocate-selected-shapes parent-id to-index)))))) + (st/emit! (dw/relocate-selected-shapes parent-id to-index)))))) on-hold (mf/use-fn @@ -201,17 +203,18 @@ (when-not expanded? (st/emit! (dwc/toggle-collapse id))))) - [dprops dref] (when sortable? - (hooks/use-sortable - :data-type "penpot/layer" - :on-drop on-drop - :on-drag on-drag - :on-hold on-hold - :disabled @disable-drag - :detect-center? container? - :data {:id (:id item) - :index index - :name (:name item)})) + [dprops dref] + (hooks/use-sortable + :data-type "penpot/layer" + :on-drop on-drop + :on-drag on-drag + :on-hold on-hold + :disabled @disable-drag + :detect-center? container? + :data {:id (:id item) + :index index + :name (:name item)} + :draggable? (and sortable? (not workspace-read-only?))) ref (mf/use-ref)] @@ -257,6 +260,7 @@ :main-instance? main-instance?}]] [:& layer-name {:shape item :name-ref ref + :disabled-double-click workspace-read-only? :on-start-edit #(reset! disable-drag true) :on-stop-edit #(reset! disable-drag false)}] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options.cljs b/frontend/src/app/main/ui/workspace/sidebar/options.cljs index 360151a9c..413a776c8 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options.cljs @@ -68,7 +68,11 @@ selected-shapes (into [] (keep (d/getf objects)) selected)] [:div.tool-window [:div.tool-window-content - [:& tab-container {:on-change-tab #(st/emit! (udw/set-options-mode %)) + [:& tab-container {:on-change-tab (fn [options-mode] + (st/emit! (udw/set-options-mode options-mode)) + (if (= options-mode :prototype) ;;TODO remove, only for test palba + (st/emit! :interrupt (udw/deselect-all true) (udw/set-workspace-read-only true)) + (st/emit! :interrupt (udw/set-workspace-read-only false)))) :selected section} [:& tab-element {:id :design :title (tr "workspace.options.design")} diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs index 24672804a..8f3d81ed6 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs @@ -320,7 +320,7 @@ (cond typography [:& typography-entry {:typography typography - :read-only? (not= (:typography-ref-file values) file-id) + :local? (= (:typography-ref-file values) file-id) :file (get shared-libs (:typography-ref-file values)) :on-detach handle-detach-typography :on-change handle-change-typography}] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs index 4fd7e6aed..62c77cb0d 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs @@ -18,6 +18,7 @@ [app.main.store :as st] [app.main.ui.components.editable-select :refer [editable-select]] [app.main.ui.components.numeric-input :refer [numeric-input]] + [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.main.ui.workspace.sidebar.options.common :refer [advanced-options]] [app.util.dom :as dom] @@ -455,11 +456,13 @@ ;; In summary, this need to a good UX/UI/IMPL rework. (mf/defc typography-entry - [{:keys [typography read-only? selected? on-click on-change on-detach on-context-menu editing? focus-name? file]}] - (let [open? (mf/use-state editing?) - hover-detach (mf/use-state false) - name-input-ref (mf/use-ref) - on-change-ref (mf/use-ref nil) + [{:keys [typography local? selected? on-click on-change on-detach on-context-menu editing? focus-name? file]}] + (let [open? (mf/use-state editing?) + hover-detach (mf/use-state false) + name-input-ref (mf/use-ref) + on-change-ref (mf/use-ref nil) + workspace-read-only? (mf/use-ctx ctx/workspace-read-only?) + editable? (and local? (not workspace-read-only?)) on-name-blur (mf/use-callback @@ -511,7 +514,7 @@ [:& advanced-options {:visible? @open? :on-close #(reset! open? false)} - (if read-only? + (if (not editable?) [:div.element-set-content.typography-read-only-data [:div.row-flex.typography-name [:span (:name typography)]] @@ -544,13 +547,14 @@ [:span.label (tr "workspace.assets.typography.text-transform")] [:span (:text-transform typography)]] - [:div.row-flex - [:a.go-to-lib-button - {:on-click #(st/emit! (rt/nav-new-window* {:rname :workspace - :path-params {:project-id (:project-id file) - :file-id (:id file)} - :query-params {:page-id (get-in file [:data :pages 0])}}))} - (tr "workspace.assets.typography.go-to-edit")]]] + (when-not local? + [:div.row-flex + [:a.go-to-lib-button + {:on-click #(st/emit! (rt/nav-new-window* {:rname :workspace + :path-params {:project-id (:project-id file) + :file-id (:id file)} + :query-params {:page-id (get-in file [:data :pages 0])}}))} + (tr "workspace.assets.typography.go-to-edit")]])] [:* [:div.element-set-content diff --git a/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs b/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs index 0821a50ab..e4235954b 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs @@ -27,42 +27,46 @@ (mf/defc page-item [{:keys [page index deletable? selected?] :as props}] - (let [local (mf/use-state {}) - input-ref (mf/use-ref) - id (:id page) - state (mf/use-state {:menu-open false}) + (let [local (mf/use-state {}) + input-ref (mf/use-ref) + id (:id page) + state (mf/use-state {:menu-open false}) - delete-fn (mf/use-callback (mf/deps id) #(st/emit! (dw/delete-page id))) - navigate-fn (mf/use-callback (mf/deps id) #(st/emit! :interrupt (dw/go-to-page id))) + delete-fn (mf/use-callback (mf/deps id) #(st/emit! (dw/delete-page id))) + navigate-fn (mf/use-callback (mf/deps id) #(st/emit! :interrupt (dw/go-to-page id))) + workspace-read-only? (mf/use-ctx ctx/workspace-read-only?) on-context-menu (mf/use-callback - (mf/deps id) + (mf/deps id workspace-read-only?) (fn [event] (dom/prevent-default event) (dom/stop-propagation event) - (let [pos (dom/get-client-position event)] - (swap! state assoc - :menu-open true - :top (:y pos) - :left (:x pos))))) + (when-not workspace-read-only? + (let [pos (dom/get-client-position event)] + (swap! state assoc + :menu-open true + :top (:y pos) + :left (:x pos)))))) on-delete (mf/use-callback (mf/deps id) #(st/emit! (modal/show - {:type :confirm - :title (tr "modals.delete-page.title") - :message (tr "modals.delete-page.body") - :on-accept delete-fn}))) + {:type :confirm + :title (tr "modals.delete-page.title") + :message (tr "modals.delete-page.body") + :on-accept delete-fn}))) on-double-click (mf/use-callback + (mf/deps workspace-read-only?) (fn [event] (dom/prevent-default event) (dom/stop-propagation event) - (swap! local assoc :edition true) - (swap! state assoc :menu-open false))) + (when-not workspace-read-only? + (swap! local assoc :edition true) + (swap! state assoc :menu-open false)))) on-blur (mf/use-callback @@ -75,13 +79,13 @@ on-key-down (mf/use-callback - (fn [event] - (cond - (kbd/enter? event) - (on-blur event) + (fn [event] + (cond + (kbd/enter? event) + (on-blur event) - (kbd/esc? event) - (swap! local assoc :edition false)))) + (kbd/esc? event) + (swap! local assoc :edition false)))) on-drop (mf/use-callback @@ -100,7 +104,8 @@ :on-drop on-drop :data {:id id :index index - :name (:name page)})] + :name (:name page)} + :draggable? (not workspace-read-only?))] (mf/use-effect (mf/deps selected?) @@ -141,22 +146,23 @@ [:* [:span (:name page)] [:div.page-actions - (when deletable? + (when (and deletable? (not workspace-read-only?)) [:a {:on-click on-delete} i/trash])]])]] - [:& context-menu - {:selectable false - :show (:menu-open @state) - :on-close #(swap! state assoc :menu-open false) - :top (:top @state) - :left (:left @state) - :options (cond-> [] - deletable? - (conj [(tr "workspace.assets.delete") on-delete]) + (when-not workspace-read-only? + [:& context-menu + {:selectable false + :show (:menu-open @state) + :on-close #(swap! state assoc :menu-open false) + :top (:top @state) + :left (:left @state) + :options (cond-> [] + deletable? + (conj [(tr "workspace.assets.delete") on-delete]) - :always - (-> (conj [(tr "workspace.assets.rename") on-double-click]) - (conj [(tr "workspace.assets.duplicate") on-duplicate])))}]])) + :always + (-> (conj [(tr "workspace.assets.rename") on-double-click]) + (conj [(tr "workspace.assets.duplicate") on-duplicate])))}])])) ;; --- Page Item Wrapper @@ -198,26 +204,26 @@ (mf/defc sitemap [] - (let [file (mf/deref refs/workspace-file) - create (mf/use-callback - (mf/deps file) - (fn [] - (st/emit! (dw/create-page {:file-id (:id file) - :project-id (:project-id file)})))) - show-pages? (mf/use-state true) - - {:keys [on-pointer-down on-lost-pointer-capture on-mouse-move parent-ref size]} + (let [{:keys [on-pointer-down on-lost-pointer-capture on-mouse-move parent-ref size]} (use-resize-hook :sitemap 200 38 400 :y false nil) - size (if @show-pages? size 38) - toggle-pages - (mf/use-callback #(reset! show-pages? not))] + file (mf/deref refs/workspace-file) + create (mf/use-callback + (mf/deps file) + (fn [] + (st/emit! (dw/create-page {:file-id (:id file) + :project-id (:project-id file)})))) + show-pages? (mf/use-state true) + size (if @show-pages? size 38) + toggle-pages (mf/use-callback #(reset! show-pages? not)) + workspace-read-only? (mf/use-ctx ctx/workspace-read-only?)] [:div#sitemap.tool-window {:ref parent-ref :style #js {"--height" (str size "px")}} [:div.tool-window-bar [:span (tr "workspace.sidebar.sitemap")] - [:div.add-page {:on-click create} i/close] + (when-not workspace-read-only? + [:div.add-page {:on-click create} i/close]) [:div.collapse-pages {:on-click toggle-pages :style {:transform (when (not @show-pages?) "rotate(-90deg)")}} i/arrow-slide]] diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index 80217083d..7e6e6c19b 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -130,22 +130,25 @@ node-editing? (and edition (not= :text (get-in base-objects [edition :type]))) text-editing? (and edition (= :text (get-in base-objects [edition :type]))) + workspace-read-only? (mf/use-ctx ctx/workspace-read-only?) + on-click (actions/on-click hover selected edition drawing-path? drawing-tool space? selrect) - on-context-menu (actions/on-context-menu hover hover-ids) - on-double-click (actions/on-double-click hover hover-ids drawing-path? base-objects edition) + on-context-menu (actions/on-context-menu hover hover-ids workspace-read-only?) + on-double-click (actions/on-double-click hover hover-ids drawing-path? base-objects edition workspace-read-only?) on-drag-enter (actions/on-drag-enter) on-drag-over (actions/on-drag-over) on-drop (actions/on-drop file viewport-ref zoom) on-mouse-down (actions/on-mouse-down @hover selected edition drawing-tool text-editing? node-editing? - drawing-path? create-comment? space? viewport-ref zoom panning) + drawing-path? create-comment? space? viewport-ref zoom panning + workspace-read-only?) on-mouse-up (actions/on-mouse-up disable-paste) on-pointer-down (actions/on-pointer-down) on-pointer-enter (actions/on-pointer-enter in-viewport?) on-pointer-leave (actions/on-pointer-leave in-viewport?) on-pointer-move (actions/on-pointer-move viewport-ref zoom move-stream) on-pointer-up (actions/on-pointer-up) - on-move-selected (actions/on-move-selected hover hover-ids selected space?) - on-menu-selected (actions/on-menu-selected hover hover-ids selected) + on-move-selected (actions/on-move-selected hover hover-ids selected space? workspace-read-only?) + on-menu-selected (actions/on-menu-selected hover hover-ids selected workspace-read-only?) on-frame-enter (actions/on-frame-enter frame-hover) on-frame-leave (actions/on-frame-leave frame-hover) @@ -184,7 +187,7 @@ disabled-guides? (or drawing-tool transform)] - (hooks/setup-dom-events viewport-ref zoom disable-paste in-viewport?) + (hooks/setup-dom-events viewport-ref zoom disable-paste in-viewport? workspace-read-only?) (hooks/setup-viewport-size viewport-ref) (hooks/setup-cursor cursor alt? mod? space? panning drawing-tool drawing-path? node-editing?) (hooks/setup-keyboard alt? mod? space?) diff --git a/frontend/src/app/main/ui/workspace/viewport/actions.cljs b/frontend/src/app/main/ui/workspace/viewport/actions.cljs index bbf0c740b..4316ee855 100644 --- a/frontend/src/app/main/ui/workspace/viewport/actions.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/actions.cljs @@ -34,11 +34,12 @@ (defn on-mouse-down [{:keys [id blocked hidden type]} selected edition drawing-tool text-editing? - node-editing? drawing-path? create-comment? space? viewport-ref zoom panning] + node-editing? drawing-path? create-comment? space? viewport-ref zoom panning + workspace-read-only?] (mf/use-callback (mf/deps id blocked hidden type selected edition drawing-tool text-editing? node-editing? drawing-path? create-comment? @space? viewport-ref zoom - panning) + panning workspace-read-only?) (fn [bevent] (when (or (dom/class? (dom/get-target bevent) "viewport-controls") (dom/class? (dom/get-target bevent) "viewport-selrect")) @@ -81,7 +82,8 @@ (cond node-editing? ;; Handle path node area selection - (st/emit! (dwdp/handle-area-selection shift?)) + (when-not workspace-read-only? + (st/emit! (dwdp/handle-area-selection shift?))) (and @space? mod?) (let [raw-pt (dom/get-client-position event) @@ -93,18 +95,20 @@ (st/emit! (dw/start-panning)) drawing-tool - (st/emit! (dd/start-drawing drawing-tool)) + (when-not workspace-read-only? + (st/emit! (dd/start-drawing drawing-tool))) (or (not id) mod?) (st/emit! (dw/handle-area-selection shift? mod?)) (not drawing-tool) - (st/emit! (dw/start-move-selected id shift?))))))))))) + (when-not workspace-read-only? + (st/emit! (dw/start-move-selected id shift?)))))))))))) (defn on-move-selected - [hover hover-ids selected space?] + [hover hover-ids selected space? workspace-read-only?] (mf/use-callback - (mf/deps @hover @hover-ids selected @space?) + (mf/deps @hover @hover-ids selected @space? workspace-read-only?) (fn [bevent] (let [event (.-nativeEvent bevent) shift? (kbd/shift? event) @@ -117,7 +121,8 @@ (not @space?)) (dom/prevent-default bevent) (dom/stop-propagation bevent) - (st/emit! (dw/start-move-selected))))))) + (when-not workspace-read-only? + (st/emit! (dw/start-move-selected)))))))) (defn on-frame-select [selected] @@ -167,10 +172,10 @@ (st/emit! (dw/select-shape (:id @hover) shift?)))))))) (defn on-double-click - [hover hover-ids drawing-path? objects edition] + [hover hover-ids drawing-path? objects edition workspace-read-only?] (mf/use-callback - (mf/deps @hover @hover-ids drawing-path? edition) + (mf/deps @hover @hover-ids drawing-path? edition workspace-read-only?) (fn [event] (dom/stop-propagation event) (let [ctrl? (kbd/ctrl? event) @@ -189,7 +194,7 @@ (fn [] (when (and (not drawing-path?) shape) (cond - (and editable? (not= id edition)) + (and editable? (not= id edition) (not workspace-read-only?)) (st/emit! (dw/select-shape id) (dw/start-editing-selected)) @@ -202,33 +207,37 @@ (st/emit! (dw/select-shape (:id selected))))))))))))) (defn on-context-menu - [hover hover-ids] + [hover hover-ids workspace-read-only?] (mf/use-callback - (mf/deps @hover @hover-ids) + (mf/deps @hover @hover-ids workspace-read-only?) (fn [event] - (when (or (dom/class? (dom/get-target event) "viewport-controls") - (dom/class? (dom/get-target event) "viewport-selrect")) + (if workspace-read-only? (dom/prevent-default event) + (when (or (dom/class? (dom/get-target event) "viewport-controls") + (dom/class? (dom/get-target event) "viewport-selrect") + (workspace-read-only?)) + (dom/prevent-default event) - (let [position (dom/get-client-position event)] + (let [position (dom/get-client-position event)] ;; Delayed callback because we need to wait to the previous context menu to be closed - (timers/schedule - #(st/emit! - (if (some? @hover) - (dw/show-shape-context-menu {:position position - :shape @hover - :hover-ids @hover-ids}) - (dw/show-context-menu {:position position}))))))))) + (timers/schedule + #(st/emit! + (if (some? @hover) + (dw/show-shape-context-menu {:position position + :shape @hover + :hover-ids @hover-ids}) + (dw/show-context-menu {:position position})))))))))) (defn on-menu-selected - [hover hover-ids selected] + [hover hover-ids selected workspace-read-only?] (mf/use-callback - (mf/deps @hover @hover-ids selected) + (mf/deps @hover @hover-ids selected workspace-read-only?) (fn [event] (dom/prevent-default event) (dom/stop-propagation event) - (let [position (dom/get-client-position event)] - (st/emit! (dw/show-shape-context-menu {:position position :hover-ids @hover-ids})))))) + (when-not workspace-read-only? + (let [position (dom/get-client-position event)] + (st/emit! (dw/show-shape-context-menu {:position position :hover-ids @hover-ids}))))))) (defn on-mouse-up [disable-paste] @@ -484,12 +493,13 @@ :blobs (seq files)}] (st/emit! (dwm/upload-media-workspace params)))))))) -(defn on-paste [disable-paste in-viewport?] +(defn on-paste [disable-paste in-viewport? workspace-read-only?] (mf/use-callback + (mf/deps workspace-read-only?) (fn [event] ;; We disable the paste just after mouse-up of a middle button so when panning won't ;; paste the content into the workspace (let [tag-name (-> event dom/get-target dom/get-tag-name)] - (when (and (not (#{"INPUT" "TEXTAREA"} tag-name)) (not @disable-paste)) + (when (and (not (#{"INPUT" "TEXTAREA"} tag-name)) (not @disable-paste) (not workspace-read-only?)) (st/emit! (dw/paste-from-event event @in-viewport?))))))) diff --git a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs index 2ea2310da..ffd21250e 100644 --- a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs @@ -32,14 +32,14 @@ [rumext.v2 :as mf]) (:import goog.events.EventType)) -(defn setup-dom-events [viewport-ref zoom disable-paste in-viewport?] +(defn setup-dom-events [viewport-ref zoom disable-paste in-viewport? workspace-read-only?] (let [on-key-down (actions/on-key-down) on-key-up (actions/on-key-up) on-mouse-move (actions/on-mouse-move viewport-ref zoom) on-mouse-wheel (actions/on-mouse-wheel viewport-ref zoom) - on-paste (actions/on-paste disable-paste in-viewport?)] + on-paste (actions/on-paste disable-paste in-viewport? workspace-read-only?)] (mf/use-layout-effect - (mf/deps on-key-down on-key-up on-mouse-move on-mouse-wheel on-paste) + (mf/deps on-key-down on-key-up on-mouse-move on-mouse-wheel on-paste workspace-read-only?) (fn [] (let [node (mf/ref-val viewport-ref) keys [(events/listen js/document EventType.KEYDOWN on-key-down) diff --git a/frontend/src/app/main/ui/workspace/viewport/selection.cljs b/frontend/src/app/main/ui/workspace/viewport/selection.cljs index 0a1cb7821..f4f829fc7 100644 --- a/frontend/src/app/main/ui/workspace/viewport/selection.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/selection.cljs @@ -15,6 +15,7 @@ [app.main.data.workspace :as dw] [app.main.refs :as refs] [app.main.store :as st] + [app.main.ui.context :as ctx] [app.main.ui.cursors :as cur] [app.main.ui.workspace.shapes.path.editor :refer [path-editor]] [app.util.dom :as dom] @@ -286,13 +287,14 @@ (mf/defc controls-handlers {::mf/wrap-props false} [props] - (let [shape (obj/get props "shape") - zoom (obj/get props "zoom") - color (obj/get props "color") - on-resize (obj/get props "on-resize") - on-rotate (obj/get props "on-rotate") - disable-handlers (obj/get props "disable-handlers") - current-transform (mf/deref refs/current-transform) + (let [shape (obj/get props "shape") + zoom (obj/get props "zoom") + color (obj/get props "color") + on-resize (obj/get props "on-resize") + on-rotate (obj/get props "on-rotate") + disable-handlers (obj/get props "disable-handlers") + current-transform (mf/deref refs/current-transform) + workspace-read-only? (mf/use-ctx ctx/workspace-read-only?) selrect (:selrect shape) transform (gsh/transform-matrix shape {:no-flip true}) @@ -302,7 +304,8 @@ (gpt/angle) (mod 360))] - (when (not (#{:move :rotate} current-transform)) + (when (and (not (#{:move :rotate} current-transform)) + (not workspace-read-only?)) [:g.controls {:pointer-events (if disable-handlers "none" "visible")} ;; Handlers (for [{:keys [type position props]} (handlers-for-selection selrect shape zoom)] diff --git a/frontend/src/app/main/ui/workspace/viewport/widgets.cljs b/frontend/src/app/main/ui/workspace/viewport/widgets.cljs index d78b53463..8e6fae8f1 100644 --- a/frontend/src/app/main/ui/workspace/viewport/widgets.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/widgets.cljs @@ -16,6 +16,7 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.streams :as ms] + [app.main.ui.context :as ctx] [app.main.ui.hooks :as hooks] [app.main.ui.icons :as i] [app.main.ui.workspace.viewport.path-actions :refer [path-actions]] @@ -87,15 +88,17 @@ (mf/defc frame-title {::mf/wrap [mf/memo]} [{:keys [frame selected? zoom show-artboard-names? on-frame-enter on-frame-leave on-frame-select]}] - (let [on-mouse-down + (let [workspace-read-only? (mf/use-ctx ctx/workspace-read-only?) + on-mouse-down (mf/use-callback - (mf/deps (:id frame) on-frame-select) + (mf/deps (:id frame) on-frame-select workspace-read-only?) (fn [bevent] (let [event (.-nativeEvent bevent)] (when (= 1 (.-which event)) (dom/prevent-default event) (dom/stop-propagation event) - (on-frame-select event (:id frame)))))) + (when-not workspace-read-only? + (on-frame-select event (:id frame))))))) on-double-click (mf/use-callback @@ -105,13 +108,14 @@ on-context-menu (mf/use-callback - (mf/deps frame) + (mf/deps frame workspace-read-only?) (fn [bevent] (let [event (.-nativeEvent bevent) position (dom/get-client-position event)] (dom/prevent-default event) (dom/stop-propagation event) - (st/emit! (dw/show-shape-context-menu {:position position :shape frame}))))) + (when-not workspace-read-only? + (st/emit! (dw/show-shape-context-menu {:position position :shape frame})))))) on-pointer-enter (mf/use-callback @@ -156,15 +160,15 @@ {::mf/wrap-props false ::mf/wrap [mf/memo]} [props] - (let [objects (unchecked-get props "objects") - zoom (unchecked-get props "zoom") - selected (or (unchecked-get props "selected") #{}) + (let [objects (unchecked-get props "objects") + zoom (unchecked-get props "zoom") + selected (or (unchecked-get props "selected") #{}) show-artboard-names? (unchecked-get props "show-artboard-names?") - on-frame-enter (unchecked-get props "on-frame-enter") - on-frame-leave (unchecked-get props "on-frame-leave") - on-frame-select (unchecked-get props "on-frame-select") - frames (ctt/get-frames objects) - focus (unchecked-get props "focus")] + on-frame-enter (unchecked-get props "on-frame-enter") + on-frame-leave (unchecked-get props "on-frame-leave") + on-frame-select (unchecked-get props "on-frame-select") + frames (ctt/get-frames objects) + focus (unchecked-get props "focus")] [:g.frame-titles (for [frame frames] diff --git a/frontend/src/debug.cljs b/frontend/src/debug.cljs index f64e98f4b..0edef0ee2 100644 --- a/frontend/src/debug.cljs +++ b/frontend/src/debug.cljs @@ -316,3 +316,8 @@ objects (get-in @st/state [:workspace-data :pages-index page-id :objects])] (.log js/console (modif->js (:workspace-modifiers @st/state) objects))) nil) + +(defn ^:export set-workspace-read-only + [read-only?] + (st/emit! (dw/set-workspace-read-only read-only?))) +