mirror of
https://github.com/penpot/penpot.git
synced 2025-03-18 10:41:29 -05:00
Merge pull request #2654 from penpot/eva-a11y-dashboard
✨ Improve dashboard accessibility
This commit is contained in:
commit
ac7cb3c8c7
28 changed files with 761 additions and 283 deletions
|
@ -9,6 +9,7 @@
|
|||
height: 100%;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
margin-bottom: 0;
|
||||
|
||||
.grid-row {
|
||||
display: grid;
|
||||
|
@ -26,6 +27,9 @@
|
|||
margin: $size-3 $size-4 $size-4 $size-2;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
a {
|
||||
width: 100%;
|
||||
}
|
||||
@media #{$bp-max-1366} {
|
||||
height: 200px;
|
||||
flex: 1 0 230px;
|
||||
|
@ -193,13 +197,15 @@
|
|||
&.project-th {
|
||||
background-color: $color-white;
|
||||
|
||||
&:hover {
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:focus-within {
|
||||
.project-th-actions {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.selected {
|
||||
.selected {
|
||||
.grid-item-th {
|
||||
border: 2px solid $color-primary;
|
||||
}
|
||||
|
@ -241,7 +247,8 @@
|
|||
width: 18px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&:hover,
|
||||
&:focus {
|
||||
> svg {
|
||||
fill: $color-primary-dark;
|
||||
}
|
||||
|
@ -312,7 +319,7 @@
|
|||
background-repeat: no-repeat;
|
||||
border-top-left-radius: $br-small;
|
||||
border-top-right-radius: $br-small;
|
||||
height: 70%;
|
||||
height: 230px;
|
||||
max-height: 160px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
@ -430,6 +437,7 @@
|
|||
background-color: rgba(227, 227, 227, 0.3);
|
||||
padding: 13px;
|
||||
margin-right: 13px;
|
||||
height: 230px;
|
||||
&.loader {
|
||||
justify-items: center;
|
||||
}
|
||||
|
|
|
@ -75,6 +75,7 @@
|
|||
.dashboard-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 13px;
|
||||
|
||||
h1 {
|
||||
color: $color-black;
|
||||
|
@ -83,7 +84,6 @@
|
|||
font-size: $fs22;
|
||||
font-weight: 600;
|
||||
z-index: 10;
|
||||
margin-left: 13px;
|
||||
}
|
||||
|
||||
.context-menu.is-open {
|
||||
|
|
|
@ -7,13 +7,10 @@
|
|||
.dashboard-sidebar {
|
||||
background-color: $color-white;
|
||||
z-index: 1;
|
||||
|
||||
.sidebar-inside {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
padding-top: $size-2;
|
||||
}
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
padding-top: $size-2;
|
||||
|
||||
.sidebar-content {
|
||||
display: flex;
|
||||
|
@ -67,7 +64,8 @@
|
|||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
svg {
|
||||
width: 15px;
|
||||
height: 13px;
|
||||
|
@ -78,15 +76,18 @@
|
|||
.current-team {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
font-size: $fs14;
|
||||
padding: 0px 10px;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.team-name {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
height: 48px;
|
||||
height: 40px;
|
||||
align-items: center;
|
||||
|
||||
&.action {
|
||||
|
@ -151,7 +152,7 @@
|
|||
.switch-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
justify-content: center;
|
||||
svg {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
|
@ -200,6 +201,9 @@
|
|||
display: flex;
|
||||
flex-shrink: 0;
|
||||
padding: $size-2;
|
||||
a {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: $color-black;
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
border-radius: 8px;
|
||||
min-height: 211px;
|
||||
|
||||
.img {
|
||||
.thumbnail {
|
||||
border-top-left-radius: 6px;
|
||||
border-bottom-left-radius: 6px;
|
||||
padding: 30px;
|
||||
|
@ -133,15 +133,10 @@
|
|||
display: none;
|
||||
width: 0;
|
||||
}
|
||||
.text {
|
||||
.info {
|
||||
// font-size: $fs12;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.walkthrough {
|
||||
.img {
|
||||
.thumbnail {
|
||||
background-image: url("/images/walkthrough-cover.png");
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
|
@ -149,7 +144,7 @@
|
|||
}
|
||||
}
|
||||
.tutorial {
|
||||
.img {
|
||||
.thumbnail {
|
||||
background-image: url("/images/hands-on-tutorial.png");
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
|
@ -258,7 +253,8 @@
|
|||
}
|
||||
|
||||
.project-actions {
|
||||
display: none;
|
||||
display: flex;
|
||||
opacity: 0;
|
||||
margin-left: $size-6;
|
||||
|
||||
.btn-small {
|
||||
|
@ -273,6 +269,8 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 14px;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
svg {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
|
@ -286,9 +284,12 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:focus-within {
|
||||
.project-actions {
|
||||
display: flex;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -439,7 +440,8 @@
|
|||
width: 100%;
|
||||
text-align: right;
|
||||
height: 56px;
|
||||
div {
|
||||
button {
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
height: 58px;
|
||||
display: inline-block;
|
||||
|
@ -518,11 +520,13 @@
|
|||
|
||||
.card-container {
|
||||
width: 275px;
|
||||
height: 100%;
|
||||
margin-top: 20px;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.template-card {
|
||||
|
|
|
@ -38,6 +38,10 @@
|
|||
&:hover {
|
||||
background-color: $color-primary-lighter;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border: 1px black solid;
|
||||
}
|
||||
}
|
||||
|
||||
&.with-check {
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
<body>
|
||||
{{>../public/images/sprites/symbol/icons.svg}}
|
||||
{{>../public/images/sprites/symbol/cursors.svg}}
|
||||
<section id="app" tabindex="1"></section>
|
||||
<div id="app" tabindex="0"></div>
|
||||
<section id="modal"></section>
|
||||
{{# manifest}}
|
||||
<script src="{{& shared}}"></script>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
{{/manifest}}
|
||||
</head>
|
||||
<body>
|
||||
<section id="app" tabindex="1"></section>
|
||||
<div id="app" tabindex="0"></div>
|
||||
{{# manifest}}
|
||||
<script src="{{& shared}}"></script>
|
||||
<script src="{{& render}}"></script>
|
||||
|
|
|
@ -250,20 +250,20 @@
|
|||
(when (contains? @cf/flags :login)
|
||||
[:div.link-entry
|
||||
[:& lk/link {:action #(st/emit! (rt/nav :auth-recovery-request))
|
||||
:name (tr "auth.forgot-password")
|
||||
:data-test "forgot-password"}]])
|
||||
:data-test "forgot-password"}
|
||||
(tr "auth.forgot-password")]])
|
||||
|
||||
(when (contains? @cf/flags :registration)
|
||||
[:div.link-entry
|
||||
[:span (tr "auth.register") " "]
|
||||
[:& lk/link {:action #(st/emit! (rt/nav :auth-register {} params))
|
||||
:name (tr "auth.register-submit")
|
||||
:data-test "register-submit"}]])]
|
||||
:data-test "register-submit"}
|
||||
(tr "auth.register-submit")]])]
|
||||
|
||||
(when (contains? @cf/flags :demo-users)
|
||||
[:div.links.demo
|
||||
[:div.link-entry
|
||||
[:span (tr "auth.create-demo-profile") " "]
|
||||
[:& lk/link {:action #(st/emit! (du/create-demo-profile))
|
||||
:name (tr "auth.create-demo-account")
|
||||
:data-test "demo-account-link"}]]])]])
|
||||
:data-test "demo-account-link"}
|
||||
(tr "auth.create-demo-account")]]])]])
|
||||
|
|
|
@ -101,5 +101,5 @@
|
|||
[:div.links
|
||||
[:div.link-entry
|
||||
[:& lk/link {:action go-back
|
||||
:name (tr "labels.go-back")
|
||||
:data-test "go-back-link"}]]]]]))
|
||||
:data-test "go-back-link"}
|
||||
(tr "labels.go-back")]]]]]))
|
||||
|
|
|
@ -167,14 +167,14 @@
|
|||
[:span (tr "auth.already-have-account") " "]
|
||||
|
||||
[:& lk/link {:action #(st/emit! (rt/nav :auth-login {} params))
|
||||
:name (tr "auth.login-here")
|
||||
:data-test "login-here-link"}]]
|
||||
:data-test "login-here-link"}
|
||||
(tr "auth.login-here")]]
|
||||
|
||||
(when (contains? @cf/flags :demo-users)
|
||||
[:div.link-entry
|
||||
[:span (tr "auth.create-demo-profile") " "]
|
||||
[:& lk/link {:action #(st/emit! (du/create-demo-profile))
|
||||
:name (tr "auth.create-demo-account")}]])]])
|
||||
[:& lk/link {:action #(st/emit! (du/create-demo-profile))}
|
||||
(tr "auth.create-demo-account")]])]])
|
||||
|
||||
;; --- PAGE: register validation
|
||||
|
||||
|
@ -271,8 +271,8 @@
|
|||
|
||||
[:div.links
|
||||
[:div.link-entry
|
||||
[:& lk/link {:action #(st/emit! (rt/nav :auth-register {} {}))
|
||||
:name (tr "labels.go-back")}]]]])
|
||||
[:& lk/link {:action #(st/emit! (rt/nav :auth-register {} {}))}
|
||||
(tr "labels.go-back")]]]])
|
||||
|
||||
(mf/defc register-success-page
|
||||
[{:keys [params] :as props}]
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
(mf/defc button-link [{:keys [action icon name klass]}]
|
||||
[:a.btn-primary.btn-large.button-link
|
||||
{:class klass
|
||||
:tabindex "0"
|
||||
:tab-index "0"
|
||||
:on-click action
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
|
|
121
frontend/src/app/main/ui/components/dropdown_menu.cljs
Normal file
121
frontend/src/app/main/ui/components/dropdown_menu.cljs
Normal file
|
@ -0,0 +1,121 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns app.main.ui.components.dropdown-menu
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.config :as cfg]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.globals :as globals]
|
||||
[app.util.keyboard :as kbd]
|
||||
[goog.events :as events]
|
||||
[goog.object :as gobj]
|
||||
[rumext.v2 :as mf])
|
||||
(:import goog.events.EventType))
|
||||
|
||||
(mf/defc dropdown-menu-item
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
|
||||
(let [children (gobj/get props "children")
|
||||
on-click (gobj/get props "on-click")
|
||||
on-key-down (gobj/get props "on-key-down")
|
||||
id (gobj/get props "id")
|
||||
klass (gobj/get props "klass")
|
||||
key (gobj/get props "klass")
|
||||
data-test (gobj/get props "data-test")]
|
||||
[:li {:id id
|
||||
:class klass
|
||||
:tab-index "0"
|
||||
:on-key-down on-key-down
|
||||
:on-click on-click
|
||||
:key key
|
||||
:role "menuitem"
|
||||
:data-test data-test}
|
||||
children]))
|
||||
|
||||
(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")
|
||||
|
||||
on-click
|
||||
(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)))
|
||||
(if ref
|
||||
(let [parent (mf/ref-val ref)]
|
||||
(when-not (or (not parent) (.contains parent target))
|
||||
(on-close)))
|
||||
(on-close)))))
|
||||
|
||||
on-keyup
|
||||
(fn [event]
|
||||
(when (kbd/esc? event)
|
||||
(on-close)))
|
||||
|
||||
on-key-down
|
||||
(fn [event]
|
||||
(let [first-id (dom/get-element (first ids))
|
||||
first-element (dom/get-element first-id)
|
||||
len (count ids)]
|
||||
|
||||
(when (kbd/home? event)
|
||||
(when first-element
|
||||
(dom/focus! first-element)))
|
||||
|
||||
(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)
|
||||
(nth ids (- actual-index 1)))]
|
||||
(dom/focus! (dom/get-element previous-id))))
|
||||
|
||||
(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)
|
||||
(nth ids (+ 1 actual-index)))]
|
||||
(dom/focus! (dom/get-element next-id))))
|
||||
|
||||
(when (kbd/tab? event)
|
||||
(on-close))))
|
||||
|
||||
on-mount
|
||||
(fn []
|
||||
(let [keys [(events/listen globals/document EventType.CLICK on-click)
|
||||
(events/listen globals/document EventType.CONTEXTMENU on-click)
|
||||
(events/listen globals/document EventType.KEYUP on-keyup)
|
||||
(events/listen globals/document EventType.KEYDOWN on-key-down)]]
|
||||
#(doseq [key keys]
|
||||
(events/unlistenByKey key))))]
|
||||
|
||||
(mf/use-effect on-mount)
|
||||
[:ul {:class list-class
|
||||
:role "menu"}
|
||||
children]))
|
||||
|
||||
(mf/defc dropdown-menu
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(assert (fn? (gobj/get props "on-close")) "missing `on-close` prop")
|
||||
(assert (boolean? (gobj/get props "show")) "missing `show` prop")
|
||||
|
||||
(when (gobj/get props "show")
|
||||
(mf/element dropdown-menu' props)))
|
|
@ -39,5 +39,6 @@
|
|||
:type "file"
|
||||
:ref input-ref
|
||||
:on-change on-files-selected
|
||||
:data-test data-test}]]))
|
||||
:data-test data-test
|
||||
:aria-label "uploader"}]]))
|
||||
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
:placeholder label
|
||||
:on-change on-change
|
||||
:type @type'
|
||||
:tabindex "0")
|
||||
:tab-index "0")
|
||||
(cond-> (and value is-checkbox?) (assoc :default-checked value))
|
||||
(cond-> (and touched? (:message error)) (assoc "aria-invalid" "true"
|
||||
"aria-describedby" (dm/str "error-" input-name)))
|
||||
|
@ -224,7 +224,7 @@
|
|||
{:name "submit"
|
||||
:class (when (or (not (:valid @form)) (true? disabled)) "btn-disabled")
|
||||
:disabled (or (not (:valid @form)) (true? disabled))
|
||||
:tabindex "0"
|
||||
:tab-index "0"
|
||||
:on-click on-click
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
|
|
|
@ -9,12 +9,13 @@
|
|||
[app.util.keyboard :as kbd]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc link [{:keys [action name klass data-test]}]
|
||||
[:a {:on-click action
|
||||
:klass klass
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(action event)))
|
||||
:tabindex "0"
|
||||
:data-test data-test}
|
||||
name])
|
||||
(mf/defc link [{:keys [action klass data-test keyboard-action children]}]
|
||||
(let [keyboard-action (or keyboard-action action)]
|
||||
[:a {:on-click action
|
||||
:class klass
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(keyboard-action event)))
|
||||
:tab-index "0"
|
||||
:data-test data-test}
|
||||
[:* children]]))
|
||||
|
|
|
@ -145,15 +145,20 @@
|
|||
|
||||
[:div.dashboard-templates-section {:class (when collapsed "collapsed")}
|
||||
[:div.title
|
||||
[:div {:on-click toggle-collapse}
|
||||
[:button {:tab-index "0"
|
||||
:on-click toggle-collapse}
|
||||
[:span (tr "dashboard.libraries-and-templates")]
|
||||
[:span.icon (if collapsed i/arrow-up i/arrow-down)]]]
|
||||
[:div.content {:ref content-ref
|
||||
:style {:left @card-offset :width (str container-size "px")}}
|
||||
(for [num-item (range (count templates)) :let [item (nth templates num-item)]]
|
||||
[:div.card-container {:id (str/concat "card-container-" num-item)
|
||||
:key (:id item)
|
||||
:on-click #(import-template item)}
|
||||
[:a.card-container {:tab-index "0"
|
||||
:id (str/concat "card-container-" num-item)
|
||||
:key (:id item)
|
||||
:on-click #(import-template item)
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(import-template item)))}
|
||||
[:div.template-card
|
||||
[:div.img-container
|
||||
[:img {:src (:thumbnail-uri item)
|
||||
|
@ -163,14 +168,16 @@
|
|||
[:div.card-container
|
||||
[:div.template-card
|
||||
[:div.img-container
|
||||
[:a {:href "https://penpot.app/libraries-templates.html" :target "_blank" :on-click handle-template-link}
|
||||
[:a {:tab-index "0"
|
||||
:href "https://penpot.app/libraries-templates.html" :target "_blank" :on-click handle-template-link}
|
||||
[:div.template-link
|
||||
[:div.template-link-title (tr "dashboard.libraries-and-templates")]
|
||||
[:div.template-link-text (tr "dashboard.libraries-and-templates.explore")]]]]]]]
|
||||
(when (< @card-offset 0)
|
||||
[:div.button.left {:on-click move-left} i/go-prev])
|
||||
[:button.button.left {:on-click move-left} i/go-prev])
|
||||
(when more-cards
|
||||
[:div.button.right {:on-click move-right} i/go-next])]))
|
||||
[:button.button.right {:on-click move-right
|
||||
:aria-label (tr "labels.next")} i/go-next])]))
|
||||
|
||||
(mf/defc dashboard-content
|
||||
[{:keys [team projects project section search-term profile] :as props}]
|
||||
|
@ -285,7 +292,7 @@
|
|||
;; components on team change. Many components assumes that the
|
||||
;; team is already set so don't put the team into mf/deps.
|
||||
(when team
|
||||
[:section.dashboard-layout {:key (:id team)}
|
||||
[:main.dashboard-layout {:key (:id team)}
|
||||
[:& sidebar
|
||||
{:team team
|
||||
:projects projects
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
[app.main.ui.icons :as i]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.keyboard :as kbd]
|
||||
[potok.core :as ptk]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
|
@ -54,7 +55,11 @@
|
|||
|
||||
[:div.dashboard-comments-section
|
||||
[:div.button
|
||||
{:on-click show-dropdown
|
||||
{:tab-index "0"
|
||||
:on-click show-dropdown
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(show-dropdown event)))
|
||||
:data-test "open-comments"
|
||||
:class (dom/classnames :open @show-dropdown?
|
||||
:unread (boolean (seq tgroups)))}
|
||||
|
@ -64,7 +69,13 @@
|
|||
[:div.dropdown.comments-section.comment-threads-section.
|
||||
[:div.header
|
||||
[:h3 (tr "labels.comments")]
|
||||
[:span.close {:on-click hide-dropdown} i/close]]
|
||||
[:span.close {:tab-index (if @show-dropdown?
|
||||
"0"
|
||||
"-1")
|
||||
:on-click hide-dropdown
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(hide-dropdown event)))} i/close]]
|
||||
|
||||
[:hr]
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
[app.main.ui.icons :as i]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.keyboard :as kbd]
|
||||
[app.util.webapi :as wapi]
|
||||
[beicon.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
|
@ -63,7 +64,7 @@
|
|||
|
||||
[:header.dashboard-header
|
||||
(if (:is-default project)
|
||||
[:div.dashboard-title
|
||||
[:div.dashboard-title#dashboard-drafts-title
|
||||
[:h1 (tr "labels.drafts")]]
|
||||
|
||||
(if (:edition @local)
|
||||
|
@ -75,7 +76,9 @@
|
|||
(with-meta {::ev/origin "project"}))))
|
||||
(swap! local assoc :edition false)))}]
|
||||
[:div.dashboard-title
|
||||
[:h1 {:on-double-click on-edit :data-test "project-title"}
|
||||
[:h1 {:on-double-click on-edit
|
||||
:data-test "project-title"
|
||||
:id (:id project)}
|
||||
(:name project)]]))
|
||||
|
||||
[:& project-menu {:project project
|
||||
|
@ -87,19 +90,35 @@
|
|||
:on-import on-import}]
|
||||
|
||||
[:div.dashboard-header-actions
|
||||
[:a.btn-secondary.btn-small {:on-click on-create-click :data-test "new-file"}
|
||||
[:a.btn-secondary.btn-small
|
||||
{:tab-index "0"
|
||||
:on-click on-create-click
|
||||
:data-test "new-file"
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(on-create-click event)))}
|
||||
(tr "dashboard.new-file")]
|
||||
|
||||
(when-not (:is-default project)
|
||||
[:div.icon.pin-icon.tooltip.tooltip-bottom
|
||||
{:class (when (:is-pinned project) "active")
|
||||
:on-click toggle-pin :alt (tr "dashboard.pin-unpin")}
|
||||
[:button.icon.pin-icon.tooltip.tooltip-bottom
|
||||
{:tab-index "0"
|
||||
:class (when (:is-pinned project) "active")
|
||||
:on-click toggle-pin
|
||||
:alt (tr "dashboard.pin-unpin")
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(toggle-pin event)))}
|
||||
(if (:is-pinned project)
|
||||
i/pin-fill
|
||||
i/pin)])
|
||||
|
||||
[:div.icon.tooltip.tooltip-bottom-left
|
||||
{:on-click on-menu-click :alt (tr "dashboard.options")}
|
||||
{:tab-index "0"
|
||||
:on-click on-menu-click
|
||||
:alt (tr "dashboard.options")
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(on-menu-click event)))}
|
||||
i/actions]]]))
|
||||
|
||||
(mf/defc files-section
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
(use-set-page-title team section)
|
||||
|
||||
[:header.dashboard-header
|
||||
[:div.dashboard-title
|
||||
[:div.dashboard-title#dashboard-fonts-title
|
||||
[:h1 (tr "labels.fonts")]]
|
||||
[:nav
|
||||
#_[:ul
|
||||
|
@ -134,8 +134,9 @@
|
|||
[:& i18n/tr-html {:tag-name "span"
|
||||
:label "dashboard.fonts.hero-text2"}]]]]
|
||||
|
||||
[:div.btn-primary
|
||||
{:on-click on-click}
|
||||
[:button.btn-primary
|
||||
{:on-click on-click
|
||||
:tab-index "0"}
|
||||
[:span (tr "labels.add-custom-font")]
|
||||
[:& file-uploader {:input-id "font-upload"
|
||||
:accept cm/str-font-types
|
||||
|
|
|
@ -192,7 +192,7 @@
|
|||
|
||||
on-menu-close
|
||||
(mf/use-fn
|
||||
#(swap! local assoc :menu-open false))
|
||||
#(swap! local assoc :menu-open false))
|
||||
|
||||
on-select
|
||||
(fn [event]
|
||||
|
@ -215,31 +215,31 @@
|
|||
|
||||
on-drag-start
|
||||
(mf/use-fn
|
||||
(mf/deps selected-files)
|
||||
(fn [event]
|
||||
(let [offset (dom/get-offset-position (.-nativeEvent event))
|
||||
(mf/deps selected-files)
|
||||
(fn [event]
|
||||
(let [offset (dom/get-offset-position (.-nativeEvent event))
|
||||
|
||||
select-current? (not (contains? selected-files (:id file)))
|
||||
select-current? (not (contains? selected-files (:id file)))
|
||||
|
||||
item-el (mf/ref-val node-ref)
|
||||
counter-el (create-counter-element item-el
|
||||
(if select-current?
|
||||
1
|
||||
(count selected-files)))]
|
||||
(when select-current?
|
||||
(st/emit! (dd/clear-selected-files))
|
||||
(st/emit! (dd/toggle-file-select file)))
|
||||
item-el (mf/ref-val node-ref)
|
||||
counter-el (create-counter-element item-el
|
||||
(if select-current?
|
||||
1
|
||||
(count selected-files)))]
|
||||
(when select-current?
|
||||
(st/emit! (dd/clear-selected-files))
|
||||
(st/emit! (dd/toggle-file-select file)))
|
||||
|
||||
(dnd/set-data! event "penpot/files" "dummy")
|
||||
(dnd/set-allowed-effect! event "move")
|
||||
(dnd/set-data! event "penpot/files" "dummy")
|
||||
(dnd/set-allowed-effect! event "move")
|
||||
|
||||
;; set-drag-image requires that the element is rendered and
|
||||
;; visible to the user at the moment of creating the ghost
|
||||
;; image (to make a snapshot), but you may remove it right
|
||||
;; afterwards, in the next render cycle.
|
||||
(dom/append-child! item-el counter-el)
|
||||
(dnd/set-drag-image! event item-el (:x offset) (:y offset))
|
||||
(ts/raf #(.removeChild ^js item-el counter-el)))))
|
||||
(dom/append-child! item-el counter-el)
|
||||
(dnd/set-drag-image! event item-el (:x offset) (:y offset))
|
||||
(ts/raf #(.removeChild ^js item-el counter-el)))))
|
||||
|
||||
on-menu-click
|
||||
(mf/use-fn
|
||||
|
@ -276,44 +276,60 @@
|
|||
(when (and (not selected?) (:menu-open @local))
|
||||
(swap! local assoc :menu-open false)))
|
||||
|
||||
[:div.grid-item.project-th
|
||||
{:class (dom/classnames :selected selected?
|
||||
:library library-view?)
|
||||
:ref node-ref
|
||||
:draggable true
|
||||
:on-click on-select
|
||||
:on-double-click on-navigate
|
||||
:on-drag-start on-drag-start
|
||||
:on-context-menu on-menu-click}
|
||||
[:li.grid-item.project-th
|
||||
[:a
|
||||
{:tab-index "0"
|
||||
:class (dom/classnames :selected selected?
|
||||
:library library-view?)
|
||||
:ref node-ref
|
||||
:draggable true
|
||||
:on-click on-select
|
||||
:on-key-down (fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(when (kbd/enter? event)
|
||||
(on-navigate event))
|
||||
(when (kbd/shift? event)
|
||||
(when (or (kbd/down-arrow? event) (kbd/left-arrow? event) (kbd/up-arrow? event) (kbd/right-arrow? event))
|
||||
(on-select event)) ;; TODO Fix this
|
||||
))
|
||||
:on-double-click on-navigate
|
||||
:on-drag-start on-drag-start
|
||||
:on-context-menu on-menu-click}
|
||||
|
||||
[:div.overlay]
|
||||
(if library-view?
|
||||
[:& grid-item-library {:file file}]
|
||||
[:& grid-item-thumbnail {:file file}])
|
||||
(when (and (:is-shared file) (not library-view?))
|
||||
[:div.item-badge i/library])
|
||||
[:div.item-info
|
||||
(if (:edition @local)
|
||||
[:& inline-edition {:content (:name file)
|
||||
:on-end edit}]
|
||||
[:h3 (:name file)])
|
||||
[:& grid-item-metadata {:modified-at (:modified-at file)}]]
|
||||
[:div.project-th-actions {:class (dom/classnames
|
||||
:force-display (:menu-open @local))}
|
||||
[:div.project-th-icon.menu
|
||||
{:ref menu-ref
|
||||
:on-click on-menu-click}
|
||||
i/actions
|
||||
(when selected?
|
||||
[:& file-menu {:files (vals selected-files)
|
||||
:show? (:menu-open @local)
|
||||
:left (+ 24 (:x (:menu-pos @local)))
|
||||
:top (:y (:menu-pos @local))
|
||||
:navigate? navigate?
|
||||
:on-edit on-edit
|
||||
:on-menu-close on-menu-close
|
||||
:origin origin
|
||||
:dashboard-local dashboard-local}])]]]))
|
||||
[:div.overlay]
|
||||
(if library-view?
|
||||
[:& grid-item-library {:file file}]
|
||||
[:& grid-item-thumbnail {:file file}])
|
||||
(when (and (:is-shared file) (not library-view?))
|
||||
[:div.item-badge i/library])
|
||||
[:div.item-info
|
||||
(if (:edition @local)
|
||||
[:& inline-edition {:content (:name file)
|
||||
:on-end edit}]
|
||||
[:h3 (:name file)])
|
||||
[:& grid-item-metadata {:modified-at (:modified-at file)}]]
|
||||
[:div.project-th-actions {:class (dom/classnames
|
||||
:force-display (:menu-open @local))}
|
||||
[:div.project-th-icon.menu
|
||||
{:tab-index "0"
|
||||
:ref menu-ref
|
||||
:on-click on-menu-click
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(prn "entro en on-menu-click")
|
||||
(on-menu-click event)))}
|
||||
i/actions
|
||||
(when selected?
|
||||
[:& file-menu {:files (vals selected-files)
|
||||
:show? (:menu-open @local)
|
||||
:left (+ 24 (:x (:menu-pos @local)))
|
||||
:top (:y (:menu-pos @local))
|
||||
:navigate? navigate?
|
||||
:on-edit on-edit
|
||||
:on-menu-close on-menu-close
|
||||
:origin origin
|
||||
:dashboard-local dashboard-local}])]]]]
|
||||
))
|
||||
|
||||
|
||||
(mf/defc grid
|
||||
|
@ -361,7 +377,7 @@
|
|||
(reset! dragging? false)
|
||||
(import-files (.-files (.-dataTransfer e))))))]
|
||||
|
||||
[:section.dashboard-grid
|
||||
[:div.dashboard-grid
|
||||
{:on-drag-enter on-drag-enter
|
||||
:on-drag-over on-drag-over
|
||||
:on-drag-leave on-drag-leave
|
||||
|
@ -372,11 +388,11 @@
|
|||
[:& loading-placeholder]
|
||||
|
||||
(seq files)
|
||||
[:div.grid-row
|
||||
[:ul.grid-row
|
||||
{:style {:grid-template-columns (str "repeat(" limit ", 1fr)")}}
|
||||
|
||||
(when @dragging?
|
||||
[:div.grid-item])
|
||||
[:li.grid-item])
|
||||
|
||||
(for [item files]
|
||||
[:& grid-item
|
||||
|
@ -396,11 +412,11 @@
|
|||
[{:keys [files selected-files dragging? limit] :as props}]
|
||||
(let [elements limit
|
||||
limit (if dragging? (dec limit) limit)]
|
||||
[:div.grid-row.no-wrap
|
||||
[:ul.grid-row.no-wrap
|
||||
{:style {:grid-template-columns (dm/str "repeat(" elements ", 1fr)")}}
|
||||
|
||||
(when dragging?
|
||||
[:div.grid-item.dragged])
|
||||
[:li.grid-item.dragged])
|
||||
(for [item (take limit files)]
|
||||
[:& grid-item
|
||||
{:id (:id item)
|
||||
|
@ -481,10 +497,10 @@
|
|||
mdata {:on-success on-drop-success}]
|
||||
(st/emit! (dd/move-files (with-meta data mdata))))))))]
|
||||
|
||||
[:section.dashboard-grid {:on-drag-enter on-drag-enter
|
||||
:on-drag-over on-drag-over
|
||||
:on-drag-leave on-drag-leave
|
||||
:on-drop on-drop}
|
||||
[:div.dashboard-grid {:on-drag-enter on-drag-enter
|
||||
:on-drag-over on-drag-over
|
||||
:on-drag-leave on-drag-leave
|
||||
:on-drop on-drop}
|
||||
(cond
|
||||
(nil? files)
|
||||
[:& loading-placeholder]
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
[{:keys [project-id on-finish-import]} external-ref]
|
||||
|
||||
(let [on-file-selected (use-import-file project-id on-finish-import)]
|
||||
[:form.import-file
|
||||
[:form.import-file {:aria-hidden "true"}
|
||||
[:& file-uploader {:accept ".penpot,.zip"
|
||||
:multi true
|
||||
:ref external-ref
|
||||
|
|
|
@ -71,7 +71,7 @@
|
|||
|
||||
[:*
|
||||
[:header.dashboard-header {:ref rowref}
|
||||
[:div.dashboard-title
|
||||
[:div.dashboard-title#dashboard-libraries-title
|
||||
[:h1 (tr "dashboard.libraries-title")]]]
|
||||
[:section.dashboard-container.no-bg.dashboard-shared
|
||||
[:& grid {:files files
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
(create-fn "dashboard:empty-folder-placeholder")))]
|
||||
(cond
|
||||
(true? dragging?)
|
||||
[:div.grid-row.no-wrap
|
||||
[:ul.grid-row.no-wrap
|
||||
{:style {:grid-template-columns (str "repeat(" limit ", 1fr)")}}
|
||||
[:div.grid-item]]
|
||||
[:li.grid-item]]
|
||||
|
||||
(= :libraries origin)
|
||||
[:div.grid-empty-placeholder.libs {:data-test "empty-placeholder"}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
[app.main.ui.icons :as i]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.keyboard :as kbd]
|
||||
[app.util.router :as rt]
|
||||
[app.util.time :as dt]
|
||||
[app.util.webapi :as wapi]
|
||||
|
@ -35,9 +36,9 @@
|
|||
[]
|
||||
(let [on-click (mf/use-fn #(st/emit! (dd/create-project)))]
|
||||
[:header.dashboard-header
|
||||
[:div.dashboard-title
|
||||
[:div.dashboard-title#dashboard-projects-title
|
||||
[:h1 (tr "dashboard.projects-title")]]
|
||||
[:a.btn-secondary.btn-small
|
||||
[:button.btn-secondary.btn-small
|
||||
{:on-click on-click
|
||||
:data-test "new-project-button"}
|
||||
(tr "dashboard.new-project")]]))
|
||||
|
@ -112,11 +113,11 @@
|
|||
(swap! state #(assoc % :status :importing))
|
||||
(st/emit! (with-meta (dd/clone-template (with-meta params mdata))
|
||||
{::ev/origin "get-started-hero-block"})))))]
|
||||
[:div.tutorial
|
||||
[:div.img]
|
||||
[:article.tutorial
|
||||
[:div.thumbnail]
|
||||
[:div.text
|
||||
[:div.title (tr "dasboard.tutorial-hero.title")]
|
||||
[:div.info (tr "dasboard.tutorial-hero.info")]
|
||||
[:h2.title (tr "dasboard.tutorial-hero.title")]
|
||||
[:p.info (tr "dasboard.tutorial-hero.info")]
|
||||
[:button.btn-primary.action {:on-click download-tutorial}
|
||||
(case (:status @state)
|
||||
:waiting (tr "dasboard.tutorial-hero.start")
|
||||
|
@ -136,12 +137,15 @@
|
|||
(st/emit! (ptk/event ::ev/event {::ev/name "show-walkthrough"
|
||||
::ev/origin "get-started-hero-block"
|
||||
:section "dashboard"})))]
|
||||
[:div.walkthrough
|
||||
[:div.img]
|
||||
[:article.walkthrough
|
||||
[:div.thumbnail]
|
||||
[:div.text
|
||||
[:div.title (tr "dasboard.walkthrough-hero.title")]
|
||||
[:div.info (tr "dasboard.walkthrough-hero.info")]
|
||||
[:a.btn-primary.action {:href " https://design.penpot.app/walkthrough" :target "_blank" :on-click handle-walkthrough-link}
|
||||
[:h2.title (tr "dasboard.walkthrough-hero.title")]
|
||||
[:p.info (tr "dasboard.walkthrough-hero.info")]
|
||||
[:a.btn-primary.action
|
||||
{:href " https://design.penpot.app/walkthrough"
|
||||
:target "_blank"
|
||||
:on-click handle-walkthrough-link}
|
||||
(tr "dasboard.walkthrough-hero.start")]]
|
||||
[:button.close
|
||||
{:on-click close-walkthrough
|
||||
|
@ -165,8 +169,8 @@
|
|||
width (mf/use-state nil)
|
||||
rowref (mf/use-ref)
|
||||
itemsize (if (>= @width 1030)
|
||||
280
|
||||
230)
|
||||
280
|
||||
230)
|
||||
|
||||
ratio (if (some? @width) (/ @width itemsize) 0)
|
||||
nitems (mth/floor ratio)
|
||||
|
@ -256,9 +260,9 @@
|
|||
(vreset! mnt? false)
|
||||
(rx/dispose! sub))))
|
||||
|
||||
[:div.dashboard-project-row
|
||||
[:article.dashboard-project-row
|
||||
{:class (when first? "first")}
|
||||
[:div.project {:ref rowref}
|
||||
[:header.project {:ref rowref}
|
||||
[:div.project-name-wrapper
|
||||
(if (:edition? @local)
|
||||
[:& inline-edition {:content (:name project)
|
||||
|
@ -285,23 +289,39 @@
|
|||
[:span.recent-files-row-title-info (str ", " time)]))
|
||||
[:div.project-actions
|
||||
(when-not (:is-default project)
|
||||
[:span.pin-icon.tooltip.tooltip-bottom
|
||||
[:button.pin-icon.tooltip.tooltip-bottom
|
||||
{:class (when (:is-pinned project) "active")
|
||||
:on-click toggle-pin :alt (tr "dashboard.pin-unpin")}
|
||||
:on-click toggle-pin
|
||||
:alt (tr "dashboard.pin-unpin")
|
||||
:aria-label (tr "dashboard.pin-unpin")
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(toggle-pin event)))
|
||||
:tab-index "0"}
|
||||
(if (:is-pinned project)
|
||||
i/pin-fill
|
||||
i/pin)])
|
||||
|
||||
[:a.btn-secondary.btn-small.tooltip.tooltip-bottom
|
||||
[:button.btn-secondary.btn-small.tooltip.tooltip-bottom
|
||||
{:on-click on-create-click
|
||||
:alt (tr "dashboard.new-file")
|
||||
:data-test "project-new-file"}
|
||||
:aria-label (tr "dashboard.new-file")
|
||||
:data-test "project-new-file"
|
||||
:tab-index "0"
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(on-create-click event)))}
|
||||
i/close]
|
||||
|
||||
[:a.btn-secondary.btn-small.tooltip.tooltip-bottom
|
||||
[:button.btn-secondary.btn-small.tooltip.tooltip-bottom
|
||||
{:on-click on-menu-click
|
||||
:alt (tr "dashboard.options")
|
||||
:data-test "project-options"}
|
||||
:aria-label (tr "dashboard.options")
|
||||
:data-test "project-options"
|
||||
:tab-index "0"
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(on-menu-click event)))}
|
||||
i/actions]]]
|
||||
|
||||
(when (and (> limit 0)
|
||||
|
@ -387,7 +407,7 @@
|
|||
[:& interface-walkthrough
|
||||
{:close-walkthrough close-walkthrough}])])
|
||||
|
||||
[:section.dashboard-container.no-bg
|
||||
[:div.dashboard-container.no-bg
|
||||
(for [{:keys [id] :as project} projects]
|
||||
(let [files (when recent-map
|
||||
(->> (vals recent-map)
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
(rx/dispose! sub)))))
|
||||
[:*
|
||||
[:header.dashboard-header
|
||||
[:div.dashboard-title
|
||||
[:div.dashboard-title#dashboard-search-title
|
||||
[:h1 (tr "dashboard.title-search")]]]
|
||||
|
||||
[:section.dashboard-container.search.no-bg {:ref rowref}
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
[app.main.data.users :as du]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||
[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-section]]
|
||||
[app.main.ui.dashboard.inline-edition :refer [inline-edition]]
|
||||
[app.main.ui.dashboard.project-menu :refer [project-menu]]
|
||||
|
@ -29,6 +30,7 @@
|
|||
[app.util.keyboard :as kbd]
|
||||
[app.util.object :as obj]
|
||||
[app.util.router :as rt]
|
||||
[app.util.timers :as ts]
|
||||
[beicon.core :as rx]
|
||||
[cljs.spec.alpha :as s]
|
||||
[goog.functions :as f]
|
||||
|
@ -54,6 +56,20 @@
|
|||
(fn []
|
||||
(st/emit! (dd/go-to-files (:id item)))))
|
||||
|
||||
on-key-down
|
||||
(mf/use-callback
|
||||
(mf/deps item)
|
||||
(fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(st/emit! (dd/go-to-files (:id item))
|
||||
(ts/schedule-on-idle
|
||||
(fn []
|
||||
(let [project-title (dom/get-element (str (:id item)))]
|
||||
(when project-title
|
||||
(dom/set-attribute! project-title "tabindex" "0")
|
||||
(dom/focus! project-title)
|
||||
(dom/set-attribute! project-title "tabindex" "-1")))))))))
|
||||
|
||||
on-menu-click
|
||||
(mf/use-callback
|
||||
(fn [event]
|
||||
|
@ -79,25 +95,25 @@
|
|||
|
||||
on-drag-enter
|
||||
(mf/use-callback
|
||||
(mf/deps selected-project)
|
||||
(fn [e]
|
||||
(when (dnd/has-type? e "penpot/files")
|
||||
(dom/prevent-default e)
|
||||
(when-not (dnd/from-child? e)
|
||||
(when (not= selected-project (:id item))
|
||||
(swap! local assoc :dragging? true))))))
|
||||
(mf/deps selected-project)
|
||||
(fn [e]
|
||||
(when (dnd/has-type? e "penpot/files")
|
||||
(dom/prevent-default e)
|
||||
(when-not (dnd/from-child? e)
|
||||
(when (not= selected-project (:id item))
|
||||
(swap! local assoc :dragging? true))))))
|
||||
|
||||
on-drag-over
|
||||
(mf/use-callback
|
||||
(fn [e]
|
||||
(when (dnd/has-type? e "penpot/files")
|
||||
(dom/prevent-default e))))
|
||||
(fn [e]
|
||||
(when (dnd/has-type? e "penpot/files")
|
||||
(dom/prevent-default e))))
|
||||
|
||||
on-drag-leave
|
||||
(mf/use-callback
|
||||
(fn [e]
|
||||
(when-not (dnd/from-child? e)
|
||||
(swap! local assoc :dragging? false))))
|
||||
(fn [e]
|
||||
(when-not (dnd/from-child? e)
|
||||
(swap! local assoc :dragging? false))))
|
||||
|
||||
on-drop-success
|
||||
(mf/use-callback
|
||||
|
@ -117,9 +133,11 @@
|
|||
(st/emit! (dd/move-files (with-meta data mdata)))))))]
|
||||
|
||||
[:*
|
||||
[:li {:class (if selected? "current"
|
||||
(when (:dragging? @local) "dragging"))
|
||||
[:li {:tab-index "0"
|
||||
:class (if selected? "current"
|
||||
(when (:dragging? @local) "dragging"))
|
||||
:on-click on-click
|
||||
:on-key-down on-key-down
|
||||
:on-double-click on-edit-open
|
||||
:on-context-menu on-menu-click
|
||||
:on-drag-enter on-drag-enter
|
||||
|
@ -143,15 +161,6 @@
|
|||
focused? (mf/use-state false)
|
||||
emit! (mf/use-memo #(f/debounce st/emit! 500))
|
||||
|
||||
on-search-focus
|
||||
(mf/use-callback
|
||||
(mf/deps team-id)
|
||||
(fn [event]
|
||||
(reset! focused? true)
|
||||
(let [value (dom/get-target-val event)]
|
||||
(dom/select-text! (dom/get-target event))
|
||||
(emit! (dd/go-to-search value)))))
|
||||
|
||||
on-search-blur
|
||||
(mf/use-callback
|
||||
(fn [_]
|
||||
|
@ -167,16 +176,25 @@
|
|||
on-clear-click
|
||||
(mf/use-callback
|
||||
(mf/deps team-id)
|
||||
(fn [_]
|
||||
(fn [e]
|
||||
(let [search-input (dom/get-element "search-input")]
|
||||
(dom/clean-value! search-input)
|
||||
(dom/focus! search-input)
|
||||
(emit! (dd/go-to-search)))))
|
||||
(emit! (dd/go-to-search))
|
||||
(dom/prevent-default e)
|
||||
(dom/stop-propagation e))))
|
||||
|
||||
on-key-press
|
||||
(mf/use-callback
|
||||
(fn [e]
|
||||
(when (kbd/enter? e)
|
||||
(ts/schedule-on-idle
|
||||
(fn []
|
||||
(let [search-title (dom/get-element (str "dashboard-search-title"))]
|
||||
(when search-title
|
||||
(dom/set-attribute! search-title "tabindex" "0")
|
||||
(dom/focus! search-title)
|
||||
(dom/set-attribute! search-title "tabindex" "-1")))))
|
||||
(dom/prevent-default e)
|
||||
(dom/stop-propagation e))))]
|
||||
|
||||
|
@ -185,10 +203,11 @@
|
|||
{:key "images-search-box"
|
||||
:id "search-input"
|
||||
:type "text"
|
||||
:aria-label (tr "dashboard.search-placeholder")
|
||||
:placeholder (tr "dashboard.search-placeholder")
|
||||
:default-value search-term
|
||||
:auto-complete "off"
|
||||
:on-focus on-search-focus
|
||||
;; :on-focus on-search-focus
|
||||
:on-blur on-search-blur
|
||||
:on-change on-search-change
|
||||
:on-key-press on-key-press
|
||||
|
@ -196,18 +215,20 @@
|
|||
|
||||
(if (or @focused? (seq search-term))
|
||||
[:div.clear-search
|
||||
{:on-click on-clear-click}
|
||||
{:tab-index "0"
|
||||
:on-click on-clear-click
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(on-clear-click event)))}
|
||||
i/close]
|
||||
|
||||
[:div.search
|
||||
{:on-click on-clear-click}
|
||||
i/search])]))
|
||||
|
||||
(mf/defc teams-selector-dropdown
|
||||
[{:keys [team profile] :as props}]
|
||||
(let [teams (mf/deref refs/teams)
|
||||
|
||||
on-create-clicked
|
||||
(mf/defc teams-selector-dropdown-items
|
||||
[{:keys [team profile teams] :as props}]
|
||||
(let [on-create-clicked
|
||||
(mf/use-callback
|
||||
#(st/emit! (modal/show :team-form {})))
|
||||
|
||||
|
@ -216,24 +237,40 @@
|
|||
(fn [team-id]
|
||||
(st/emit! (dd/go-to-projects team-id))))]
|
||||
|
||||
[:ul.dropdown.teams-dropdown
|
||||
[:li.team-name {:on-click (partial team-selected (:default-team-id profile))}
|
||||
[:*
|
||||
[:& dropdown-menu-item {:on-click (partial team-selected (:default-team-id profile))
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(team-selected (:default-team-id profile) event)))
|
||||
:id "teams-selector-default-team"
|
||||
:klass "team-name"}
|
||||
[:span.team-icon i/logo-icon]
|
||||
[:span.team-text (tr "dashboard.your-penpot")]
|
||||
(when (= (:default-team-id profile) (:id team))
|
||||
[:span.icon i/tick])]
|
||||
|
||||
(for [team-item (remove :is-default (vals teams))]
|
||||
[:li.team-name {:on-click (partial team-selected (:id team-item))
|
||||
:key (dm/str (:id team-item))}
|
||||
[:& dropdown-menu-item {:on-click (partial team-selected (:id team-item))
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(team-selected (:id team-item) event)))
|
||||
:id (str "teams-selector-" (:id team-item))
|
||||
:klass "team-name"
|
||||
:key (dm/str (:id team-item))}
|
||||
[:span.team-icon
|
||||
[:img {:src (cf/resolve-team-photo-url team-item)
|
||||
:alt (:name team-item)}]]
|
||||
[:span.team-text {:title (:name team-item)} (:name team-item)]
|
||||
(when (= (:id team-item) (:id team))
|
||||
[:span.icon i/tick])])
|
||||
[:hr]
|
||||
[:li.team-name.action {:on-click on-create-clicked :data-test "create-new-team"}
|
||||
[:hr {:role "separator"}]
|
||||
[:& dropdown-menu-item {:on-click on-create-clicked
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(on-create-clicked event)))
|
||||
:id "teams-selector-create-team"
|
||||
:klass "team-name action"
|
||||
:key "teams-selector-create-team"}
|
||||
[:span.team-icon.new-team i/close]
|
||||
[:span.team-text (tr "dashboard.create-new-team")]]]))
|
||||
|
||||
|
@ -288,11 +325,11 @@
|
|||
|
||||
on-leave-clicked
|
||||
#(st/emit! (modal/show
|
||||
{:type :confirm
|
||||
:title (tr "modals.leave-confirm.title")
|
||||
:message (tr "modals.leave-confirm.message")
|
||||
:accept-label (tr "modals.leave-confirm.accept")
|
||||
:on-accept leave-fn}))
|
||||
{:type :confirm
|
||||
:title (tr "modals.leave-confirm.title")
|
||||
:message (tr "modals.leave-confirm.message")
|
||||
:accept-label (tr "modals.leave-confirm.accept")
|
||||
:on-accept leave-fn}))
|
||||
|
||||
on-leave-as-owner-clicked
|
||||
(fn []
|
||||
|
@ -305,55 +342,145 @@
|
|||
|
||||
leave-and-close
|
||||
#(st/emit! (modal/show
|
||||
{:type :confirm
|
||||
:title (tr "modals.leave-confirm.title")
|
||||
:message (tr "modals.leave-and-close-confirm.message" (:name team))
|
||||
:scd-message (tr "modals.leave-and-close-confirm.hint")
|
||||
:accept-label (tr "modals.leave-confirm.accept")
|
||||
:on-accept delete-fn}))
|
||||
{:type :confirm
|
||||
:title (tr "modals.leave-confirm.title")
|
||||
:message (tr "modals.leave-and-close-confirm.message" (:name team))
|
||||
:scd-message (tr "modals.leave-and-close-confirm.hint")
|
||||
:accept-label (tr "modals.leave-confirm.accept")
|
||||
:on-accept delete-fn}))
|
||||
|
||||
on-delete-clicked
|
||||
#(st/emit!
|
||||
(modal/show
|
||||
{:type :confirm
|
||||
: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}))]
|
||||
(modal/show
|
||||
{:type :confirm
|
||||
: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}))]
|
||||
|
||||
[:*
|
||||
[:& dropdown-menu-item {:on-click go-members
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(go-members)))
|
||||
:id "teams-options-members"
|
||||
:key "teams-options-members"
|
||||
:data-test "team-members"}
|
||||
(tr "labels.members")]
|
||||
[:& dropdown-menu-item {:on-click go-invitations
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(go-invitations)))
|
||||
:id "teams-options-invitations"
|
||||
:key "teams-options-invitations"
|
||||
:data-test "team-invitations"}
|
||||
(tr "labels.invitations")]
|
||||
|
||||
[:ul.dropdown.options-dropdown
|
||||
[:li {:on-click go-members :data-test "team-members"} (tr "labels.members")]
|
||||
[:li {:on-click go-invitations :data-test "team-invitations"} (tr "labels.invitations")]
|
||||
(when (contains? @cf/flags :webhooks)
|
||||
[:li {:on-click go-webhooks :data-test "team-webhooks"} (tr "labels.webhooks")])
|
||||
[:li {:on-click go-settings :data-test "team-settings"} (tr "labels.settings")]
|
||||
[:& dropdown-menu-item {:on-click go-webhooks
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(go-webhooks)))
|
||||
:id "teams-options-webhooks"
|
||||
:key "teams-options-webhooks"}
|
||||
(tr "labels.webhooks")])
|
||||
|
||||
[:& dropdown-menu-item {:on-click go-settings
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(go-settings)))
|
||||
:id "teams-options-settings"
|
||||
:key "teams-options-settings"
|
||||
:data-test "team-settings"}
|
||||
(tr "labels.settings")]
|
||||
|
||||
[:hr]
|
||||
(when can-rename?
|
||||
[:li {:on-click on-rename-clicked :data-test "rename-team"} (tr "labels.rename")])
|
||||
[:& dropdown-menu-item {:on-click on-rename-clicked
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(on-rename-clicked)))
|
||||
:id "teams-options-rename"
|
||||
:key "teams-options-rename"
|
||||
:data-test "rename-team"}
|
||||
(tr "labels.rename")])
|
||||
|
||||
(cond
|
||||
(= (count members) 1)
|
||||
[:li {:on-click leave-and-close} (tr "dashboard.leave-team")]
|
||||
[:& dropdown-menu-item {:on-click leave-and-close
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(leave-and-close)))
|
||||
:id "teams-options-leave-team"
|
||||
:key "teams-options-leave-team"}
|
||||
(tr "dashboard.leave-team")]
|
||||
|
||||
|
||||
(get-in team [:permissions :is-owner])
|
||||
[:li {:on-click on-leave-as-owner-clicked :data-test "leave-team"} (tr "dashboard.leave-team")]
|
||||
[:& dropdown-menu-item {:on-click on-leave-as-owner-clicked
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(on-leave-as-owner-clicked)))
|
||||
:id "teams-options-leave-team"
|
||||
:key "teams-options-leave-team"
|
||||
:data-test "leave-team"}
|
||||
(tr "dashboard.leave-team")]
|
||||
|
||||
(> (count members) 1)
|
||||
[:li {:on-click on-leave-clicked} (tr "dashboard.leave-team")])
|
||||
[:& dropdown-menu-item {:on-click on-leave-clicked
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(on-leave-clicked)))
|
||||
:id "teams-options-leave-team"
|
||||
:key "teams-options-leave-team"}
|
||||
(tr "dashboard.leave-team")])
|
||||
|
||||
|
||||
(when (get-in team [:permissions :is-owner])
|
||||
[:li.warning {:on-click on-delete-clicked :data-test "delete-team"} (tr "dashboard.delete-team")])]))
|
||||
[:& dropdown-menu-item {:on-click on-delete-clicked
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(on-delete-clicked)))
|
||||
:id "teams-options-delete-team"
|
||||
:key "teams-options-delete-team"
|
||||
:klass "warning"
|
||||
:data-test "delete-team"}
|
||||
(tr "dashboard.delete-team")])]))
|
||||
|
||||
|
||||
(mf/defc sidebar-team-switch
|
||||
[{:keys [team profile] :as props}]
|
||||
(let [show-team-opts-ddwn? (mf/use-state false)
|
||||
show-teams-ddwn? (mf/use-state false)]
|
||||
(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")]]
|
||||
|
||||
[:div.sidebar-team-switch
|
||||
[:div.switch-content
|
||||
[:div.current-team {:on-click #(reset! show-teams-ddwn? true)}
|
||||
[:button.current-team {:tab-index "0"
|
||||
:on-click #(reset! show-teams-ddwn? true)
|
||||
:on-key-down (fn [event]
|
||||
(when (or (kbd/space? event) (kbd/enter? event))
|
||||
(dom/prevent-default event)
|
||||
(reset! show-teams-ddwn? true)
|
||||
(ts/schedule-on-idle
|
||||
(fn []
|
||||
(let [first-element (dom/get-element (first ids))]
|
||||
(when first-element
|
||||
(dom/focus! first-element)))))))}
|
||||
(if (:is-default team)
|
||||
[:div.team-name
|
||||
[:span.team-icon i/logo-icon]
|
||||
|
@ -368,17 +495,33 @@
|
|||
i/arrow-down]]
|
||||
|
||||
(when-not (:is-default team)
|
||||
[:div.switch-options {:on-click #(reset! show-team-opts-ddwn? true)}
|
||||
[:button.switch-options {:on-click #(reset! show-team-opts-ddwn? true)
|
||||
:tab-index "0"
|
||||
:on-key-down (fn [event]
|
||||
(when (or (kbd/space? event) (kbd/enter? event))
|
||||
(dom/prevent-default event)
|
||||
(reset! show-team-opts-ddwn? true)
|
||||
(ts/schedule-on-idle
|
||||
(fn []
|
||||
(let [first-element (dom/get-element (first options-ids))]
|
||||
(when first-element
|
||||
(dom/focus! first-element)))))))}
|
||||
i/actions])]
|
||||
|
||||
;; Teams Dropdown
|
||||
[:& dropdown {:show @show-teams-ddwn?
|
||||
:on-close #(reset! show-teams-ddwn? false)}
|
||||
[:& teams-selector-dropdown {:team team
|
||||
:profile profile}]]
|
||||
[:& dropdown-menu {:show @show-teams-ddwn?
|
||||
:on-close #(reset! show-teams-ddwn? false)
|
||||
:ids ids
|
||||
:list-class "dropdown teams-dropdown"}
|
||||
[:& teams-selector-dropdown-items {:ids ids
|
||||
:team team
|
||||
:profile profile
|
||||
:teams teams}]]
|
||||
|
||||
[:& dropdown {:show @show-team-opts-ddwn?
|
||||
:on-close #(reset! show-team-opts-ddwn? false)}
|
||||
[:& dropdown-menu {:show @show-team-opts-ddwn?
|
||||
:on-close #(reset! show-team-opts-ddwn? false)
|
||||
:ids options-ids
|
||||
:list-class "dropdown options-dropdown"}
|
||||
[:& team-options-dropdown {:team team
|
||||
:profile profile}]]]))
|
||||
|
||||
|
@ -400,11 +543,34 @@
|
|||
(mf/deps team)
|
||||
#(st/emit! (rt/nav :dashboard-projects {:team-id (:id team)})))
|
||||
|
||||
go-projects-with-key
|
||||
(mf/use-callback
|
||||
(mf/deps team)
|
||||
#(st/emit! (rt/nav :dashboard-projects {:team-id (:id team)})
|
||||
(ts/schedule-on-idle
|
||||
(fn []
|
||||
(let [projects-title (dom/get-element "dashboard-projects-title")]
|
||||
(when projects-title
|
||||
(dom/set-attribute! projects-title "tabindex" "0")
|
||||
(dom/focus! projects-title)
|
||||
(dom/set-attribute! projects-title "tabindex" "-1")))))))
|
||||
|
||||
go-fonts
|
||||
(mf/use-callback
|
||||
(mf/deps team)
|
||||
#(st/emit! (rt/nav :dashboard-fonts {:team-id (:id team)})))
|
||||
|
||||
go-fonts-with-key
|
||||
(mf/use-callback
|
||||
(mf/deps team)
|
||||
#(st/emit! (rt/nav :dashboard-fonts {:team-id (:id team)})
|
||||
(ts/schedule-on-idle
|
||||
(fn []
|
||||
(let [font-title (dom/get-element "dashboard-fonts-title")]
|
||||
(when font-title
|
||||
(dom/set-attribute! font-title "tabindex" "0")
|
||||
(dom/focus! font-title)
|
||||
(dom/set-attribute! font-title "tabindex" "-1")))))))
|
||||
go-drafts
|
||||
(mf/use-callback
|
||||
(mf/deps team default-project-id)
|
||||
|
@ -412,11 +578,36 @@
|
|||
(st/emit! (rt/nav :dashboard-files
|
||||
{:team-id (:id team)
|
||||
:project-id default-project-id}))))
|
||||
|
||||
go-drafts-with-key
|
||||
(mf/use-callback
|
||||
(mf/deps team default-project-id)
|
||||
#(st/emit! (rt/nav :dashboard-files {:team-id (:id team)
|
||||
:project-id default-project-id})
|
||||
(ts/schedule-on-idle
|
||||
(fn []
|
||||
(let [drafts-title (dom/get-element "dashboard-drafts-title")]
|
||||
(when drafts-title
|
||||
(dom/set-attribute! drafts-title "tabindex" "0")
|
||||
(dom/focus! drafts-title)
|
||||
(dom/set-attribute! drafts-title "tabindex" "-1")))))))
|
||||
|
||||
go-libs
|
||||
(mf/use-callback
|
||||
(mf/deps team)
|
||||
#(st/emit! (rt/nav :dashboard-libraries {:team-id (:id team)})))
|
||||
|
||||
go-libs-with-key
|
||||
(mf/use-callback
|
||||
(mf/deps team)
|
||||
#(st/emit! (rt/nav :dashboard-libraries {:team-id (:id team)})
|
||||
(ts/schedule-on-idle
|
||||
(fn []
|
||||
(let [libs-title (dom/get-element "dashboard-libraries-title")]
|
||||
(when libs-title
|
||||
(dom/set-attribute! libs-title "tabindex" "0")
|
||||
(dom/focus! libs-title)
|
||||
(dom/set-attribute! libs-title "tabindex" "-1")))))))
|
||||
pinned-projects
|
||||
(->> (vals projects)
|
||||
(remove :is-default)
|
||||
|
@ -430,28 +621,32 @@
|
|||
[:div.sidebar-content-section
|
||||
[:ul.sidebar-nav.no-overflow
|
||||
[:li.recent-projects
|
||||
{:on-click go-projects
|
||||
:class-name (when projects? "current")}
|
||||
[:span.element-title (tr "labels.projects")]]
|
||||
{:class-name (when projects? "current")}
|
||||
[:& link {:action go-projects
|
||||
:keyboard-action go-projects-with-key}
|
||||
[:span.element-title (tr "labels.projects")]]]
|
||||
|
||||
[:li {:on-click go-drafts
|
||||
:class-name (when drafts? "current")}
|
||||
[:span.element-title (tr "labels.drafts")]]
|
||||
[:li {:class-name (when drafts? "current")}
|
||||
[:& link {:action go-drafts
|
||||
:keyboard-action go-drafts-with-key}
|
||||
[:span.element-title (tr "labels.drafts")]]]
|
||||
|
||||
|
||||
[:li {:on-click go-libs
|
||||
:class-name (when libs? "current")}
|
||||
[:span.element-title (tr "labels.shared-libraries")]]]]
|
||||
[:li {:class-name (when libs? "current")}
|
||||
[:& link {:action go-libs
|
||||
:keyboard-action go-libs-with-key}
|
||||
[:span.element-title (tr "labels.shared-libraries")]]]]]
|
||||
|
||||
[:hr]
|
||||
|
||||
[:div.sidebar-content-section
|
||||
[:ul.sidebar-nav.no-overflow
|
||||
[:li
|
||||
{:on-click go-fonts
|
||||
:data-test "fonts"
|
||||
:class-name (when fonts? "current")}
|
||||
[:span.element-title (tr "labels.fonts")]]]]
|
||||
[:li {:class-name (when fonts? "current")}
|
||||
|
||||
[:& link {:action go-fonts
|
||||
:keyboard-action go-fonts-with-key
|
||||
:data-test "fonts"}
|
||||
[:span.element-title (tr "labels.fonts")]]]]]
|
||||
|
||||
[:hr]
|
||||
[:div.sidebar-content-section {:data-test "pinned-projects"}
|
||||
|
@ -492,42 +687,106 @@
|
|||
(st/emit! (modal/show {:type :release-notes :version version}))))))]
|
||||
|
||||
[:div.profile-section
|
||||
[:div.profile {:on-click #(reset! show true)
|
||||
[:div.profile {:tab-index "0"
|
||||
:on-click #(reset! show true)
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(reset! show true)))
|
||||
:data-test "profile-btn"}
|
||||
[:img {:src photo
|
||||
:alt (:fullname profile)}]
|
||||
[:span (:fullname profile)]]
|
||||
|
||||
[:& dropdown {:on-close #(reset! show false)
|
||||
:show @show}
|
||||
[:& dropdown-menu {:on-close #(reset! show false)
|
||||
:show @show}
|
||||
[:ul.dropdown
|
||||
[:li {:on-click (partial on-click :settings-profile)
|
||||
[:li {:tab-index (if show
|
||||
"0"
|
||||
"-1")
|
||||
:on-click (partial on-click :settings-profile)
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(on-click :settings-profile event)))
|
||||
:data-test "profile-profile-opt"}
|
||||
[:span.text (tr "labels.your-account")]]
|
||||
[:li.separator {:on-click #(dom/open-new-window "https://help.penpot.app")
|
||||
[:li.separator {:tab-index (if show
|
||||
"0"
|
||||
"-1")
|
||||
:on-click #(dom/open-new-window "https://help.penpot.app")
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(dom/open-new-window "https://help.penpot.app")))
|
||||
:data-test "help-center-profile-opt"}
|
||||
[:span.text (tr "labels.help-center")]]
|
||||
[:li {:on-click #(dom/open-new-window "https://community.penpot.app")}
|
||||
[:li {:tab-index (if show
|
||||
"0"
|
||||
"-1")
|
||||
:on-click #(dom/open-new-window "https://community.penpot.app")
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(dom/open-new-window "https://community.penpot.app")))}
|
||||
[:span.text (tr "labels.community")]]
|
||||
[:li {:on-click #(dom/open-new-window "https://www.youtube.com/c/Penpot")}
|
||||
[:li {:tab-index (if show
|
||||
"0"
|
||||
"-1")
|
||||
:on-click #(dom/open-new-window "https://www.youtube.com/c/Penpot")
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(dom/open-new-window "https://www.youtube.com/c/Penpot")))}
|
||||
[:span.text (tr "labels.tutorials")]]
|
||||
[:li {:on-click show-release-notes}
|
||||
[:li {:tab-index (if show
|
||||
"0"
|
||||
"-1")
|
||||
:on-click show-release-notes
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(show-release-notes)))}
|
||||
[:span (tr "labels.release-notes")]]
|
||||
|
||||
[:li.separator {:on-click #(dom/open-new-window "https://penpot.app/libraries-templates.html")
|
||||
[:li.separator {:tab-index (if show
|
||||
"0"
|
||||
"-1")
|
||||
:on-click #(dom/open-new-window "https://penpot.app/libraries-templates.html")
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(dom/open-new-window "https://penpot.app/libraries-templates.html")))
|
||||
:data-test "libraries-templates-profile-opt"}
|
||||
[:span.text (tr "labels.libraries-and-templates")]]
|
||||
[:li {:on-click #(dom/open-new-window "https://github.com/penpot/penpot")}
|
||||
[:li {:tab-index (if show
|
||||
"0"
|
||||
"-1")
|
||||
:on-click #(dom/open-new-window "https://github.com/penpot/penpot")
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(dom/open-new-window "https://github.com/penpot/penpot")))}
|
||||
[:span (tr "labels.github-repo")]]
|
||||
[:li {:on-click #(dom/open-new-window "https://penpot.app/terms.html")}
|
||||
[:li {:tab-index (if show
|
||||
"0"
|
||||
"-1")
|
||||
:on-click #(dom/open-new-window "https://penpot.app/terms.html")
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(dom/open-new-window "https://penpot.app/terms.html")))}
|
||||
[:span (tr "auth.terms-of-service")]]
|
||||
|
||||
(when (contains? @cf/flags :user-feedback)
|
||||
[:li.separator {:on-click (partial on-click :settings-feedback)
|
||||
[:li.separator {:tab-index (if show
|
||||
"0"
|
||||
"-1")
|
||||
:on-click (partial on-click :settings-feedback)
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(on-click :settings-feedback event)))
|
||||
:data-test "feedback-profile-opt"}
|
||||
[:span.text (tr "labels.give-feedback")]])
|
||||
|
||||
[:li.separator {:on-click #(on-click (du/logout) %)
|
||||
[:li.separator {:tab-index (if show
|
||||
"0"
|
||||
"-1")
|
||||
:on-click #(on-click (du/logout) %)
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(on-click (du/logout) event)))
|
||||
:data-test "logout-profile-opt"}
|
||||
[:span.icon i/exit]
|
||||
[:span.text (tr "labels.logout")]]]]
|
||||
|
@ -542,9 +801,8 @@
|
|||
[props]
|
||||
(let [team (obj/get props "team")
|
||||
profile (obj/get props "profile")]
|
||||
[:div.dashboard-sidebar
|
||||
[:div.sidebar-inside
|
||||
[:> sidebar-content props]
|
||||
[:& profile-section
|
||||
{:profile profile
|
||||
:team team}]]]))
|
||||
[:nav.dashboard-sidebar
|
||||
[:> sidebar-content props]
|
||||
[:& profile-section
|
||||
{:profile profile
|
||||
:team team}]]))
|
||||
|
|
|
@ -101,8 +101,7 @@
|
|||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [profile locale section]}]
|
||||
[:div.dashboard-sidebar.settings
|
||||
[:div.sidebar-inside
|
||||
[:& sidebar-content {:profile profile
|
||||
:section section}]
|
||||
[:& profile-section {:profile profile
|
||||
:locale locale}]]])
|
||||
[:& sidebar-content {:profile profile
|
||||
:section section}]
|
||||
[:& profile-section {:profile profile
|
||||
:locale locale}]])
|
||||
|
|
|
@ -40,11 +40,15 @@
|
|||
(def space? (is-key? " "))
|
||||
(def up-arrow? (is-key? "ArrowUp"))
|
||||
(def down-arrow? (is-key? "ArrowDown"))
|
||||
(def left-arrow? (is-key? "ArrowLeft"))
|
||||
(def right-arrow? (is-key? "ArrowRight"))
|
||||
(def alt-key? (is-key? "Alt"))
|
||||
(def ctrl-key? (is-key? "Control"))
|
||||
(def meta-key? (is-key? "Meta"))
|
||||
(def comma? (is-key? ","))
|
||||
(def backspace? (is-key? "Backspace"))
|
||||
(def home? (is-key? "Home"))
|
||||
(def tab? (is-key? "Tab"))
|
||||
|
||||
(defn editing? [e]
|
||||
(.-editing ^js e))
|
||||
|
|
Loading…
Add table
Reference in a new issue