mirror of
https://github.com/penpot/penpot.git
synced 2025-06-09 08:11:38 +02:00
✨ Add improved abstraction for team permissions
Relevant changes: - replace user-viewer? with can-edit removing many double negations on the code - always use team permissions making the permissions access uniform around all the code - expose team permissions to ui tree through ctx/team-permissions context
This commit is contained in:
parent
b3fcbd91e4
commit
d6da8afdce
29 changed files with 412 additions and 391 deletions
|
@ -182,11 +182,10 @@
|
||||||
uchg (vec undo-changes)
|
uchg (vec undo-changes)
|
||||||
rchg (vec redo-changes)
|
rchg (vec redo-changes)
|
||||||
features (features/get-team-enabled-features state)
|
features (features/get-team-enabled-features state)
|
||||||
user-viewer? (not (dm/get-in state [:workspace-file :permissions :can-edit]))]
|
permissions (:permissions state)]
|
||||||
|
|
||||||
;; Prevent commit changes by a viewer team member (it really should never happen)
|
;; Prevent commit changes by a viewer team member (it really should never happen)
|
||||||
(if user-viewer?
|
(when (:can-edit permissions)
|
||||||
(rx/empty)
|
|
||||||
(rx/of (-> params
|
(rx/of (-> params
|
||||||
(assoc :undo-group undo-group)
|
(assoc :undo-group undo-group)
|
||||||
(assoc :features features)
|
(assoc :features features)
|
||||||
|
|
|
@ -7,10 +7,11 @@
|
||||||
(ns app.main.data.common
|
(ns app.main.data.common
|
||||||
"A general purpose events."
|
"A general purpose events."
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.schema :as sm]
|
[app.common.schema :as sm]
|
||||||
[app.common.types.components-list :as ctkl]
|
[app.common.types.components-list :as ctkl]
|
||||||
[app.common.types.team :as tt]
|
[app.common.types.team :as ctt]
|
||||||
[app.config :as cf]
|
[app.config :as cf]
|
||||||
[app.main.data.modal :as modal]
|
[app.main.data.modal :as modal]
|
||||||
[app.main.data.notifications :as ntf]
|
[app.main.data.notifications :as ntf]
|
||||||
|
@ -196,7 +197,7 @@
|
||||||
(rx/tap on-success)
|
(rx/tap on-success)
|
||||||
(rx/catch on-error))))))
|
(rx/catch on-error))))))
|
||||||
|
|
||||||
(defn- change-role-msg
|
(defn- get-change-role-msg
|
||||||
[role]
|
[role]
|
||||||
(case role
|
(case role
|
||||||
:viewer (tr "dashboard.permissions-change.viewer")
|
:viewer (tr "dashboard.permissions-change.viewer")
|
||||||
|
@ -204,26 +205,23 @@
|
||||||
:admin (tr "dashboard.permissions-change.admin")
|
:admin (tr "dashboard.permissions-change.admin")
|
||||||
:owner (tr "dashboard.permissions-change.owner")))
|
:owner (tr "dashboard.permissions-change.owner")))
|
||||||
|
|
||||||
|
(defn change-team-role
|
||||||
(defn change-team-permissions
|
[{:keys [team-id role]}]
|
||||||
[{:keys [team-id role workspace?]}]
|
|
||||||
(dm/assert! (uuid? team-id))
|
(dm/assert! (uuid? team-id))
|
||||||
(dm/assert! (contains? tt/valid-roles role))
|
(dm/assert! (contains? ctt/valid-roles role))
|
||||||
(ptk/reify ::change-team-permissions
|
|
||||||
|
(ptk/reify ::change-team-role
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ _ _]
|
(watch [_ _ _]
|
||||||
(rx/of (ntf/info (change-role-msg role))))
|
(rx/of (ntf/info (get-change-role-msg role))))
|
||||||
|
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [route (if workspace?
|
(let [permissions (get ctt/permissions-for-role role)]
|
||||||
[:workspace-file :permissions]
|
(-> state
|
||||||
[:teams team-id :permissions])]
|
(update :permissions merge permissions)
|
||||||
(update-in state route
|
(update-in [:team :permissions] merge permissions)
|
||||||
(fn [permissions]
|
(d/update-in-when [:teams team-id :permissions] merge permissions))))))
|
||||||
(merge permissions (get tt/permissions-for-role role))))))))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(defn team-membership-change
|
(defn team-membership-change
|
||||||
[{:keys [team-id team-name change]}]
|
[{:keys [team-id team-name change]}]
|
||||||
|
@ -232,11 +230,11 @@
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(when (= :removed change)
|
(when (= :removed change)
|
||||||
(let [msg (tr "dashboard.removed-from-team" team-name)]
|
(let [message (tr "dashboard.removed-from-team" team-name)
|
||||||
|
profile (:profile state)]
|
||||||
(rx/concat
|
(rx/concat
|
||||||
(rx/of (rt/nav :dashboard-projects {:team-id (get-in state [:profile :default-team-id])}))
|
(rx/of (rt/nav :dashboard-projects {:team-id (:default-team-id profile)}))
|
||||||
(->> (rx/of (ntf/info msg))
|
(->> (rx/of (ntf/info message))
|
||||||
;; Delay so the navigation can finish
|
;; Delay so the navigation can finish
|
||||||
(rx/delay 250))))))))
|
(rx/delay 250))))))))
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
[app.common.files.helpers :as cfh]
|
[app.common.files.helpers :as cfh]
|
||||||
[app.common.logging :as log]
|
[app.common.logging :as log]
|
||||||
[app.common.schema :as sm]
|
[app.common.schema :as sm]
|
||||||
[app.common.types.team :as tt]
|
[app.common.types.team :as ctt]
|
||||||
[app.common.uri :as u]
|
[app.common.uri :as u]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.config :as cf]
|
[app.config :as cf]
|
||||||
|
@ -482,7 +482,7 @@
|
||||||
(defn update-team-member-role
|
(defn update-team-member-role
|
||||||
[{:keys [role member-id] :as params}]
|
[{:keys [role member-id] :as params}]
|
||||||
(dm/assert! (uuid? member-id))
|
(dm/assert! (uuid? member-id))
|
||||||
(dm/assert! (contains? tt/valid-roles role))
|
(dm/assert! (contains? ctt/valid-roles role))
|
||||||
|
|
||||||
(ptk/reify ::update-team-member-role
|
(ptk/reify ::update-team-member-role
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
|
@ -605,7 +605,7 @@
|
||||||
(sm/check-email! email))
|
(sm/check-email! email))
|
||||||
|
|
||||||
(dm/assert! (uuid? team-id))
|
(dm/assert! (uuid? team-id))
|
||||||
(dm/assert! (contains? tt/valid-roles role))
|
(dm/assert! (contains? ctt/valid-roles role))
|
||||||
|
|
||||||
(ptk/reify ::update-team-invitation-role
|
(ptk/reify ::update-team-invitation-role
|
||||||
IDeref
|
IDeref
|
||||||
|
@ -1211,19 +1211,18 @@
|
||||||
;; Notifications
|
;; Notifications
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(defn- handle-change-team-role
|
||||||
(defn- handle-change-team-permissions-dashboard
|
[params]
|
||||||
[msg]
|
(ptk/reify ::handle-change-team-role
|
||||||
(ptk/reify ::handle-change-team-permissions-dashboard
|
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ _ _]
|
(watch [_ _ _]
|
||||||
(rx/of (dc/change-team-permissions (assoc msg :workspace? false))
|
(rx/of (dc/change-team-role params)
|
||||||
(modal/hide)))))
|
(modal/hide)))))
|
||||||
|
|
||||||
(defn- process-message
|
(defn- process-message
|
||||||
[{:keys [type] :as msg}]
|
[{:keys [type] :as msg}]
|
||||||
(case type
|
(case type
|
||||||
:notification (dc/handle-notification msg)
|
:notification (dc/handle-notification msg)
|
||||||
:team-role-change (handle-change-team-permissions-dashboard msg)
|
:team-role-change (handle-change-team-role msg)
|
||||||
:team-membership-change (dc/team-membership-change msg)
|
:team-membership-change (dc/team-membership-change msg)
|
||||||
nil))
|
nil))
|
|
@ -97,6 +97,7 @@
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(-> state
|
(-> state
|
||||||
(assoc :team team)
|
(assoc :team team)
|
||||||
|
(assoc :permissions (:permissions team))
|
||||||
(assoc :current-team-id (:id team))))
|
(assoc :current-team-id (:id team))))
|
||||||
|
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
|
|
|
@ -68,7 +68,7 @@
|
||||||
(ptk/reify ::set-workspace-read-only
|
(ptk/reify ::set-workspace-read-only
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(assoc-in state [:workspace-global :read-only?] read-only?))
|
(update state :workspace-global assoc :read-only? read-only?))
|
||||||
|
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ _ _]
|
(watch [_ _ _]
|
||||||
|
|
|
@ -97,26 +97,22 @@
|
||||||
|
|
||||||
(rx/concat stream (rx/of (dws/send endmsg)))))))
|
(rx/concat stream (rx/of (dws/send endmsg)))))))
|
||||||
|
|
||||||
|
(defn- handle-change-team-role
|
||||||
(defn- handle-change-team-permissions
|
|
||||||
[{:keys [role] :as msg}]
|
[{:keys [role] :as msg}]
|
||||||
(ptk/reify ::handle-change-team-permissions
|
(ptk/reify ::handle-change-team-role
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ _ _]
|
(watch [_ _ _]
|
||||||
(let [viewer? (= :viewer role)]
|
|
||||||
|
|
||||||
(rx/concat
|
(rx/concat
|
||||||
(rx/of :interrupt
|
(rx/of :interrupt
|
||||||
(dwe/clear-edition-mode)
|
(dwe/clear-edition-mode)
|
||||||
(dwc/set-workspace-read-only false))
|
(dwc/set-workspace-read-only false))
|
||||||
(->> (rx/of (dc/change-team-permissions msg))
|
(->> (rx/of (dc/change-team-role msg))
|
||||||
;; Delay so anything that launched :interrupt can finish
|
;; Delay so anything that launched :interrupt can finish
|
||||||
(rx/delay 100))
|
(rx/delay 100))
|
||||||
(if viewer?
|
(if (= :viewer role)
|
||||||
(rx/of (modal/hide)
|
(rx/of (modal/hide)
|
||||||
(dwly/set-options-mode :inspect))
|
(dwly/set-options-mode :inspect))
|
||||||
(rx/of (dwly/set-options-mode :design))))))))
|
(rx/of (dwly/set-options-mode :design)))))))
|
||||||
|
|
||||||
|
|
||||||
(defn- process-message
|
(defn- process-message
|
||||||
[{:keys [type] :as msg}]
|
[{:keys [type] :as msg}]
|
||||||
|
@ -129,7 +125,7 @@
|
||||||
:file-change (handle-file-change msg)
|
:file-change (handle-file-change msg)
|
||||||
:library-change (handle-library-change msg)
|
:library-change (handle-library-change msg)
|
||||||
:notification (dc/handle-notification msg)
|
:notification (dc/handle-notification msg)
|
||||||
:team-role-change (handle-change-team-permissions (assoc msg :workspace? true))
|
:team-role-change (handle-change-team-role msg)
|
||||||
:team-membership-change (dc/team-membership-change msg)
|
:team-membership-change (dc/team-membership-change msg)
|
||||||
nil))
|
nil))
|
||||||
|
|
||||||
|
|
|
@ -37,18 +37,16 @@
|
||||||
;; Shortcuts
|
;; Shortcuts
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
(defn toggle-layout-flag
|
(defn- toggle-layout-flag
|
||||||
[flag]
|
[flag]
|
||||||
(-> (dw/toggle-layout-flag flag)
|
(-> (dw/toggle-layout-flag flag)
|
||||||
(vary-meta assoc ::ev/origin "workspace-shortcuts")))
|
(vary-meta assoc ::ev/origin "workspace-shortcuts")))
|
||||||
|
|
||||||
(defn emit-when-no-readonly
|
(defn- emit-when-no-readonly
|
||||||
[& events]
|
[& events]
|
||||||
(let [file (deref refs/workspace-file)
|
(let [can-edit? (:can-edit (deref refs/permissions))
|
||||||
user-viewer? (not (dm/get-in file [:permissions :can-edit]))
|
read-only? (deref refs/workspace-read-only?)]
|
||||||
read-only? (or (deref refs/workspace-read-only?)
|
(when (and can-edit? (not read-only?))
|
||||||
user-viewer?)]
|
|
||||||
(when-not read-only?
|
|
||||||
(run! st/emit! events))))
|
(run! st/emit! events))))
|
||||||
|
|
||||||
(def esc-pressed
|
(def esc-pressed
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
(ns app.main.data.workspace.text.shortcuts
|
(ns app.main.data.workspace.text.shortcuts
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.files.helpers :as cfh]
|
||||||
[app.common.text :as txt]
|
[app.common.text :as txt]
|
||||||
[app.main.data.shortcuts :as ds]
|
[app.main.data.shortcuts :as ds]
|
||||||
[app.main.data.workspace.texts :as dwt]
|
[app.main.data.workspace.texts :as dwt]
|
||||||
|
@ -190,16 +190,19 @@
|
||||||
|
|
||||||
(defn- update-attrs-when-no-readonly [props]
|
(defn- update-attrs-when-no-readonly [props]
|
||||||
(let [undo-id (js/Symbol)
|
(let [undo-id (js/Symbol)
|
||||||
file (deref refs/workspace-file)
|
|
||||||
user-viewer? (not (dm/get-in file [:permissions :can-edit]))
|
can-edit? (:can-edit (deref refs/permissions))
|
||||||
read-only? (or (deref refs/workspace-read-only?)
|
read-only? (deref refs/workspace-read-only?)
|
||||||
user-viewer?)
|
|
||||||
shapes-with-children (deref refs/selected-shapes-with-children)
|
text-shapes (->> (deref refs/selected-shapes-with-children)
|
||||||
text-shapes (filter #(= (:type %) :text) shapes-with-children)
|
(filter cfh/text-shape?)
|
||||||
|
(not-empty))
|
||||||
|
|
||||||
props (if (> (count text-shapes) 1)
|
props (if (> (count text-shapes) 1)
|
||||||
(blend-props text-shapes props)
|
(blend-props text-shapes props)
|
||||||
props)]
|
props)]
|
||||||
(when (and (not read-only?) text-shapes)
|
|
||||||
|
(when (and can-edit? (not read-only?) text-shapes)
|
||||||
(st/emit! (dwu/start-undo-transaction undo-id))
|
(st/emit! (dwu/start-undo-transaction undo-id))
|
||||||
(run! #(update-attrs % props) text-shapes)
|
(run! #(update-attrs % props) text-shapes)
|
||||||
(st/emit! (dwu/commit-undo-transaction undo-id)))))
|
(st/emit! (dwu/commit-undo-transaction undo-id)))))
|
||||||
|
|
|
@ -27,6 +27,12 @@
|
||||||
(def profile
|
(def profile
|
||||||
(l/derived :profile st/state))
|
(l/derived :profile st/state))
|
||||||
|
|
||||||
|
(def team
|
||||||
|
(l/derived :team st/state))
|
||||||
|
|
||||||
|
(def permissions
|
||||||
|
(l/derived :permissions st/state))
|
||||||
|
|
||||||
(def teams
|
(def teams
|
||||||
(l/derived :teams st/state))
|
(l/derived :teams st/state))
|
||||||
|
|
||||||
|
|
|
@ -32,4 +32,4 @@
|
||||||
(def is-component? (mf/create-context false))
|
(def is-component? (mf/create-context false))
|
||||||
(def sidebar (mf/create-context nil))
|
(def sidebar (mf/create-context nil))
|
||||||
|
|
||||||
(def user-viewer? (mf/create-context nil))
|
(def team-permissions (mf/create-context nil))
|
||||||
|
|
|
@ -65,7 +65,8 @@
|
||||||
content-width (mf/use-state 0)
|
content-width (mf/use-state 0)
|
||||||
project-id (:id project)
|
project-id (:id project)
|
||||||
team-id (:id team)
|
team-id (:id team)
|
||||||
you-viewer? (not (dm/get-in team [:permissions :can-edit]))
|
|
||||||
|
permissions (:permissions team)
|
||||||
|
|
||||||
dashboard-local (mf/deref refs/dashboard-local)
|
dashboard-local (mf/deref refs/dashboard-local)
|
||||||
file-menu-open? (:menu-open dashboard-local)
|
file-menu-open? (:menu-open dashboard-local)
|
||||||
|
@ -87,8 +88,9 @@
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
#(st/emit! (dd/clear-selected-files)))
|
#(st/emit! (dd/clear-selected-files)))
|
||||||
|
|
||||||
show-templates (and (contains? cf/flags :dashboard-templates-section)
|
show-templates
|
||||||
(not you-viewer?))]
|
(and (contains? cf/flags :dashboard-templates-section)
|
||||||
|
(not (:can-edit permissions)))]
|
||||||
|
|
||||||
(mf/with-effect []
|
(mf/with-effect []
|
||||||
(let [key1 (events/listen js/window "resize" on-resize)]
|
(let [key1 (events/listen js/window "resize" on-resize)]
|
||||||
|
@ -117,7 +119,7 @@
|
||||||
:content-width @content-width}])]
|
:content-width @content-width}])]
|
||||||
|
|
||||||
:dashboard-fonts
|
:dashboard-fonts
|
||||||
[:& fonts-page {:team team :you-viewer? you-viewer?}]
|
[:& fonts-page {:team team}]
|
||||||
|
|
||||||
:dashboard-font-providers
|
:dashboard-font-providers
|
||||||
[:& font-providers-page {:team team}]
|
[:& font-providers-page {:team team}]
|
||||||
|
@ -125,7 +127,7 @@
|
||||||
:dashboard-files
|
:dashboard-files
|
||||||
(when project
|
(when project
|
||||||
[:*
|
[:*
|
||||||
[:& files-section {:team team :project project :you-viewer? you-viewer?}]
|
[:& files-section {:team team :project project}]
|
||||||
(when show-templates
|
(when show-templates
|
||||||
[:& templates-section {:profile profile
|
[:& templates-section {:profile profile
|
||||||
:team-id team-id
|
:team-id team-id
|
||||||
|
@ -138,7 +140,7 @@
|
||||||
:search-term search-term}]
|
:search-term search-term}]
|
||||||
|
|
||||||
:dashboard-libraries
|
:dashboard-libraries
|
||||||
[:& libraries-page {:team team :you-viewer? you-viewer?}]
|
[:& libraries-page {:team team}]
|
||||||
|
|
||||||
:dashboard-team-members
|
:dashboard-team-members
|
||||||
[:& team-members-page {:team team :profile profile :invite-email invite-email}]
|
[:& team-members-page {:team team :profile profile :invite-email invite-email}]
|
||||||
|
@ -231,8 +233,7 @@
|
||||||
|
|
||||||
invite-email (-> route :query-params :invite-email)
|
invite-email (-> route :query-params :invite-email)
|
||||||
|
|
||||||
teams (mf/deref refs/teams)
|
team (mf/deref refs/team)
|
||||||
team (get teams team-id)
|
|
||||||
|
|
||||||
projects (mf/deref refs/dashboard-projects)
|
projects (mf/deref refs/dashboard-projects)
|
||||||
project (get projects project-id)
|
project (get projects project-id)
|
||||||
|
@ -261,6 +262,7 @@
|
||||||
|
|
||||||
[:& (mf/provider ctx/current-team-id) {:value team-id}
|
[:& (mf/provider ctx/current-team-id) {:value team-id}
|
||||||
[:& (mf/provider ctx/current-project-id) {:value project-id}
|
[:& (mf/provider ctx/current-project-id) {:value project-id}
|
||||||
|
[:& (mf/provider ctx/team-permissions) {:value (:permissions team)}
|
||||||
;; NOTE: dashboard events and other related functions assumes
|
;; NOTE: dashboard events and other related functions assumes
|
||||||
;; that the team is a implicit context variable that is
|
;; that the team is a implicit context variable that is
|
||||||
;; available using react context or accessing
|
;; available using react context or accessing
|
||||||
|
@ -286,4 +288,4 @@
|
||||||
:section section
|
:section section
|
||||||
:search-term search-term
|
:search-term search-term
|
||||||
:team team
|
:team team
|
||||||
:invite-email invite-email}])])]]))
|
:invite-email invite-email}])])]]]))
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
|
|
||||||
(mf/defc file-menu
|
(mf/defc file-menu
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
[{:keys [files show? on-edit on-menu-close top left navigate? origin parent-id you-viewer?]}]
|
[{:keys [files show? on-edit on-menu-close top left navigate? origin parent-id can-edit]}]
|
||||||
(assert (seq files) "missing `files` prop")
|
(assert (seq files) "missing `files` prop")
|
||||||
(assert (boolean? show?) "missing `show?` prop")
|
(assert (boolean? show?) "missing `show?` prop")
|
||||||
(assert (fn? on-edit) "missing `on-edit` prop")
|
(assert (fn? on-edit) "missing `on-edit` prop")
|
||||||
|
@ -245,13 +245,12 @@
|
||||||
|
|
||||||
options
|
options
|
||||||
(if multi?
|
(if multi?
|
||||||
[(when-not you-viewer?
|
[(when can-edit
|
||||||
{:name (tr "dashboard.duplicate-multi" file-count)
|
{:name (tr "dashboard.duplicate-multi" file-count)
|
||||||
:id "duplicate-multi"
|
:id "duplicate-multi"
|
||||||
:handler on-duplicate})
|
:handler on-duplicate})
|
||||||
|
|
||||||
(when (and (or (seq current-projects) (seq other-teams))
|
(when (and (or (seq current-projects) (seq other-teams)) can-edit)
|
||||||
(not you-viewer?))
|
|
||||||
{:name (tr "dashboard.move-to-multi" file-count)
|
{:name (tr "dashboard.move-to-multi" file-count)
|
||||||
:id "file-move-multi"
|
:id "file-move-multi"
|
||||||
:options sub-options})
|
:options sub-options})
|
||||||
|
@ -269,14 +268,12 @@
|
||||||
:id "file-standard-export-multi"
|
:id "file-standard-export-multi"
|
||||||
:handler on-export-standard-files}
|
:handler on-export-standard-files}
|
||||||
|
|
||||||
(when (and (:is-shared file)
|
(when (and (:is-shared file) can-edit)
|
||||||
(not you-viewer?))
|
|
||||||
{:name (tr "labels.unpublish-multi-files" file-count)
|
{:name (tr "labels.unpublish-multi-files" file-count)
|
||||||
:id "file-unpublish-multi"
|
:id "file-unpublish-multi"
|
||||||
:handler on-del-shared})
|
:handler on-del-shared})
|
||||||
|
|
||||||
(when (and (not is-lib-page?)
|
(when (and (not is-lib-page?) can-edit)
|
||||||
(not you-viewer?))
|
|
||||||
{:name :separator}
|
{:name :separator}
|
||||||
{:name (tr "labels.delete-multi-files" file-count)
|
{:name (tr "labels.delete-multi-files" file-count)
|
||||||
:id "file-delete-multi"
|
:id "file-delete-multi"
|
||||||
|
@ -285,14 +282,12 @@
|
||||||
[{:name (tr "dashboard.open-in-new-tab")
|
[{:name (tr "dashboard.open-in-new-tab")
|
||||||
:id "file-open-new-tab"
|
:id "file-open-new-tab"
|
||||||
:handler on-new-tab}
|
:handler on-new-tab}
|
||||||
(when (and (not is-search-page?)
|
(when (and (not is-search-page?) can-edit)
|
||||||
(not you-viewer?))
|
|
||||||
{:name (tr "labels.rename")
|
{:name (tr "labels.rename")
|
||||||
:id "file-rename"
|
:id "file-rename"
|
||||||
:handler on-edit})
|
:handler on-edit})
|
||||||
|
|
||||||
(when (and (not is-search-page?)
|
(when (and (not is-search-page?) can-edit)
|
||||||
(not you-viewer?))
|
|
||||||
{:name (tr "dashboard.duplicate")
|
{:name (tr "dashboard.duplicate")
|
||||||
:id "file-duplicate"
|
:id "file-duplicate"
|
||||||
:handler on-duplicate})
|
:handler on-duplicate})
|
||||||
|
@ -300,13 +295,13 @@
|
||||||
(when (and (not is-lib-page?)
|
(when (and (not is-lib-page?)
|
||||||
(not is-search-page?)
|
(not is-search-page?)
|
||||||
(or (seq current-projects) (seq other-teams))
|
(or (seq current-projects) (seq other-teams))
|
||||||
(not you-viewer?))
|
can-edit)
|
||||||
{:name (tr "dashboard.move-to")
|
{:name (tr "dashboard.move-to")
|
||||||
:id "file-move-to"
|
:id "file-move-to"
|
||||||
:options sub-options})
|
:options sub-options})
|
||||||
|
|
||||||
(when (and (not is-search-page?)
|
(when (and (not is-search-page?)
|
||||||
(not you-viewer?))
|
can-edit)
|
||||||
(if (:is-shared file)
|
(if (:is-shared file)
|
||||||
{:name (tr "dashboard.unpublish-shared")
|
{:name (tr "dashboard.unpublish-shared")
|
||||||
:id "file-del-shared"
|
:id "file-del-shared"
|
||||||
|
@ -330,10 +325,10 @@
|
||||||
:id "download-standard-file"
|
:id "download-standard-file"
|
||||||
:handler on-export-standard-files}
|
:handler on-export-standard-files}
|
||||||
|
|
||||||
(when (and (not is-lib-page?) (not is-search-page?) (not you-viewer?))
|
(when (and (not is-lib-page?) (not is-search-page?) can-edit)
|
||||||
{:name :separator})
|
{:name :separator})
|
||||||
|
|
||||||
(when (and (not is-lib-page?) (not is-search-page?) (not you-viewer?))
|
(when (and (not is-lib-page?) (not is-search-page?) can-edit)
|
||||||
{:name (tr "labels.delete")
|
{:name (tr "labels.delete")
|
||||||
:id "file-delete"
|
:id "file-delete"
|
||||||
:handler on-delete})])]
|
:handler on-delete})])]
|
||||||
|
|
|
@ -28,8 +28,10 @@
|
||||||
(def ^:private menu-icon
|
(def ^:private menu-icon
|
||||||
(i/icon-xref :menu (stl/css :menu-icon)))
|
(i/icon-xref :menu (stl/css :menu-icon)))
|
||||||
|
|
||||||
(mf/defc header
|
(mf/defc header*
|
||||||
[{:keys [project create-fn you-viewer?] :as props}]
|
{::mf/props :obj
|
||||||
|
::mf/private true}
|
||||||
|
[{:keys [project create-fn can-edit]}]
|
||||||
(let [local (mf/use-state
|
(let [local (mf/use-state
|
||||||
{:menu-open false
|
{:menu-open false
|
||||||
:edition false})
|
:edition false})
|
||||||
|
@ -72,8 +74,7 @@
|
||||||
[:div#dashboard-drafts-title {:class (stl/css :dashboard-title)}
|
[:div#dashboard-drafts-title {:class (stl/css :dashboard-title)}
|
||||||
[:h1 (tr "labels.drafts")]]
|
[:h1 (tr "labels.drafts")]]
|
||||||
|
|
||||||
(if (and (:edition @local)
|
(if (and (:edition @local) can-edit)
|
||||||
(not you-viewer?))
|
|
||||||
[:& inline-edition
|
[:& inline-edition
|
||||||
{:content (:name project)
|
{:content (:name project)
|
||||||
:on-end (fn [name]
|
:on-end (fn [name]
|
||||||
|
@ -89,7 +90,7 @@
|
||||||
(:name project)]]))
|
(:name project)]]))
|
||||||
|
|
||||||
[:div {:class (stl/css :dashboard-header-actions)}
|
[:div {:class (stl/css :dashboard-header-actions)}
|
||||||
(when-not you-viewer?
|
(when ^boolean can-edit
|
||||||
[:a {:class (stl/css :btn-secondary :btn-small :new-file)
|
[:a {:class (stl/css :btn-secondary :btn-small :new-file)
|
||||||
:tab-index "0"
|
:tab-index "0"
|
||||||
:on-click on-create-click
|
:on-click on-create-click
|
||||||
|
@ -106,7 +107,7 @@
|
||||||
:on-click toggle-pin
|
:on-click toggle-pin
|
||||||
:on-key-down (fn [event] (when (kbd/enter? event) (toggle-pin event)))}])
|
:on-key-down (fn [event] (when (kbd/enter? event) (toggle-pin event)))}])
|
||||||
|
|
||||||
(when-not you-viewer?
|
(when ^boolean can-edit
|
||||||
[:div {:class (stl/css :icon)
|
[:div {:class (stl/css :icon)
|
||||||
:tab-index "0"
|
:tab-index "0"
|
||||||
:on-click on-menu-click
|
:on-click on-menu-click
|
||||||
|
@ -116,7 +117,7 @@
|
||||||
(on-menu-click event)))}
|
(on-menu-click event)))}
|
||||||
menu-icon])
|
menu-icon])
|
||||||
|
|
||||||
(when-not you-viewer?
|
(when ^boolean can-edit
|
||||||
[:& project-menu {:project project
|
[:& project-menu {:project project
|
||||||
:show? (:menu-open @local)
|
:show? (:menu-open @local)
|
||||||
:left (- (:x (:menu-pos @local)) 180)
|
:left (- (:x (:menu-pos @local)) 180)
|
||||||
|
@ -126,8 +127,10 @@
|
||||||
:on-import on-import}])]]))
|
:on-import on-import}])]]))
|
||||||
|
|
||||||
(mf/defc files-section
|
(mf/defc files-section
|
||||||
[{:keys [project team you-viewer?] :as props}]
|
{::mf/props :obj}
|
||||||
|
[{:keys [project team]}]
|
||||||
(let [files-map (mf/deref refs/dashboard-files)
|
(let [files-map (mf/deref refs/dashboard-files)
|
||||||
|
can-edit? (-> team :permissions :can-edit)
|
||||||
project-id (:id project)
|
project-id (:id project)
|
||||||
is-draft-proyect (:is-default project)
|
is-draft-proyect (:is-default project)
|
||||||
|
|
||||||
|
@ -139,7 +142,7 @@
|
||||||
(sort-by :modified-at)
|
(sort-by :modified-at)
|
||||||
(reverse)))
|
(reverse)))
|
||||||
file-count (or (count files) 0)
|
file-count (or (count files) 0)
|
||||||
empty-state-viewer (and you-viewer?
|
empty-state-viewer (and (not can-edit?)
|
||||||
(= 0 file-count))
|
(= 0 file-count))
|
||||||
|
|
||||||
on-file-created
|
on-file-created
|
||||||
|
@ -171,9 +174,9 @@
|
||||||
(dd/clear-selected-files)))
|
(dd/clear-selected-files)))
|
||||||
|
|
||||||
[:*
|
[:*
|
||||||
[:& header {:team team
|
[:> header* {:team team
|
||||||
|
:can-edit can-edit?
|
||||||
:project project
|
:project project
|
||||||
:you-viewer? you-viewer?
|
|
||||||
:create-fn create-file}]
|
:create-fn create-file}]
|
||||||
[:section {:class (stl/css :dashboard-container :no-bg)
|
[:section {:class (stl/css :dashboard-container :no-bg)
|
||||||
:ref rowref}
|
:ref rowref}
|
||||||
|
@ -188,7 +191,7 @@
|
||||||
(tr "dashboard.empty-placeholder-files-subtitle"))}]
|
(tr "dashboard.empty-placeholder-files-subtitle"))}]
|
||||||
[:& grid {:project project
|
[:& grid {:project project
|
||||||
:files files
|
:files files
|
||||||
:you-viewer? you-viewer?
|
:can-edit can-edit?
|
||||||
:origin :files
|
:origin :files
|
||||||
:create-fn create-file
|
:create-fn create-file
|
||||||
:limit limit}])]]))
|
:limit limit}])]]))
|
||||||
|
|
|
@ -269,7 +269,7 @@
|
||||||
{::mf/props :obj
|
{::mf/props :obj
|
||||||
::mf/private true
|
::mf/private true
|
||||||
::mf/memo true}
|
::mf/memo true}
|
||||||
[{:keys [font-id variants you-viewer?]}]
|
[{:keys [font-id variants can-edit]}]
|
||||||
(let [font (first variants)
|
(let [font (first variants)
|
||||||
|
|
||||||
menu-open* (mf/use-state false)
|
menu-open* (mf/use-state false)
|
||||||
|
@ -361,11 +361,11 @@
|
||||||
[:div {:class (stl/css :table-field :variants)}
|
[:div {:class (stl/css :table-field :variants)}
|
||||||
(for [{:keys [id] :as item} variants]
|
(for [{:keys [id] :as item} variants]
|
||||||
[:div {:class (stl/css-case :variant true
|
[:div {:class (stl/css-case :variant true
|
||||||
:inhert-variant you-viewer?)
|
:inhert-variant (not can-edit))
|
||||||
:key (dm/str id)}
|
:key (dm/str id)}
|
||||||
[:span {:class (stl/css :label)}
|
[:span {:class (stl/css :label)}
|
||||||
[:& font-variant-display-name {:variant item}]]
|
[:& font-variant-display-name {:variant item}]]
|
||||||
(when-not you-viewer?
|
(when can-edit
|
||||||
[:span
|
[:span
|
||||||
{:class (stl/css :icon :close)
|
{:class (stl/css :icon :close)
|
||||||
:data-id (dm/str id)
|
:data-id (dm/str id)
|
||||||
|
@ -384,7 +384,7 @@
|
||||||
:on-click on-cancel}
|
:on-click on-cancel}
|
||||||
i/close]]
|
i/close]]
|
||||||
|
|
||||||
(when-not you-viewer?
|
(when can-edit
|
||||||
[:div {:class (stl/css :table-field :options)}
|
[:div {:class (stl/css :table-field :options)}
|
||||||
[:span {:class (stl/css :icon)
|
[:span {:class (stl/css :icon)
|
||||||
:on-click on-menu-open}
|
:on-click on-menu-open}
|
||||||
|
@ -397,7 +397,7 @@
|
||||||
:on-edit on-edit}]]))]))
|
:on-edit on-edit}]]))]))
|
||||||
|
|
||||||
(mf/defc installed-fonts
|
(mf/defc installed-fonts
|
||||||
[{:keys [fonts you-viewer?] :as props}]
|
[{:keys [fonts can-edit] :as props}]
|
||||||
(let [sterm (mf/use-state "")
|
(let [sterm (mf/use-state "")
|
||||||
|
|
||||||
matches?
|
matches?
|
||||||
|
@ -426,7 +426,7 @@
|
||||||
(group-by :font-id))]
|
(group-by :font-id))]
|
||||||
[:& installed-font {:key (dm/str font-id "-installed")
|
[:& installed-font {:key (dm/str font-id "-installed")
|
||||||
:font-id font-id
|
:font-id font-id
|
||||||
:you-viewer? you-viewer?
|
:can-edit can-edit
|
||||||
:variants variants}])]
|
:variants variants}])]
|
||||||
|
|
||||||
(nil? fonts)
|
(nil? fonts)
|
||||||
|
@ -435,27 +435,33 @@
|
||||||
[:div {:class (stl/css :label)} (tr "dashboard.loading-fonts")]]
|
[:div {:class (stl/css :label)} (tr "dashboard.loading-fonts")]]
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(if you-viewer?
|
(if ^boolean can-edit
|
||||||
[:> empty-placeholder* {:title (tr "dashboard.fonts.empty-placeholder-viewer")
|
|
||||||
:subtitle (tr "dashboard.fonts.empty-placeholder-viewer-sub")
|
|
||||||
:type 2}]
|
|
||||||
|
|
||||||
[:div {:class (stl/css :fonts-placeholder)}
|
[:div {:class (stl/css :fonts-placeholder)}
|
||||||
[:div {:class (stl/css :icon)} i/text]
|
[:div {:class (stl/css :icon)} i/text]
|
||||||
[:div {:class (stl/css :label)} (tr "dashboard.fonts.empty-placeholder")]]))]))
|
[:div {:class (stl/css :label)} (tr "dashboard.fonts.empty-placeholder")]]
|
||||||
|
|
||||||
|
[:> empty-placeholder*
|
||||||
|
{:title (tr "dashboard.fonts.empty-placeholder-viewer")
|
||||||
|
:subtitle (tr "dashboard.fonts.empty-placeholder-viewer-sub")
|
||||||
|
:type 2}]))]))
|
||||||
|
|
||||||
|
|
||||||
(mf/defc fonts-page
|
(mf/defc fonts-page
|
||||||
[{:keys [team you-viewer?] :as props}]
|
{::mf/props :obj}
|
||||||
(let [fonts (mf/deref refs/dashboard-fonts)]
|
[{:keys [team]}]
|
||||||
|
(let [fonts (mf/deref refs/dashboard-fonts)
|
||||||
|
permissions (:permissions team)
|
||||||
|
can-edit (:can-edit permissions)]
|
||||||
[:*
|
[:*
|
||||||
[:& header {:team team :section :fonts}]
|
[:& header {:team team :section :fonts}]
|
||||||
[:section {:class (stl/css :dashboard-container :dashboard-fonts)}
|
[:section {:class (stl/css :dashboard-container :dashboard-fonts)}
|
||||||
(when-not you-viewer?
|
(when ^boolean can-edit
|
||||||
[:& uploaded-fonts {:team team :installed-fonts fonts}])
|
[:& uploaded-fonts {:team team :installed-fonts fonts}])
|
||||||
[:& installed-fonts {:team team :fonts fonts :you-viewer? you-viewer?}]]]))
|
[:& installed-fonts {:team team :fonts fonts :can-edit can-edit}]]]))
|
||||||
|
|
||||||
(mf/defc font-providers-page
|
(mf/defc font-providers-page
|
||||||
[{:keys [team] :as props}]
|
{::mf/props :obj}
|
||||||
|
[{:keys [team]}]
|
||||||
[:*
|
[:*
|
||||||
[:& header {:team team :section :providers}]
|
[:& header {:team team :section :providers}]
|
||||||
[:section {:class (stl/css :dashboard-container)}
|
[:section {:class (stl/css :dashboard-container)}
|
||||||
|
|
|
@ -73,7 +73,7 @@
|
||||||
|
|
||||||
(mf/defc grid-item-thumbnail
|
(mf/defc grid-item-thumbnail
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
[{:keys [file-id revn thumbnail-id background-color you-viewer?]}]
|
[{:keys [file-id revn thumbnail-id background-color can-edit]}]
|
||||||
(let [container (mf/use-ref)
|
(let [container (mf/use-ref)
|
||||||
visible? (h/use-visible container :once? true)]
|
visible? (h/use-visible container :once? true)]
|
||||||
|
|
||||||
|
@ -94,12 +94,12 @@
|
||||||
(when visible?
|
(when visible?
|
||||||
(if thumbnail-id
|
(if thumbnail-id
|
||||||
[:img {:class (stl/css :grid-item-thumbnail-image)
|
[:img {:class (stl/css :grid-item-thumbnail-image)
|
||||||
:draggable (dm/str (not you-viewer?))
|
:draggable (dm/str can-edit)
|
||||||
:src (cf/resolve-media thumbnail-id)
|
:src (cf/resolve-media thumbnail-id)
|
||||||
:loading "lazy"
|
:loading "lazy"
|
||||||
:decoding "async"}]
|
:decoding "async"}]
|
||||||
[:> loader* {:class (stl/css :grid-loader)
|
[:> loader* {:class (stl/css :grid-loader)
|
||||||
:draggable (dm/str (not you-viewer?))
|
:draggable (dm/str can-edit)
|
||||||
:overlay true
|
:overlay true
|
||||||
:title (tr "labels.loading")}]))]))
|
:title (tr "labels.loading")}]))]))
|
||||||
|
|
||||||
|
@ -233,7 +233,7 @@
|
||||||
|
|
||||||
(mf/defc grid-item
|
(mf/defc grid-item
|
||||||
{:wrap [mf/memo]}
|
{:wrap [mf/memo]}
|
||||||
[{:keys [file origin library-view? you-viewer?] :as props}]
|
[{:keys [file origin library-view? can-edit] :as props}]
|
||||||
(let [file-id (:id file)
|
(let [file-id (:id file)
|
||||||
|
|
||||||
;; FIXME: this breaks react hooks rule, hooks should never to
|
;; FIXME: this breaks react hooks rule, hooks should never to
|
||||||
|
@ -276,10 +276,10 @@
|
||||||
|
|
||||||
on-drag-start
|
on-drag-start
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps selected-files you-viewer?)
|
(mf/deps selected-files can-edit)
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(st/emit! (dd/hide-file-menu))
|
(st/emit! (dd/hide-file-menu))
|
||||||
(when-not you-viewer?
|
(when can-edit
|
||||||
(let [offset (dom/get-offset-position (.-nativeEvent event))
|
(let [offset (dom/get-offset-position (.-nativeEvent event))
|
||||||
|
|
||||||
select-current? (not (contains? selected-files (:id file)))
|
select-current? (not (contains? selected-files (:id file)))
|
||||||
|
@ -359,7 +359,7 @@
|
||||||
{:class (stl/css-case :selected selected? :library library-view?)
|
{:class (stl/css-case :selected selected? :library library-view?)
|
||||||
:ref node-ref
|
:ref node-ref
|
||||||
:title (:name file)
|
:title (:name file)
|
||||||
:draggable (dm/str (not you-viewer?))
|
:draggable (dm/str can-edit)
|
||||||
:on-click on-select
|
:on-click on-select
|
||||||
:on-key-down handle-key-down
|
:on-key-down handle-key-down
|
||||||
:on-double-click on-navigate
|
:on-double-click on-navigate
|
||||||
|
@ -372,7 +372,7 @@
|
||||||
[:& grid-item-library {:file file}]
|
[:& grid-item-library {:file file}]
|
||||||
[:& grid-item-thumbnail
|
[:& grid-item-thumbnail
|
||||||
{:file-id (:id file)
|
{:file-id (:id file)
|
||||||
:you-viewer? you-viewer?
|
:can-edit can-edit
|
||||||
:revn (:revn file)
|
:revn (:revn file)
|
||||||
:thumbnail-id (:thumbnail-id file)
|
:thumbnail-id (:thumbnail-id file)
|
||||||
:background-color (dm/get-in file [:data :options :background])}])
|
:background-color (dm/get-in file [:data :options :background])}])
|
||||||
|
@ -408,7 +408,7 @@
|
||||||
:show? (:menu-open dashboard-local)
|
:show? (:menu-open dashboard-local)
|
||||||
:left (+ 24 (:x (:menu-pos dashboard-local)))
|
:left (+ 24 (:x (:menu-pos dashboard-local)))
|
||||||
:top (:y (:menu-pos dashboard-local))
|
:top (:y (:menu-pos dashboard-local))
|
||||||
:you-viewer? you-viewer?
|
:can-edit can-edit
|
||||||
:navigate? true
|
:navigate? true
|
||||||
:on-edit on-edit
|
:on-edit on-edit
|
||||||
:on-menu-close on-menu-close
|
:on-menu-close on-menu-close
|
||||||
|
@ -416,7 +416,7 @@
|
||||||
:parent-id (str file-id "-action-menu")}]])]]]]]))
|
:parent-id (str file-id "-action-menu")}]])]]]]]))
|
||||||
|
|
||||||
(mf/defc grid
|
(mf/defc grid
|
||||||
[{:keys [files project origin limit library-view? create-fn you-viewer?] :as props}]
|
[{:keys [files project origin limit library-view? create-fn can-edit] :as props}]
|
||||||
(let [dragging? (mf/use-state false)
|
(let [dragging? (mf/use-state false)
|
||||||
project-id (:id project)
|
project-id (:id project)
|
||||||
node-ref (mf/use-var nil)
|
node-ref (mf/use-var nil)
|
||||||
|
@ -433,7 +433,7 @@
|
||||||
on-drag-enter
|
on-drag-enter
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(fn [e]
|
(fn [e]
|
||||||
(when-not you-viewer?
|
(when can-edit
|
||||||
(when (and (not (dnd/has-type? e "penpot/files"))
|
(when (and (not (dnd/has-type? e "penpot/files"))
|
||||||
(or (dnd/has-type? e "Files")
|
(or (dnd/has-type? e "Files")
|
||||||
(dnd/has-type? e "application/x-moz-file")))
|
(dnd/has-type? e "application/x-moz-file")))
|
||||||
|
@ -464,7 +464,7 @@
|
||||||
(import-files (.-files (.-dataTransfer e))))))]
|
(import-files (.-files (.-dataTransfer e))))))]
|
||||||
|
|
||||||
[:div {:class (stl/css :dashboard-grid)
|
[:div {:class (stl/css :dashboard-grid)
|
||||||
:dragabble (dm/str (not you-viewer?))
|
:dragabble (dm/str can-edit)
|
||||||
:on-drag-enter on-drag-enter
|
:on-drag-enter on-drag-enter
|
||||||
:on-drag-over on-drag-over
|
:on-drag-over on-drag-over
|
||||||
:on-drag-leave on-drag-leave
|
:on-drag-leave on-drag-leave
|
||||||
|
@ -486,18 +486,18 @@
|
||||||
:key (:id item)
|
:key (:id item)
|
||||||
:navigate? true
|
:navigate? true
|
||||||
:origin origin
|
:origin origin
|
||||||
:you-viewer? you-viewer?
|
:can-edit can-edit
|
||||||
:library-view? library-view?}])])
|
:library-view? library-view?}])])
|
||||||
|
|
||||||
:else
|
:else
|
||||||
[:& empty-placeholder
|
[:& empty-placeholder
|
||||||
{:limit limit
|
{:limit limit
|
||||||
:you-viewer? you-viewer?
|
:can-edit can-edit
|
||||||
:create-fn create-fn
|
:create-fn create-fn
|
||||||
:origin origin}])]))
|
:origin origin}])]))
|
||||||
|
|
||||||
(mf/defc line-grid-row
|
(mf/defc line-grid-row
|
||||||
[{:keys [files selected-files dragging? limit you-viewer?] :as props}]
|
[{:keys [files selected-files dragging? limit can-edit] :as props}]
|
||||||
(let [elements limit
|
(let [elements limit
|
||||||
limit (if dragging? (dec limit) limit)]
|
limit (if dragging? (dec limit) limit)]
|
||||||
[:ul {:class (stl/css :grid-row :no-wrap)
|
[:ul {:class (stl/css :grid-row :no-wrap)
|
||||||
|
@ -511,12 +511,12 @@
|
||||||
{:id (:id item)
|
{:id (:id item)
|
||||||
:file item
|
:file item
|
||||||
:selected-files selected-files
|
:selected-files selected-files
|
||||||
:you-viewer? you-viewer?
|
:can-edit can-edit
|
||||||
:key (:id item)
|
:key (:id item)
|
||||||
:navigate? false}])]))
|
:navigate? false}])]))
|
||||||
|
|
||||||
(mf/defc line-grid
|
(mf/defc line-grid
|
||||||
[{:keys [project team files limit create-fn you-viewer?] :as props}]
|
[{:keys [project team files limit create-fn can-edit] :as props}]
|
||||||
(let [dragging? (mf/use-state false)
|
(let [dragging? (mf/use-state false)
|
||||||
project-id (:id project)
|
project-id (:id project)
|
||||||
team-id (:id team)
|
team-id (:id team)
|
||||||
|
@ -535,9 +535,9 @@
|
||||||
|
|
||||||
on-drag-enter
|
on-drag-enter
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps selected-project you-viewer?)
|
(mf/deps selected-project can-edit)
|
||||||
(fn [e]
|
(fn [e]
|
||||||
(when-not you-viewer?
|
(when can-edit
|
||||||
(cond
|
(cond
|
||||||
(dnd/has-type? e "penpot/files")
|
(dnd/has-type? e "penpot/files")
|
||||||
(do
|
(do
|
||||||
|
@ -595,7 +595,7 @@
|
||||||
(import-files (.-files (.-dataTransfer e)))))))]
|
(import-files (.-files (.-dataTransfer e)))))))]
|
||||||
|
|
||||||
[:div {:class (stl/css :dashboard-grid)
|
[:div {:class (stl/css :dashboard-grid)
|
||||||
:dragabble (dm/str (not you-viewer?))
|
:dragabble (dm/str can-edit)
|
||||||
:on-drag-enter on-drag-enter
|
:on-drag-enter on-drag-enter
|
||||||
:on-drag-over on-drag-over
|
:on-drag-over on-drag-over
|
||||||
:on-drag-leave on-drag-leave
|
:on-drag-leave on-drag-leave
|
||||||
|
@ -609,12 +609,12 @@
|
||||||
:team-id team-id
|
:team-id team-id
|
||||||
:selected-files selected-files
|
:selected-files selected-files
|
||||||
:dragging? @dragging?
|
:dragging? @dragging?
|
||||||
:you-viewer? you-viewer?
|
:can-edit can-edit
|
||||||
:limit limit}]
|
:limit limit}]
|
||||||
|
|
||||||
:else
|
:else
|
||||||
[:& empty-placeholder
|
[:& empty-placeholder
|
||||||
{:dragging? @dragging?
|
{:dragging? @dragging?
|
||||||
:limit limit
|
:limit limit
|
||||||
:you-viewer? you-viewer?
|
:can-edit can-edit
|
||||||
:create-fn create-fn}])]))
|
:create-fn create-fn}])]))
|
||||||
|
|
|
@ -19,9 +19,11 @@
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
(mf/defc libraries-page
|
(mf/defc libraries-page
|
||||||
[{:keys [team you-viewer?] :as props}]
|
{::mf/props :obj}
|
||||||
|
[{:keys [team] :as props}]
|
||||||
(let [files-map (mf/deref refs/dashboard-shared-files)
|
(let [files-map (mf/deref refs/dashboard-shared-files)
|
||||||
projects (mf/deref refs/dashboard-projects)
|
projects (mf/deref refs/dashboard-projects)
|
||||||
|
can-edit (-> team :permissions :can-edit)
|
||||||
|
|
||||||
default-project (->> projects vals (d/seek :is-default))
|
default-project (->> projects vals (d/seek :is-default))
|
||||||
|
|
||||||
|
@ -56,6 +58,6 @@
|
||||||
:project default-project
|
:project default-project
|
||||||
:origin :libraries
|
:origin :libraries
|
||||||
:limit limit
|
:limit limit
|
||||||
:you-viewer? you-viewer?
|
:can-edit can-edit
|
||||||
:library-view? components-v2}]]]))
|
:library-view? components-v2}]]]))
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
(mf/defc empty-placeholder
|
(mf/defc empty-placeholder
|
||||||
[{:keys [dragging? limit origin create-fn you-viewer?]}]
|
[{:keys [dragging? limit origin create-fn can-edit]}]
|
||||||
(let [on-click
|
(let [on-click
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps create-fn)
|
(mf/deps create-fn)
|
||||||
|
@ -28,11 +28,14 @@
|
||||||
[:li {:class (stl/css :grid-item :grid-empty-placeholder :dragged)}]]
|
[:li {:class (stl/css :grid-item :grid-empty-placeholder :dragged)}]]
|
||||||
|
|
||||||
(= :libraries origin)
|
(= :libraries origin)
|
||||||
[:> empty-placeholder* {:title (tr "dashboard.empty-placeholder-libraries-title")
|
[:> empty-placeholder*
|
||||||
|
{:title (tr "dashboard.empty-placeholder-libraries-title")
|
||||||
:type 2
|
:type 2
|
||||||
:subtitle (when you-viewer? (tr "dashboard.empty-placeholder-libraries-subtitle-viewer-role"))
|
:subtitle (when-not can-edit
|
||||||
|
(tr "dashboard.empty-placeholder-libraries-subtitle-viewer-role"))
|
||||||
:class (stl/css :empty-placeholder-libraries)}
|
:class (stl/css :empty-placeholder-libraries)}
|
||||||
(when-not you-viewer?
|
|
||||||
|
(when can-edit
|
||||||
[:> i18n/tr-html* {:content (tr "dashboard.empty-placeholder-libraries")
|
[:> i18n/tr-html* {:content (tr "dashboard.empty-placeholder-libraries")
|
||||||
:class (stl/css :placeholder-markdown)
|
:class (stl/css :placeholder-markdown)
|
||||||
:tag-name "span"}])]
|
:tag-name "span"}])]
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
(ns app.main.ui.dashboard.projects
|
(ns app.main.ui.dashboard.projects
|
||||||
(:require-macros [app.main.style :as stl])
|
(:require-macros [app.main.style :as stl])
|
||||||
(:require
|
(:require
|
||||||
[app.common.data.macros :as dm]
|
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.main.data.dashboard :as dd]
|
[app.main.data.dashboard :as dd]
|
||||||
[app.main.data.events :as ev]
|
[app.main.data.events :as ev]
|
||||||
|
@ -46,12 +45,12 @@
|
||||||
|
|
||||||
(mf/defc header
|
(mf/defc header
|
||||||
{::mf/wrap [mf/memo]}
|
{::mf/wrap [mf/memo]}
|
||||||
[{:keys [you-viewer?]}]
|
[{:keys [can-edit]}]
|
||||||
(let [on-click (mf/use-fn #(st/emit! (dd/create-project)))]
|
(let [on-click (mf/use-fn #(st/emit! (dd/create-project)))]
|
||||||
[:header {:class (stl/css :dashboard-header) :data-testid "dashboard-header"}
|
[:header {:class (stl/css :dashboard-header) :data-testid "dashboard-header"}
|
||||||
[:div#dashboard-projects-title {:class (stl/css :dashboard-title)}
|
[:div#dashboard-projects-title {:class (stl/css :dashboard-title)}
|
||||||
[:h1 (tr "dashboard.projects-title")]]
|
[:h1 (tr "dashboard.projects-title")]]
|
||||||
(when-not you-viewer?
|
(when can-edit
|
||||||
[:button {:class (stl/css :btn-secondary :btn-small)
|
[:button {:class (stl/css :btn-secondary :btn-small)
|
||||||
:on-click on-click
|
:on-click on-click
|
||||||
:data-testid "new-project-button"}
|
:data-testid "new-project-button"}
|
||||||
|
@ -101,13 +100,13 @@
|
||||||
(l/derived :builtin-templates st/state))
|
(l/derived :builtin-templates st/state))
|
||||||
|
|
||||||
(mf/defc project-item
|
(mf/defc project-item
|
||||||
[{:keys [project first? team files you-viewer?] :as props}]
|
[{:keys [project first? team files can-edit] :as props}]
|
||||||
(let [locale (mf/deref i18n/locale)
|
(let [locale (mf/deref i18n/locale)
|
||||||
file-count (or (:count project) 0)
|
file-count (or (:count project) 0)
|
||||||
project-id (:id project)
|
project-id (:id project)
|
||||||
is-draft-proyect (:is-default project)
|
is-draft-proyect (:is-default project)
|
||||||
team-id (:id team)
|
team-id (:id team)
|
||||||
empty-state-viewer (and you-viewer?
|
empty-state-viewer (and (not can-edit)
|
||||||
(= 0 file-count))
|
(= 0 file-count))
|
||||||
|
|
||||||
dstate (mf/deref refs/dashboard-local)
|
dstate (mf/deref refs/dashboard-local)
|
||||||
|
@ -225,7 +224,7 @@
|
||||||
:title (if (:is-default project)
|
:title (if (:is-default project)
|
||||||
(tr "labels.drafts")
|
(tr "labels.drafts")
|
||||||
(:name project))
|
(:name project))
|
||||||
:on-context-menu (when-not you-viewer? on-menu-click)}
|
:on-context-menu (when can-edit on-menu-click)}
|
||||||
(if (:is-default project)
|
(if (:is-default project)
|
||||||
(tr "labels.drafts")
|
(tr "labels.drafts")
|
||||||
(:name project))])
|
(:name project))])
|
||||||
|
@ -246,7 +245,7 @@
|
||||||
(when-not (:is-default project)
|
(when-not (:is-default project)
|
||||||
[:> pin-button* {:class (stl/css :pin-button) :is-pinned (:is-pinned project) :on-click toggle-pin :tab-index 0}])
|
[:> pin-button* {:class (stl/css :pin-button) :is-pinned (:is-pinned project) :on-click toggle-pin :tab-index 0}])
|
||||||
|
|
||||||
(when-not you-viewer?
|
(when ^boolean can-edit
|
||||||
[:button {:class (stl/css :add-file-btn)
|
[:button {:class (stl/css :add-file-btn)
|
||||||
:on-click on-create-click
|
:on-click on-create-click
|
||||||
:title (tr "dashboard.new-file")
|
:title (tr "dashboard.new-file")
|
||||||
|
@ -255,7 +254,7 @@
|
||||||
:on-key-down handle-create-click}
|
:on-key-down handle-create-click}
|
||||||
add-icon])
|
add-icon])
|
||||||
|
|
||||||
(when-not you-viewer?
|
(when ^boolean can-edit
|
||||||
[:button {:class (stl/css :options-btn)
|
[:button {:class (stl/css :options-btn)
|
||||||
:on-click on-menu-click
|
:on-click on-menu-click
|
||||||
:title (tr "dashboard.options")
|
:title (tr "dashboard.options")
|
||||||
|
@ -263,7 +262,8 @@
|
||||||
:data-testid "project-options"
|
:data-testid "project-options"
|
||||||
:on-key-down handle-menu-click}
|
:on-key-down handle-menu-click}
|
||||||
menu-icon])]
|
menu-icon])]
|
||||||
(when-not you-viewer?
|
|
||||||
|
(when ^boolean can-edit
|
||||||
[:& project-menu
|
[:& project-menu
|
||||||
{:project project
|
{:project project
|
||||||
:show? (:menu-open @local)
|
:show? (:menu-open @local)
|
||||||
|
@ -289,7 +289,7 @@
|
||||||
:team team
|
:team team
|
||||||
:files files
|
:files files
|
||||||
:create-fn create-file
|
:create-fn create-file
|
||||||
:you-viewer? you-viewer?
|
:can-edit can-edit
|
||||||
:limit limit}])]
|
:limit limit}])]
|
||||||
|
|
||||||
(when (and (> limit 0)
|
(when (and (> limit 0)
|
||||||
|
@ -309,14 +309,16 @@
|
||||||
|
|
||||||
(mf/defc projects-section
|
(mf/defc projects-section
|
||||||
[{:keys [team projects profile] :as props}]
|
[{:keys [team projects profile] :as props}]
|
||||||
|
|
||||||
(let [projects (->> (vals projects)
|
(let [projects (->> (vals projects)
|
||||||
(sort-by :modified-at)
|
(sort-by :modified-at)
|
||||||
(reverse))
|
(reverse))
|
||||||
recent-map (mf/deref recent-files-ref)
|
recent-map (mf/deref recent-files-ref)
|
||||||
you-owner? (dm/get-in team [:permissions :is-owner])
|
permisions (:permissions team)
|
||||||
you-admin? (dm/get-in team [:permissions :is-admin])
|
|
||||||
you-viewer? (not (dm/get-in team [:permissions :can-edit]))
|
can-edit (:can-edit permisions)
|
||||||
can-invite? (or you-owner? you-admin?)
|
can-invite (or (:is-owner permisions)
|
||||||
|
(:is-admin permisions))
|
||||||
|
|
||||||
show-team-hero* (mf/use-state #(get storage/global ::show-team-hero true))
|
show-team-hero* (mf/use-state #(get storage/global ::show-team-hero true))
|
||||||
show-team-hero? (deref show-team-hero*)
|
show-team-hero? (deref show-team-hero*)
|
||||||
|
@ -348,11 +350,11 @@
|
||||||
|
|
||||||
(when (seq projects)
|
(when (seq projects)
|
||||||
[:*
|
[:*
|
||||||
[:& header {:you-viewer? you-viewer?}]
|
[:& header {:can-edit can-edit}]
|
||||||
[:div {:class (stl/css :projects-container)}
|
[:div {:class (stl/css :projects-container)}
|
||||||
[:*
|
[:*
|
||||||
(when (and show-team-hero?
|
(when (and show-team-hero?
|
||||||
can-invite?
|
can-invite
|
||||||
(not is-defalt-team?))
|
(not is-defalt-team?))
|
||||||
[:> team-hero* {:team team :on-close on-close}])
|
[:> team-hero* {:team team :on-close on-close}])
|
||||||
|
|
||||||
|
@ -362,7 +364,7 @@
|
||||||
:with-team-hero (and (not is-my-penpot)
|
:with-team-hero (and (not is-my-penpot)
|
||||||
(not is-defalt-team?)
|
(not is-defalt-team?)
|
||||||
show-team-hero?
|
show-team-hero?
|
||||||
can-invite?))}
|
can-invite))}
|
||||||
(for [{:keys [id] :as project} projects]
|
(for [{:keys [id] :as project} projects]
|
||||||
(let [files (when recent-map
|
(let [files (when recent-map
|
||||||
(->> (vals recent-map)
|
(->> (vals recent-map)
|
||||||
|
@ -371,6 +373,6 @@
|
||||||
[:& project-item {:project project
|
[:& project-item {:project project
|
||||||
:team team
|
:team team
|
||||||
:files files
|
:files files
|
||||||
:you-viewer? you-viewer?
|
:can-edit can-edit
|
||||||
:first? (= project (first projects))
|
:first? (= project (first projects))
|
||||||
:key id}]))]]]])))
|
:key id}]))]]]])))
|
||||||
|
|
|
@ -255,28 +255,30 @@
|
||||||
(mf/defc rol-info
|
(mf/defc rol-info
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
[{:keys [member team on-set-admin on-set-editor on-set-owner on-set-viewer profile]}]
|
[{:keys [member team on-set-admin on-set-editor on-set-owner on-set-viewer profile]}]
|
||||||
(let [member-is-owner? (:is-owner member)
|
(let [member-is-owner (:is-owner member)
|
||||||
member-is-admin? (and (:is-admin member) (not member-is-owner?))
|
member-is-admin (and (:is-admin member) (not member-is-owner))
|
||||||
member-is-editor? (and (:can-edit member) (and (not member-is-admin?) (not member-is-owner?)))
|
member-is-editor (and (:can-edit member) (and (not member-is-admin) (not member-is-owner)))
|
||||||
show? (mf/use-state false)
|
show? (mf/use-state false)
|
||||||
|
|
||||||
you-owner? (dm/get-in team [:permissions :is-owner])
|
permissions (:permissions team)
|
||||||
you-admin? (dm/get-in team [:permissions :is-admin])
|
is-owner (:is-owner permissions)
|
||||||
is-you? (= (:id profile) (:id member))
|
is-admin (:is-admin permissions)
|
||||||
|
|
||||||
can-change-rol? (or you-owner? you-admin?)
|
is-you (= (:id profile) (:id member))
|
||||||
not-superior? (or you-owner? (and can-change-rol? (or member-is-admin? member-is-editor?)))
|
|
||||||
|
can-change-rol (or is-owner is-admin)
|
||||||
|
not-superior (or is-admin (and can-change-rol (or member-is-admin member-is-editor)))
|
||||||
|
|
||||||
role (cond
|
role (cond
|
||||||
member-is-owner? "labels.owner"
|
member-is-owner "labels.owner"
|
||||||
member-is-admin? "labels.admin"
|
member-is-admin "labels.admin"
|
||||||
member-is-editor? "labels.editor"
|
member-is-editor "labels.editor"
|
||||||
:else "labels.viewer")
|
:else "labels.viewer")
|
||||||
|
|
||||||
on-show (mf/use-fn #(reset! show? true))
|
on-show (mf/use-fn #(reset! show? true))
|
||||||
on-hide (mf/use-fn #(reset! show? false))]
|
on-hide (mf/use-fn #(reset! show? false))]
|
||||||
[:*
|
[:*
|
||||||
(if (and can-change-rol? not-superior? (not (and is-you? you-owner?)))
|
(if (and can-change-rol not-superior (not (and is-you is-owner)))
|
||||||
[:div {:class (stl/css :rol-selector :has-priv)
|
[:div {:class (stl/css :rol-selector :has-priv)
|
||||||
:on-click on-show}
|
:on-click on-show}
|
||||||
[:span {:class (stl/css :rol-label)} (tr role)]
|
[:span {:class (stl/css :rol-label)} (tr role)]
|
||||||
|
@ -295,7 +297,7 @@
|
||||||
[:li {:on-click on-set-viewer
|
[:li {:on-click on-set-viewer
|
||||||
:class (stl/css :rol-dropdown-item)}
|
:class (stl/css :rol-dropdown-item)}
|
||||||
(tr "labels.viewer")]
|
(tr "labels.viewer")]
|
||||||
(when you-owner?
|
(when is-owner
|
||||||
[:li {:on-click (partial on-set-owner member)
|
[:li {:on-click (partial on-set-owner member)
|
||||||
:class (stl/css :rol-dropdown-item)}
|
:class (stl/css :rol-dropdown-item)}
|
||||||
(tr "labels.owner")])]]]))
|
(tr "labels.owner")])]]]))
|
||||||
|
@ -320,7 +322,6 @@
|
||||||
:on-click on-show}
|
:on-click on-show}
|
||||||
menu-icon]
|
menu-icon]
|
||||||
|
|
||||||
|
|
||||||
[:& dropdown {:show @show? :on-close on-hide}
|
[:& dropdown {:show @show? :on-close on-hide}
|
||||||
[:ul {:class (stl/css :actions-dropdown)}
|
[:ul {:class (stl/css :actions-dropdown)}
|
||||||
(when is-you?
|
(when is-you?
|
||||||
|
@ -910,12 +911,13 @@
|
||||||
(tr "dashboard.webhooks.create")]])
|
(tr "dashboard.webhooks.create")]])
|
||||||
|
|
||||||
(mf/defc webhook-actions
|
(mf/defc webhook-actions
|
||||||
{::mf/wrap-props false}
|
{::mf/props :obj
|
||||||
[{:keys [on-edit on-delete can-edit?]}]
|
::mf/private true}
|
||||||
|
[{:keys [on-edit on-delete can-edit]}]
|
||||||
(let [show? (mf/use-state false)
|
(let [show? (mf/use-state false)
|
||||||
on-show (mf/use-fn #(reset! show? true))
|
on-show (mf/use-fn #(reset! show? true))
|
||||||
on-hide (mf/use-fn #(reset! show? false))]
|
on-hide (mf/use-fn #(reset! show? false))]
|
||||||
(if can-edit?
|
(if can-edit
|
||||||
[:*
|
[:*
|
||||||
[:button {:class (stl/css :menu-btn)
|
[:button {:class (stl/css :menu-btn)
|
||||||
:on-click on-show}
|
:on-click on-show}
|
||||||
|
@ -948,7 +950,7 @@
|
||||||
creator-id (:profile-id webhook)
|
creator-id (:profile-id webhook)
|
||||||
profile (mf/deref refs/profile)
|
profile (mf/deref refs/profile)
|
||||||
user-id (:id profile)
|
user-id (:id profile)
|
||||||
can-edit? (or (:can-edit permissions)
|
can-edit (or (:can-edit permissions)
|
||||||
(= creator-id user-id))
|
(= creator-id user-id))
|
||||||
on-edit
|
on-edit
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
|
@ -1002,8 +1004,8 @@
|
||||||
[:div {:class (stl/css :table-field :actions)}
|
[:div {:class (stl/css :table-field :actions)}
|
||||||
[:& webhook-actions
|
[:& webhook-actions
|
||||||
{:on-edit on-edit
|
{:on-edit on-edit
|
||||||
:can-edit? can-edit?
|
:on-delete on-delete
|
||||||
:on-delete on-delete}]]]))
|
:can-edit can-edit}]]]))
|
||||||
|
|
||||||
(mf/defc webhooks-list
|
(mf/defc webhooks-list
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
|
@ -1053,9 +1055,9 @@
|
||||||
|
|
||||||
stats (mf/deref refs/dashboard-team-stats)
|
stats (mf/deref refs/dashboard-team-stats)
|
||||||
|
|
||||||
you-owner? (get-in team [:permissions :is-owner])
|
permissions (:permissions team)
|
||||||
you-admin? (get-in team [:permissions :is-admin])
|
can-edit (or (:is-owner permissions)
|
||||||
can-edit? (or you-owner? you-admin?)
|
(:is-admin permissions))
|
||||||
|
|
||||||
on-image-click
|
on-image-click
|
||||||
(mf/use-callback #(dom/click (mf/ref-val finput)))
|
(mf/use-callback #(dom/click (mf/ref-val finput)))
|
||||||
|
@ -1086,13 +1088,13 @@
|
||||||
[:div {:class (stl/css :block-text)}
|
[:div {:class (stl/css :block-text)}
|
||||||
(:name team)]
|
(:name team)]
|
||||||
[:div {:class (stl/css :team-icon)}
|
[:div {:class (stl/css :team-icon)}
|
||||||
(when can-edit?
|
(when can-edit
|
||||||
[:button {:class (stl/css :update-overlay)
|
[:button {:class (stl/css :update-overlay)
|
||||||
:on-click on-image-click}
|
:on-click on-image-click}
|
||||||
image-icon])
|
image-icon])
|
||||||
[:img {:class (stl/css :team-image)
|
[:img {:class (stl/css :team-image)
|
||||||
:src (cfg/resolve-team-photo-url team)}]
|
:src (cfg/resolve-team-photo-url team)}]
|
||||||
(when can-edit?
|
(when can-edit
|
||||||
[:& file-uploader {:accept "image/jpeg,image/png"
|
[:& file-uploader {:accept "image/jpeg,image/png"
|
||||||
:multi false
|
:multi false
|
||||||
:ref finput
|
:ref finput
|
||||||
|
|
|
@ -68,9 +68,8 @@
|
||||||
(mf/defc team-form-step-2
|
(mf/defc team-form-step-2
|
||||||
{::mf/props :obj}
|
{::mf/props :obj}
|
||||||
[{:keys [name on-back go-to-team?]}]
|
[{:keys [name on-back go-to-team?]}]
|
||||||
(let [initial (mf/use-memo
|
(let [initial (mf/with-memo []
|
||||||
#(do {:role "editor"
|
{:role "editor" :name name})
|
||||||
:name name}))
|
|
||||||
|
|
||||||
form (fm/use-form :schema schema:invite-form
|
form (fm/use-form :schema schema:invite-form
|
||||||
:initial initial)
|
:initial initial)
|
||||||
|
|
|
@ -165,16 +165,16 @@
|
||||||
(let [layout (mf/deref refs/workspace-layout)
|
(let [layout (mf/deref refs/workspace-layout)
|
||||||
wglobal (mf/deref refs/workspace-global)
|
wglobal (mf/deref refs/workspace-global)
|
||||||
|
|
||||||
|
team (mf/deref refs/team)
|
||||||
file (mf/deref refs/workspace-file)
|
file (mf/deref refs/workspace-file)
|
||||||
project (mf/deref refs/workspace-project)
|
project (mf/deref refs/workspace-project)
|
||||||
|
|
||||||
team-id (:team-id project)
|
team-id (:team-id project)
|
||||||
file-name (:name file)
|
file-name (:name file)
|
||||||
|
permissions (:permissions team)
|
||||||
|
|
||||||
user-viewer? (not (dm/get-in file [:permissions :can-edit]))
|
read-only? (mf/deref refs/workspace-read-only?)
|
||||||
read-only? (or (mf/deref refs/workspace-read-only?)
|
read-only? (or read-only? (not (:can-edit permissions)))
|
||||||
user-viewer?)
|
|
||||||
|
|
||||||
file-ready* (mf/with-memo [file-id]
|
file-ready* (mf/with-memo [file-id]
|
||||||
(make-file-ready-ref file-id))
|
(make-file-ready-ref file-id))
|
||||||
|
@ -214,7 +214,7 @@
|
||||||
[:& (mf/provider ctx/current-page-id) {:value page-id}
|
[:& (mf/provider ctx/current-page-id) {:value page-id}
|
||||||
[:& (mf/provider ctx/components-v2) {:value components-v2?}
|
[:& (mf/provider ctx/components-v2) {:value components-v2?}
|
||||||
[:& (mf/provider ctx/workspace-read-only?) {:value read-only?}
|
[:& (mf/provider ctx/workspace-read-only?) {:value read-only?}
|
||||||
[:& (mf/provider ctx/user-viewer?) {:value user-viewer?}
|
[:& (mf/provider ctx/team-permissions) {:value permissions}
|
||||||
[:section {:class (stl/css :workspace)
|
[:section {:class (stl/css :workspace)
|
||||||
:style {:background-color background-color
|
:style {:background-color background-color
|
||||||
:touch-action "none"}}
|
:touch-action "none"}}
|
||||||
|
|
|
@ -534,15 +534,17 @@
|
||||||
[:& menu-entry {:title (tr "workspace.assets.duplicate")
|
[:& menu-entry {:title (tr "workspace.assets.duplicate")
|
||||||
:on-click do-duplicate}]]))
|
:on-click do-duplicate}]]))
|
||||||
|
|
||||||
(mf/defc viewport-context-menu
|
(mf/defc viewport-context-menu*
|
||||||
[{:keys [read-only?]}]
|
{::mf/props :obj}
|
||||||
|
[]
|
||||||
(let [focus (mf/deref refs/workspace-focus-selected)
|
(let [focus (mf/deref refs/workspace-focus-selected)
|
||||||
|
read-only? (mf/use-ctx ctx/workspace-read-only?)
|
||||||
do-paste #(st/emit! (dw/paste-from-clipboard))
|
do-paste #(st/emit! (dw/paste-from-clipboard))
|
||||||
do-hide-ui #(st/emit! (-> (dw/toggle-layout-flag :hide-ui)
|
do-hide-ui #(st/emit! (-> (dw/toggle-layout-flag :hide-ui)
|
||||||
(vary-meta assoc ::ev/origin "workspace-context-menu")))
|
(vary-meta assoc ::ev/origin "workspace-context-menu")))
|
||||||
do-toggle-focus-mode #(st/emit! (dw/toggle-focus-mode))]
|
do-toggle-focus-mode #(st/emit! (dw/toggle-focus-mode))]
|
||||||
[:*
|
[:*
|
||||||
(when-not read-only?
|
(when-not ^boolean read-only?
|
||||||
[:& menu-entry {:title (tr "workspace.shape.menu.paste")
|
[:& menu-entry {:title (tr "workspace.shape.menu.paste")
|
||||||
:shortcut (sc/get-tooltip :paste)
|
:shortcut (sc/get-tooltip :paste)
|
||||||
:on-click do-paste}])
|
:on-click do-paste}])
|
||||||
|
@ -640,6 +642,8 @@
|
||||||
:disabled (and (not single?) (not can-merge?))}]]))
|
:disabled (and (not single?) (not can-merge?))}]]))
|
||||||
|
|
||||||
|
|
||||||
|
;; FIXME: optimize because it is rendered always
|
||||||
|
|
||||||
(mf/defc context-menu
|
(mf/defc context-menu
|
||||||
[]
|
[]
|
||||||
(let [mdata (mf/deref menu-ref)
|
(let [mdata (mf/deref menu-ref)
|
||||||
|
@ -648,10 +652,8 @@
|
||||||
dropdown-ref (mf/use-ref)
|
dropdown-ref (mf/use-ref)
|
||||||
read-only? (mf/use-ctx ctx/workspace-read-only?)]
|
read-only? (mf/use-ctx ctx/workspace-read-only?)]
|
||||||
|
|
||||||
(mf/use-effect
|
(mf/with-effect [mdata]
|
||||||
(mf/deps mdata)
|
(when-let [dropdown (mf/ref-val dropdown-ref)]
|
||||||
#(let [dropdown (mf/ref-val dropdown-ref)]
|
|
||||||
(when dropdown
|
|
||||||
(let [bounding-rect (dom/get-bounding-rect dropdown)
|
(let [bounding-rect (dom/get-bounding-rect dropdown)
|
||||||
window-size (dom/get-window-size)
|
window-size (dom/get-window-size)
|
||||||
delta-x (max (- (+ (:right bounding-rect) 250) (:width window-size)) 0)
|
delta-x (max (- (+ (:right bounding-rect) 250) (:width window-size)) 0)
|
||||||
|
@ -659,7 +661,7 @@
|
||||||
new-style (str "top: " (- top delta-y) "px; "
|
new-style (str "top: " (- top delta-y) "px; "
|
||||||
"left: " (- left delta-x) "px;")]
|
"left: " (- left delta-x) "px;")]
|
||||||
(when (or (> delta-x 0) (> delta-y 0))
|
(when (or (> delta-x 0) (> delta-y 0))
|
||||||
(.setAttribute ^js dropdown "style" new-style))))))
|
(.setAttribute ^js dropdown "style" new-style)))))
|
||||||
|
|
||||||
[:& dropdown {:show (boolean mdata)
|
[:& dropdown {:show (boolean mdata)
|
||||||
:on-close #(st/emit! dw/hide-context-menu)}
|
:on-close #(st/emit! dw/hide-context-menu)}
|
||||||
|
@ -669,11 +671,11 @@
|
||||||
:on-context-menu prevent-default}
|
:on-context-menu prevent-default}
|
||||||
|
|
||||||
[:ul {:class (stl/css :context-list)}
|
[:ul {:class (stl/css :context-list)}
|
||||||
(if read-only?
|
(if ^boolean read-only?
|
||||||
[:& viewport-context-menu {:mdata mdata :read-only? read-only?}]
|
[:> viewport-context-menu* {:mdata mdata}]
|
||||||
(case (:kind mdata)
|
(case (:kind mdata)
|
||||||
:shape [:& shape-context-menu {:mdata mdata}]
|
:shape [:& shape-context-menu {:mdata mdata}]
|
||||||
:page [:& page-item-context-menu {:mdata mdata}]
|
:page [:& page-item-context-menu {:mdata mdata}]
|
||||||
:grid-track [:& grid-track-context-menu {:mdata mdata}]
|
:grid-track [:& grid-track-context-menu {:mdata mdata}]
|
||||||
:grid-cells [:& grid-cells-context-menu {:mdata mdata}]
|
:grid-cells [:& grid-cells-context-menu {:mdata mdata}]
|
||||||
[:& viewport-context-menu {:mdata mdata}]))]]]))
|
[:& viewport-context-menu* {:mdata mdata}]))]]]))
|
||||||
|
|
|
@ -42,8 +42,9 @@
|
||||||
|
|
||||||
;; --- Header menu and submenus
|
;; --- Header menu and submenus
|
||||||
|
|
||||||
(mf/defc help-info-menu
|
(mf/defc help-info-menu*
|
||||||
{::mf/wrap-props false
|
{::mf/props :obj
|
||||||
|
::mf/private true
|
||||||
::mf/wrap [mf/memo]}
|
::mf/wrap [mf/memo]}
|
||||||
[{:keys [layout on-close]}]
|
[{:keys [layout on-close]}]
|
||||||
(let [nav-to-helpc-center
|
(let [nav-to-helpc-center
|
||||||
|
@ -172,8 +173,9 @@
|
||||||
[:span {:class (stl/css-case :feedback true
|
[:span {:class (stl/css-case :feedback true
|
||||||
:item-name true)} (tr "labels.give-feedback")]])]))
|
:item-name true)} (tr "labels.give-feedback")]])]))
|
||||||
|
|
||||||
(mf/defc preferences-menu
|
(mf/defc preferences-menu*
|
||||||
{::mf/wrap-props false
|
{::mf/props :obj
|
||||||
|
::mf/private true
|
||||||
::mf/wrap [mf/memo]}
|
::mf/wrap [mf/memo]}
|
||||||
[{:keys [layout profile toggle-flag on-close toggle-theme]}]
|
[{:keys [layout profile toggle-flag on-close toggle-theme]}]
|
||||||
(let [show-nudge-options (mf/use-fn #(modal/show! {:type :nudge-option}))]
|
(let [show-nudge-options (mf/use-fn #(modal/show! {:type :nudge-option}))]
|
||||||
|
@ -283,8 +285,9 @@
|
||||||
(for [sc (scd/split-sc (sc/get-tooltip :toggle-theme))]
|
(for [sc (scd/split-sc (sc/get-tooltip :toggle-theme))]
|
||||||
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]]))
|
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]]))
|
||||||
|
|
||||||
(mf/defc view-menu
|
(mf/defc view-menu*
|
||||||
{::mf/wrap-props false
|
{::mf/props :obj
|
||||||
|
::mf/private true
|
||||||
::mf/wrap [mf/memo]}
|
::mf/wrap [mf/memo]}
|
||||||
[{:keys [layout toggle-flag on-close]}]
|
[{:keys [layout toggle-flag on-close]}]
|
||||||
(let [read-only? (mf/use-ctx ctx/workspace-read-only?)
|
(let [read-only? (mf/use-ctx ctx/workspace-read-only?)
|
||||||
|
@ -412,13 +415,17 @@
|
||||||
(for [sc (scd/split-sc (sc/get-tooltip :hide-ui))]
|
(for [sc (scd/split-sc (sc/get-tooltip :hide-ui))]
|
||||||
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]]))
|
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]]))
|
||||||
|
|
||||||
(mf/defc edit-menu
|
(mf/defc edit-menu*
|
||||||
{::mf/wrap-props false
|
{::mf/props :obj
|
||||||
|
::mf/private true
|
||||||
::mf/wrap [mf/memo]}
|
::mf/wrap [mf/memo]}
|
||||||
[{:keys [on-close user-viewer?]}]
|
[{:keys [on-close]}]
|
||||||
(let [select-all (mf/use-fn #(st/emit! (dw/select-all)))
|
(let [select-all (mf/use-fn #(st/emit! (dw/select-all)))
|
||||||
undo (mf/use-fn #(st/emit! dwu/undo))
|
undo (mf/use-fn #(st/emit! dwu/undo))
|
||||||
redo (mf/use-fn #(st/emit! dwu/redo))]
|
redo (mf/use-fn #(st/emit! dwu/redo))
|
||||||
|
perms (mf/use-ctx ctx/team-permissions)
|
||||||
|
can-edit (:can-edit perms)]
|
||||||
|
|
||||||
[:& dropdown-menu {:show true
|
[:& dropdown-menu {:show true
|
||||||
:list-class (stl/css-case :sub-menu true
|
:list-class (stl/css-case :sub-menu true
|
||||||
:edit true)
|
:edit true)
|
||||||
|
@ -439,7 +446,7 @@
|
||||||
:key sc}
|
:key sc}
|
||||||
sc])]]
|
sc])]]
|
||||||
|
|
||||||
(when-not :user-viewer? user-viewer?
|
(when can-edit
|
||||||
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
|
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
|
||||||
:on-click undo
|
:on-click undo
|
||||||
:on-key-down (fn [event]
|
:on-key-down (fn [event]
|
||||||
|
@ -453,7 +460,7 @@
|
||||||
:key sc}
|
:key sc}
|
||||||
sc])]])
|
sc])]])
|
||||||
|
|
||||||
(when-not :user-viewer? user-viewer?
|
(when can-edit
|
||||||
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
|
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
|
||||||
:on-click redo
|
:on-click redo
|
||||||
:on-key-down (fn [event]
|
:on-key-down (fn [event]
|
||||||
|
@ -468,9 +475,10 @@
|
||||||
:key sc}
|
:key sc}
|
||||||
sc])]])]))
|
sc])]])]))
|
||||||
|
|
||||||
(mf/defc file-menu
|
(mf/defc file-menu*
|
||||||
{::mf/wrap-props false}
|
{::mf/props :obj
|
||||||
[{:keys [on-close file user-viewer?]}]
|
::mf/private true}
|
||||||
|
[{:keys [on-close file can-edit]}]
|
||||||
(let [file-id (:id file)
|
(let [file-id (:id file)
|
||||||
shared? (:is-shared file)
|
shared? (:is-shared file)
|
||||||
|
|
||||||
|
@ -564,7 +572,7 @@
|
||||||
:id "file-menu-remove-shared"}
|
:id "file-menu-remove-shared"}
|
||||||
[:span {:class (stl/css :item-name)} (tr "dashboard.unpublish-shared")]]
|
[:span {:class (stl/css :item-name)} (tr "dashboard.unpublish-shared")]]
|
||||||
|
|
||||||
(when-not user-viewer?
|
(when can-edit
|
||||||
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
|
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
|
||||||
:on-click on-add-shared
|
:on-click on-add-shared
|
||||||
:on-key-down on-add-shared-key-down
|
:on-key-down on-add-shared-key-down
|
||||||
|
@ -613,8 +621,9 @@
|
||||||
[:span {:class (stl/css :item-name)}
|
[:span {:class (stl/css :item-name)}
|
||||||
(tr "dashboard.export-frames")]])]))
|
(tr "dashboard.export-frames")]])]))
|
||||||
|
|
||||||
(mf/defc plugins-menu
|
(mf/defc plugins-menu*
|
||||||
{::mf/wrap-props false
|
{::mf/props :obj
|
||||||
|
::mf/private true
|
||||||
::mf/wrap [mf/memo]}
|
::mf/wrap [mf/memo]}
|
||||||
[{:keys [open-plugins on-close]}]
|
[{:keys [open-plugins on-close]}]
|
||||||
(when (features/active-feature? @st/state "plugins/runtime")
|
(when (features/active-feature? @st/state "plugins/runtime")
|
||||||
|
@ -659,15 +668,13 @@
|
||||||
[:span {:class (stl/css :item-name)} name]])])))
|
[:span {:class (stl/css :item-name)} name]])])))
|
||||||
|
|
||||||
(mf/defc menu
|
(mf/defc menu
|
||||||
{::mf/wrap-props false}
|
{::mf/props :obj}
|
||||||
[{:keys [layout file profile]}]
|
[{:keys [layout file profile]}]
|
||||||
(let [show-menu* (mf/use-state false)
|
(let [show-menu* (mf/use-state false)
|
||||||
show-menu? (deref show-menu*)
|
show-menu? (deref show-menu*)
|
||||||
sub-menu* (mf/use-state false)
|
sub-menu* (mf/use-state false)
|
||||||
sub-menu (deref sub-menu*)
|
sub-menu (deref sub-menu*)
|
||||||
|
|
||||||
user-viewer? (mf/use-ctx ctx/user-viewer?)
|
|
||||||
|
|
||||||
open-menu
|
open-menu
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(fn [event]
|
(fn [event]
|
||||||
|
@ -814,24 +821,21 @@
|
||||||
|
|
||||||
(case sub-menu
|
(case sub-menu
|
||||||
:file
|
:file
|
||||||
[:& file-menu
|
[:> file-menu* {:file file
|
||||||
{:file file
|
:on-close close-sub-menu}]
|
||||||
:on-close close-sub-menu
|
|
||||||
:user-viewer? user-viewer?}]
|
|
||||||
|
|
||||||
:edit
|
:edit
|
||||||
[:& edit-menu
|
[:> edit-menu*
|
||||||
{:on-close close-sub-menu
|
{:on-close close-sub-menu}]
|
||||||
:user-viewer? user-viewer?}]
|
|
||||||
|
|
||||||
:view
|
:view
|
||||||
[:& view-menu
|
[:> view-menu*
|
||||||
{:layout layout
|
{:layout layout
|
||||||
:toggle-flag toggle-flag
|
:toggle-flag toggle-flag
|
||||||
:on-close close-sub-menu}]
|
:on-close close-sub-menu}]
|
||||||
|
|
||||||
:preferences
|
:preferences
|
||||||
[:& preferences-menu
|
[:> preferences-menu*
|
||||||
{:layout layout
|
{:layout layout
|
||||||
:profile profile
|
:profile profile
|
||||||
:toggle-flag toggle-flag
|
:toggle-flag toggle-flag
|
||||||
|
@ -839,12 +843,12 @@
|
||||||
:on-close close-sub-menu}]
|
:on-close close-sub-menu}]
|
||||||
|
|
||||||
:plugins
|
:plugins
|
||||||
[:& plugins-menu
|
[:& plugins-menu*
|
||||||
{:open-plugins open-plugins-manager
|
{:open-plugins open-plugins-manager
|
||||||
:on-close close-sub-menu}]
|
:on-close close-sub-menu}]
|
||||||
|
|
||||||
:help-info
|
:help-info
|
||||||
[:& help-info-menu
|
[:& help-info-menu*
|
||||||
{:layout layout
|
{:layout layout
|
||||||
:on-close close-sub-menu}]
|
:on-close close-sub-menu}]
|
||||||
|
|
||||||
|
|
|
@ -134,8 +134,7 @@
|
||||||
::mf/props :obj}
|
::mf/props :obj}
|
||||||
[{:keys [selected shapes shapes-with-children page-id file-id on-change-section on-expand]}]
|
[{:keys [selected shapes shapes-with-children page-id file-id on-change-section on-expand]}]
|
||||||
(let [objects (mf/deref refs/workspace-page-objects)
|
(let [objects (mf/deref refs/workspace-page-objects)
|
||||||
|
permissions (mf/use-ctx ctx/team-permissions)
|
||||||
user-viewer? (mf/use-ctx ctx/user-viewer?)
|
|
||||||
|
|
||||||
selected-shapes (into [] (keep (d/getf objects)) selected)
|
selected-shapes (into [] (keep (d/getf objects)) selected)
|
||||||
first-selected-shape (first selected-shapes)
|
first-selected-shape (first selected-shapes)
|
||||||
|
@ -176,10 +175,7 @@
|
||||||
|
|
||||||
|
|
||||||
tabs
|
tabs
|
||||||
(if user-viewer?
|
(if (:can-edit permissions)
|
||||||
#js [#js {:label (tr "workspace.options.inspect")
|
|
||||||
:id "inspect"
|
|
||||||
:content inspect-content}]
|
|
||||||
#js [#js {:label (tr "workspace.options.design")
|
#js [#js {:label (tr "workspace.options.design")
|
||||||
:id "design"
|
:id "design"
|
||||||
:content design-content}
|
:content design-content}
|
||||||
|
@ -189,6 +185,9 @@
|
||||||
:content interactions-content}
|
:content interactions-content}
|
||||||
|
|
||||||
#js {:label (tr "workspace.options.inspect")
|
#js {:label (tr "workspace.options.inspect")
|
||||||
|
:id "inspect"
|
||||||
|
:content inspect-content}]
|
||||||
|
#js [#js {:label (tr "workspace.options.inspect")
|
||||||
:id "inspect"
|
:id "inspect"
|
||||||
:content inspect-content}])]
|
:content inspect-content}])]
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
id (:id page)
|
id (:id page)
|
||||||
delete-fn (mf/use-fn (mf/deps id) #(st/emit! (dw/delete-page id)))
|
delete-fn (mf/use-fn (mf/deps id) #(st/emit! (dw/delete-page id)))
|
||||||
navigate-fn (mf/use-fn (mf/deps id) #(st/emit! :interrupt (dw/go-to-page id)))
|
navigate-fn (mf/use-fn (mf/deps id) #(st/emit! :interrupt (dw/go-to-page id)))
|
||||||
workspace-read-only? (mf/use-ctx ctx/workspace-read-only?)
|
read-only? (mf/use-ctx ctx/workspace-read-only?)
|
||||||
|
|
||||||
on-delete
|
on-delete
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
|
@ -47,11 +47,11 @@
|
||||||
|
|
||||||
on-double-click
|
on-double-click
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps workspace-read-only?)
|
(mf/deps read-only?)
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(when-not workspace-read-only?
|
(when-not read-only?
|
||||||
(st/emit! (dw/start-rename-page-item id)))))
|
(st/emit! (dw/start-rename-page-item id)))))
|
||||||
|
|
||||||
on-blur
|
on-blur
|
||||||
|
@ -86,15 +86,15 @@
|
||||||
:data {:id id
|
:data {:id id
|
||||||
:index index
|
:index index
|
||||||
:name (:name page)}
|
:name (:name page)}
|
||||||
:draggable? (not workspace-read-only?))
|
:draggable? (not read-only?))
|
||||||
|
|
||||||
on-context-menu
|
on-context-menu
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps id workspace-read-only?)
|
(mf/deps id read-only?)
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(when-not workspace-read-only?
|
(when-not read-only?
|
||||||
(let [position (dom/get-client-position event)]
|
(let [position (dom/get-client-position event)]
|
||||||
(st/emit! (dw/show-page-item-context-menu
|
(st/emit! (dw/show-page-item-context-menu
|
||||||
{:position position
|
{:position position
|
||||||
|
@ -147,7 +147,7 @@
|
||||||
[:span {:class (stl/css :page-name) :data-testid "page-name"}
|
[:span {:class (stl/css :page-name) :data-testid "page-name"}
|
||||||
(:name page)]
|
(:name page)]
|
||||||
[:div {:class (stl/css :page-actions)}
|
[:div {:class (stl/css :page-actions)}
|
||||||
(when (and deletable? (not workspace-read-only?))
|
(when (and deletable? (not read-only?))
|
||||||
[:button {:on-click on-delete}
|
[:button {:on-click on-delete}
|
||||||
i/delete])]])]]))
|
i/delete])]])]]))
|
||||||
|
|
||||||
|
@ -206,8 +206,7 @@
|
||||||
(st/emit! (dw/create-page {:file-id file-id :project-id project-id}))
|
(st/emit! (dw/create-page {:file-id file-id :project-id project-id}))
|
||||||
(-> event dom/get-current-target dom/blur!)))
|
(-> event dom/get-current-target dom/blur!)))
|
||||||
read-only? (mf/use-ctx ctx/workspace-read-only?)
|
read-only? (mf/use-ctx ctx/workspace-read-only?)
|
||||||
user-viewer? (mf/use-ctx ctx/user-viewer?)]
|
permissions (mf/use-ctx ctx/team-permissions)]
|
||||||
|
|
||||||
|
|
||||||
[:div {:class (stl/css :sitemap)
|
[:div {:class (stl/css :sitemap)
|
||||||
:style #js {"--height" (str size "px")}}
|
:style #js {"--height" (str size "px")}}
|
||||||
|
@ -220,7 +219,7 @@
|
||||||
:class (stl/css :title-spacing-sitemap)}
|
:class (stl/css :title-spacing-sitemap)}
|
||||||
|
|
||||||
(if ^boolean read-only?
|
(if ^boolean read-only?
|
||||||
(when (not ^boolean user-viewer?)
|
(when ^boolean (:can-edit permissions)
|
||||||
[:& badge-notification {:is-focus true
|
[:& badge-notification {:is-focus true
|
||||||
:size :small
|
:size :small
|
||||||
:content (tr "labels.view-only")}])
|
:content (tr "labels.view-only")}])
|
||||||
|
|
|
@ -95,8 +95,11 @@
|
||||||
|
|
||||||
vbox' (mf/use-debounce 100 vbox)
|
vbox' (mf/use-debounce 100 vbox)
|
||||||
|
|
||||||
|
permissions (mf/use-ctx ctx/team-permissions)
|
||||||
|
read-only? (mf/use-ctx ctx/workspace-read-only?)
|
||||||
|
|
||||||
;; DEREFS
|
;; DEREFS
|
||||||
user-viewer? (mf/use-ctx ctx/user-viewer?)
|
|
||||||
drawing (mf/deref refs/workspace-drawing)
|
drawing (mf/deref refs/workspace-drawing)
|
||||||
focus (mf/deref refs/workspace-focus-selected)
|
focus (mf/deref refs/workspace-focus-selected)
|
||||||
|
|
||||||
|
@ -169,12 +172,11 @@
|
||||||
text-editing? (and edition (= :text (get-in base-objects [edition :type])))
|
text-editing? (and edition (= :text (get-in base-objects [edition :type])))
|
||||||
grid-editing? (and edition (ctl/grid-layout? base-objects edition))
|
grid-editing? (and edition (ctl/grid-layout? base-objects edition))
|
||||||
|
|
||||||
workspace-read-only? (mf/use-ctx ctx/workspace-read-only?)
|
|
||||||
mode-inspect? (= options-mode :inspect)
|
mode-inspect? (= options-mode :inspect)
|
||||||
|
|
||||||
on-click (actions/on-click hover selected edition drawing-path? drawing-tool space? selrect z?)
|
on-click (actions/on-click hover selected edition drawing-path? drawing-tool space? selrect z?)
|
||||||
on-context-menu (actions/on-context-menu hover hover-ids workspace-read-only?)
|
on-context-menu (actions/on-context-menu hover hover-ids read-only?)
|
||||||
on-double-click (actions/on-double-click hover hover-ids hover-top-frame-id drawing-path? base-objects edition drawing-tool z? workspace-read-only?)
|
on-double-click (actions/on-double-click hover hover-ids hover-top-frame-id drawing-path? base-objects edition drawing-tool z? read-only?)
|
||||||
|
|
||||||
comp-inst-ref (mf/use-ref false)
|
comp-inst-ref (mf/use-ref false)
|
||||||
on-drag-enter (actions/on-drag-enter comp-inst-ref)
|
on-drag-enter (actions/on-drag-enter comp-inst-ref)
|
||||||
|
@ -182,19 +184,19 @@
|
||||||
on-drag-end (actions/on-drag-over comp-inst-ref)
|
on-drag-end (actions/on-drag-over comp-inst-ref)
|
||||||
on-drop (actions/on-drop file comp-inst-ref)
|
on-drop (actions/on-drop file comp-inst-ref)
|
||||||
on-pointer-down (actions/on-pointer-down @hover selected edition drawing-tool text-editing? node-editing? grid-editing?
|
on-pointer-down (actions/on-pointer-down @hover selected edition drawing-tool text-editing? node-editing? grid-editing?
|
||||||
drawing-path? create-comment? space? panning z? workspace-read-only?)
|
drawing-path? create-comment? space? panning z? read-only?)
|
||||||
|
|
||||||
on-pointer-up (actions/on-pointer-up disable-paste)
|
on-pointer-up (actions/on-pointer-up disable-paste)
|
||||||
|
|
||||||
on-pointer-enter (actions/on-pointer-enter in-viewport?)
|
on-pointer-enter (actions/on-pointer-enter in-viewport?)
|
||||||
on-pointer-leave (actions/on-pointer-leave in-viewport?)
|
on-pointer-leave (actions/on-pointer-leave in-viewport?)
|
||||||
on-pointer-move (actions/on-pointer-move move-stream)
|
on-pointer-move (actions/on-pointer-move move-stream)
|
||||||
on-move-selected (actions/on-move-selected hover hover-ids selected space? z? workspace-read-only?)
|
on-move-selected (actions/on-move-selected hover hover-ids selected space? z? read-only?)
|
||||||
on-menu-selected (actions/on-menu-selected hover hover-ids selected workspace-read-only?)
|
on-menu-selected (actions/on-menu-selected hover hover-ids selected read-only?)
|
||||||
|
|
||||||
on-frame-enter (actions/on-frame-enter frame-hover)
|
on-frame-enter (actions/on-frame-enter frame-hover)
|
||||||
on-frame-leave (actions/on-frame-leave frame-hover)
|
on-frame-leave (actions/on-frame-leave frame-hover)
|
||||||
on-frame-select (actions/on-frame-select selected workspace-read-only?)
|
on-frame-select (actions/on-frame-select selected read-only?)
|
||||||
|
|
||||||
disable-events? (contains? layout :comments)
|
disable-events? (contains? layout :comments)
|
||||||
show-comments? (= drawing-tool :comments)
|
show-comments? (= drawing-tool :comments)
|
||||||
|
@ -267,9 +269,9 @@
|
||||||
|
|
||||||
rule-area-size (/ rulers/ruler-area-size zoom)]
|
rule-area-size (/ rulers/ruler-area-size zoom)]
|
||||||
|
|
||||||
(hooks/setup-dom-events zoom disable-paste in-viewport? workspace-read-only? drawing-tool drawing-path?)
|
(hooks/setup-dom-events zoom disable-paste in-viewport? read-only? drawing-tool drawing-path?)
|
||||||
(hooks/setup-viewport-size vport viewport-ref)
|
(hooks/setup-viewport-size vport viewport-ref)
|
||||||
(hooks/setup-cursor cursor alt? mod? space? panning drawing-tool drawing-path? node-editing? z? workspace-read-only?)
|
(hooks/setup-cursor cursor alt? mod? space? panning drawing-tool drawing-path? node-editing? z? read-only?)
|
||||||
(hooks/setup-keyboard alt? mod? space? z? shift?)
|
(hooks/setup-keyboard alt? mod? space? z? shift?)
|
||||||
(hooks/setup-hover-shapes page-id move-stream base-objects transform selected mod? hover measure-hover
|
(hooks/setup-hover-shapes page-id move-stream base-objects transform selected mod? hover measure-hover
|
||||||
hover-ids hover-top-frame-id @hover-disabled? focus zoom show-measures?)
|
hover-ids hover-top-frame-id @hover-disabled? focus zoom show-measures?)
|
||||||
|
@ -278,7 +280,7 @@
|
||||||
(hooks/setup-active-frames base-objects hover-ids selected active-frames zoom transform vbox)
|
(hooks/setup-active-frames base-objects hover-ids selected active-frames zoom transform vbox)
|
||||||
|
|
||||||
[:div {:class (stl/css :viewport) :style #js {"--zoom" zoom} :data-testid "viewport"}
|
[:div {:class (stl/css :viewport) :style #js {"--zoom" zoom} :data-testid "viewport"}
|
||||||
(when-not user-viewer?
|
(when (:can-edit permissions)
|
||||||
[:& top-bar/top-bar {:layout layout}])
|
[:& top-bar/top-bar {:layout layout}])
|
||||||
[:div {:class (stl/css :viewport-overlays)}
|
[:div {:class (stl/css :viewport-overlays)}
|
||||||
;; The behaviour inside a foreign object is a bit different that in plain HTML so we wrap
|
;; The behaviour inside a foreign object is a bit different that in plain HTML so we wrap
|
||||||
|
@ -288,7 +290,7 @@
|
||||||
[:div {:style {:pointer-events (when-not (dbg/enabled? :html-text) "none")
|
[:div {:style {:pointer-events (when-not (dbg/enabled? :html-text) "none")
|
||||||
;; some opacity because to debug auto-width events will fill the screen
|
;; some opacity because to debug auto-width events will fill the screen
|
||||||
:opacity 0.6}}
|
:opacity 0.6}}
|
||||||
(when-not workspace-read-only?
|
(when (and (:can-edit permissions) (not read-only?))
|
||||||
[:& stvh/viewport-texts
|
[:& stvh/viewport-texts
|
||||||
{:key (dm/str "texts-" page-id)
|
{:key (dm/str "texts-" page-id)
|
||||||
:page-id page-id
|
:page-id page-id
|
||||||
|
|
|
@ -41,11 +41,11 @@
|
||||||
|
|
||||||
(defn on-pointer-down
|
(defn on-pointer-down
|
||||||
[{:keys [id blocked hidden type]} selected edition drawing-tool text-editing?
|
[{:keys [id blocked hidden type]} selected edition drawing-tool text-editing?
|
||||||
node-editing? grid-editing? drawing-path? create-comment? space? panning z? workspace-read-only?]
|
node-editing? grid-editing? drawing-path? create-comment? space? panning z? read-only?]
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps id blocked hidden type selected edition drawing-tool text-editing?
|
(mf/deps id blocked hidden type selected edition drawing-tool text-editing?
|
||||||
node-editing? grid-editing? drawing-path? create-comment? @z? @space?
|
node-editing? grid-editing? drawing-path? create-comment? @z? @space?
|
||||||
panning workspace-read-only?)
|
panning read-only?)
|
||||||
(fn [bevent]
|
(fn [bevent]
|
||||||
;; We need to handle editor related stuff here because
|
;; We need to handle editor related stuff here because
|
||||||
;; handling on editor dom node does not works properly.
|
;; handling on editor dom node does not works properly.
|
||||||
|
@ -101,24 +101,24 @@
|
||||||
(cond
|
(cond
|
||||||
node-editing?
|
node-editing?
|
||||||
;; Handle path node area selection
|
;; Handle path node area selection
|
||||||
(when-not workspace-read-only?
|
(when-not read-only?
|
||||||
(st/emit! (dwdp/handle-area-selection shift?)))
|
(st/emit! (dwdp/handle-area-selection shift?)))
|
||||||
|
|
||||||
drawing-tool
|
drawing-tool
|
||||||
(when-not workspace-read-only?
|
(when-not read-only?
|
||||||
(st/emit! (dd/start-drawing drawing-tool)))
|
(st/emit! (dd/start-drawing drawing-tool)))
|
||||||
|
|
||||||
(or (not id) mod?)
|
(or (not id) mod?)
|
||||||
(st/emit! (dw/handle-area-selection shift?))
|
(st/emit! (dw/handle-area-selection shift?))
|
||||||
|
|
||||||
(not drawing-tool)
|
(not drawing-tool)
|
||||||
(when-not workspace-read-only?
|
(when-not read-only?
|
||||||
(st/emit! (dw/start-move-selected id shift?)))))))))))))
|
(st/emit! (dw/start-move-selected id shift?)))))))))))))
|
||||||
|
|
||||||
(defn on-move-selected
|
(defn on-move-selected
|
||||||
[hover hover-ids selected space? z? workspace-read-only?]
|
[hover hover-ids selected space? z? read-only?]
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps @hover @hover-ids selected @space? @z? workspace-read-only?)
|
(mf/deps @hover @hover-ids selected @space? @z? read-only?)
|
||||||
(fn [bevent]
|
(fn [bevent]
|
||||||
(let [event (.-nativeEvent bevent)
|
(let [event (.-nativeEvent bevent)
|
||||||
shift? (kbd/shift? event)
|
shift? (kbd/shift? event)
|
||||||
|
@ -132,20 +132,20 @@
|
||||||
|
|
||||||
(dom/prevent-default bevent)
|
(dom/prevent-default bevent)
|
||||||
(dom/stop-propagation bevent)
|
(dom/stop-propagation bevent)
|
||||||
(when-not (or workspace-read-only? @z?)
|
(when-not (or read-only? @z?)
|
||||||
(st/emit! (dw/start-move-selected))))))))
|
(st/emit! (dw/start-move-selected))))))))
|
||||||
|
|
||||||
(defn on-frame-select
|
(defn on-frame-select
|
||||||
[selected workspace-read-only?]
|
[selected read-only?]
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps selected workspace-read-only?)
|
(mf/deps selected read-only?)
|
||||||
(fn [event id]
|
(fn [event id]
|
||||||
(let [shift? (kbd/shift? event)
|
(let [shift? (kbd/shift? event)
|
||||||
selected? (contains? selected id)
|
selected? (contains? selected id)
|
||||||
selected-drawtool (deref refs/selected-drawing-tool)]
|
selected-drawtool (deref refs/selected-drawing-tool)]
|
||||||
(st/emit! (when (or shift? (not selected?))
|
(st/emit! (when (or shift? (not selected?))
|
||||||
(dw/select-shape id shift?))
|
(dw/select-shape id shift?))
|
||||||
(when (and (nil? selected-drawtool) (not shift?) (not workspace-read-only?))
|
(when (and (nil? selected-drawtool) (not shift?) (not read-only?))
|
||||||
(dw/start-move-selected)))))))
|
(dw/start-move-selected)))))))
|
||||||
|
|
||||||
(defn on-frame-enter
|
(defn on-frame-enter
|
||||||
|
@ -195,10 +195,10 @@
|
||||||
(st/emit! (dw/increase-zoom pt)))))))))
|
(st/emit! (dw/increase-zoom pt)))))))))
|
||||||
|
|
||||||
(defn on-double-click
|
(defn on-double-click
|
||||||
[hover hover-ids hover-top-frame-id drawing-path? objects edition drawing-tool z? workspace-read-only?]
|
[hover hover-ids hover-top-frame-id drawing-path? objects edition drawing-tool z? read-only?]
|
||||||
|
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps @hover @hover-ids @hover-top-frame-id drawing-path? edition drawing-tool @z? workspace-read-only?)
|
(mf/deps @hover @hover-ids @hover-top-frame-id drawing-path? edition drawing-tool @z? read-only?)
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(when-not @z?
|
(when-not @z?
|
||||||
|
@ -223,7 +223,7 @@
|
||||||
(fn []
|
(fn []
|
||||||
(when (and (not drawing-path?) shape)
|
(when (and (not drawing-path?) shape)
|
||||||
(cond
|
(cond
|
||||||
(and editable? (not= id edition) (not workspace-read-only?))
|
(and editable? (not= id edition) (not read-only?))
|
||||||
(st/emit! (dw/select-shape id)
|
(st/emit! (dw/select-shape id)
|
||||||
(dw/start-editing-selected))
|
(dw/start-editing-selected))
|
||||||
|
|
||||||
|
@ -231,16 +231,16 @@
|
||||||
(do (reset! hover selected-shape)
|
(do (reset! hover selected-shape)
|
||||||
(st/emit! (dw/select-shape (:id selected-shape))))
|
(st/emit! (dw/select-shape (:id selected-shape))))
|
||||||
|
|
||||||
(and (not selected-shape) (some? grid-layout-id) (not workspace-read-only?))
|
(and (not selected-shape) (some? grid-layout-id) (not read-only?))
|
||||||
(st/emit! (dw/start-edition-mode grid-layout-id)))))))))))
|
(st/emit! (dw/start-edition-mode grid-layout-id)))))))))))
|
||||||
|
|
||||||
(defn on-context-menu
|
(defn on-context-menu
|
||||||
[hover hover-ids workspace-read-only?]
|
[hover hover-ids read-only?]
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps @hover @hover-ids workspace-read-only?)
|
(mf/deps @hover @hover-ids read-only?)
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
;;(when-not workspace-read-only?
|
;;(when-not read-only?
|
||||||
(when (or (dom/class? (dom/get-target event) "viewport-controls")
|
(when (or (dom/class? (dom/get-target event) "viewport-controls")
|
||||||
(dom/child? (dom/get-target event) (dom/query ".grid-layout-editor"))
|
(dom/child? (dom/get-target event) (dom/query ".grid-layout-editor"))
|
||||||
(dom/class? (dom/get-target event) "viewport-selrect"))
|
(dom/class? (dom/get-target event) "viewport-selrect"))
|
||||||
|
@ -248,20 +248,20 @@
|
||||||
;; Delayed callback because we need to wait to the previous context menu to be closed
|
;; Delayed callback because we need to wait to the previous context menu to be closed
|
||||||
(ts/schedule
|
(ts/schedule
|
||||||
#(st/emit!
|
#(st/emit!
|
||||||
(if (and (not workspace-read-only?) (some? @hover))
|
(if (and (not read-only?) (some? @hover))
|
||||||
(dw/show-shape-context-menu {:position position
|
(dw/show-shape-context-menu {:position position
|
||||||
:shape @hover
|
:shape @hover
|
||||||
:hover-ids @hover-ids})
|
:hover-ids @hover-ids})
|
||||||
(dw/show-context-menu {:position position})))))))))
|
(dw/show-context-menu {:position position})))))))))
|
||||||
|
|
||||||
(defn on-menu-selected
|
(defn on-menu-selected
|
||||||
[hover hover-ids selected workspace-read-only?]
|
[hover hover-ids selected read-only?]
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps @hover @hover-ids selected workspace-read-only?)
|
(mf/deps @hover @hover-ids selected read-only?)
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(when-not workspace-read-only?
|
(when-not read-only?
|
||||||
(let [position (dom/get-client-position event)]
|
(let [position (dom/get-client-position event)]
|
||||||
(st/emit! (dw/show-shape-context-menu {:position position :hover-ids @hover-ids})))))))
|
(st/emit! (dw/show-shape-context-menu {:position position :hover-ids @hover-ids})))))))
|
||||||
|
|
||||||
|
@ -538,14 +538,14 @@
|
||||||
(st/emit! (dwm/upload-media-workspace params))))))))
|
(st/emit! (dwm/upload-media-workspace params))))))))
|
||||||
|
|
||||||
(defn on-paste
|
(defn on-paste
|
||||||
[disable-paste in-viewport? workspace-read-only?]
|
[disable-paste in-viewport? read-only?]
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps workspace-read-only?)
|
(mf/deps read-only?)
|
||||||
(fn [event]
|
(fn [event]
|
||||||
;; We disable the paste just after mouse-up of a middle button so
|
;; We disable the paste just after mouse-up of a middle button so
|
||||||
;; when panning won't paste the content into the workspace
|
;; when panning won't paste the content into the workspace
|
||||||
(let [tag-name (-> event dom/get-target dom/get-tag-name)]
|
(let [tag-name (-> event dom/get-target dom/get-tag-name)]
|
||||||
(when (and (not (#{"INPUT" "TEXTAREA"} tag-name))
|
(when (and (not (#{"INPUT" "TEXTAREA"} tag-name))
|
||||||
(not @disable-paste)
|
(not @disable-paste)
|
||||||
(not workspace-read-only?))
|
(not read-only?))
|
||||||
(st/emit! (dw/paste-from-event event @in-viewport?)))))))
|
(st/emit! (dw/paste-from-event event @in-viewport?)))))))
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
:pages []
|
:pages []
|
||||||
:pages-index {}}
|
:pages-index {}}
|
||||||
:workspace-libraries {}
|
:workspace-libraries {}
|
||||||
:features/team #{"components/v2"}})
|
:features-team #{"components/v2"}})
|
||||||
|
|
||||||
(def ^:private idmap (atom {}))
|
(def ^:private idmap (atom {}))
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
:current-page-id nil
|
:current-page-id nil
|
||||||
:workspace-data nil
|
:workspace-data nil
|
||||||
:workspace-libraries {}
|
:workspace-libraries {}
|
||||||
:features/team #{"components/v2"}})
|
:features-team #{"components/v2"}})
|
||||||
|
|
||||||
(defn- on-error
|
(defn- on-error
|
||||||
[cause]
|
[cause]
|
||||||
|
@ -33,6 +33,7 @@
|
||||||
(let [state (-> initial-state
|
(let [state (-> initial-state
|
||||||
(assoc :current-file-id (:id file)
|
(assoc :current-file-id (:id file)
|
||||||
:current-page-id (cthf/current-page-id file)
|
:current-page-id (cthf/current-page-id file)
|
||||||
|
:permissions {:can-edit true}
|
||||||
:workspace-file (dissoc file :data)
|
:workspace-file (dissoc file :data)
|
||||||
:workspace-data (:data file)))
|
:workspace-data (:data file)))
|
||||||
store (ptk/store {:state state :on-error on-error})]
|
store (ptk/store {:state state :on-error on-error})]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue