mirror of
https://github.com/penpot/penpot.git
synced 2025-03-15 17:21:17 -05:00
Merge pull request #2145 from penpot/palba-templates-carousel
🎉 Add Libraries & Templates carousel
This commit is contained in:
commit
5b5fe8ebbc
16 changed files with 456 additions and 43 deletions
|
@ -8,6 +8,7 @@
|
|||
- Add cosmetic changes in viewer mode [Taiga #3688](https://tree.taiga.io/project/penpot/us/3688)
|
||||
- Outline highlights on layer hovering [Taiga #2645](https://tree.taiga.io/project/penpot/us/2645) by @andrewzhurov
|
||||
- Add zoom to shape on double click up on its icon [Taiga #3929](https://tree.taiga.io/project/penpot/us/3929) by @andrewzhurov
|
||||
- Add Libraries & Templates carousel [Taiga #3860](https://tree.taiga.io/project/penpot/us/3860)
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
|
|
|
@ -400,4 +400,4 @@
|
|||
|
||||
(sv/defmethod ::retrieve-list-of-builtin-templates
|
||||
[cfg _params]
|
||||
(mapv #(select-keys % [:id :name]) (:templates cfg)))
|
||||
(mapv #(select-keys % [:id :name :thumbnail-uri]) (:templates cfg)))
|
||||
|
|
1
frontend/resources/images/icons/arrow-up.svg
Normal file
1
frontend/resources/images/icons/arrow-up.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 132 132"><path d="m132 95-4 4-4 4-29-29-29-29-29 29-29 29-4-4-4-4 33-33 33-33 33 33Z"/></svg>
|
After Width: | Height: | Size: 146 B |
|
@ -33,6 +33,7 @@
|
|||
.dashboard-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.verify-token {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
.dashboard-sidebar {
|
||||
background-color: $color-white;
|
||||
z-index: 1;
|
||||
|
||||
.sidebar-inside {
|
||||
display: flex;
|
||||
|
|
|
@ -254,3 +254,155 @@
|
|||
height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-templates-section {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 285px;
|
||||
transition: bottom 300ms;
|
||||
|
||||
&.collapsed {
|
||||
bottom: -228px;
|
||||
transition: bottom 300ms;
|
||||
}
|
||||
|
||||
.title {
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
height: 56px;
|
||||
cursor: pointer;
|
||||
div {
|
||||
height: 58px;
|
||||
display: inline-block;
|
||||
line-height: 58px;
|
||||
text-align: center;
|
||||
border-top: 2px solid #e4e4e4;
|
||||
border-left: 2px solid #e4e4e4;
|
||||
border-right: 2px solid #e4e4e4;
|
||||
border-top-left-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
margin-right: 30px;
|
||||
background-color: $color-white;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
span {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
line-height: normal;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: $color-black;
|
||||
margin-left: 20px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
svg {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
position: absolute;
|
||||
top: 133px;
|
||||
border: 2px solid #e0e4e9;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
line-height: 35px;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
cursor: pointer;
|
||||
background-color: $color-white;
|
||||
|
||||
svg {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
&.left {
|
||||
left: 0;
|
||||
margin-left: 43px;
|
||||
}
|
||||
|
||||
&.right {
|
||||
right: 0;
|
||||
margin-right: 43px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border: 2px solid $color-primary;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
background-color: $color-white;
|
||||
width: 200%;
|
||||
height: 229px;
|
||||
border-top: 2px solid #e4e4e4;
|
||||
border-left: 2px solid #e4e4e4;
|
||||
margin-left: 5px;
|
||||
position: absolute;
|
||||
|
||||
.card-container {
|
||||
width: 275px;
|
||||
height: 100%;
|
||||
margin-top: 20px;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.template-card {
|
||||
display: inline-block;
|
||||
width: 255px;
|
||||
font-size: 16px;
|
||||
color: #181a22;
|
||||
cursor: pointer;
|
||||
.img-container {
|
||||
width: 100%;
|
||||
height: 135px;
|
||||
margin-bottom: 15px;
|
||||
border-radius: 5px;
|
||||
border: 2px solid #e0e4e9;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.card-name {
|
||||
padding: 0 5px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 23px;
|
||||
svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.template-link {
|
||||
border: 2px solid transparent;
|
||||
margin: 30px;
|
||||
padding: 32px 0;
|
||||
}
|
||||
|
||||
.template-link-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: $color-gray-60;
|
||||
}
|
||||
|
||||
.template-link-text {
|
||||
font-size: 12px;
|
||||
color: $color-gray-30;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.img-container {
|
||||
border: 2px solid $color-primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
|
||||
(declare fetch-projects)
|
||||
(declare fetch-team-members)
|
||||
(declare fetch-builtin-templates)
|
||||
|
||||
(defn initialize
|
||||
[{:keys [id] :as params}]
|
||||
|
@ -87,7 +88,8 @@
|
|||
(ptk/watch (fetch-projects) state stream)
|
||||
(ptk/watch (fetch-team-members) state stream)
|
||||
(ptk/watch (du/fetch-teams) state stream)
|
||||
(ptk/watch (du/fetch-users {:team-id id}) state stream)))))
|
||||
(ptk/watch (du/fetch-users {:team-id id}) state stream)
|
||||
(ptk/watch (fetch-builtin-templates) state stream)))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Data Fetching (context aware: current team)
|
||||
|
@ -293,6 +295,24 @@
|
|||
(->> (rp/query :team-recent-files {:team-id team-id})
|
||||
(rx/map recent-files-fetched)))))))
|
||||
|
||||
|
||||
;; --- EVENT: fetch-team-invitations
|
||||
|
||||
(defn builtin-templates-fetched
|
||||
[libraries]
|
||||
(ptk/reify ::libraries-fetched
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc state :builtin-templates libraries))))
|
||||
|
||||
(defn fetch-builtin-templates
|
||||
[]
|
||||
(ptk/reify ::fetch-builtin-templates
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(->> (rp/command :retrieve-list-of-builtin-templates)
|
||||
(rx/map builtin-templates-fetched)))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Data Selection
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -795,6 +815,27 @@
|
|||
(rx/tap on-success)
|
||||
(rx/catch on-error))))))
|
||||
|
||||
|
||||
;; --- EVENT: clone-template
|
||||
(defn clone-template
|
||||
[{:keys [template-id project-id] :as params}]
|
||||
(us/assert ::us/uuid project-id)
|
||||
(ptk/reify ::clone-template
|
||||
IDeref
|
||||
(-deref [_]
|
||||
{:template-id template-id
|
||||
:project-id project-id})
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(let [{:keys [on-success on-error]
|
||||
:or {on-success identity
|
||||
on-error rx/throw}} (meta params)]
|
||||
|
||||
(->> (rp/command! :clone-template {:project-id project-id :template-id template-id})
|
||||
(rx/tap on-success)
|
||||
(rx/catch on-error))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Navigation
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
|
@ -95,6 +95,7 @@
|
|||
(derive :app.main.data.dashboard/set-file-shared ::generic-action)
|
||||
(derive :app.main.data.dashboard/update-team-member-role ::generic-action)
|
||||
(derive :app.main.data.dashboard/update-team-photo ::generic-action)
|
||||
(derive :app.main.data.dashboard/clone-template ::generic-action)
|
||||
(derive :app.main.data.fonts/add-font ::generic-action)
|
||||
(derive :app.main.data.fonts/delete-font ::generic-action)
|
||||
(derive :app.main.data.fonts/delete-font-variant ::generic-action)
|
||||
|
|
|
@ -7,9 +7,13 @@
|
|||
(ns app.main.ui.dashboard
|
||||
(:require
|
||||
[app.common.colors :as clr]
|
||||
[app.common.data :as d]
|
||||
[app.common.spec :as us]
|
||||
[app.main.data.dashboard :as dd]
|
||||
[app.main.data.dashboard.shortcuts :as sc]
|
||||
[app.main.data.events :as ev]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.users :as du]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.context :as ctx]
|
||||
|
@ -23,9 +27,16 @@
|
|||
[app.main.ui.dashboard.sidebar :refer [sidebar]]
|
||||
[app.main.ui.dashboard.team :refer [team-settings-page team-members-page team-invitations-page]]
|
||||
[app.main.ui.hooks :as hooks]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[app.util.keyboard :as kbd]
|
||||
[app.util.object :as obj]
|
||||
[app.util.router :as rt]
|
||||
[cuerdas.core :as str]
|
||||
[goog.events :as events]
|
||||
[okulary.core :as l]
|
||||
[potok.core :as ptk]
|
||||
[rumext.alpha :as mf])
|
||||
(:import goog.events.EventType))
|
||||
|
||||
|
@ -48,40 +59,180 @@
|
|||
(uuid-str? project-id)
|
||||
(assoc :project-id (uuid project-id)))))
|
||||
|
||||
(def builtin-templates
|
||||
(l/derived :builtin-templates st/state))
|
||||
|
||||
(mf/defc templates-section
|
||||
[{:keys [default-project-id profile project team content-width] :as props}]
|
||||
(let [templates (mf/deref builtin-templates)
|
||||
route (mf/deref refs/route)
|
||||
route-name (get-in route [:data :name])
|
||||
section (if (= route-name :dashboard-files)
|
||||
(if (= (:id project) default-project-id)
|
||||
"dashboard-drafts"
|
||||
"dashboard-project")
|
||||
(name route-name))
|
||||
props (some-> profile (get :props {}))
|
||||
collapsed (:builtin-templates-collapsed-status props false)
|
||||
card-offset (mf/use-state 0)
|
||||
|
||||
card-width 275
|
||||
num-cards (count templates)
|
||||
;; We need space for num-cards plus the libraries&templates link
|
||||
more-cards (> (+ @card-offset (* (+ 1 num-cards) card-width)) content-width)
|
||||
content-ref (mf/use-ref)
|
||||
|
||||
toggle-collapse
|
||||
(fn []
|
||||
(st/emit!
|
||||
(du/update-profile-props {:builtin-templates-collapsed-status (not collapsed)})))
|
||||
|
||||
move-left
|
||||
(fn []
|
||||
(when-not (zero? @card-offset)
|
||||
(dom/animate! (mf/ref-val content-ref)
|
||||
[#js {:left (str @card-offset "px")}
|
||||
#js {:left (str (+ @card-offset card-width) "px")}]
|
||||
#js {:duration 200
|
||||
:easing "linear"})
|
||||
(reset! card-offset (+ @card-offset card-width))))
|
||||
|
||||
move-right
|
||||
(fn []
|
||||
(when more-cards (swap! card-offset inc)
|
||||
(dom/animate! (mf/ref-val content-ref)
|
||||
[#js {:left (str @card-offset "px")}
|
||||
#js {:left (str (- @card-offset card-width) "px")}]
|
||||
#js {:duration 200
|
||||
:easing "linear"})
|
||||
(reset! card-offset (- @card-offset card-width))))
|
||||
|
||||
on-finish-import
|
||||
(fn [template]
|
||||
(st/emit!
|
||||
(ptk/event ::ev/event {::ev/name "import-template-finish"
|
||||
::ev/origin "dashboard"
|
||||
:template (:name template)
|
||||
:section section})
|
||||
(when (not (some? project)) (rt/nav :dashboard-files
|
||||
{:team-id (:id team)
|
||||
:project-id default-project-id}))))
|
||||
|
||||
import-template
|
||||
(fn [template]
|
||||
(let [templates-project-id (if project (:id project) default-project-id)]
|
||||
(st/emit!
|
||||
(ptk/event ::ev/event {::ev/name "import-template-launch"
|
||||
::ev/origin "dashboard"
|
||||
:template (:name template)
|
||||
:section section})
|
||||
|
||||
(modal/show
|
||||
{:type :import
|
||||
:project-id templates-project-id
|
||||
:files []
|
||||
:template template
|
||||
:on-finish-import (partial on-finish-import template)}))))
|
||||
|
||||
handle-template-link
|
||||
(fn []
|
||||
(st/emit! (ptk/event ::ev/event {::ev/name "explore-libraries-click"
|
||||
::ev/origin "dashboard"
|
||||
:section section})))]
|
||||
|
||||
[:div.dashboard-templates-section {:class (when collapsed "collapsed")}
|
||||
[:div.title {:on-click toggle-collapse}
|
||||
[:div
|
||||
[: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}}
|
||||
(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)}
|
||||
[:div.template-card
|
||||
[:div.img-container
|
||||
[:img {:src (:thumbnail-uri item)}]]
|
||||
[:div.card-name [:span (:name item)] [:span.icon i/download]]]])
|
||||
|
||||
[:div.card-container
|
||||
[:div.template-card
|
||||
[:div.img-container
|
||||
[:a {: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])
|
||||
(when more-cards
|
||||
[:div.button.right {:on-click move-right} i/go-next])]))
|
||||
|
||||
(mf/defc dashboard-content
|
||||
[{:keys [team projects project section search-term profile] :as props}]
|
||||
[:div.dashboard-content {:on-click #(st/emit! (dd/clear-selected-files))}
|
||||
(case section
|
||||
:dashboard-projects
|
||||
[:& projects-section {:team team :projects projects}]
|
||||
(let [container (mf/use-ref)
|
||||
content-width (mf/use-state 0)
|
||||
default-project-id
|
||||
(->> (vals projects)
|
||||
(d/seek :is-default)
|
||||
(:id))
|
||||
on-resize
|
||||
(fn [_]
|
||||
(let [dom (mf/ref-val container)
|
||||
width (obj/get dom "clientWidth")]
|
||||
(reset! content-width width)))]
|
||||
|
||||
:dashboard-fonts
|
||||
[:& fonts-page {:team team}]
|
||||
(mf/use-effect
|
||||
#(let [key1 (events/listen js/window "resize" on-resize)]
|
||||
(fn []
|
||||
(events/unlistenByKey key1))))
|
||||
|
||||
:dashboard-font-providers
|
||||
[:& font-providers-page {:team team}]
|
||||
(mf/use-effect on-resize)
|
||||
[:div.dashboard-content {:on-click #(st/emit! (dd/clear-selected-files)) :ref container}
|
||||
(case section
|
||||
:dashboard-projects
|
||||
[:*
|
||||
[:& projects-section {:team team :projects projects}]
|
||||
[:& templates-section {:profile profile
|
||||
:project project
|
||||
:default-project-id default-project-id
|
||||
:team team
|
||||
:content-width @content-width}]]
|
||||
|
||||
:dashboard-files
|
||||
(when project
|
||||
[:& files-section {:team team :project project}])
|
||||
:dashboard-fonts
|
||||
[:& fonts-page {:team team}]
|
||||
|
||||
:dashboard-search
|
||||
[:& search-page {:team team
|
||||
:search-term search-term}]
|
||||
:dashboard-font-providers
|
||||
[:& font-providers-page {:team team}]
|
||||
|
||||
:dashboard-libraries
|
||||
[:& libraries-page {:team team}]
|
||||
:dashboard-files
|
||||
(when project
|
||||
[:*
|
||||
[:& files-section {:team team :project project}]
|
||||
[:& templates-section {:profile profile
|
||||
:project project
|
||||
:default-project-id default-project-id
|
||||
:team team
|
||||
:content-width @content-width}]])
|
||||
|
||||
:dashboard-team-members
|
||||
[:& team-members-page {:team team :profile profile}]
|
||||
:dashboard-search
|
||||
[:& search-page {:team team
|
||||
:search-term search-term}]
|
||||
|
||||
:dashboard-team-invitations
|
||||
[:& team-invitations-page {:team team}]
|
||||
:dashboard-libraries
|
||||
[*
|
||||
[:& libraries-page {:team team}]]
|
||||
|
||||
:dashboard-team-settings
|
||||
[:& team-settings-page {:team team :profile profile}]
|
||||
:dashboard-team-members
|
||||
[:& team-members-page {:team team :profile profile}]
|
||||
|
||||
nil)])
|
||||
:dashboard-team-invitations
|
||||
[:& team-invitations-page {:team team}]
|
||||
|
||||
:dashboard-team-settings
|
||||
[:& team-settings-page {:team team :profile profile}]
|
||||
|
||||
nil)]))
|
||||
|
||||
(mf/defc dashboard
|
||||
[{:keys [route profile] :as props}]
|
||||
|
|
|
@ -118,7 +118,7 @@
|
|||
(dom/prevent-default event)
|
||||
(st/emit! (with-meta (dd/create-file {:project-id (:id project)})
|
||||
{::ev/origin origin}))))]
|
||||
|
||||
|
||||
(mf/use-effect
|
||||
(fn []
|
||||
(let [node (mf/ref-val rowref)
|
||||
|
@ -134,7 +134,7 @@
|
|||
(fn []
|
||||
(vreset! mnt? false)
|
||||
(rx/dispose! sub)))))
|
||||
|
||||
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps project)
|
||||
|
@ -152,7 +152,7 @@
|
|||
(dd/clear-selected-files))))
|
||||
|
||||
[:*
|
||||
[:& header {:team team :project project
|
||||
[:& header {:team team :project project
|
||||
:on-create-clicked on-create-clicked}]
|
||||
[:section.dashboard-container.no-bg {:ref rowref}
|
||||
[:& grid {:project project
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.logging :as log]
|
||||
[app.main.data.dashboard :as dd]
|
||||
[app.main.data.events :as ev]
|
||||
[app.main.data.messages :as dm]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.file-uploader :refer [file-uploader]]
|
||||
|
@ -236,10 +238,11 @@
|
|||
(mf/defc import-dialog
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :import}
|
||||
[{:keys [project-id files on-finish-import]}]
|
||||
[{:keys [project-id files template on-finish-import]}]
|
||||
(let [state (mf/use-state
|
||||
{:status :analyzing
|
||||
:editing nil
|
||||
:importing-templates 0
|
||||
:files (->> files
|
||||
(mapv #(assoc % :status :analyzing)))})
|
||||
|
||||
|
@ -278,19 +281,50 @@
|
|||
(dom/prevent-default event)
|
||||
(st/emit! (modal/hide)))))
|
||||
|
||||
on-template-cloned-success
|
||||
(fn []
|
||||
(swap! state
|
||||
(fn [state]
|
||||
(-> state
|
||||
(assoc :status :importing :importing-templates 0))))
|
||||
(st/emit! (dd/fetch-recent-files)))
|
||||
|
||||
on-template-cloned-error
|
||||
(fn []
|
||||
(st/emit!
|
||||
(modal/hide)
|
||||
(dm/error (tr "dashboard.libraries-and-templates.import-error"))))
|
||||
|
||||
continue-files
|
||||
(fn []
|
||||
(let [files (->> @state :files (filterv #(and (= :ready (:status %)) (not (:deleted? %)))))]
|
||||
(import-files project-id files))
|
||||
|
||||
(swap! state
|
||||
(fn [state]
|
||||
(-> state
|
||||
(assoc :status :importing)
|
||||
(update :files mark-files-importing)))))
|
||||
|
||||
continue-template
|
||||
(fn []
|
||||
(let [mdata {:on-success on-template-cloned-success :on-error on-template-cloned-error}
|
||||
params {:project-id project-id :template-id (:id template)}]
|
||||
(swap! state
|
||||
(fn [state]
|
||||
(-> state
|
||||
(assoc :status :importing :importing-templates 1))))
|
||||
(st/emit! (dd/clone-template (with-meta params mdata)))))
|
||||
|
||||
|
||||
handle-continue
|
||||
(mf/use-callback
|
||||
(mf/deps project-id (:files @state))
|
||||
(fn [event]
|
||||
(dom/prevent-default event)
|
||||
(let [files (->> @state :files (filterv #(and (= :ready (:status %)) (not (:deleted? %)))))]
|
||||
(import-files project-id files))
|
||||
|
||||
(swap! state
|
||||
(fn [state]
|
||||
(-> state
|
||||
(assoc :status :importing)
|
||||
(update :files mark-files-importing))))))
|
||||
(if (some? template)
|
||||
(continue-template)
|
||||
(continue-files))))
|
||||
|
||||
handle-accept
|
||||
(mf/use-callback
|
||||
|
@ -299,10 +333,15 @@
|
|||
(st/emit! (modal/hide))
|
||||
(when on-finish-import (on-finish-import))))
|
||||
|
||||
num-importing (+
|
||||
(->> @state :files (filter #(= (:status %) :importing)) count)
|
||||
(:importing-templates @state))
|
||||
|
||||
|
||||
warning-files (->> @state :files (filter #(and (= (:status %) :import-finish) (d/not-empty? (:errors %)))) count)
|
||||
success-files (->> @state :files (filter #(and (= (:status %) :import-finish) (empty? (:errors %)))) count)
|
||||
pending-analysis? (> (->> @state :files (filter #(= (:status %) :analyzing)) count) 0)
|
||||
pending-import? (> (->> @state :files (filter #(= (:status %) :importing)) count) 0)
|
||||
pending-import? (> num-importing 0)
|
||||
files (->> (:files @state) (filterv (comp not :deleted?)))]
|
||||
|
||||
(mf/use-effect
|
||||
|
@ -334,7 +373,7 @@
|
|||
|
||||
[:div.feedback-banner
|
||||
[:div.icon i/checkbox-checked]
|
||||
[:div.message (tr "dashboard.import.import-message" success-files)]]))
|
||||
[:div.message (tr "dashboard.import.import-message" (if (some? template) 1 success-files))]]))
|
||||
|
||||
(for [file files]
|
||||
(let [editing? (and (some? (:file-id file))
|
||||
|
@ -342,7 +381,13 @@
|
|||
[:& import-entry {:state state
|
||||
:file file
|
||||
:editing? editing?
|
||||
:can-be-deleted? (> (count files) 1)}]))]
|
||||
:can-be-deleted? (> (count files) 1)}]))
|
||||
|
||||
(when (some? template)
|
||||
[:& import-entry {:state state
|
||||
:file (assoc template :status (if (= 1 (:importing-templates @state)) :importing :ready))
|
||||
:editing? false
|
||||
:can-be-deleted? false}])]
|
||||
|
||||
[:div.modal-footer
|
||||
[:div.action-buttons
|
||||
|
|
|
@ -172,7 +172,7 @@
|
|||
(dom/clean-value! search-input)
|
||||
(dom/focus! search-input)
|
||||
(emit! (dd/go-to-search)))))
|
||||
|
||||
|
||||
on-key-press
|
||||
(mf/use-callback
|
||||
(fn [e]
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
(def arrow-down (icon-xref :arrow-down))
|
||||
(def arrow-end (icon-xref :arrow-end))
|
||||
(def arrow-slide (icon-xref :arrow-slide))
|
||||
(def arrow-up (icon-xref :arrow-up))
|
||||
(def artboard (icon-xref :artboard))
|
||||
(def at (icon-xref :at))
|
||||
(def auto-direction (icon-xref :auto-direction))
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
[app.config :as cf]
|
||||
[app.main.data.events :as ev]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.users :as du]
|
||||
[app.main.data.users :as du]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.onboarding.newsletter]
|
||||
[app.main.ui.onboarding.questions]
|
||||
|
@ -22,7 +22,7 @@
|
|||
|
||||
;; --- ONBOARDING LIGHTBOX
|
||||
|
||||
(defn send-event
|
||||
(defn send-event
|
||||
[event-name]
|
||||
(st/emit! (ptk/event ::ev/event {::ev/name event-name
|
||||
::ev/origin "dashboard"})))
|
||||
|
|
|
@ -643,6 +643,15 @@ msgstr "Your name"
|
|||
msgid "dashboard.your-penpot"
|
||||
msgstr "Your Penpot"
|
||||
|
||||
msgid "dashboard.libraries-and-templates"
|
||||
msgstr "Libraries & Templates"
|
||||
|
||||
msgid "dashboard.libraries-and-templates.explore"
|
||||
msgstr "Explore more of them and know how to contribute"
|
||||
|
||||
msgid "dashboard.libraries-and-templates.import-error"
|
||||
msgstr "There was a problem importing the template. The template wasn't imported."
|
||||
|
||||
#: src/app/main/ui/alert.cljs
|
||||
msgid "ds.alert-ok"
|
||||
msgstr "Ok"
|
||||
|
|
|
@ -663,6 +663,15 @@ msgstr "Tu nombre"
|
|||
msgid "dashboard.your-penpot"
|
||||
msgstr "Tu Penpot"
|
||||
|
||||
msgid "dashboard.libraries-and-templates"
|
||||
msgstr "Bibliotecas y plantillas"
|
||||
|
||||
msgid "dashboard.libraries-and-templates.explore"
|
||||
msgstr "Explora más y descubre cómo contribuir"
|
||||
|
||||
msgid "dashboard.libraries-and-templates.import-error"
|
||||
msgstr "Hubo un problema importando la plantilla. No ha podido ser importada."
|
||||
|
||||
#: src/app/main/ui/alert.cljs
|
||||
msgid "ds.alert-ok"
|
||||
msgstr "Ok"
|
||||
|
|
Loading…
Add table
Reference in a new issue