mirror of
https://github.com/penpot/penpot.git
synced 2025-04-12 23:11:23 -05:00
🎉 Edit assets
This commit is contained in:
parent
af2c49dd16
commit
d40f27e18c
10 changed files with 364 additions and 63 deletions
|
@ -449,7 +449,7 @@
|
|||
[path]
|
||||
(let [[basedir libraries] (read-file path)]
|
||||
(db/with-atomic [conn db/pool]
|
||||
(let [project-id (create-project-if-not-exists conn {:name "Media loader"})]
|
||||
(let [project-id (create-project-if-not-exists conn {:name "System libraries"})]
|
||||
(run! #(process-library conn basedir project-id %) libraries)))))
|
||||
|
||||
(defn -main
|
||||
|
|
|
@ -161,9 +161,10 @@
|
|||
|
||||
(def ^:private sql:select-color-for-update
|
||||
"select c.*,
|
||||
lib.team_id as team_id
|
||||
p.team_id as team_id
|
||||
from color as c
|
||||
inner join color_library as lib on (lib.id = c.library_id)
|
||||
inner join file as f on f.id = c.file_id
|
||||
inner join project as p on p.id = f.project_id
|
||||
where c.id = ?
|
||||
for update of c")
|
||||
|
||||
|
@ -175,6 +176,26 @@
|
|||
row))
|
||||
|
||||
|
||||
;; --- Mutation: Update Color
|
||||
|
||||
(s/def ::update-color
|
||||
(s/keys :req-un [::profile-id ::id ::content]))
|
||||
|
||||
(sm/defmutation ::update-color
|
||||
[{:keys [profile-id id content] :as params}]
|
||||
(db/with-atomic [conn db/pool]
|
||||
(let [clr (select-color-for-update conn id)
|
||||
;; IMPORTANT: if the previous name was equal to the hex content,
|
||||
;; we must rename it in addition to changing the value.
|
||||
new-name (if (= (:name clr) (:content clr))
|
||||
content
|
||||
(:name clr))]
|
||||
(teams/check-edition-permissions! conn profile-id (:team-id clr))
|
||||
(db/update! conn :color
|
||||
{:name new-name
|
||||
:content content}
|
||||
{:id id}))))
|
||||
|
||||
;; --- Delete Color
|
||||
|
||||
(declare delete-color)
|
||||
|
|
|
@ -71,7 +71,8 @@
|
|||
(defn check-edition-permissions!
|
||||
[conn profile-id team-id]
|
||||
(let [row (db/exec-one! conn [sql:team-permissions profile-id team-id])]
|
||||
(when-not (or (:can-edit row)
|
||||
(when-not (or (= team-id uuid/zero)
|
||||
(:can-edit row)
|
||||
(:is-admin row)
|
||||
(:is-owner row))
|
||||
(ex/raise :type :validation
|
||||
|
|
|
@ -30,7 +30,8 @@
|
|||
(defn check-edition-permissions!
|
||||
[conn profile-id team-id]
|
||||
(let [row (db/exec-one! conn [sql:team-permissions profile-id team-id])]
|
||||
(when-not (or (:can-edit row)
|
||||
(when-not (or (= team-id uuid/zero) ;; We can write global-project owned items
|
||||
(:can-edit row)
|
||||
(:is-admin row)
|
||||
(:is-owner row))
|
||||
(ex/raise :type :validation
|
||||
|
@ -39,10 +40,9 @@
|
|||
(defn check-read-permissions!
|
||||
[conn profile-id team-id]
|
||||
(let [row (db/exec-one! conn [sql:team-permissions profile-id team-id])]
|
||||
(when-not (or (:can-edit row)
|
||||
(when-not (or (= team-id uuid/zero) ;; We can read global-project owned items
|
||||
(:can-edit row)
|
||||
(:is-admin row)
|
||||
(:is-owner row)
|
||||
;; We can read global-project owned items
|
||||
(= team-id #uuid "00000000-0000-0000-0000-000000000000"))
|
||||
(:is-owner row))
|
||||
(ex/raise :type :validation
|
||||
:code :not-authorized))))
|
||||
|
|
|
@ -522,7 +522,7 @@
|
|||
}
|
||||
},
|
||||
"ds.button.save" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/library.cljs:55" ],
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/library.cljs:55", "src/uxbox/main/ui/workspace/sidebar/assets.cljs:69" ],
|
||||
"translations" : {
|
||||
"en" : "Save",
|
||||
"fr" : "Sauvegarder",
|
||||
|
@ -657,7 +657,7 @@
|
|||
}
|
||||
},
|
||||
"errors.image-format-unsupported" : {
|
||||
"used-in" : [ "src/uxbox/main/data/workspace/persistence.cljs:370", "src/uxbox/main/data/users.cljs:177", "src/uxbox/main/data/images.cljs:376" ],
|
||||
"used-in" : [ "src/uxbox/main/data/workspace/persistence.cljs:390", "src/uxbox/main/data/users.cljs:177", "src/uxbox/main/data/images.cljs:376" ],
|
||||
"translations" : {
|
||||
"en" : "The image format is not supported (must be svg, jpg or png).",
|
||||
"fr" : "Le format d'image n'est pas supporté (doit être svg, jpg ou png).",
|
||||
|
@ -666,7 +666,7 @@
|
|||
}
|
||||
},
|
||||
"errors.image-too-large" : {
|
||||
"used-in" : [ "src/uxbox/main/data/workspace/persistence.cljs:368", "src/uxbox/main/data/users.cljs:175", "src/uxbox/main/data/images.cljs:374" ],
|
||||
"used-in" : [ "src/uxbox/main/data/workspace/persistence.cljs:388", "src/uxbox/main/data/users.cljs:175", "src/uxbox/main/data/images.cljs:374" ],
|
||||
"translations" : {
|
||||
"en" : "The image is too large to be inserted (must be under 5mb).",
|
||||
"fr" : "L'image est trop grande (doit être inférieure à 5 Mo).",
|
||||
|
@ -675,7 +675,7 @@
|
|||
}
|
||||
},
|
||||
"errors.image-type-mismatch" : {
|
||||
"used-in" : [ "src/uxbox/main/data/workspace/persistence.cljs:335", "src/uxbox/main/data/workspace/persistence.cljs:385", "src/uxbox/main/data/users.cljs:191", "src/uxbox/main/data/images.cljs:391" ],
|
||||
"used-in" : [ "src/uxbox/main/data/workspace/persistence.cljs:355", "src/uxbox/main/data/workspace/persistence.cljs:405", "src/uxbox/main/data/users.cljs:191", "src/uxbox/main/data/images.cljs:391" ],
|
||||
"translations" : {
|
||||
"en" : "Seems that the contents of the image does not match the file extension.",
|
||||
"fr" : "",
|
||||
|
@ -684,7 +684,7 @@
|
|||
}
|
||||
},
|
||||
"errors.image-type-not-allowed" : {
|
||||
"used-in" : [ "src/uxbox/main/data/workspace/persistence.cljs:332", "src/uxbox/main/data/workspace/persistence.cljs:382", "src/uxbox/main/data/users.cljs:188", "src/uxbox/main/data/images.cljs:388" ],
|
||||
"used-in" : [ "src/uxbox/main/data/workspace/persistence.cljs:352", "src/uxbox/main/data/workspace/persistence.cljs:402", "src/uxbox/main/data/users.cljs:188", "src/uxbox/main/data/images.cljs:388" ],
|
||||
"translations" : {
|
||||
"en" : "Seems that this is not a valid image.",
|
||||
"fr" : "",
|
||||
|
@ -729,7 +729,7 @@
|
|||
}
|
||||
},
|
||||
"errors.unexpected-error" : {
|
||||
"used-in" : [ "src/uxbox/main/data/workspace/persistence.cljs:338", "src/uxbox/main/data/workspace/persistence.cljs:388", "src/uxbox/main/data/users.cljs:194", "src/uxbox/main/data/images.cljs:394", "src/uxbox/main/ui/settings/change_email.cljs:51", "src/uxbox/main/ui/workspace/sidebar/options/exports.cljs:65", "src/uxbox/main/ui/auth/register.cljs:54" ],
|
||||
"used-in" : [ "src/uxbox/main/data/workspace/persistence.cljs:358", "src/uxbox/main/data/workspace/persistence.cljs:408", "src/uxbox/main/data/users.cljs:194", "src/uxbox/main/data/images.cljs:394", "src/uxbox/main/ui/settings/change_email.cljs:51", "src/uxbox/main/ui/workspace/sidebar/options/exports.cljs:65", "src/uxbox/main/ui/auth/register.cljs:54" ],
|
||||
"translations" : {
|
||||
"en" : "An unexpected error occurred.",
|
||||
"fr" : "Une erreur inattendue c'est produite",
|
||||
|
@ -774,7 +774,7 @@
|
|||
}
|
||||
},
|
||||
"image.loading" : {
|
||||
"used-in" : [ "src/uxbox/main/data/workspace/persistence.cljs:346", "src/uxbox/main/data/workspace/persistence.cljs:397", "src/uxbox/main/data/users.cljs:201", "src/uxbox/main/data/images.cljs:403" ],
|
||||
"used-in" : [ "src/uxbox/main/data/workspace/persistence.cljs:366", "src/uxbox/main/data/workspace/persistence.cljs:417", "src/uxbox/main/data/users.cljs:201", "src/uxbox/main/data/images.cljs:403" ],
|
||||
"translations" : {
|
||||
"en" : "Loading image...",
|
||||
"fr" : "Chargement de l'image...",
|
||||
|
@ -783,7 +783,7 @@
|
|||
}
|
||||
},
|
||||
"modal.create-color.new-color" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/library.cljs:49" ],
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/library.cljs:49", "src/uxbox/main/ui/workspace/sidebar/assets.cljs:62" ],
|
||||
"translations" : {
|
||||
"en" : "New Color",
|
||||
"fr" : "Nouvelle couleur",
|
||||
|
@ -1359,7 +1359,7 @@
|
|||
}
|
||||
},
|
||||
"workspace.assets.assets" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/assets.cljs:144" ],
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/assets.cljs:325" ],
|
||||
"translations" : {
|
||||
"en" : "Assets",
|
||||
"fr" : "",
|
||||
|
@ -1368,7 +1368,7 @@
|
|||
}
|
||||
},
|
||||
"workspace.assets.box-filter-all" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/assets.cljs:154" ],
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/assets.cljs:342" ],
|
||||
"translations" : {
|
||||
"en" : "All assets",
|
||||
"fr" : "",
|
||||
|
@ -1377,7 +1377,7 @@
|
|||
}
|
||||
},
|
||||
"workspace.assets.box-filter-colors" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/assets.cljs:156" ],
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/assets.cljs:344" ],
|
||||
"translations" : {
|
||||
"en" : "Colors",
|
||||
"fr" : "",
|
||||
|
@ -1386,7 +1386,7 @@
|
|||
}
|
||||
},
|
||||
"workspace.assets.box-filter-graphics" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/assets.cljs:155" ],
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/assets.cljs:343" ],
|
||||
"translations" : {
|
||||
"en" : "Graphics",
|
||||
"fr" : "",
|
||||
|
@ -1395,7 +1395,7 @@
|
|||
}
|
||||
},
|
||||
"workspace.assets.colors" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/assets.cljs:91" ],
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/assets.cljs:247" ],
|
||||
"translations" : {
|
||||
"en" : "Colors",
|
||||
"fr" : "",
|
||||
|
@ -1404,7 +1404,7 @@
|
|||
}
|
||||
},
|
||||
"workspace.assets.delete" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/assets.cljs:83" ],
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/assets.cljs:136", "src/uxbox/main/ui/workspace/sidebar/assets.cljs:231" ],
|
||||
"translations" : {
|
||||
"en" : "Delete",
|
||||
"fr" : "",
|
||||
|
@ -1412,8 +1412,17 @@
|
|||
"es" : "Borrar"
|
||||
}
|
||||
},
|
||||
"workspace.assets.edit" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/assets.cljs:230" ],
|
||||
"translations" : {
|
||||
"en" : "Edit",
|
||||
"fr" : "",
|
||||
"ru" : "",
|
||||
"es" : "Editar"
|
||||
}
|
||||
},
|
||||
"workspace.assets.file-library" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/assets.cljs:104" ],
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/assets.cljs:271" ],
|
||||
"translations" : {
|
||||
"en" : "File library",
|
||||
"fr" : "",
|
||||
|
@ -1422,7 +1431,7 @@
|
|||
}
|
||||
},
|
||||
"workspace.assets.graphics" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/assets.cljs:68" ],
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/assets.cljs:113" ],
|
||||
"translations" : {
|
||||
"en" : "Graphics",
|
||||
"fr" : "",
|
||||
|
@ -1430,8 +1439,26 @@
|
|||
"es" : "Gráficos"
|
||||
}
|
||||
},
|
||||
"workspace.assets.not-found" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/assets.cljs:282" ],
|
||||
"translations" : {
|
||||
"en" : "No assets found",
|
||||
"fr" : "",
|
||||
"ru" : "",
|
||||
"es" : "No se encontraron recursos"
|
||||
}
|
||||
},
|
||||
"workspace.assets.rename" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/assets.cljs:229" ],
|
||||
"translations" : {
|
||||
"en" : "Rename",
|
||||
"fr" : "",
|
||||
"ru" : "",
|
||||
"es" : "Renombrar"
|
||||
}
|
||||
},
|
||||
"workspace.assets.search" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/assets.cljs:147" ],
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/assets.cljs:329" ],
|
||||
"translations" : {
|
||||
"en" : "Search assets",
|
||||
"fr" : "",
|
||||
|
|
|
@ -15,16 +15,14 @@
|
|||
margin: $small $small 0 $small;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
background-color: $color-gray-50;
|
||||
.search-block {
|
||||
border: 1px solid $color-gray-30;
|
||||
color: $color-gray-10;
|
||||
font-size: $fs12;
|
||||
margin: $small $small 0 $small;
|
||||
padding: $x-small;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&:focus {
|
||||
color: lighten($color-gray-10, 8%);
|
||||
&:focus-within {
|
||||
border-color: $color-primary !important;
|
||||
}
|
||||
|
||||
|
@ -32,6 +30,35 @@
|
|||
border-color: $color-gray-20;
|
||||
}
|
||||
|
||||
& .search-input {
|
||||
background-color: $color-gray-50;
|
||||
border: none;
|
||||
color: $color-gray-10;
|
||||
font-size: $fs12;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
flex-grow: 1;
|
||||
|
||||
&:focus {
|
||||
color: lighten($color-gray-10, 8%);
|
||||
}
|
||||
}
|
||||
|
||||
& .search-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
svg {
|
||||
fill: $color-gray-30;
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
}
|
||||
|
||||
&.close {
|
||||
transform: rotate(45deg);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.input-select {
|
||||
|
@ -104,6 +131,7 @@
|
|||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
& img {
|
||||
max-height: 100%;
|
||||
|
@ -136,6 +164,7 @@
|
|||
}
|
||||
|
||||
.group-list {
|
||||
max-height: 30rem;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
|
@ -145,6 +174,7 @@
|
|||
margin-top: $x-small;
|
||||
font-size: $fs11;
|
||||
color: $color-white;
|
||||
cursor: pointer;
|
||||
|
||||
& .color-block {
|
||||
width: 20px;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
[beicon.core :as rx]
|
||||
[clojure.set :as set]
|
||||
[potok.core :as ptk]
|
||||
[uxbox.common.data :as d]
|
||||
[uxbox.common.spec :as us]
|
||||
[uxbox.main.repo :as rp]
|
||||
[uxbox.main.store :as st]
|
||||
|
@ -249,21 +250,85 @@
|
|||
(declare create-color-result)
|
||||
|
||||
(defn create-color
|
||||
[library-id color]
|
||||
(s/assert (s/nilable uuid?) library-id)
|
||||
[file-id color]
|
||||
(s/assert (s/nilable uuid?) file-id)
|
||||
(ptk/reify ::create-color
|
||||
ptk/WatchEvent
|
||||
(watch [_ state s]
|
||||
|
||||
(->> (rp/mutation! :create-color {:library-id library-id
|
||||
(->> (rp/mutation! :create-color {:file-id file-id
|
||||
:content color
|
||||
:name color})
|
||||
(rx/map (partial create-color-result library-id))))))
|
||||
(rx/map (partial create-color-result file-id))))))
|
||||
|
||||
(defn create-color-result
|
||||
[library-id item]
|
||||
[file-id color]
|
||||
(ptk/reify ::create-color-result
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(update-in [:library-items :palettes library-id] #(into [item] %) )))))
|
||||
(assoc-in [:workspace-colors (:id color)] color)
|
||||
(assoc-in [:workspace-local :color-for-rename] (:id color))))))
|
||||
|
||||
(def clear-color-for-rename
|
||||
(ptk/reify ::clear-color-for-rename
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:workspace-local :color-for-rename] nil))))
|
||||
|
||||
(declare rename-color-result)
|
||||
|
||||
(defn rename-color
|
||||
[file-id color-id name]
|
||||
(ptk/reify ::rename-color
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(->> (rp/mutation! :rename-color {:id color-id
|
||||
:name name})
|
||||
(rx/map (partial rename-color-result file-id))))))
|
||||
|
||||
(defn rename-color-result
|
||||
[file-id color]
|
||||
(ptk/reify ::rename-color-result
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(assoc-in [:workspace-colors (:id color)] color)))))
|
||||
|
||||
(declare update-color-result)
|
||||
|
||||
(defn update-color
|
||||
[file-id color-id content]
|
||||
(ptk/reify ::update-color
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(->> (rp/mutation! :update-color {:id color-id
|
||||
:content content})
|
||||
(rx/map (partial update-color-result file-id))))))
|
||||
|
||||
(defn update-color-result
|
||||
[file-id color]
|
||||
(ptk/reify ::update-color-result
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(assoc-in [:workspace-colors (:id color)] color)))))
|
||||
|
||||
(declare delete-color-result)
|
||||
|
||||
(defn delete-color
|
||||
[file-id color-id]
|
||||
(ptk/reify ::delete-color
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(->> (rp/mutation! :delete-color {:id color-id})
|
||||
(rx/map #(delete-color-result file-id color-id))))))
|
||||
|
||||
(defn delete-color-result
|
||||
[file-id color-id]
|
||||
(ptk/reify ::delete-color-result
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(d/dissoc-in [:workspace-colors color-id])))))
|
||||
|
||||
|
|
|
@ -324,7 +324,7 @@
|
|||
;; --- Upload Image
|
||||
|
||||
(declare image-uploaded)
|
||||
(def allowed-file-types #{"image/jpeg" "image/png" "image/webp"})
|
||||
(def allowed-file-types #{"image/jpeg" "image/png" "image/webp" "image/svg+xml"})
|
||||
(def max-file-size (* 5 1024 1024))
|
||||
|
||||
;; TODO: unify with create-images at main/data/images.cljs
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
[uxbox.main.ui.icons :as i]
|
||||
[uxbox.main.data.workspace :as dw]
|
||||
[uxbox.main.data.images :as di]
|
||||
[uxbox.main.data.colors :as dcol]
|
||||
[uxbox.main.refs :as refs]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.main.ui.keyboard :as kbd]
|
||||
|
@ -30,18 +31,46 @@
|
|||
[uxbox.util.i18n :as i18n :refer [tr]]
|
||||
[uxbox.util.data :refer [classnames]]
|
||||
[uxbox.main.data.library :as dlib]
|
||||
[uxbox.main.ui.modal :as modal]
|
||||
[uxbox.main.ui.colorpicker :refer [colorpicker most-used-colors]]
|
||||
[uxbox.main.ui.components.tab-container :refer [tab-container tab-element]]
|
||||
[uxbox.main.ui.components.file-uploader :refer [file-uploader]]
|
||||
[uxbox.main.ui.components.context-menu :refer [context-menu]]))
|
||||
|
||||
(defn matches-search
|
||||
[name search-term]
|
||||
(if (empty? search-term)
|
||||
(if (str/empty? search-term)
|
||||
true
|
||||
(let [st (str/trim (str/lower search-term))
|
||||
nm (str/trim (str/lower name))]
|
||||
(str/includes? nm st))))
|
||||
|
||||
(mf/defc modal-edit-color
|
||||
[{:keys [color-value on-accept on-cancel] :as ctx}]
|
||||
(let [state (mf/use-state {:current-color color-value})]
|
||||
(letfn [(accept [event]
|
||||
(dom/prevent-default event)
|
||||
(modal/hide!)
|
||||
(when on-accept (on-accept (:current-color @state))))
|
||||
|
||||
(cancel [event]
|
||||
(dom/prevent-default event)
|
||||
(modal/hide!)
|
||||
(when on-cancel (on-cancel)))]
|
||||
|
||||
[:div.modal-create-color
|
||||
[:h3.modal-create-color-title (tr "modal.create-color.new-color")]
|
||||
[:& colorpicker {:value (:current-color @state)
|
||||
:colors (into-array @most-used-colors)
|
||||
:disable-opacity true
|
||||
:on-change #(swap! state assoc :current-color %)}]
|
||||
|
||||
[:input.btn-primary {:type "button"
|
||||
:value (tr "ds.button.save")
|
||||
:on-click accept}]
|
||||
|
||||
[:a.close {:href "#" :on-click cancel} i/close]])))
|
||||
|
||||
(mf/defc graphics-box
|
||||
[{:keys [library-id images] :as props}]
|
||||
(let [state (mf/use-state {:menu-open false
|
||||
|
@ -104,25 +133,125 @@
|
|||
:on-close #(swap! state assoc :menu-open false)
|
||||
:top (:top @state)
|
||||
:left (:left @state)
|
||||
:options [[(tr "workspace.assets.delete") delete-graphic]]}]]
|
||||
]))
|
||||
:options [[(tr "workspace.assets.delete") delete-graphic]]}]]]))
|
||||
|
||||
|
||||
(mf/defc color-item
|
||||
[{:keys [color library-id] :as props}]
|
||||
(let [workspace-local @refs/workspace-local
|
||||
color-for-rename (:color-for-rename workspace-local)
|
||||
|
||||
edit-input-ref (mf/use-ref)
|
||||
|
||||
state (mf/use-state {:menu-open false
|
||||
:top nil
|
||||
:left nil
|
||||
:editing (= color-for-rename (:id color))})
|
||||
|
||||
rename-color
|
||||
(fn [name]
|
||||
(st/emit! (dcol/rename-color library-id (:id color) name)))
|
||||
|
||||
edit-color
|
||||
(fn [value opacity]
|
||||
(st/emit! (dcol/update-color library-id (:id color) value)))
|
||||
|
||||
delete-color
|
||||
(fn []
|
||||
(st/emit! (dcol/delete-color library-id (:id color))))
|
||||
|
||||
rename-color-clicked
|
||||
(fn [event]
|
||||
(dom/prevent-default event)
|
||||
(swap! state assoc :editing true))
|
||||
|
||||
input-blur
|
||||
(fn [event]
|
||||
(let [target (dom/event->target event)
|
||||
name (dom/get-value target)]
|
||||
(rename-color name)
|
||||
(st/emit! dcol/clear-color-for-rename)
|
||||
(swap! state assoc :editing false)))
|
||||
|
||||
input-key-down
|
||||
(fn [event]
|
||||
(when (kbd/esc? event)
|
||||
(st/emit! dcol/clear-color-for-rename)
|
||||
(swap! state assoc :editing false))
|
||||
(when (kbd/enter? event)
|
||||
(input-blur event)))
|
||||
|
||||
edit-color-clicked
|
||||
(fn [event]
|
||||
(modal/show! modal-edit-color
|
||||
{:color-value (:content color)
|
||||
:on-accept edit-color}))
|
||||
|
||||
on-context-menu
|
||||
(fn [event]
|
||||
(let [pos (dom/get-client-position event)
|
||||
top (:y pos)
|
||||
left (- (:x pos) 20)]
|
||||
(dom/prevent-default event)
|
||||
(swap! state assoc
|
||||
:menu-open true
|
||||
:top top
|
||||
:left left)))]
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps (:editing @state))
|
||||
#(when (:editing @state)
|
||||
(let [edit-input (mf/ref-val edit-input-ref)]
|
||||
(dom/select-text! edit-input))
|
||||
nil))
|
||||
|
||||
[:div.group-list-item {:on-context-menu on-context-menu}
|
||||
[:div.color-block {:style {:background-color (:content color)}}]
|
||||
(if (:editing @state)
|
||||
[:input.element-name
|
||||
{:type "text"
|
||||
:ref edit-input-ref
|
||||
:on-blur input-blur
|
||||
:on-key-down input-key-down
|
||||
:auto-focus true
|
||||
:default-value (:name color "")}]
|
||||
[:div.name-block
|
||||
{:on-double-click rename-color-clicked}
|
||||
(:name color)
|
||||
(when-not (= (:name color) (:content color))
|
||||
[:span (:content color)])])
|
||||
[:& context-menu
|
||||
{:selectable false
|
||||
:show (:menu-open @state)
|
||||
:on-close #(swap! state assoc :menu-open false)
|
||||
:top (:top @state)
|
||||
:left (:left @state)
|
||||
:options [[(tr "workspace.assets.rename") rename-color-clicked]
|
||||
[(tr "workspace.assets.edit") edit-color-clicked]
|
||||
[(tr "workspace.assets.delete") delete-color]]}]]))
|
||||
|
||||
(mf/defc colors-box
|
||||
[{:keys [colors] :as props}]
|
||||
(let [add-color #(println "añadir color")]
|
||||
[{:keys [library-id colors] :as props}]
|
||||
(let [add-color
|
||||
(fn [value opacity]
|
||||
(st/emit! (dcol/create-color library-id value)))
|
||||
|
||||
add-color-clicked
|
||||
(fn [event]
|
||||
(modal/show! modal-edit-color
|
||||
{:color-value "#406280"
|
||||
:on-accept add-color}))]
|
||||
|
||||
[:div.asset-group
|
||||
[:div.group-title
|
||||
(tr "workspace.assets.colors")
|
||||
[:span (str "\u00A0(") (count colors) ")"] ;; Unicode 00A0 is non-breaking space
|
||||
[:div.group-button {:on-click add-color} i/plus]]
|
||||
[:div.group-button {:on-click add-color-clicked} i/plus]]
|
||||
[:div.group-list
|
||||
(for [color (sort-by :name colors)]
|
||||
[:div.group-list-item {:key (:name color)
|
||||
:on-context-menu #(println "context")}
|
||||
[:div.color-block {:style {:background-color (:content color)}}]
|
||||
(:name color)
|
||||
(when-not (= (:name color) (:content color))
|
||||
[:span (:content color)])])]]))
|
||||
[:& color-item {:key (:id color)
|
||||
:color color
|
||||
:library-id library-id}])]]))
|
||||
|
||||
(mf/defc library-toolbox
|
||||
[{:keys [library-id
|
||||
|
@ -141,11 +270,18 @@
|
|||
i/arrow-slide]
|
||||
[:span (tr "workspace.assets.file-library")]]
|
||||
(when @open?
|
||||
[:div.tool-window-content
|
||||
(when (or (= box-filter :all) (= box-filter :graphics))
|
||||
[:& graphics-box {:library-id library-id :images images}])
|
||||
(when (or (= box-filter :all) (= box-filter :colors))
|
||||
[:& colors-box {:colors colors}])])]))
|
||||
(let [show-graphics (and (or (= box-filter :all) (= box-filter :graphics))
|
||||
(or (> (count images) 0) (str/empty? search-term)))
|
||||
show-colors (and (or (= box-filter :all) (= box-filter :colors))
|
||||
(or (> (count colors) 0) (str/empty? search-term)))]
|
||||
[:div.tool-window-content
|
||||
(when show-graphics
|
||||
[:& graphics-box {:library-id library-id :images images}])
|
||||
(when show-colors
|
||||
[:& colors-box {:library-id library-id :colors colors}])
|
||||
(when (and (not show-graphics) (not show-colors))
|
||||
[:div.asset-group
|
||||
[:div.group-title (tr "workspace.assets.not-found")]])]))]))
|
||||
|
||||
(mf/defc assets-toolbox
|
||||
[]
|
||||
|
@ -160,7 +296,8 @@
|
|||
filtered-images (filter #(matches-search (:name %) (:search-term @state))
|
||||
(vals file-images))
|
||||
|
||||
filtered-colors (filter #(matches-search (:name %) (:search-term @state))
|
||||
filtered-colors (filter #(or (matches-search (:name %) (:search-term @state))
|
||||
(matches-search (:content %) (:search-term @state)))
|
||||
(vals file-colors))
|
||||
|
||||
on-search-term-change (fn [event]
|
||||
|
@ -168,6 +305,9 @@
|
|||
(dom/get-value))]
|
||||
(swap! state assoc :search-term value)))
|
||||
|
||||
on-search-clear-click (fn [event]
|
||||
(swap! state assoc :search-term ""))
|
||||
|
||||
on-box-filter-change (fn [event]
|
||||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
|
@ -186,11 +326,18 @@
|
|||
[:div.tool-window-content
|
||||
[:div.assets-bar-title (tr "workspace.assets.assets")]
|
||||
|
||||
[:input.search-input
|
||||
{:placeholder (tr "workspace.assets.search")
|
||||
:type "text"
|
||||
:value (:search-term @state)
|
||||
:on-change on-search-term-change}]
|
||||
[:div.search-block
|
||||
[:input.search-input
|
||||
{:placeholder (tr "workspace.assets.search")
|
||||
:type "text"
|
||||
:value (:search-term @state)
|
||||
:on-change on-search-term-change}]
|
||||
(if (str/empty? (:search-term @state))
|
||||
[:div.search-icon
|
||||
i/search]
|
||||
[:div.search-icon.close
|
||||
{:on-click on-search-clear-click}
|
||||
i/close])]
|
||||
|
||||
[:select.input-select {:value (:box-filter @state)
|
||||
:on-change on-box-filter-change}
|
||||
|
|
|
@ -49,7 +49,17 @@
|
|||
;; Images
|
||||
{:name "Unsplash"
|
||||
:images {:path "./images/unsplash"
|
||||
:regex #"^.*\.jpg$"}}
|
||||
:regex #"^.*\.jpg$"}
|
||||
:colors ["Chateau Green" "#419860"
|
||||
"Toast" "#987567"
|
||||
"Confetti" "#EAC75B"
|
||||
"Thunderbird" "#C23D1F"
|
||||
"Coffee Bean" "#331113"
|
||||
"Lavender Magenta" "#EF88DF"
|
||||
"Persian Rose" "#F536A6"
|
||||
"Royal Blue" "#5C4AEE"
|
||||
"Biscay" "#202362"
|
||||
"Olivine" "#98C277"]}
|
||||
|
||||
;; Colors
|
||||
{:name "Flat design"
|
||||
|
|
Loading…
Add table
Reference in a new issue