mirror of https://github.com/penpot/penpot.git
♻️ Refactor dropdown-menu and make dropdown visibility exclusive (#6956)
* 🐛 Fix having multiple dropdown menus opened on dashboard page * ♻️ Refactor dropdown-menu Make it follow new standards and make it external api more usable, not depending on manually provided list of ids. This also implements the autoclosing of "other" active/open dropdown-menu (or other similar components). * 📎 Add PR feedback changes * 🐛 Fix incorrect event handling on project-menu * 🐛 Fix unexpected exception --------- Co-authored-by: Andrey Antukh <niwi@niwi.nz>
This commit is contained in:
parent
07b15819d4
commit
c8f5ec4698
|
|
@ -20,6 +20,7 @@
|
|||
- Fix missing font when copy&paste a chunk of text [Taiga #11522](https://tree.taiga.io/project/penpot/issue/11522)
|
||||
- Fix bad swap slot after two swaps [Taiga #11659](https://tree.taiga.io/project/penpot/issue/11659)
|
||||
- Fix missing package for the penport_exporter Docker image [GitHub #7205](https://github.com/penpot/penpot/issues/7025)
|
||||
- Fix issue where multiple dropdown menus could be opened simultaneously on the dashboard page [Taiga #11500](https://tree.taiga.io/project/penpot/issue/11500)
|
||||
|
||||
## 2.9.0 (Unreleased)
|
||||
|
||||
|
|
|
|||
|
|
@ -8,45 +8,44 @@
|
|||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.config :as cfg]
|
||||
[app.main.store :as st]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.globals :as globals]
|
||||
[app.util.keyboard :as kbd]
|
||||
[app.util.object :as obj]
|
||||
[beicon.v2.core :as rx]
|
||||
[goog.events :as events]
|
||||
[goog.object :as gobj]
|
||||
[potok.v2.core :as ptk]
|
||||
[rumext.v2 :as mf])
|
||||
(:import goog.events.EventType))
|
||||
|
||||
(mf/defc dropdown-menu-item*
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [props (-> (obj/clone props)
|
||||
(obj/set! "role" "menuitem"))]
|
||||
[{:keys [can-focus] :rest props}]
|
||||
(let [can-focus (d/nilv can-focus true)
|
||||
tab-index (if can-focus "0" "-1")
|
||||
props (mf/spread-props props {:role "menuitem" :tab-index tab-index})]
|
||||
[:> :li props]))
|
||||
|
||||
(mf/defc dropdown-menu'
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [children (gobj/get props "children")
|
||||
on-close (gobj/get props "on-close")
|
||||
ref (gobj/get props "container")
|
||||
ids (gobj/get props "ids")
|
||||
list-class (gobj/get props "list-class")
|
||||
ids (filter some? ids)
|
||||
on-click
|
||||
(fn [event]
|
||||
(let [target (dom/get-target event)
|
||||
(mf/defc internal-dropdown-menu*
|
||||
{::mf/private true}
|
||||
[{:keys [on-close children class id]}]
|
||||
|
||||
;; MacOS ctrl+click sends two events: context-menu and click.
|
||||
;; In order to not have two handlings we ignore ctrl+click for this platform
|
||||
mac-ctrl-click? (and (cfg/check-platform? :macos) (kbd/ctrl? event))]
|
||||
(when (and (not mac-ctrl-click?)
|
||||
(not (.-data-no-close ^js target)))
|
||||
(if ref
|
||||
(let [parent (mf/ref-val ref)]
|
||||
(when-not (or (not parent) (.contains parent target))
|
||||
(on-close)))
|
||||
(on-close)))))
|
||||
(assert (fn? on-close) "missing `on-close` prop")
|
||||
|
||||
(let [on-click
|
||||
(mf/use-fn
|
||||
(mf/deps on-close)
|
||||
(fn [event]
|
||||
(let [target (dom/get-target event)
|
||||
;; MacOS ctrl+click sends two events: context-menu and click.
|
||||
;; In order to not have two handlings we ignore ctrl+click for this platform
|
||||
mac-ctrl-click? (and (cfg/check-platform? :macos) (kbd/ctrl? event))]
|
||||
(when (and (not mac-ctrl-click?)
|
||||
(not (.-data-no-close ^js target))
|
||||
(fn? on-close))
|
||||
(on-close)))))
|
||||
|
||||
container
|
||||
(mf/use-ref)
|
||||
|
||||
on-keyup
|
||||
(fn [event]
|
||||
|
|
@ -55,35 +54,49 @@
|
|||
|
||||
on-key-down
|
||||
(fn [event]
|
||||
(let [first-id (dom/get-element (first ids))
|
||||
first-element (dom/get-element first-id)
|
||||
len (count ids)]
|
||||
(when-let [container (mf/ref-val container)]
|
||||
(let [entries (vec (dom/query-all container "[role=menuitem]"))]
|
||||
|
||||
(when (kbd/home? event)
|
||||
(when first-element
|
||||
(dom/focus! first-element)))
|
||||
(cond
|
||||
(kbd/up-arrow? event)
|
||||
(let [selected (dom/get-active)
|
||||
index (d/index-of-pred entries #(identical? % selected))
|
||||
target (if (nil? index)
|
||||
(peek entries)
|
||||
(or (get entries (dec index)) (peek entries)))]
|
||||
|
||||
(when (kbd/up-arrow? event)
|
||||
(let [actual-selected (dom/get-active)
|
||||
actual-id (dom/get-attribute actual-selected "id")
|
||||
actual-index (d/index-of ids actual-id)
|
||||
previous-id (if (= 0 actual-index)
|
||||
(last ids)
|
||||
(get ids (- actual-index 1) (last ids)))]
|
||||
(dom/focus! (dom/get-element previous-id))))
|
||||
(dom/focus! target))
|
||||
|
||||
(when (kbd/down-arrow? event)
|
||||
(let [actual-selected (dom/get-active)
|
||||
actual-id (dom/get-attribute actual-selected "id")
|
||||
actual-index (d/index-of ids actual-id)
|
||||
next-id (if (= (- len 1) actual-index)
|
||||
(first ids)
|
||||
(get ids (+ 1 actual-index) (first ids)))
|
||||
node-item (dom/get-element next-id)]
|
||||
(dom/focus! node-item)))
|
||||
(kbd/down-arrow? event)
|
||||
(let [selected (dom/get-active)
|
||||
index (d/index-of-pred entries #(identical? % selected))
|
||||
target (if (nil? index)
|
||||
(first entries)
|
||||
(or (get entries (inc index)) (first entries)))]
|
||||
(dom/focus! target))
|
||||
|
||||
(when (kbd/tab? event)
|
||||
(on-close))))]
|
||||
(kbd/enter? event)
|
||||
(let [selected (dom/get-active)]
|
||||
(dom/prevent-default event)
|
||||
(dom/click! selected))
|
||||
|
||||
(kbd/tab? event)
|
||||
(on-close)))))]
|
||||
|
||||
(mf/with-effect [id]
|
||||
(when id
|
||||
(st/emit! (ptk/data-event :dropdown/open {:id id}))))
|
||||
|
||||
(mf/with-effect [on-close id]
|
||||
(when id
|
||||
(let [stream (->> st/stream
|
||||
(rx/filter (ptk/type? :dropdown/open))
|
||||
(rx/map deref)
|
||||
(rx/filter #(not= id (:id %)))
|
||||
(rx/take 1))
|
||||
subs (rx/subs! nil nil on-close stream)]
|
||||
(fn []
|
||||
(rx/dispose! subs)))))
|
||||
|
||||
(mf/with-effect []
|
||||
(let [keys [(events/listen globals/document EventType.CLICK on-click)
|
||||
|
|
@ -93,21 +106,10 @@
|
|||
#(doseq [key keys]
|
||||
(events/unlistenByKey key))))
|
||||
|
||||
[:ul {:class list-class :role "menu"} children]))
|
||||
[:ul {:class class :role "menu" :ref container} children]))
|
||||
|
||||
(mf/defc dropdown-menu
|
||||
{::mf/props :obj}
|
||||
[props]
|
||||
(assert (fn? (gobj/get props "on-close")) "missing `on-close` prop")
|
||||
(assert (boolean? (gobj/get props "show")) "missing `show` prop")
|
||||
(mf/defc dropdown-menu*
|
||||
[{:keys [show] :as props}]
|
||||
(when show
|
||||
[:> internal-dropdown-menu* props]))
|
||||
|
||||
(let [ids (obj/get props "ids")
|
||||
ids (or ids
|
||||
(->> (obj/get props "children")
|
||||
(keep (fn [o]
|
||||
(let [props (obj/get o "props")]
|
||||
(obj/get props "id"))))))]
|
||||
(when (gobj/get props "show")
|
||||
(mf/element
|
||||
dropdown-menu'
|
||||
(mf/spread-props props {:ids ids})))))
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@
|
|||
::ev/origin "dashboard"})))))
|
||||
|
||||
[:div {:class (stl/css :dashboard-comments-section)}
|
||||
[:& dropdown {:show show? :on-close on-hide-comments}
|
||||
[:& dropdown {:show show? :on-close on-hide-comments :dropdown-id "dashboard-comments"}
|
||||
[:div {:class (stl/css :dropdown :comments-section :comment-threads-section)}
|
||||
[:div {:class (stl/css :header)}
|
||||
[:h3 {:class (stl/css :header-title)} (tr "dashboard.notifications")]
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(defn- get-project-name
|
||||
|
|
@ -55,11 +56,11 @@
|
|||
projects))
|
||||
|
||||
(mf/defc file-menu*
|
||||
[{:keys [files on-edit on-menu-close top left navigate origin parent-id can-edit]}]
|
||||
[{:keys [files on-edit on-close top left navigate origin parent-id can-edit]}]
|
||||
|
||||
(assert (seq files) "missing `files` prop")
|
||||
(assert (fn? on-edit) "missing `on-edit` prop")
|
||||
(assert (fn? on-menu-close) "missing `on-menu-close` prop")
|
||||
(assert (fn? on-close) "missing `on-close` prop")
|
||||
(assert (boolean? navigate) "missing `navigate` prop")
|
||||
|
||||
(let [is-lib-page? (= :libraries origin)
|
||||
|
|
@ -161,13 +162,12 @@
|
|||
(on-move-accept params team-id project-id))))))
|
||||
|
||||
add-shared
|
||||
#(st/emit! (dd/set-file-shared (assoc file :is-shared true)))
|
||||
(fn []
|
||||
(st/emit! (dd/set-file-shared (assoc file :is-shared true))))
|
||||
|
||||
del-shared
|
||||
(mf/use-fn
|
||||
(mf/deps files)
|
||||
(fn [_]
|
||||
(run! #(st/emit! (dd/set-file-shared (assoc % :is-shared false))) files)))
|
||||
(fn [_]
|
||||
(run! #(st/emit! (dd/set-file-shared (assoc % :is-shared false))) files))
|
||||
|
||||
on-add-shared
|
||||
(fn [event]
|
||||
|
|
@ -186,32 +186,35 @@
|
|||
:count-libraries file-count})))
|
||||
|
||||
on-export-files
|
||||
(mf/use-fn
|
||||
(mf/deps files)
|
||||
(fn [format]
|
||||
(st/emit! (with-meta (fexp/export-files files format)
|
||||
{::ev/origin "dashboard"}))))
|
||||
(fn [format]
|
||||
(st/emit! (with-meta (fexp/export-files files format)
|
||||
{::ev/origin "dashboard"})))
|
||||
|
||||
on-export-binary-files
|
||||
(mf/use-fn
|
||||
(mf/deps on-export-files)
|
||||
(partial on-export-files :binfile-v1))
|
||||
(partial on-export-files :binfile-v1)
|
||||
|
||||
on-export-binary-files-v3
|
||||
(mf/use-fn
|
||||
(mf/deps on-export-files)
|
||||
(partial on-export-files :binfile-v3))
|
||||
(partial on-export-files :binfile-v3)
|
||||
|
||||
on-export-standard-files
|
||||
(mf/use-fn
|
||||
(mf/deps on-export-files)
|
||||
(partial on-export-files :legacy-zip))]
|
||||
(partial on-export-files :legacy-zip)]
|
||||
|
||||
(mf/with-effect []
|
||||
(->> (rp/cmd! :get-all-projects)
|
||||
(rx/map group-by-team)
|
||||
(rx/subs! #(reset! teams* %))))
|
||||
|
||||
(mf/with-effect [on-close]
|
||||
(st/emit! (ptk/data-event :dropdown/open {:id "file-menu"}))
|
||||
(let [stream (->> st/stream
|
||||
(rx/filter (ptk/type? :dropdown/open))
|
||||
(rx/map deref)
|
||||
(rx/filter #(not= "file-menu" (:id %)))
|
||||
(rx/take 1))
|
||||
subs (rx/subs! nil nil on-close stream)]
|
||||
(fn []
|
||||
(rx/dispose! subs))))
|
||||
|
||||
(let [sub-options
|
||||
(concat
|
||||
(for [project current-projects]
|
||||
|
|
@ -328,7 +331,7 @@
|
|||
:handler on-delete})])]
|
||||
|
||||
[:> context-menu*
|
||||
{:on-close on-menu-close
|
||||
{:on-close on-close
|
||||
:fixed (or (not= top 0) (not= left 0))
|
||||
:show true
|
||||
:min-width true
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@
|
|||
:left (- (:x (:menu-pos @local)) 180)
|
||||
:top (:y (:menu-pos @local))
|
||||
:on-edit on-edit
|
||||
:on-menu-close on-menu-close
|
||||
:on-close on-menu-close
|
||||
:on-import on-import}])]]))
|
||||
|
||||
(mf/defc files-section*
|
||||
|
|
|
|||
|
|
@ -439,7 +439,7 @@
|
|||
:can-edit can-edit
|
||||
:navigate true
|
||||
:on-edit on-edit
|
||||
:on-menu-close on-menu-close
|
||||
:on-close on-menu-close
|
||||
:origin origin
|
||||
:parent-id (dm/str file-id "-action-menu")}]])]]]]]))
|
||||
|
||||
|
|
|
|||
|
|
@ -17,11 +17,12 @@
|
|||
[app.main.ui.dashboard.import :as udi]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc project-menu*
|
||||
{::mf/props :obj}
|
||||
[{:keys [project show on-edit on-menu-close top left on-import]}]
|
||||
[{:keys [project show on-edit on-close top left on-import]}]
|
||||
(let [top (or top 0)
|
||||
left (or left 0)
|
||||
|
||||
|
|
@ -42,7 +43,8 @@
|
|||
(with-meta project {:on-success on-duplicate-success}))))
|
||||
|
||||
toggle-pin
|
||||
#(st/emit! (dd/toggle-project-pin project))
|
||||
(fn []
|
||||
(st/emit! (dd/toggle-project-pin project)))
|
||||
|
||||
on-move-success
|
||||
(fn [team-id]
|
||||
|
|
@ -63,25 +65,23 @@
|
|||
(dcm/go-to-dashboard-recent :team-id team-id))))
|
||||
|
||||
on-delete
|
||||
#(st/emit!
|
||||
(modal/show
|
||||
{:type :confirm
|
||||
:title (tr "modals.delete-project-confirm.title")
|
||||
:message (tr "modals.delete-project-confirm.message")
|
||||
:accept-label (tr "modals.delete-project-confirm.accept")
|
||||
:on-accept delete-fn}))
|
||||
(fn []
|
||||
(st/emit!
|
||||
(modal/show {:type :confirm
|
||||
:title (tr "modals.delete-project-confirm.title")
|
||||
:message (tr "modals.delete-project-confirm.message")
|
||||
:accept-label (tr "modals.delete-project-confirm.accept")
|
||||
:on-accept delete-fn})))
|
||||
|
||||
file-input (mf/use-ref nil)
|
||||
file-input
|
||||
(mf/use-ref nil)
|
||||
|
||||
on-import-files
|
||||
(mf/use-callback
|
||||
(fn []
|
||||
(dom/click (mf/ref-val file-input))))
|
||||
(fn [] (dom/click! (mf/ref-val file-input)))
|
||||
|
||||
on-finish-import
|
||||
(mf/use-callback
|
||||
(fn []
|
||||
(when (fn? on-import) (on-import))))
|
||||
(mf/use-fn
|
||||
(fn [] (when (fn? on-import) (on-import))))
|
||||
|
||||
options
|
||||
[(when-not (:is-default project)
|
||||
|
|
@ -116,9 +116,21 @@
|
|||
:id "project-delete"
|
||||
:handler on-delete})]]
|
||||
|
||||
(mf/with-effect [show on-close]
|
||||
(when ^boolean show
|
||||
(st/emit! (ptk/data-event :dropdown/open {:id "project-menu"}))
|
||||
(let [stream (->> st/stream
|
||||
(rx/filter (ptk/type? :dropdown/open))
|
||||
(rx/map deref)
|
||||
(rx/filter #(not= "project-menu" (:id %)))
|
||||
(rx/take 1))
|
||||
subs (rx/subs! nil nil on-close stream)]
|
||||
(fn []
|
||||
(rx/dispose! subs)))))
|
||||
|
||||
[:*
|
||||
[:> context-menu*
|
||||
{:on-close on-menu-close
|
||||
{:on-close on-close
|
||||
:show show
|
||||
:fixed (or (not= top 0) (not= left 0))
|
||||
:min-width true
|
||||
|
|
|
|||
|
|
@ -271,7 +271,7 @@
|
|||
:left (+ 24 (:x (:menu-pos @local)))
|
||||
:top (:y (:menu-pos @local))
|
||||
:on-edit on-edit-open
|
||||
:on-menu-close on-menu-close
|
||||
:on-close on-menu-close
|
||||
:on-import on-import}])]]]
|
||||
|
||||
[:div {:class (stl/css :grid-container) :ref rowref}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.spec :as us]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.main.data.auth :as da]
|
||||
|
|
@ -21,7 +20,7 @@
|
|||
[app.main.refs :as refs]
|
||||
[app.main.router :as rt]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.dropdown-menu :refer [dropdown-menu
|
||||
[app.main.ui.components.dropdown-menu :refer [dropdown-menu*
|
||||
dropdown-menu-item*]]
|
||||
[app.main.ui.components.link :refer [link]]
|
||||
[app.main.ui.dashboard.comments :refer [comments-icon* comments-section]]
|
||||
|
|
@ -37,7 +36,6 @@
|
|||
[app.util.object :as obj]
|
||||
[app.util.timers :as ts]
|
||||
[beicon.v2.core :as rx]
|
||||
[cljs.spec.alpha :as s]
|
||||
[cuerdas.core :as str]
|
||||
[goog.functions :as f]
|
||||
[potok.v2.core :as ptk]
|
||||
|
|
@ -194,7 +192,7 @@
|
|||
:left (:x (:menu-pos local))
|
||||
:top (:y (:menu-pos local))
|
||||
:on-edit on-edit-open
|
||||
:on-menu-close on-menu-close}]]))
|
||||
:on-close on-menu-close}]]))
|
||||
|
||||
(mf/defc sidebar-search
|
||||
[{:keys [search-term team-id] :as props}]
|
||||
|
|
@ -273,47 +271,24 @@
|
|||
:on-click on-clear-click}
|
||||
search-icon])]))
|
||||
|
||||
(mf/defc teams-selector-dropdown-items
|
||||
(mf/defc teams-selector-dropdown*
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [team profile teams] :as props}]
|
||||
(let [on-create-clicked
|
||||
(mf/use-fn
|
||||
#(st/emit! (modal/show :team-form {})))
|
||||
[{:keys [team profile teams] :rest props}]
|
||||
(let [on-create-click
|
||||
(mf/use-fn #(st/emit! (modal/show :team-form {})))
|
||||
|
||||
team-selected
|
||||
on-team-click
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(let [team-id (-> (dom/get-current-target event)
|
||||
(dom/get-data "value")
|
||||
(uuid/parse))]
|
||||
(st/emit! (dcm/go-to-dashboard-recent :team-id team-id)))))
|
||||
(st/emit! (dcm/go-to-dashboard-recent :team-id team-id)))))]
|
||||
|
||||
handle-select-default
|
||||
(mf/use-fn
|
||||
(mf/deps profile team-selected)
|
||||
(fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(team-selected (:default-team-id profile) event))))
|
||||
[:> dropdown-menu* props
|
||||
|
||||
handle-select-team
|
||||
(mf/use-fn
|
||||
(mf/deps team-selected)
|
||||
(fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(team-selected event))))
|
||||
|
||||
handle-creation-key-down
|
||||
(mf/use-fn
|
||||
(mf/deps on-create-clicked)
|
||||
(fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(on-create-clicked event))))]
|
||||
|
||||
[:*
|
||||
[:> dropdown-menu-item* {:on-click team-selected
|
||||
[:> dropdown-menu-item* {:on-click on-team-click
|
||||
:data-value (:default-team-id profile)
|
||||
:on-key-down handle-select-default
|
||||
:id "teams-selector-default-team"
|
||||
:class (stl/css :team-dropdown-item)}
|
||||
[:span {:class (stl/css :penpot-icon)} i/logo-icon]
|
||||
|
||||
|
|
@ -322,12 +297,10 @@
|
|||
tick-icon)]
|
||||
|
||||
(for [team-item (remove :is-default (vals teams))]
|
||||
[:> dropdown-menu-item* {:on-click team-selected
|
||||
[:> dropdown-menu-item* {:on-click on-team-click
|
||||
:data-value (:id team-item)
|
||||
:on-key-down handle-select-team
|
||||
:id (str "teams-selector-" (:id team-item))
|
||||
:class (stl/css :team-dropdown-item)
|
||||
:key (str "teams-selector-" (:id team-item))}
|
||||
:key (str (:id team-item))}
|
||||
[:img {:src (cf/resolve-team-photo-url team-item)
|
||||
:class (stl/css :team-picture)
|
||||
:alt (:name team-item)}]
|
||||
|
|
@ -342,21 +315,14 @@
|
|||
(when (= (:id team-item) (:id team))
|
||||
tick-icon)])
|
||||
|
||||
[:hr {:role "separator"
|
||||
:class (stl/css :team-separator)}]
|
||||
[:> dropdown-menu-item* {:on-click on-create-clicked
|
||||
:on-key-down handle-creation-key-down
|
||||
:id "teams-selector-create-team"
|
||||
[:hr {:role "separator" :class (stl/css :team-separator)}]
|
||||
[:> dropdown-menu-item* {:on-click on-create-click
|
||||
:class (stl/css :team-dropdown-item :action)}
|
||||
[:span {:class (stl/css :icon-wrapper)} add-icon]
|
||||
[:span {:class (stl/css :team-text)} (tr "dashboard.create-new-team")]]]))
|
||||
|
||||
(s/def ::member-id ::us/uuid)
|
||||
(s/def ::leave-modal-form
|
||||
(s/keys :req-un [::member-id]))
|
||||
|
||||
(mf/defc team-options-dropdown
|
||||
[{:keys [team profile] :as props}]
|
||||
(mf/defc team-options-dropdown*
|
||||
[{:keys [team profile] :rest props}]
|
||||
(let [go-members #(st/emit! (dcm/go-to-dashboard-members))
|
||||
go-invitations #(st/emit! (dcm/go-to-dashboard-invitations))
|
||||
go-webhooks #(st/emit! (dcm/go-to-dashboard-webhooks))
|
||||
|
|
@ -449,218 +415,125 @@
|
|||
:title (tr "modals.delete-team-confirm.title")
|
||||
:message (tr "modals.delete-team-confirm.message")
|
||||
:accept-label (tr "modals.delete-team-confirm.accept")
|
||||
:on-accept delete-fn})))
|
||||
:on-accept delete-fn})))]
|
||||
[:> dropdown-menu* props
|
||||
|
||||
handle-members
|
||||
(mf/use-fn
|
||||
(mf/deps go-members)
|
||||
(fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(go-members))))
|
||||
|
||||
handle-invitations
|
||||
(mf/use-fn
|
||||
(mf/deps go-invitations)
|
||||
(fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(go-invitations))))
|
||||
|
||||
handle-webhooks
|
||||
(mf/use-fn
|
||||
(mf/deps go-webhooks)
|
||||
(fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(go-webhooks))))
|
||||
|
||||
handle-settings
|
||||
(mf/use-fn
|
||||
(mf/deps go-settings)
|
||||
(fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(go-settings))))
|
||||
|
||||
|
||||
handle-rename
|
||||
(mf/use-fn
|
||||
(mf/deps on-rename-clicked)
|
||||
(fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(on-rename-clicked))))
|
||||
|
||||
|
||||
handle-leave-and-close
|
||||
(mf/use-fn
|
||||
(mf/deps leave-and-close)
|
||||
(fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(leave-and-close))))
|
||||
|
||||
handle-leave-as-owner-clicked
|
||||
(mf/use-fn
|
||||
(mf/deps on-leave-as-owner-clicked)
|
||||
(fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(on-leave-as-owner-clicked))))
|
||||
|
||||
|
||||
handle-on-leave-clicked
|
||||
(mf/use-fn
|
||||
(mf/deps on-leave-clicked)
|
||||
(fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(on-leave-clicked))))
|
||||
|
||||
handle-on-delete-clicked
|
||||
(mf/use-fn
|
||||
(mf/deps on-delete-clicked)
|
||||
(fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(on-delete-clicked))))]
|
||||
|
||||
[:*
|
||||
[:> dropdown-menu-item* {:on-click go-members
|
||||
:on-key-down handle-members
|
||||
:className (stl/css :team-options-item)
|
||||
:id "teams-options-members"
|
||||
:data-testid "team-members"}
|
||||
:class (stl/css :team-options-item)
|
||||
:data-testid "team-members"}
|
||||
(tr "labels.members")]
|
||||
[:> dropdown-menu-item* {:on-click go-invitations
|
||||
:on-key-down handle-invitations
|
||||
:className (stl/css :team-options-item)
|
||||
:id "teams-options-invitations"
|
||||
:data-testid "team-invitations"}
|
||||
:class (stl/css :team-options-item)
|
||||
:data-testid "team-invitations"}
|
||||
(tr "labels.invitations")]
|
||||
|
||||
(when (contains? cf/flags :webhooks)
|
||||
[:> dropdown-menu-item* {:on-click go-webhooks
|
||||
:on-key-down handle-webhooks
|
||||
:className (stl/css :team-options-item)
|
||||
:id "teams-options-webhooks"}
|
||||
[:> dropdown-menu-item* {:on-click go-webhooks
|
||||
:class (stl/css :team-options-item)}
|
||||
(tr "labels.webhooks")])
|
||||
|
||||
[:> dropdown-menu-item* {:on-click go-settings
|
||||
:on-key-down handle-settings
|
||||
:className (stl/css :team-options-item)
|
||||
:id "teams-options-settings"
|
||||
:data-testid "team-settings"}
|
||||
:class (stl/css :team-options-item)
|
||||
:data-testid "team-settings"}
|
||||
(tr "labels.settings")]
|
||||
|
||||
[:hr {:class (stl/css :team-option-separator)}]
|
||||
(when can-rename?
|
||||
[:> dropdown-menu-item* {:on-click on-rename-clicked
|
||||
:on-key-down handle-rename
|
||||
:id "teams-options-rename"
|
||||
:className (stl/css :team-options-item)
|
||||
:data-testid "rename-team"}
|
||||
:class (stl/css :team-options-item)
|
||||
:data-testid "rename-team"}
|
||||
(tr "labels.rename")])
|
||||
|
||||
(cond
|
||||
(= (count members) 1)
|
||||
[:> dropdown-menu-item* {:on-click leave-and-close
|
||||
:on-key-down handle-leave-and-close
|
||||
:className (stl/css :team-options-item)
|
||||
:id "teams-options-leave-team"}
|
||||
[:> dropdown-menu-item* {:on-click leave-and-close
|
||||
:class (stl/css :team-options-item)}
|
||||
(tr "dashboard.leave-team")]
|
||||
|
||||
|
||||
(get-in team [:permissions :is-owner])
|
||||
[:> dropdown-menu-item* {:on-click on-leave-as-owner-clicked
|
||||
:on-key-down handle-leave-as-owner-clicked
|
||||
:id "teams-options-leave-team"
|
||||
:className (stl/css :team-options-item)
|
||||
:data-testid "leave-team"}
|
||||
:class (stl/css :team-options-item)
|
||||
:data-testid "leave-team"}
|
||||
(tr "dashboard.leave-team")]
|
||||
|
||||
(> (count members) 1)
|
||||
[:> dropdown-menu-item* {:on-click on-leave-clicked
|
||||
:on-key-down handle-on-leave-clicked
|
||||
:className (stl/css :team-options-item)
|
||||
:id "teams-options-leave-team"}
|
||||
[:> dropdown-menu-item* {:on-click on-leave-clicked
|
||||
:class (stl/css :team-options-item)}
|
||||
(tr "dashboard.leave-team")])
|
||||
|
||||
(when (get-in team [:permissions :is-owner])
|
||||
[:> dropdown-menu-item* {:on-click on-delete-clicked
|
||||
:on-key-down handle-on-delete-clicked
|
||||
:id "teams-options-delete-team"
|
||||
:className (stl/css :team-options-item :warning)
|
||||
:data-testid "delete-team"}
|
||||
:class (stl/css :team-options-item :warning)
|
||||
:data-testid "delete-team"}
|
||||
(tr "dashboard.delete-team")])]))
|
||||
|
||||
(mf/defc sidebar-team-switch
|
||||
[{:keys [team profile] :as props}]
|
||||
(let [teams (mf/deref refs/teams)
|
||||
teams-without-default (into {} (filter (fn [[_ v]] (= false (:is-default v))) teams))
|
||||
team-ids (map #(str "teams-selector-" %) (keys teams-without-default))
|
||||
ids (concat ["teams-selector-default-team"] team-ids ["teams-selector-create-team"])
|
||||
show-team-opts-ddwn? (mf/use-state false)
|
||||
show-teams-ddwn? (mf/use-state false)
|
||||
can-rename? (or (get-in team [:permissions :is-owner]) (get-in team [:permissions :is-admin]))
|
||||
options-ids ["teams-options-members"
|
||||
"teams-options-invitations"
|
||||
(when (contains? cf/flags :webhooks)
|
||||
"teams-options-webhooks")
|
||||
"teams-options-settings"
|
||||
(when can-rename?
|
||||
"teams-options-rename")
|
||||
"teams-options-leave-team"
|
||||
(when (get-in team [:permissions :is-owner])
|
||||
"teams-options-delete-team")]
|
||||
(let [teams (mf/deref refs/teams)
|
||||
|
||||
subscription
|
||||
(get team :subscription)
|
||||
|
||||
;; _ (prn "--------------- sidebar-team-switch")
|
||||
;; _ (app.common.pprint/pprint teams)
|
||||
subscription-type
|
||||
(get-subscription-type subscription)
|
||||
|
||||
handle-show-team-click
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(swap! show-teams-ddwn? not)
|
||||
(reset! show-team-opts-ddwn? false))
|
||||
show-team-options-menu*
|
||||
(mf/use-state false)
|
||||
|
||||
handle-show-team-keydown
|
||||
(fn [event]
|
||||
(when (or (kbd/space? event) (kbd/enter? event))
|
||||
(dom/prevent-default event)
|
||||
(reset! show-teams-ddwn? true)
|
||||
(reset! show-team-opts-ddwn? false)
|
||||
(ts/schedule-on-idle
|
||||
(fn []
|
||||
(let [first-element (dom/get-element (first ids))]
|
||||
(when first-element
|
||||
(dom/focus! first-element)))))))
|
||||
show-team-options-menu?
|
||||
(deref show-team-options-menu*)
|
||||
|
||||
close-team-opts-ddwn
|
||||
show-teams-menu*
|
||||
(mf/use-state false)
|
||||
|
||||
show-teams-menu?
|
||||
(deref show-teams-menu*)
|
||||
|
||||
on-show-teams-click
|
||||
(mf/use-fn
|
||||
#(reset! show-team-opts-ddwn? false))
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(swap! show-teams-menu* not)))
|
||||
|
||||
handle-show-opts-click
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(swap! show-team-opts-ddwn? not)
|
||||
(reset! show-teams-ddwn? false))
|
||||
on-show-teams-keydown
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(when (or (kbd/space? event)
|
||||
(kbd/enter? event))
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(some-> (dom/get-current-target event)
|
||||
(dom/click!)))))
|
||||
|
||||
handle-show-opts-keydown
|
||||
(fn [event]
|
||||
(when (or (kbd/space? event) (kbd/enter? event))
|
||||
(dom/prevent-default event)
|
||||
(reset! show-team-opts-ddwn? true)
|
||||
(reset! show-teams-ddwn? false)
|
||||
(ts/schedule-on-idle
|
||||
(fn []
|
||||
(let [first-element (dom/get-element (first options-ids))]
|
||||
(when first-element
|
||||
(dom/focus! first-element)))))))
|
||||
close-team-options-menu
|
||||
(mf/use-fn #(reset! show-team-options-menu* false))
|
||||
|
||||
handle-close-team
|
||||
(fn []
|
||||
(reset! show-teams-ddwn? false))
|
||||
subscription (:subscription team)
|
||||
subscription-type (get-subscription-type subscription)]
|
||||
on-show-options-click
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(swap! show-team-options-menu* not)))
|
||||
|
||||
on-show-options-keydown
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(when (or (kbd/space? event)
|
||||
(kbd/enter? event))
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
|
||||
(some-> (dom/get-current-target event)
|
||||
(dom/click!)))))
|
||||
|
||||
close-teams-menu
|
||||
(mf/use-fn #(reset! show-teams-menu* false))]
|
||||
|
||||
[:div {:class (stl/css :sidebar-team-switch)}
|
||||
[:div {:class (stl/css :switch-content)}
|
||||
[:button {:class (stl/css :current-team)
|
||||
:on-click handle-show-team-click
|
||||
:on-key-down handle-show-team-keydown}
|
||||
:on-click on-show-teams-click
|
||||
:on-key-down on-show-teams-keydown}
|
||||
(cond
|
||||
(:is-default team)
|
||||
[:div {:class (stl/css :team-name)}
|
||||
|
|
@ -691,29 +564,28 @@
|
|||
|
||||
(when-not (:is-default team)
|
||||
[:button {:class (stl/css :switch-options)
|
||||
:on-click handle-show-opts-click
|
||||
:on-click on-show-options-click
|
||||
:aria-label "team-management"
|
||||
:tab-index "0"
|
||||
:on-key-down handle-show-opts-keydown}
|
||||
:on-key-down on-show-options-keydown}
|
||||
menu-icon])]
|
||||
|
||||
;; Teams Dropdown
|
||||
|
||||
[:& dropdown-menu {:show @show-teams-ddwn?
|
||||
:on-close handle-close-team
|
||||
:ids ids
|
||||
:list-class (stl/css :dropdown :teams-dropdown)}
|
||||
[:& teams-selector-dropdown-items {:ids ids
|
||||
:team team
|
||||
:profile profile
|
||||
:teams teams}]]
|
||||
[:> teams-selector-dropdown* {:show show-teams-menu?
|
||||
:on-close close-teams-menu
|
||||
:id "team-list"
|
||||
:class (stl/css :dropdown :teams-dropdown)
|
||||
:team team
|
||||
:profile profile
|
||||
:teams teams}]
|
||||
|
||||
[:& dropdown-menu {:show @show-team-opts-ddwn?
|
||||
:on-close close-team-opts-ddwn
|
||||
:ids options-ids
|
||||
:list-class (stl/css :dropdown :options-dropdown)}
|
||||
[:& team-options-dropdown {:team team
|
||||
:profile profile}]]]))
|
||||
[:> team-options-dropdown* {:show show-team-options-menu?
|
||||
:on-close close-team-options-menu
|
||||
:id "team-options"
|
||||
:class (stl/css :dropdown :options-dropdown)
|
||||
:team team
|
||||
:profile profile}]]))
|
||||
|
||||
(mf/defc sidebar-content*
|
||||
{::mf/private true
|
||||
|
|
@ -806,7 +678,7 @@
|
|||
(mf/use-layout-effect
|
||||
(mf/deps pinned-projects)
|
||||
(fn []
|
||||
(let [dom (mf/ref-val container)
|
||||
(let [dom (mf/ref-val container)
|
||||
client-height (obj/get dom "clientHeight")
|
||||
scroll-height (obj/get dom "scrollHeight")]
|
||||
(reset! overflow* (> scroll-height client-height)))))
|
||||
|
|
@ -878,15 +750,17 @@
|
|||
(mf/defc profile-section*
|
||||
{::mf/props :obj}
|
||||
[{:keys [profile team]}]
|
||||
(let [show* (mf/use-state false)
|
||||
show (deref show*)
|
||||
photo (cf/resolve-profile-photo-url profile)
|
||||
(let [show-profile-menu* (mf/use-state false)
|
||||
show-profile-menu? (deref show-profile-menu*)
|
||||
|
||||
photo
|
||||
(cf/resolve-profile-photo-url profile)
|
||||
|
||||
on-click
|
||||
(mf/use-fn
|
||||
(fn [section event]
|
||||
(dom/stop-propagation event)
|
||||
(reset! show* false)
|
||||
(reset! show-profile-menu* false)
|
||||
(if (keyword? section)
|
||||
(st/emit! (rt/nav section))
|
||||
(st/emit! section))))
|
||||
|
|
@ -917,24 +791,16 @@
|
|||
(mf/use-fn
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(swap! show* not)))
|
||||
(swap! show-profile-menu* not)))
|
||||
|
||||
handle-key-down
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(reset! show* true))))
|
||||
(reset! show-profile-menu* true))))
|
||||
|
||||
on-close
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(reset! show* false))
|
||||
|
||||
handle-key-down-profile
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(on-click :settings-profile event))))
|
||||
(mf/use-fn #(reset! show-profile-menu* false))
|
||||
|
||||
handle-click-url
|
||||
(mf/use-fn
|
||||
|
|
@ -943,40 +809,13 @@
|
|||
(dom/get-data "url"))]
|
||||
(dom/open-new-window url))))
|
||||
|
||||
handle-keydown-url
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(let [url (-> (dom/get-current-target event)
|
||||
(dom/get-data "url"))]
|
||||
(when (kbd/enter? event)
|
||||
(dom/open-new-window url)))))
|
||||
|
||||
handle-show-release-notes
|
||||
(mf/use-fn
|
||||
(mf/deps show-release-notes)
|
||||
(fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(show-release-notes))))
|
||||
|
||||
handle-feedback-click
|
||||
(mf/use-fn #(on-click :settings-feedback %))
|
||||
|
||||
handle-feedback-keydown
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(on-click :settings-feedback event))))
|
||||
|
||||
handle-logout-click
|
||||
(mf/use-fn
|
||||
#(on-click (da/logout) %))
|
||||
|
||||
handle-logout-keydown
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(on-click (da/logout) event))))
|
||||
|
||||
handle-set-profile
|
||||
(mf/use-fn
|
||||
#(on-click :settings-profile %))
|
||||
|
|
@ -997,7 +836,8 @@
|
|||
:on-click on-power-up-click}
|
||||
[:div {:class (stl/css :penpot-free)}
|
||||
[:span (tr "dashboard.upgrade-plan.penpot-free")]
|
||||
[:span {:class (stl/css :no-limits)} (tr "dashboard.upgrade-plan.no-limits")]]
|
||||
[:span {:class (stl/css :no-limits)}
|
||||
(tr "dashboard.upgrade-plan.no-limits")]]
|
||||
[:div {:class (stl/css :power-up)}
|
||||
(tr "subscription.dashboard.upgrade-plan.power-up")]])
|
||||
|
||||
|
|
@ -1020,85 +860,67 @@
|
|||
:alt (:fullname profile)}]
|
||||
[:span {:class (stl/css :profile-fullname)} (:fullname profile)]]
|
||||
|
||||
[:& dropdown-menu {:on-close on-close
|
||||
:show show
|
||||
:list-class (stl/css :profile-dropdown)}
|
||||
[:li {:tab-index (if show "0" "-1")
|
||||
:class (stl/css :profile-dropdown-item)
|
||||
:on-click handle-set-profile
|
||||
:on-key-down handle-key-down-profile
|
||||
:data-testid "profile-profile-opt"}
|
||||
[:> dropdown-menu* {:on-close on-close
|
||||
:show show-profile-menu?
|
||||
:id "profile-menu"
|
||||
:class (stl/css :profile-dropdown)}
|
||||
[:> dropdown-menu-item* {:class (stl/css :profile-dropdown-item)
|
||||
:on-click handle-set-profile
|
||||
:data-testid "profile-profile-opt"}
|
||||
(tr "labels.your-account")]
|
||||
|
||||
[:li {:class (stl/css :profile-separator)}]
|
||||
|
||||
[:li {:class (stl/css :profile-dropdown-item)
|
||||
:tab-index (if show "0" "-1")
|
||||
:data-url "https://help.penpot.app"
|
||||
:on-click handle-click-url
|
||||
:on-key-down handle-keydown-url
|
||||
:data-testid "help-center-profile-opt"}
|
||||
[:> dropdown-menu-item* {:class (stl/css :profile-dropdown-item)
|
||||
:data-url "https://help.penpot.app"
|
||||
:on-click handle-click-url
|
||||
:data-testid "help-center-profile-opt"}
|
||||
(tr "labels.help-center")]
|
||||
|
||||
[:li {:tab-index (if show "0" "-1")
|
||||
:class (stl/css :profile-dropdown-item)
|
||||
:data-url "https://community.penpot.app"
|
||||
:on-click handle-click-url
|
||||
:on-key-down handle-keydown-url}
|
||||
[:> dropdown-menu-item* {:class (stl/css :profile-dropdown-item)
|
||||
:data-url "https://community.penpot.app"
|
||||
:on-click handle-click-url}
|
||||
(tr "labels.community")]
|
||||
|
||||
[:li {:tab-index (if show "0" "-1")
|
||||
:class (stl/css :profile-dropdown-item)
|
||||
:data-url "https://www.youtube.com/c/Penpot"
|
||||
:on-click handle-click-url
|
||||
:on-key-down handle-keydown-url}
|
||||
[:> dropdown-menu-item* {:class (stl/css :profile-dropdown-item)
|
||||
:data-url "https://www.youtube.com/c/Penpot"
|
||||
:on-click handle-click-url}
|
||||
(tr "labels.tutorials")]
|
||||
|
||||
[:li {:tab-index (if show "0" "-1")
|
||||
:class (stl/css :profile-dropdown-item)
|
||||
:on-click show-release-notes
|
||||
:on-key-down handle-show-release-notes}
|
||||
[:> dropdown-menu-item* {:tab-index "0"
|
||||
:class (stl/css :profile-dropdown-item)
|
||||
:on-click show-release-notes}
|
||||
(tr "labels.release-notes")]
|
||||
|
||||
[:li {:class (stl/css :profile-separator)}]
|
||||
|
||||
[:li {:class (stl/css :profile-dropdown-item)
|
||||
:tab-index (if show "0" "-1")
|
||||
:data-url "https://penpot.app/libraries-templates"
|
||||
:on-click handle-click-url
|
||||
:on-key-down handle-keydown-url
|
||||
:data-testid "libraries-templates-profile-opt"}
|
||||
[:> dropdown-menu-item* {:class (stl/css :profile-dropdown-item)
|
||||
:data-url "https://penpot.app/libraries-templates"
|
||||
:on-click handle-click-url
|
||||
:data-testid "libraries-templates-profile-opt"}
|
||||
(tr "labels.libraries-and-templates")]
|
||||
|
||||
[:li {:tab-index (if show "0" "-1")
|
||||
:class (stl/css :profile-dropdown-item)
|
||||
:data-url "https://github.com/penpot/penpot"
|
||||
:on-click handle-click-url
|
||||
:on-key-down handle-keydown-url}
|
||||
[:> dropdown-menu-item* {:class (stl/css :profile-dropdown-item)
|
||||
:data-url "https://github.com/penpot/penpot"
|
||||
:on-click handle-click-url}
|
||||
(tr "labels.github-repo")]
|
||||
|
||||
[:li {:tab-index (if show "0" "-1")
|
||||
:class (stl/css :profile-dropdown-item)
|
||||
:data-url "https://penpot.app/terms"
|
||||
:on-click handle-click-url
|
||||
:on-key-down handle-keydown-url}
|
||||
[:> dropdown-menu-item* {:class (stl/css :profile-dropdown-item)
|
||||
:data-url "https://penpot.app/terms"
|
||||
:on-click handle-click-url}
|
||||
(tr "auth.terms-of-service")]
|
||||
|
||||
[:li {:class (stl/css :profile-separator)}]
|
||||
|
||||
(when (contains? cf/flags :user-feedback)
|
||||
[:li {:class (stl/css :profile-dropdown-item)
|
||||
:tab-index (if show "0" "-1")
|
||||
:on-click handle-feedback-click
|
||||
:on-key-down handle-feedback-keydown
|
||||
:data-testid "feedback-profile-opt"}
|
||||
[:> dropdown-menu-item* {:class (stl/css :profile-dropdown-item)
|
||||
:on-click handle-feedback-click
|
||||
:data-testid "feedback-profile-opt"}
|
||||
(tr "labels.give-feedback")])
|
||||
|
||||
[:li {:class (stl/css :profile-dropdown-item :item-with-icon)
|
||||
:tab-index (if show "0" "-1")
|
||||
:on-click handle-logout-click
|
||||
:on-key-down handle-logout-keydown
|
||||
:data-testid "logout-profile-opt"}
|
||||
[:> dropdown-menu-item* {:class (stl/css :profile-dropdown-item :item-with-icon)
|
||||
:on-click handle-logout-click
|
||||
:data-testid "logout-profile-opt"}
|
||||
exit-icon
|
||||
(tr "labels.logout")]]
|
||||
|
||||
|
|
|
|||
|
|
@ -304,7 +304,7 @@
|
|||
[:div {:class (stl/css :rol-selector)}
|
||||
[:span {:class (stl/css :rol-label)} (tr role)]])
|
||||
|
||||
[:& dropdown {:show @show? :on-close on-hide}
|
||||
[:& dropdown {:show @show? :on-close on-hide :dropdown-id (str "member-role-" (:id member))}
|
||||
[:ul {:class (stl/css :roles-dropdown)
|
||||
:role "listbox"}
|
||||
[:li {:on-click on-set-viewer
|
||||
|
|
@ -341,7 +341,7 @@
|
|||
:on-click on-show}
|
||||
menu-icon]
|
||||
|
||||
[:& dropdown {:show @show? :on-close on-hide}
|
||||
[:& dropdown {:show @show? :on-close on-hide :dropdown-id (str "member-actions-" (:id member))}
|
||||
[:ul {:class (stl/css :actions-dropdown)}
|
||||
(when is-you?
|
||||
[:li {:on-click on-leave
|
||||
|
|
@ -592,7 +592,7 @@
|
|||
[:div {:class (stl/css :rol-selector)}
|
||||
[:span {:class (stl/css :rol-label)} label]])
|
||||
|
||||
[:& dropdown {:show @show? :on-close on-hide}
|
||||
[:& dropdown {:show @show? :on-close on-hide :dropdown-id "invitation-role-selector"}
|
||||
[:ul {:class (stl/css :roles-dropdown)}
|
||||
[:li {:data-role "admin"
|
||||
:class (stl/css :rol-dropdown-item)
|
||||
|
|
@ -692,7 +692,7 @@
|
|||
:on-click on-show}
|
||||
menu-icon]
|
||||
|
||||
[:& dropdown {:show @show? :on-close on-hide}
|
||||
[:& dropdown {:show @show? :on-close on-hide :dropdown-id "invitation-actions"}
|
||||
[:ul {:class (stl/css :actions-dropdown :invitations-dropdown)}
|
||||
[:li {:on-click on-copy
|
||||
:class (stl/css :action-dropdown-item)}
|
||||
|
|
@ -982,7 +982,7 @@
|
|||
[:button {:class (stl/css :menu-btn)
|
||||
:on-click on-show}
|
||||
menu-icon]
|
||||
[:& dropdown {:show @show? :on-close on-hide}
|
||||
[:& dropdown {:show @show? :on-close on-hide :dropdown-id "webhook-actions"}
|
||||
[:ul {:class (stl/css :webhook-actions-dropdown)}
|
||||
[:li {:on-click on-edit
|
||||
:class (stl/css :webhook-dropdown-item)} (tr "labels.edit")]
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
[app.main.features :as features]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.dropdown-menu :refer [dropdown-menu dropdown-menu-item*]]
|
||||
[app.main.ui.components.dropdown-menu :refer [dropdown-menu* dropdown-menu-item*]]
|
||||
[app.main.ui.context :as ctx]
|
||||
[app.main.ui.dashboard.subscription :refer [main-menu-power-up* get-subscription-type]]
|
||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
|
|
@ -93,11 +93,12 @@
|
|||
(st/emit! (modal/show {:type :onboarding}))
|
||||
(st/emit! (modal/show {:type :release-notes :version version}))))))]
|
||||
|
||||
[:& dropdown-menu {:show true
|
||||
:on-close on-close
|
||||
:list-class (stl/css-case :sub-menu true
|
||||
:help-info plugins?
|
||||
:help-info-old (not plugins?))}
|
||||
[:> dropdown-menu* {:show true
|
||||
;; :id "workspace-help-menu"
|
||||
:on-close on-close
|
||||
:class (stl/css-case :sub-menu true
|
||||
:help-info plugins?
|
||||
:help-info-old (not plugins?))}
|
||||
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
|
||||
:on-click nav-to-helpc-center
|
||||
:on-key-down (fn [event]
|
||||
|
|
@ -182,10 +183,11 @@
|
|||
[{:keys [layout profile toggle-flag on-close toggle-theme]}]
|
||||
(let [show-nudge-options (mf/use-fn #(modal/show! {:type :nudge-option}))]
|
||||
|
||||
[:& dropdown-menu {:show true
|
||||
:list-class (stl/css-case :sub-menu true
|
||||
:preferences true)
|
||||
:on-close on-close}
|
||||
[:> dropdown-menu* {:show true
|
||||
;; :id "workspace-preferences-menu"
|
||||
:class (stl/css-case :sub-menu true
|
||||
:preferences true)
|
||||
:on-close on-close}
|
||||
[:> dropdown-menu-item* {:on-click toggle-flag
|
||||
:class (stl/css :submenu-item)
|
||||
:on-key-down (fn [event]
|
||||
|
|
@ -312,10 +314,11 @@
|
|||
(-> (dw/toggle-layout-flag :textpalette)
|
||||
(vary-meta assoc ::ev/origin "workspace-menu")))))]
|
||||
|
||||
[:& dropdown-menu {:show true
|
||||
:list-class (stl/css-case :sub-menu true
|
||||
:view true)
|
||||
:on-close on-close}
|
||||
[:> dropdown-menu* {:show true
|
||||
;; :id "workspace-view-menu"
|
||||
:class (stl/css-case :sub-menu true
|
||||
:view true)
|
||||
:on-close on-close}
|
||||
|
||||
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
|
||||
:on-click toggle-flag
|
||||
|
|
@ -430,10 +433,11 @@
|
|||
perms (mf/use-ctx ctx/permissions)
|
||||
can-edit (:can-edit perms)]
|
||||
|
||||
[:& dropdown-menu {:show true
|
||||
:list-class (stl/css-case :sub-menu true
|
||||
:edit true)
|
||||
:on-close on-close}
|
||||
[:> dropdown-menu* {:show true
|
||||
;; :id "workspace-edit-menu"
|
||||
:class (stl/css-case :sub-menu true
|
||||
:edit true)
|
||||
:on-close on-close}
|
||||
|
||||
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
|
||||
:on-click select-all
|
||||
|
|
@ -592,10 +596,11 @@
|
|||
(when (kbd/enter? event)
|
||||
(on-export-frames event))))]
|
||||
|
||||
[:& dropdown-menu {:show true
|
||||
:list-class (stl/css-case :sub-menu true
|
||||
:file true)
|
||||
:on-close on-close}
|
||||
[:> dropdown-menu* {:show true
|
||||
;; :id "workspace-file-menu"
|
||||
:class (stl/css-case :sub-menu true
|
||||
:file true)
|
||||
:on-close on-close}
|
||||
|
||||
(if ^boolean shared?
|
||||
(when can-edit
|
||||
|
|
@ -690,9 +695,10 @@
|
|||
(let [plugins (preg/plugins-list)
|
||||
user-can-edit? (:can-edit (deref refs/permissions))
|
||||
permissions-peek (deref refs/plugins-permissions-peek)]
|
||||
[:& dropdown-menu {:show true
|
||||
:list-class (stl/css-case :sub-menu true :plugins true)
|
||||
:on-close on-close}
|
||||
[:> dropdown-menu* {:show true
|
||||
;; :id "workspace-plugins-menu"
|
||||
:class (stl/css-case :sub-menu true :plugins true)
|
||||
:on-close on-close}
|
||||
[:> dropdown-menu-item* {:on-click open-plugins
|
||||
:class (stl/css :submenu-item)
|
||||
:on-key-down (fn [event]
|
||||
|
|
@ -840,9 +846,10 @@
|
|||
:on-click open-menu
|
||||
:icon "menu"}]
|
||||
|
||||
[:& dropdown-menu {:show show-menu?
|
||||
:on-close close-menu
|
||||
:list-class (stl/css :menu)}
|
||||
[:> dropdown-menu* {:show show-menu?
|
||||
:id "workspace-menu"
|
||||
:on-close close-menu
|
||||
:class (stl/css :menu)}
|
||||
[:> dropdown-menu-item* {:class (stl/css :menu-item)
|
||||
:on-click on-menu-click
|
||||
:on-key-down (fn [event]
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
[app.config :as cf]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.ui.components.dropdown-menu :refer [dropdown-menu
|
||||
[app.main.ui.components.dropdown-menu :refer [dropdown-menu*
|
||||
dropdown-menu-item*]]
|
||||
[app.main.ui.components.title-bar :refer [title-bar]]
|
||||
[app.main.ui.context :as ctx]
|
||||
|
|
@ -123,9 +123,10 @@
|
|||
:icon "import-export"
|
||||
:variant "secondary"}
|
||||
(tr "workspace.tokens.tools")]
|
||||
[:& dropdown-menu {:show show-menu?
|
||||
:on-close close-menu
|
||||
:list-class (stl/css :import-export-menu)}
|
||||
[:> dropdown-menu* {:show show-menu?
|
||||
:on-close close-menu
|
||||
:id "tokens-menu"
|
||||
:class (stl/css :import-export-menu)}
|
||||
(when can-edit?
|
||||
[:> dropdown-menu-item* {:class (stl/css :import-export-menu-item)
|
||||
:on-click on-modal-show}
|
||||
|
|
|
|||
|
|
@ -469,6 +469,11 @@
|
|||
(when (some? node)
|
||||
(.focus node)))
|
||||
|
||||
(defn click!
|
||||
[^js node]
|
||||
(when (some? node)
|
||||
(.click node)))
|
||||
|
||||
(defn focus?
|
||||
[^js node]
|
||||
(and node
|
||||
|
|
@ -899,4 +904,4 @@
|
|||
;; In theory We could disable this only for the workspace. However gets too unreliable.
|
||||
;; It is better to be safe and disable for the dashboard as well.
|
||||
(set! (.. js/document -documentElement -style -overscrollBehaviorX) "none")
|
||||
(set! (.. js/document -body -style -overscrollBehaviorX) "none"))
|
||||
(set! (.. js/document -body -style -overscrollBehaviorX) "none"))
|
||||
|
|
|
|||
Loading…
Reference in New Issue