0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-10 09:08:31 -05:00

🐸 adds crud for libraries and deletion of elements

This commit is contained in:
alonso.torres 2020-03-19 12:54:30 +01:00
parent df75099000
commit a0a031dbc2
11 changed files with 374 additions and 88 deletions

View file

@ -66,6 +66,7 @@
@import 'main/partials/loader';
@import 'main/partials/context-menu';
@import 'main/partials/debug-icons-preview';
@import 'main/partials/editable-label';
//#################################################
// Resources

View file

@ -20,6 +20,19 @@
}
}
.library-content-empty {
display: flex;
flex-direction: column;
}
.library-content-empty-text {
color: #7C7C7C;
border: 1px dashed #AFB2BF;
text-align: center;
padding: 5rem;
margin: 2rem;
}
.library-page #main-bar {
position: relative;
}
@ -79,16 +92,13 @@
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
& a {
color: $color-black;
}
color: $color-black;
&:hover {
background-color: $color-primary-lighter;
}
&.current a {
&.current {
font-weight: bold;
}
}
@ -145,6 +155,12 @@
}
}
.library-top-menu-actions-delete {
display: flex;
justify-content: center;
flex-direction: column
}
.library-page-cards-container {
align-content: flex-start;
display: flex;
@ -335,3 +351,4 @@
font-size: 24px;
font-weight: normal;
}

View file

@ -0,0 +1,30 @@
.editable-label {
display: flex;
&.is-hidden {
display: none;
}
}
.editable-label-input {
border: 0;
height: 30px;
padding: 5px;
margin: 0;
width: 100%;
background-color: $color-white;
}
.editable-label-close {
background-color: $color-white;
cursor: pointer;
padding: 3px 5px;
& svg {
fill: $color-gray;
height: 15px;
transform: rotate(45deg) translateY(7px);
width: 15px;
margin: 0;
}
}

View file

@ -318,12 +318,12 @@
(->> (rp/mutation! :create-color {:library-id library-id
:content color
:name color})
(rx/map create-color-result)))))
(rx/map (partial create-color-result library-id))))))
(defn create-color-result
[item]
[library-id item]
(ptk/reify ::create-color-result
ptk/UpdateEvent
(update [_ state]
(-> state
(update-in [:library :selected-items] #(into [item] %) )))))
(update-in [:library :selected-items library-id] #(into [item] %) )))))

View file

@ -94,7 +94,8 @@
(-> state
(update-in [:library :icon-libraries] #(into [result] %))))))
;; rename-icon-library
;; delete-icon-library
;; (declare fetch-icons)
;;
@ -253,16 +254,16 @@
(rx/merge-map parse)
(rx/map prepare)
(rx/flat-map #(rp/mutation! :create-icon %))
(rx/map create-icon-result))))))
(rx/map (partial create-icon-result library-id)))))))
(defn create-icon-result
[item]
[library-id item]
(ptk/reify ::create-icon-result
ptk/UpdateEvent
(update [_ state]
(let [{:keys [id] :as item} (assoc item :type :icon)]
(-> state
(update-in [:library :selected-items] #(into [item] %)))))))
(update-in [:library :selected-items library-id] #(into [item] %)))))))
;; ;; --- Icon Persisted
;;

View file

@ -444,17 +444,17 @@
(rx/reduce conj [])
(rx/do on-success)
(rx/mapcat identity)
(rx/map create-images-result)
(rx/map (partial create-images-result library-id))
(rx/catch on-error)))))))
;; --- Image Created
(defn create-images-result
[item]
[library-id item]
#_(us/verify ::image item)
(ptk/reify ::create-images-result
ptk/UpdateEvent
(update [_ state]
(-> state
(update-in [:library :selected-items] #(into [item] %))))))
(update-in [:library :selected-items library-id] #(into [item] %))))))

View file

@ -0,0 +1,175 @@
(ns uxbox.main.data.library
(:require
[cljs.spec.alpha :as s]
[beicon.core :as rx]
[cuerdas.core :as str]
[potok.core :as ptk]
[uxbox.common.spec :as us]
[uxbox.common.data :as d]
[uxbox.main.repo :as rp]
[uxbox.main.store :as st]
[uxbox.util.dom :as dom]
[uxbox.util.webapi :as wapi]
[uxbox.util.i18n :as i18n :refer [t tr]]
[uxbox.util.router :as r]
[uxbox.util.uuid :as uuid]))
;; Retrieve libraries
(declare retrieve-libraries-result)
(defn retrieve-libraries
[type team-id]
(s/assert ::us/uuid team-id)
(let [method (case type
:icons :icon-libraries
:images :image-libraries
:palettes :color-libraries)]
(ptk/reify ::retrieve-libraries
ptk/WatchEvent
(watch [_ state stream]
(->> (rp/query! method {:team-id team-id})
(rx/map (partial retrieve-libraries-result type)))))))
(defn retrieve-libraries-result [type result]
(ptk/reify ::retrieve-libraries-result
ptk/UpdateEvent
(update [_ state]
(-> state
(assoc-in [:library type] result)))))
;; Retrieve library data
(declare retrieve-library-data-result)
(defn retrieve-library-data
[type library-id]
(ptk/reify ::retrieve-library-data
ptk/WatchEvent
(watch [_ state stream]
(let [method (case type
:icons :icons
:images :images
:palettes :colors)]
(->> (rp/query! method {:library-id library-id})
(rx/map (partial retrieve-library-data-result library-id)))))))
(defn retrieve-library-data-result
[library-id data]
(ptk/reify ::retrieve-library-data-result
ptk/UpdateEvent
(update [_ state]
(-> state
(assoc-in [:library :selected-items library-id] data)))))
;; Create library
(declare create-library-result)
(defn create-library
[type team-id name]
(ptk/reify ::create-library
ptk/WatchEvent
(watch [_ state stream]
(let [method (case type
:icons :create-icon-library
:images :create-image-library
:palettes :create-color-library)]
(->> (rp/mutation! method {:team-id team-id
:name name})
(rx/map (partial create-library-result type)))))))
(defn create-library-result
[type result]
(ptk/reify ::create-library-result
ptk/UpdateEvent
(update [_ state]
(-> state
(update-in [:library type] #(into [result] %))))))
;; Rename library
(declare rename-library-result)
(defn rename-library
[type library-id name]
(ptk/reify ::rename-library
ptk/WatchEvent
(watch [_ state stream]
(let [method (case type
:icons :rename-icon-library
:images :rename-image-library
:palettes :rename-color-library)]
(->> (rp/mutation! method {:id library-id
:name name})
(rx/map #(rename-library-result type library-id name)))))))
(defn rename-library-result
[type library-id name]
(ptk/reify ::rename-library-result
ptk/UpdateEvent
(update [_ state]
(letfn [(change-name
[library] (if (= library-id (:id library))
(assoc library :name name)
library))
(update-fn [libraries] (map change-name libraries))]
(-> state
(update-in [:library type] update-fn))))))
;; Delete library
(declare delete-library-result)
(defn delete-library
[type library-id]
(ptk/reify ::delete-library
ptk/WatchEvent
(watch [_ state stream]
(let [method (case type
:icons :delete-icon-library
:images :delete-image-library
:palettes :delete-color-library)]
(->> (rp/mutation! method {:id library-id})
(rx/map #(delete-library-result type library-id)))))))
(defn delete-library-result
[type library-id]
(ptk/reify ::create-library-result
ptk/UpdateEvent
(update [_ state]
(let [update-fn (fn [libraries]
(filter #(not= library-id (:id %)) libraries))]
(-> state
(update-in [:library type] update-fn))))))
;; Delete library item
(declare delete-item-result)
(defn delete-item
[type library-id item-id]
(ptk/reify ::delete-item
ptk/WatchEvent
(watch [_ state stream]
(let [method (case type
:icons :delete-icon
:images :delete-image
:palettes :delete-color)]
(->> (rp/mutation! method {:id item-id})
(rx/map #(delete-item-result type library-id item-id)))))))
(defn delete-item-result
[type library-id item-id]
(ptk/reify ::delete-item-result
ptk/UpdateEvent
(update [_ state]
(let [update-fn (fn [items]
(filter #(not= item-id (:id %)) items))]
(-> state
(update-in [:library :selected-items library-id] update-fn))))))
;; Rename library item

View file

@ -0,0 +1,41 @@
(ns uxbox.main.ui.components.editable-label
(:require
[rumext.alpha :as mf]
[uxbox.builtins.icons :as i]
[uxbox.main.ui.keyboard :as kbd]
[uxbox.util.dom :as dom]
[uxbox.util.data :refer [classnames]]))
(mf/defc editable-label
[{:keys [ value on-change on-cancel edit readonly class-name]}]
(let [input (mf/use-ref nil)
state (mf/use-state (:editing false))
is-editing (or edit (:editing @state))
start-editing (fn []
(swap! state assoc :editing true)
(dom/timeout 100 #(dom/focus! (mf/ref-node input))))
stop-editing (fn [] (swap! state assoc :editing false))
cancel-editing (fn []
(stop-editing)
(when on-cancel (on-cancel)))
on-dbl-click (fn [e] (when (not readonly) (start-editing)))
on-key-up (fn [e]
(cond
(kbd/esc? e)
(cancel-editing)
(kbd/enter? e)
(let [value (-> e dom/get-target dom/get-value)]
(on-change value)
(stop-editing))))
]
(if is-editing
[:div.editable-label {:class class-name}
[:input.editable-label-input {:ref input
:default-value value
:on-key-down on-key-up}]
[:span.editable-label-close {:on-click cancel-editing} i/close]]
[:span.editable-label {:class class-name
:on-double-click on-dbl-click} value]
)))

View file

@ -17,6 +17,7 @@
[uxbox.util.i18n :as i18n :refer [t tr]]
[uxbox.util.color :as uc]
[uxbox.util.dom :as dom]
[uxbox.main.data.library :as dlib]
[uxbox.main.data.icons :as dico]
[uxbox.main.data.images :as dimg]
[uxbox.main.data.colors :as dcol]
@ -27,6 +28,7 @@
[uxbox.main.ui.modal :as modal]
[uxbox.main.ui.confirm :refer [confirm-dialog]]
[uxbox.main.ui.colorpicker :refer [colorpicker most-used-colors]]
[uxbox.main.ui.components.editable-label :refer [editable-label]]
))
(mf/defc modal-create-color
@ -53,19 +55,9 @@
[:a.close {:href "#" :on-click cancel} i/close]])))
(defmulti create-library (fn [x _] x))
(defmethod create-library :icons [_ team-id]
(let [name (str "Icon Library "(gensym "l"))]
(st/emit! (dico/create-icon-library team-id name))))
(defmethod create-library :images [_ team-id]
(let [name (str "Image Library "(gensym "l"))]
(st/emit! (dimg/create-image-library team-id name))))
(defmethod create-library :palettes [_ team-id]
(let [name (str "Image Library "(gensym "l"))]
(st/emit! (dcol/create-color-library team-id name))))
(defn create-library [section team-id]
(let [name (str (str (str/title (name section)) " " (gensym "Library ")))]
(st/emit! (dlib/create-library section team-id name))))
(defmulti create-item (fn [x _ _] x))
@ -128,26 +120,42 @@
(let [path (keyword (str "dashboard-library-" (name section)))]
(dico/fetch-icon-library (:id item))
(st/emit! (rt/nav path {:team-id team-id :library-id (:id item)}))))}
[:a (:name item)]])]]))
[:& editable-label {:value (:name item)
:on-change #(st/emit! (dlib/rename-library section library-id %))}]
])]]))
(mf/defc library-top-menu
[{:keys [selected section library-id]}]
(let [state (mf/use-state {:is-open false})
locale (i18n/use-locale)]
[{:keys [selected section library-id team-id]}]
(let [state (mf/use-state {:is-open false
:editing-name false})
locale (i18n/use-locale)
stop-editing #(swap! state assoc :editing-name false)]
[:header.library-top-menu
[:div.library-top-menu-current-element
[:h2.library-top-menu-current-element-name (:name selected)]
[:& editable-label {:edit (:editing-name @state)
:on-change #(do
(stop-editing)
(st/emit! (dlib/rename-library section library-id %)))
:on-cancel #(swap! state assoc :editing-name false)
:class-name "library-top-menu-current-element-name"
:value (:name selected)}]
[:a.library-top-menu-current-action
{ :on-click #(swap! state update :is-open not)}
[:span i/arrow-down]]
[:& context-menu
{:show (:is-open @state)
:on-close #(swap! state update :is-open not)
:options [[(t locale "ds.button.rename") #(println "Rename")]
[(t locale "ds.button.delete") #(println "Delete")]]}]]
:options [[(t locale "ds.button.rename")
#(swap! state assoc :editing-name true)]
[(t locale "ds.button.delete")
#(let [path (keyword (str "dashboard-library-" (name section) "-index"))]
(do
(st/emit! (dlib/delete-library section library-id))
(st/emit! (rt/nav path {:team-id team-id}))))]]}]]
[:div.library-top-menu-actions
[:a i/trash]
[:a.library-top-menu-actions-delete i/trash]
(if (= section :palettes)
[:button.btn-dashboard
@ -158,12 +166,17 @@
[:label {:for "file-upload" :class-name "btn-dashboard"}
(t locale (str "dashboard.library.add-item." (name section)))]
[:input {:on-change #(create-item section library-id %)
:id "file-upload" :type "file" :style {:display "none"}}]]
)]]))
:id "file-upload"
:type "file"
:multiple true
:accept (case section
:images "image"
:icons "image/svg+xml"
"")
:style {:display "none"}}]])]]))
(mf/defc library-icon-card
[{:keys [id name url content metadata]}]
[{:keys [id name url content metadata library-id]}]
(let [locale (i18n/use-locale)
state (mf/use-state {:is-open false})]
[:div.library-card.library-icon
@ -189,10 +202,11 @@
[:& context-menu
{:show (:is-open @state)
:on-close #(swap! state update :is-open not)
:options [[(t locale "ds.button.delete") #(println "Delete")]]}]]]))
:options [[(t locale "ds.button.delete")
#(st/emit! (dlib/delete-item :icons library-id id))]]}]]]))
(mf/defc library-image-card
[{:keys [id name thumb-uri]}]
[{:keys [id name thumb-uri library-id]}]
(let [locale (i18n/use-locale)
state (mf/use-state {:is-open false})]
[:div.library-card.library-image
@ -213,10 +227,11 @@
[:& context-menu
{:show (:is-open @state)
:on-close #(swap! state update :is-open not)
:options [[(t locale "ds.button.delete") #(println "Delete")]]}]]]))
:options [[(t locale "ds.button.delete")
#(st/emit! (dlib/delete-item :images library-id id))]]}]]]))
(mf/defc library-color-card
[{ :keys [ id content ] }]
[{ :keys [ id content library-id] }]
(when content
(let [locale (i18n/use-locale)
state (mf/use-state {:is-open false})]
@ -240,54 +255,61 @@
[:& context-menu
{:show (:is-open @state)
:on-close #(swap! state update :is-open not)
:options [[(t locale "ds.button.delete") #(println "Delete")]]}]]])))
:options [[(t locale "ds.button.delete")
#(st/emit! (dlib/delete-item :palettes library-id id))]]}]]])))
(def icon-libraries-ref
(-> (comp (l/key :library) (l/key :icon-libraries))
(defn libraries-ref [section]
(-> (comp (l/key :library) (l/key section))
(l/derive st/state)))
(def image-libraries-ref
(-> (comp (l/key :library) (l/key :image-libraries))
(l/derive st/state)))
(def color-libraries-ref
(-> (comp (l/key :library) (l/key :color-libraries))
(l/derive st/state)))
(def selected-items-ref
(-> (comp (l/key :library) (l/key :selected-items))
(defn selected-items-ref [library-id]
(-> (comp (l/key :library) (l/key :selected-items) (l/key library-id))
(l/derive st/state)))
(mf/defc library-page
[{:keys [team-id library-id section]}]
(mf/use-effect {:fn #(case section
:icons (st/emit! (dico/fetch-icon-libraries team-id))
:images (st/emit! (dimg/fetch-image-libraries team-id))
:palettes (st/emit! (dcol/fetch-color-libraries team-id)))
:deps (mf/deps section team-id)})
(mf/use-effect {:fn #(when library-id
(case section
:icons (st/emit! (dico/fetch-icon-library library-id))
:images (st/emit! (dimg/fetch-image-library library-id))
:palettes (st/emit! (dcol/fetch-color-library library-id))))
:deps (mf/deps library-id)})
(let [libraries (case section
:icons (mf/deref icon-libraries-ref)
:images (mf/deref image-libraries-ref)
:palettes (mf/deref color-libraries-ref))
items (mf/deref selected-items-ref)
(let [libraries (mf/deref (libraries-ref section))
items (mf/deref (selected-items-ref library-id))
selected-library (first (filter #(= (:id %) library-id) libraries))]
(mf/use-effect {:fn #(st/emit! (dlib/retrieve-libraries section team-id))
:deps (mf/deps section team-id)})
(mf/use-effect {:fn #(when library-id
(st/emit! (dlib/retrieve-library-data section library-id)))
:deps (mf/deps library-id)})
(mf/use-effect {:fn #(if (and (nil? library-id) (> (count libraries) 0))
(let [path (keyword (str "dashboard-library-" (name section)))]
(st/emit! (rt/nav path {:team-id team-id :library-id (:id (first libraries))}))))
:deps (mf/deps library-id section team-id)})
[:div.library-page
[:& library-header {:section section :team-id team-id}]
[:& library-sidebar {:items libraries :team-id team-id :library-id library-id :section section}]
(when library-id
(if library-id
[:section.library-content
[:& library-top-menu {:selected selected-library :section section :library-id library-id}]
[:div.library-page-cards-container
(for [item items]
(let [item (assoc item :key (:id item))]
(case section
:icons [:& library-icon-card item]
:images [:& library-image-card item]
:palettes [:& library-color-card item ])))]])]))
[:& library-top-menu {:selected selected-library :section section :library-id library-id :team-id team-id}]
[:*
;; TODO: Fix the chunked list
#_[:& chunked-list {:items items
:initial-size 30
:chunk-size 30}
(fn [item]
(let [item (assoc item :key (:id item))]
(case section
:icons [:& library-icon-card item]
:images [:& library-image-card item]
:palettes [:& library-color-card item ])))]
(if (> (count items) 0)
[:div.library-page-cards-container
(for [item items]
(let [item (assoc item :key (:id item))]
(case section
:icons [:& library-icon-card item]
:images [:& library-image-card item]
:palettes [:& library-color-card item ])))]
[:div.library-content-empty
[:p.library-content-empty-text "You still have no elements in this library"]])]]
[:div.library-content-empty
[:p.library-content-empty-text "You still have no image libraries."]])]))

View file

@ -149,7 +149,7 @@
[& params]
{:pre [(even? (count params))]}
(str/join " " (reduce (fn [acc [k v]]
(if (true? v)
(if (and k (true? v))
(conj acc (name k))
acc))
[]

View file

@ -142,8 +142,7 @@
y (.-clientY event)]
(gpt/point x y)))
(defn get-offset-position
[event]
(let [x (.-offsetX event)
y (.-offsetY event)]
(gpt/point x y)))
(defn focus!
[node]
(.focus node))