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