0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-04-12 23:11:23 -05:00

🎉 Edit assets

This commit is contained in:
Andrés Moya 2020-07-30 14:52:58 +02:00
parent af2c49dd16
commit d40f27e18c
10 changed files with 364 additions and 63 deletions

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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))))

View file

@ -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" : "",

View file

@ -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;

View file

@ -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])))))

View file

@ -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

View file

@ -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}

View file

@ -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"