mirror of
https://github.com/penpot/penpot.git
synced 2025-03-28 15:41:25 -05:00
Merge pull request #3611 from penpot/superalex-support-for-images-as-fills
🎉 Support for images as fills
This commit is contained in:
commit
099f9c074d
46 changed files with 1278 additions and 587 deletions
|
@ -112,18 +112,21 @@
|
|||
(let [xform (comp
|
||||
(map :objects)
|
||||
(mapcat vals)
|
||||
(keep (fn [{:keys [type] :as obj}]
|
||||
(case type
|
||||
:path (get-in obj [:fill-image :id])
|
||||
:bool (get-in obj [:fill-image :id])
|
||||
(mapcat (fn [obj]
|
||||
;; NOTE: because of some bug, we ended with
|
||||
;; many shape types having the ability to
|
||||
;; have fill-image attribute (which initially
|
||||
;; designed for :path shapes).
|
||||
:group (get-in obj [:fill-image :id])
|
||||
:image (get-in obj [:metadata :id])
|
||||
|
||||
nil))))
|
||||
(sequence
|
||||
(keep :id)
|
||||
(concat [(:fill-image obj)
|
||||
(:metadata obj)]
|
||||
(map :fill-image (:fills obj))
|
||||
(map :stroke-image (:strokes obj))
|
||||
(->> (:content obj)
|
||||
(tree-seq map? :children)
|
||||
(mapcat :fills)
|
||||
(map :fill-image)))))))
|
||||
pages (concat
|
||||
(vals (:pages-index data))
|
||||
(vals (:components data)))]
|
||||
|
|
|
@ -364,6 +364,161 @@
|
|||
(t/is (nil? (sto/get-object storage (:thumbnail-id fmo1))))
|
||||
)))
|
||||
|
||||
(t/deftest file-gc-image-fills-and-strokes
|
||||
(letfn [(add-file-media-object [& {:keys [profile-id file-id]}]
|
||||
(let [mfile {:filename "sample.jpg"
|
||||
:path (th/tempfile "backend_tests/test_files/sample.jpg")
|
||||
:mtype "image/jpeg"
|
||||
:size 312043}
|
||||
params {::th/type :upload-file-media-object
|
||||
::rpc/profile-id profile-id
|
||||
:file-id file-id
|
||||
:is-local true
|
||||
:name "testfile"
|
||||
:content mfile}
|
||||
out (th/command! params)]
|
||||
|
||||
;; (th/print-result! out)
|
||||
(t/is (nil? (:error out)))
|
||||
(:result out)))
|
||||
|
||||
(update-file! [& {:keys [profile-id file-id changes revn] :or {revn 0}}]
|
||||
(let [params {::th/type :update-file
|
||||
::rpc/profile-id profile-id
|
||||
:id file-id
|
||||
:session-id (uuid/random)
|
||||
:revn revn
|
||||
:components-v2 true
|
||||
:changes changes}
|
||||
out (th/command! params)]
|
||||
;; (th/print-result! out)
|
||||
(t/is (nil? (:error out)))
|
||||
(:result out)))]
|
||||
|
||||
(let [storage (:app.storage/storage th/*system*)
|
||||
|
||||
profile (th/create-profile* 1)
|
||||
file (th/create-file* 1 {:profile-id (:id profile)
|
||||
:project-id (:default-project-id profile)
|
||||
:is-shared false})
|
||||
|
||||
fmo1 (add-file-media-object :profile-id (:id profile) :file-id (:id file))
|
||||
fmo2 (add-file-media-object :profile-id (:id profile) :file-id (:id file))
|
||||
fmo3 (add-file-media-object :profile-id (:id profile) :file-id (:id file))
|
||||
fmo4 (add-file-media-object :profile-id (:id profile) :file-id (:id file))
|
||||
fmo5 (add-file-media-object :profile-id (:id profile) :file-id (:id file))
|
||||
s-shid (uuid/random)
|
||||
t-shid (uuid/random)
|
||||
|
||||
page-id (first (get-in file [:data :pages]))]
|
||||
|
||||
;; Update file inserting a new image object
|
||||
(update-file!
|
||||
:file-id (:id file)
|
||||
:profile-id (:id profile)
|
||||
:revn 0
|
||||
:changes
|
||||
[{:type :add-obj
|
||||
:page-id page-id
|
||||
:id s-shid
|
||||
:parent-id uuid/zero
|
||||
:frame-id uuid/zero
|
||||
:components-v2 true
|
||||
:obj (cts/setup-shape
|
||||
{:id s-shid
|
||||
:name "image"
|
||||
:frame-id uuid/zero
|
||||
:parent-id uuid/zero
|
||||
:type :image
|
||||
:metadata {:id (:id fmo1) :width 100 :height 100 :mtype "image/jpeg"}
|
||||
:fills [{:opacity 1 :fill-image {:id (:id fmo2) :width 100 :height 100 :mtype "image/jpeg"}}]
|
||||
:strokes [{:opacity 1 :stroke-image {:id (:id fmo3) :width 100 :height 100 :mtype "image/jpeg"}}]})}
|
||||
{:type :add-obj
|
||||
:page-id page-id
|
||||
:id t-shid
|
||||
:parent-id uuid/zero
|
||||
:frame-id uuid/zero
|
||||
:components-v2 true
|
||||
:obj (cts/setup-shape
|
||||
{:id t-shid
|
||||
:name "text"
|
||||
:frame-id uuid/zero
|
||||
:parent-id uuid/zero
|
||||
:type :text
|
||||
:content {:type "root"
|
||||
:children [{:type "paragraph-set"
|
||||
:children [{:type "paragraph"
|
||||
:children [{:fills [{:fill-opacity 1
|
||||
:fill-image {:id (:id fmo4)
|
||||
:width 417
|
||||
:height 354
|
||||
:mtype "image/png"
|
||||
:name "text fill image"}}]
|
||||
:text "hi"}
|
||||
{:fills [{:fill-opacity 1
|
||||
:fill-color "#000000"}]
|
||||
:text "bye"}]}]}]}
|
||||
:strokes [{:opacity 1 :stroke-image {:id (:id fmo5) :width 100 :height 100 :mtype "image/jpeg"}}]})}])
|
||||
|
||||
;; run the file-gc task immediately without forced min-age
|
||||
(let [res (th/run-task! "file-gc")]
|
||||
(t/is (= 0 (:processed res))))
|
||||
|
||||
;; run the task again
|
||||
(let [res (th/run-task! "file-gc" {:min-age 0})]
|
||||
(t/is (= 1 (:processed res))))
|
||||
|
||||
;; retrieve file and check trimmed attribute
|
||||
(let [row (th/db-get :file {:id (:id file)})]
|
||||
(t/is (true? (:has-media-trimmed row))))
|
||||
|
||||
;; check file media objects
|
||||
(let [rows (th/db-exec! ["select * from file_media_object where file_id = ?" (:id file)])]
|
||||
(t/is (= 5 (count rows))))
|
||||
|
||||
;; The underlying storage objects are still available.
|
||||
(t/is (some? (sto/get-object storage (:media-id fmo5))))
|
||||
(t/is (some? (sto/get-object storage (:media-id fmo4))))
|
||||
(t/is (some? (sto/get-object storage (:media-id fmo3))))
|
||||
(t/is (some? (sto/get-object storage (:media-id fmo2))))
|
||||
(t/is (some? (sto/get-object storage (:media-id fmo1))))
|
||||
|
||||
;; proceed to remove usage of the file
|
||||
(update-file!
|
||||
:file-id (:id file)
|
||||
:profile-id (:id profile)
|
||||
:revn 0
|
||||
:changes [{:type :del-obj
|
||||
:page-id (first (get-in file [:data :pages]))
|
||||
:id s-shid}
|
||||
{:type :del-obj
|
||||
:page-id (first (get-in file [:data :pages]))
|
||||
:id t-shid}])
|
||||
|
||||
;; Now, we have deleted the usage of pointers to the
|
||||
;; file-media-objects, if we paste file-gc, they should be marked
|
||||
;; as deleted.
|
||||
(let [task (:app.tasks.file-gc/handler th/*system*)
|
||||
res (task {:min-age (dt/duration 0)})]
|
||||
(t/is (= 1 (:processed res))))
|
||||
|
||||
;; Now that file-gc have deleted the file-media-object usage,
|
||||
;; lets execute the touched-gc task, we should see that two of
|
||||
;; them are marked to be deleted.
|
||||
(let [task (:app.storage/gc-touched-task th/*system*)
|
||||
res (task {:min-age (dt/duration 0)})]
|
||||
(t/is (= 0 (:freeze res)))
|
||||
(t/is (= 2 (:delete res))))
|
||||
|
||||
;; Finally, check that some of the objects that are marked as
|
||||
;; deleted we are unable to retrieve them using standard storage
|
||||
;; public api.
|
||||
(t/is (nil? (sto/get-object storage (:media-id fmo5))))
|
||||
(t/is (nil? (sto/get-object storage (:media-id fmo4))))
|
||||
(t/is (nil? (sto/get-object storage (:media-id fmo3))))
|
||||
(t/is (nil? (sto/get-object storage (:media-id fmo2))))
|
||||
(t/is (nil? (sto/get-object storage (:media-id fmo1)))))))
|
||||
|
||||
(t/deftest permissions-checks-creating-file
|
||||
(let [profile1 (th/create-profile* 1)
|
||||
profile2 (th/create-profile* 2)
|
||||
|
|
|
@ -329,7 +329,8 @@
|
|||
image-url (or (:href attrs) (:xlink:href attrs))
|
||||
image-data (dm/get-in svg-data [:image-data image-url])
|
||||
|
||||
metadata {:width (:width image-data)
|
||||
metadata {:name name
|
||||
:width (:width image-data)
|
||||
:height (:height image-data)
|
||||
:mtype (:mtype image-data)
|
||||
:id (:id image-data)}
|
||||
|
@ -344,9 +345,11 @@
|
|||
(when (some? image-data)
|
||||
(cts/setup-shape
|
||||
(-> (calculate-rect-metadata rect transform)
|
||||
(assoc :type :image)
|
||||
(assoc :type :rect)
|
||||
(assoc :name name)
|
||||
(assoc :frame-id frame-id)
|
||||
(assoc :fills [{:fill-opacity 1
|
||||
:fill-image metadata}])
|
||||
(assoc :metadata metadata)
|
||||
(assoc :svg-viewbox rect)
|
||||
(assoc :svg-attrs props))))))
|
||||
|
|
|
@ -46,6 +46,14 @@
|
|||
::oapi/type "integer"
|
||||
::oapi/format "int64"}})
|
||||
|
||||
(sm/def! ::image-color
|
||||
[:map {:title "ImageColor"}
|
||||
[:name {:optional true} :string]
|
||||
[:width :int]
|
||||
[:height :int]
|
||||
[:mtype {:optional true} [:maybe :string]]
|
||||
[:id ::sm/uuid]])
|
||||
|
||||
(sm/def! ::gradient
|
||||
[:map {:title "Gradient"}
|
||||
[:type [::sm/one-of #{:linear :radial "linear" "radial"}]]
|
||||
|
@ -72,17 +80,19 @@
|
|||
[:modified-at {:optional true} ::sm/inst]
|
||||
[:ref-id {:optional true} ::sm/uuid]
|
||||
[:ref-file {:optional true} ::sm/uuid]
|
||||
[:gradient {:optional true} [:maybe ::gradient]]])
|
||||
[:gradient {:optional true} [:maybe ::gradient]]
|
||||
[:image {:optional true} [:maybe ::image-color]]])
|
||||
|
||||
|
||||
;; FIXME: incomplete schema
|
||||
(sm/def! ::recent-color
|
||||
[:and
|
||||
[:map {:title "RecentColot"}
|
||||
[:map {:title "RecentColor"}
|
||||
[:opacity {:optional true} [:maybe ::sm/safe-number]]
|
||||
[:color {:optional true} [:maybe ::rgb-color]]
|
||||
[:gradient {:optional true} [:maybe ::gradient]]]
|
||||
[::sm/contains-any {:strict true} [:color :gradient]]])
|
||||
[:gradient {:optional true} [:maybe ::gradient]]
|
||||
[:image {:optional true} [:maybe ::image-color]]]
|
||||
[::sm/contains-any {:strict true} [:color :gradient :image]]])
|
||||
|
||||
(def color?
|
||||
(sm/pred-fn ::color))
|
||||
|
@ -102,17 +112,19 @@
|
|||
{:color (:fill-color fill)
|
||||
:opacity (:fill-opacity fill)
|
||||
:gradient (:fill-color-gradient fill)
|
||||
:image (:fill-image fill)
|
||||
:ref-id (:fill-color-ref-id fill)
|
||||
:ref-file (:fill-color-ref-file fill)}))
|
||||
|
||||
(defn set-fill-color
|
||||
[shape position color opacity gradient]
|
||||
[shape position color opacity gradient image]
|
||||
(update-in shape [:fills position]
|
||||
(fn [fill]
|
||||
(d/without-nils (assoc fill
|
||||
:fill-color color
|
||||
:fill-opacity opacity
|
||||
:fill-color-gradient gradient)))))
|
||||
:fill-color-gradient gradient
|
||||
:fill-image image)))))
|
||||
|
||||
(defn attach-fill-color
|
||||
[shape position ref-id ref-file]
|
||||
|
@ -133,17 +145,19 @@
|
|||
(d/without-nils {:color (:stroke-color stroke)
|
||||
:opacity (:stroke-opacity stroke)
|
||||
:gradient (:stroke-color-gradient stroke)
|
||||
:image (:stroke-image stroke)
|
||||
:ref-id (:stroke-color-ref-id stroke)
|
||||
:ref-file (:stroke-color-ref-file stroke)}))
|
||||
|
||||
(defn set-stroke-color
|
||||
[shape position color opacity gradient]
|
||||
[shape position color opacity gradient image]
|
||||
(update-in shape [:strokes position]
|
||||
(fn [stroke]
|
||||
(d/without-nils (assoc stroke
|
||||
:stroke-color color
|
||||
:stroke-opacity opacity
|
||||
:stroke-color-gradient gradient)))))
|
||||
:stroke-color-gradient gradient
|
||||
:stroke-image image)))))
|
||||
|
||||
(defn attach-stroke-color
|
||||
[shape position ref-id ref-file]
|
||||
|
@ -336,7 +350,8 @@
|
|||
position
|
||||
(:color library-color)
|
||||
(:opacity library-color)
|
||||
(:gradient library-color))
|
||||
(:gradient library-color)
|
||||
(:image library-color))
|
||||
(detach-fn shape position)))
|
||||
shape))]
|
||||
|
||||
|
|
|
@ -105,7 +105,8 @@
|
|||
[:fill-opacity {:optional true} ::sm/safe-number]
|
||||
[:fill-color-gradient {:optional true} [:maybe ::ctc/gradient]]
|
||||
[:fill-color-ref-file {:optional true} [:maybe ::sm/uuid]]
|
||||
[:fill-color-ref-id {:optional true} [:maybe ::sm/uuid]]])
|
||||
[:fill-color-ref-id {:optional true} [:maybe ::sm/uuid]]
|
||||
[:fill-image {:optional true} ::ctc/image-color]])
|
||||
|
||||
(sm/def! ::stroke
|
||||
[:map {:title "Stroke"}
|
||||
|
@ -122,7 +123,8 @@
|
|||
[::sm/one-of stroke-caps]]
|
||||
[:stroke-cap-end {:optional true}
|
||||
[::sm/one-of stroke-caps]]
|
||||
[:stroke-color-gradient {:optional true} ::ctc/gradient]])
|
||||
[:stroke-color-gradient {:optional true} ::ctc/gradient]
|
||||
[:stroke-image {:optional true} ::ctc/image-color]])
|
||||
|
||||
(sm/def! ::minimal-shape-attrs
|
||||
[:map {:title "ShapeMinimalRecord"}
|
||||
|
@ -346,6 +348,13 @@
|
|||
(def valid-shape?
|
||||
(sm/pred-fn ::shape))
|
||||
|
||||
|
||||
(defn has-images?
|
||||
[{:keys [fills strokes]}]
|
||||
(or
|
||||
(some :fill-image fills)
|
||||
(some :stroke-image strokes)))
|
||||
|
||||
;; --- Initialization
|
||||
|
||||
(def ^:private minimal-rect-attrs
|
||||
|
|
BIN
frontend/resources/images/colorpicker-no-image.png
Normal file
BIN
frontend/resources/images/colorpicker-no-image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
|
@ -21,6 +21,7 @@
|
|||
.top-actions {
|
||||
display: flex;
|
||||
margin-bottom: $size-1;
|
||||
flex-direction: row-reverse;
|
||||
justify-content: space-between;
|
||||
|
||||
.picker-btn {
|
||||
|
@ -38,8 +39,44 @@
|
|||
height: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.element-set-content {
|
||||
width: auto;
|
||||
padding: 0.25rem 0;
|
||||
.custom-select {
|
||||
border: none;
|
||||
&:hover {
|
||||
border: none;
|
||||
}
|
||||
.custom-select-dropdown {
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.select-image {
|
||||
.content {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
background-image: url("/images/colorpicker-no-image.png");
|
||||
background-position: center;
|
||||
background-size: auto 6.75rem;
|
||||
height: 6.75rem;
|
||||
img {
|
||||
height: fit-content;
|
||||
width: fit-content;
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
button {
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
.gradients-buttons {
|
||||
.gradient {
|
||||
cursor: pointer;
|
||||
|
|
|
@ -143,6 +143,8 @@
|
|||
.color-text {
|
||||
width: 3rem;
|
||||
text-transform: uppercase;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.attributes-color-display {
|
||||
|
|
|
@ -1530,27 +1530,45 @@
|
|||
;; for retrieve the image data and convert it to the
|
||||
;; data-url.
|
||||
(prepare-object [objects parent-frame-id {:keys [type] :as obj}]
|
||||
(let [obj (maybe-translate obj objects parent-frame-id)]
|
||||
(if (= type :image)
|
||||
(let [url (cf/resolve-file-media (:metadata obj))]
|
||||
(->> (http/send! {:method :get
|
||||
:uri url
|
||||
:response-type :blob})
|
||||
(rx/map :body)
|
||||
(rx/mapcat wapi/read-file-as-data-url)
|
||||
(rx/map #(assoc obj ::data %))
|
||||
(rx/take 1)))
|
||||
(let [obj (maybe-translate obj objects parent-frame-id)
|
||||
;; Texts can have different fills for pieces of the text
|
||||
fill-images-data (->> (or (:position-data obj) [obj])
|
||||
(map :fills)
|
||||
(reduce into [])
|
||||
(filter :fill-image)
|
||||
(map :fill-image))
|
||||
|
||||
stroke-images-data (->> (:strokes obj)
|
||||
(filter :stroke-image)
|
||||
(map :stroke-image))
|
||||
images-data (concat
|
||||
fill-images-data
|
||||
stroke-images-data
|
||||
(when (= type :image)
|
||||
[(:metadata obj)]))]
|
||||
|
||||
(if (> (count images-data) 0)
|
||||
(->> (rx/from images-data)
|
||||
(rx/mapcat (fn [image-data]
|
||||
(let [url (cf/resolve-file-media image-data)]
|
||||
(->> (http/send! {:method :get
|
||||
:uri url
|
||||
:response-type :blob})
|
||||
(rx/map :body)
|
||||
(rx/mapcat wapi/read-file-as-data-url)
|
||||
(rx/map #(assoc image-data :data %))))))
|
||||
(rx/reduce conj [])
|
||||
(rx/map
|
||||
(fn [images]
|
||||
(assoc obj ::data images))))
|
||||
(rx/of obj))))
|
||||
|
||||
;; Collects all the items together and split images into a
|
||||
;; separated data structure for a more easy paste process.
|
||||
(collect-data [res {:keys [id metadata] :as item}]
|
||||
(collect-data [res {:keys [id] :as item}]
|
||||
(let [res (update res :objects assoc id (dissoc item ::data))]
|
||||
(if (= :image (:type item))
|
||||
(let [img-part {:id (:id metadata)
|
||||
:name (:name item)
|
||||
:file-data (::data item)}]
|
||||
(update res :images conj img-part))
|
||||
(if (::data item)
|
||||
(update res :images into (::data item))
|
||||
res)))
|
||||
|
||||
(maybe-translate [shape objects parent-frame-id]
|
||||
|
@ -1713,7 +1731,7 @@
|
|||
(letfn [;; Given a file-id and img (part generated by the
|
||||
;; copy-selected event), uploads the new media.
|
||||
(upload-media [file-id imgpart]
|
||||
(->> (http/send! {:uri (:file-data imgpart)
|
||||
(->> (http/send! {:uri (:data imgpart)
|
||||
:response-type :blob
|
||||
:method :get})
|
||||
(rx/map :body)
|
||||
|
@ -1727,19 +1745,43 @@
|
|||
(rx/map (fn [media]
|
||||
(assoc media :prev-id (:id imgpart))))))
|
||||
|
||||
(translate-staled-media [mdata attribute media-idx]
|
||||
(let [id (get-in mdata [attribute :id])
|
||||
mobj (get media-idx id)]
|
||||
(if mobj
|
||||
(update mdata attribute #(assoc %
|
||||
:id (:id mobj)
|
||||
:path (:path mobj)))
|
||||
mdata)))
|
||||
|
||||
;; Analyze the rchange and replace staled media and
|
||||
;; references to the new uploaded media-objects.
|
||||
(process-rchange [media-idx item]
|
||||
(if (and (= (:type item) :add-obj)
|
||||
(= :image (get-in item [:obj :type])))
|
||||
(update-in item [:obj :metadata]
|
||||
(fn [{:keys [id] :as mdata}]
|
||||
(if-let [mobj (get media-idx id)]
|
||||
(assoc mdata
|
||||
:id (:id mobj)
|
||||
:path (:path mobj))
|
||||
mdata)))
|
||||
item))
|
||||
(let [;; Texts can have different fills for pieces of the text
|
||||
obj (:obj item)
|
||||
fills (mapv #(translate-staled-media % :fill-image media-idx) (:fills obj))
|
||||
strokes (mapv #(translate-staled-media % :stroke-image media-idx) (:strokes obj))
|
||||
position-data (->> (:position-data obj)
|
||||
(mapv (fn [p-data]
|
||||
(let [fills (mapv #(translate-staled-media % :fill-image media-idx) (:fills p-data))]
|
||||
(assoc p-data :fills fills)))))
|
||||
content (txt/transform-nodes #(translate-staled-media % :fill-image media-idx) (:content obj))]
|
||||
|
||||
(if (= (:type item) :add-obj)
|
||||
(-> item
|
||||
(update-in [:obj :metadata]
|
||||
(fn [{:keys [id] :as mdata}]
|
||||
(if-let [mobj (get media-idx id)]
|
||||
(assoc mdata
|
||||
:id (:id mobj)
|
||||
:path (:path mobj))
|
||||
mdata)))
|
||||
(assoc-in [:obj :fills] fills)
|
||||
(assoc-in [:obj :strokes] strokes)
|
||||
(assoc-in [:obj :content] content)
|
||||
(cond->
|
||||
(> (count position-data) 0) (assoc-in [:obj :position-data] position-data)))
|
||||
item)))
|
||||
|
||||
(calculate-paste-position [state mouse-pos in-viewport?]
|
||||
(let [page-objects (wsh/lookup-page-objects state)
|
||||
|
|
|
@ -105,6 +105,9 @@
|
|||
(contains? color :opacity)
|
||||
(assoc :fill-opacity (:opacity color))
|
||||
|
||||
(contains? color :image)
|
||||
(assoc :fill-image (:image color))
|
||||
|
||||
:always
|
||||
(d/without-nils))
|
||||
|
||||
|
@ -223,9 +226,15 @@
|
|||
(assoc :stroke-color-gradient (:gradient attrs))
|
||||
|
||||
(contains? attrs :opacity)
|
||||
(assoc :stroke-opacity (:opacity attrs)))
|
||||
(assoc :stroke-opacity (:opacity attrs))
|
||||
|
||||
attrs (merge attrs color-attrs)]
|
||||
(contains? attrs :image)
|
||||
(assoc :stroke-image (:image attrs)))
|
||||
|
||||
attrs (->
|
||||
(merge attrs color-attrs)
|
||||
(dissoc :image)
|
||||
(dissoc :gradient))]
|
||||
|
||||
(rx/of (dch/update-shapes
|
||||
ids
|
||||
|
@ -455,7 +464,11 @@
|
|||
|
||||
(defn clear-color-components
|
||||
[data]
|
||||
(dissoc data :hex :alpha :r :g :b :h :s :v))
|
||||
(dissoc data :hex :alpha :r :g :b :h :s :v :image))
|
||||
|
||||
(defn clear-image-components
|
||||
[data]
|
||||
(dissoc data :hex :alpha :r :g :b :h :s :v :color))
|
||||
|
||||
(defn- create-gradient
|
||||
[type]
|
||||
|
@ -467,8 +480,14 @@
|
|||
|
||||
(defn get-color-from-colorpicker-state
|
||||
[{:keys [type current-color stops gradient] :as state}]
|
||||
(if (= type :color)
|
||||
(cond
|
||||
(= type :color)
|
||||
(clear-color-components current-color)
|
||||
|
||||
(= type :image)
|
||||
(clear-image-components current-color)
|
||||
|
||||
:else
|
||||
{:gradient (-> gradient
|
||||
(assoc :type (case type
|
||||
:linear-gradient :linear
|
||||
|
@ -487,7 +506,7 @@
|
|||
(on-change color)))))
|
||||
|
||||
(defn initialize-colorpicker
|
||||
[on-change]
|
||||
[on-change tab]
|
||||
(ptk/reify ::initialize-colorpicker
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ stream]
|
||||
|
@ -502,7 +521,14 @@
|
|||
(rx/filter (ptk/type? ::update-colorpicker-color) stream)
|
||||
(rx/filter (ptk/type? ::activate-colorpicker-gradient) stream))
|
||||
(rx/map (constantly (colorpicker-onchange-runner on-change)))
|
||||
(rx/take-until stoper))))))
|
||||
(rx/take-until stoper))))
|
||||
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :colorpicker
|
||||
(fn [state]
|
||||
(-> state
|
||||
(assoc :type tab)))))))
|
||||
|
||||
(defn finalize-colorpicker
|
||||
[]
|
||||
|
@ -522,13 +548,8 @@
|
|||
(let [current-color (:current-color state)]
|
||||
(if (some? gradient)
|
||||
(let [stop (or (:editing-stop state) 0)
|
||||
stops (mapv split-color-components (:stops gradient))
|
||||
type (case (:type gradient)
|
||||
:linear :linear-gradient
|
||||
:radial :radial-gradient
|
||||
(:type state))]
|
||||
stops (mapv split-color-components (:stops gradient))]
|
||||
(-> state
|
||||
(assoc :type type)
|
||||
(assoc :current-color (nth stops stop))
|
||||
(assoc :stops stops)
|
||||
(assoc :gradient (-> gradient
|
||||
|
@ -537,7 +558,6 @@
|
|||
(assoc :editing-stop stop)))
|
||||
|
||||
(-> state
|
||||
(assoc :type :color)
|
||||
(cond-> (or (nil? current-color)
|
||||
(not= (:color data) (:color current-color))
|
||||
(not= (:opacity data) (:opacity current-color)))
|
||||
|
@ -553,9 +573,11 @@
|
|||
(update [_ state]
|
||||
(update state :colorpicker
|
||||
(fn [state]
|
||||
(let [state (-> state
|
||||
(let [type (:type state)
|
||||
state (-> state
|
||||
(update :current-color merge changes)
|
||||
(update :current-color materialize-color-components)
|
||||
(update :current-color #(if (not= type :image) (dissoc % :image) %))
|
||||
;; current color can be a library one I'm changing via colorpicker
|
||||
(d/dissoc-in [:current-color :id])
|
||||
(d/dissoc-in [:current-color :file-id]))]
|
||||
|
@ -564,7 +586,6 @@
|
|||
(merge data)
|
||||
(materialize-color-components))))
|
||||
(-> state
|
||||
(assoc :type :color)
|
||||
(dissoc :gradient :stops :editing-stop)))))))
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
|
@ -592,6 +613,17 @@
|
|||
:editing-stop stop)
|
||||
state))))))
|
||||
|
||||
(defn activate-colorpicker-color
|
||||
[]
|
||||
(ptk/reify ::activate-colorpicker-color
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :colorpicker
|
||||
(fn [state]
|
||||
(-> state
|
||||
(assoc :type :color)
|
||||
(dissoc :editing-stop :stops :gradient)))))))
|
||||
|
||||
(defn activate-colorpicker-gradient
|
||||
[type]
|
||||
(ptk/reify ::activate-colorpicker-gradient
|
||||
|
@ -599,23 +631,32 @@
|
|||
(update [_ state]
|
||||
(update state :colorpicker
|
||||
(fn [state]
|
||||
(if (= type (:type state))
|
||||
(do
|
||||
(-> state
|
||||
(assoc :type :color)
|
||||
(dissoc :editing-stop :stops :gradient)))
|
||||
(let [gradient (create-gradient type)
|
||||
color (:current-color state)]
|
||||
(-> state
|
||||
(assoc :type type)
|
||||
(assoc :gradient gradient)
|
||||
(cond-> (not (:stops state))
|
||||
(assoc :editing-stop 0
|
||||
:stops [(assoc color :offset 0)
|
||||
(-> color
|
||||
(assoc :alpha 0)
|
||||
(assoc :offset 1)
|
||||
(materialize-color-components))]))))))))))
|
||||
(let [gradient (create-gradient type)
|
||||
color (:current-color state)]
|
||||
(-> state
|
||||
(assoc :type type)
|
||||
(assoc :gradient gradient)
|
||||
(d/dissoc-in [:current-color :image])
|
||||
(cond-> (not (:stops state))
|
||||
(assoc :editing-stop 0
|
||||
:stops [(-> color
|
||||
(assoc :offset 0)
|
||||
(materialize-color-components))
|
||||
(-> color
|
||||
(assoc :alpha 0)
|
||||
(assoc :offset 1)
|
||||
(materialize-color-components))])))))))))
|
||||
|
||||
(defn activate-colorpicker-image
|
||||
[]
|
||||
(ptk/reify ::activate-colorpicker-image
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :colorpicker
|
||||
(fn [state]
|
||||
(-> state
|
||||
(assoc :type :image)
|
||||
(dissoc :editing-stop :stops :gradient)))))))
|
||||
|
||||
(defn select-color
|
||||
[position add-color]
|
||||
|
|
|
@ -97,7 +97,8 @@
|
|||
(let [id (uuid/next)
|
||||
color (-> color
|
||||
(assoc :id id)
|
||||
(assoc :name (or (:color color)
|
||||
(assoc :name (or (get-in color [:image :name])
|
||||
(:color color)
|
||||
(uc/gradient-type->string (get-in color [:gradient :type])))))]
|
||||
(dm/assert! ::ctc/color color)
|
||||
(ptk/reify ::add-color
|
||||
|
|
|
@ -78,11 +78,13 @@
|
|||
:height height
|
||||
:x (mth/round (- x (/ width 2)))
|
||||
:y (mth/round (- y (/ height 2)))
|
||||
:metadata {:width width
|
||||
:height height
|
||||
:mtype mtype
|
||||
:id id}}]
|
||||
(rx/of (dwsh/create-and-add-shape :image x y shape))))))
|
||||
:fills [{:fill-opacity 1
|
||||
:fill-image {:name name
|
||||
:width width
|
||||
:height height
|
||||
:mtype mtype
|
||||
:id id}}]}]
|
||||
(rx/of (dwsh/create-and-add-shape :rect x y shape))))))
|
||||
|
||||
(defn svg-uploaded
|
||||
[svg-data file-id position]
|
||||
|
@ -171,64 +173,64 @@
|
|||
[:uris {:optional true} [:sequential :string]]
|
||||
[:mtype {:optional true} :string]])
|
||||
|
||||
(defn handle-media-error [error on-error]
|
||||
(if (ex/ex-info? error)
|
||||
(handle-media-error (ex-data error) on-error)
|
||||
(cond
|
||||
(= (:code error) :invalid-svg-file)
|
||||
(rx/of (msg/error (tr "errors.media-type-not-allowed")))
|
||||
|
||||
(= (:code error) :media-type-not-allowed)
|
||||
(rx/of (msg/error (tr "errors.media-type-not-allowed")))
|
||||
|
||||
(= (:code error) :unable-to-access-to-url)
|
||||
(rx/of (msg/error (tr "errors.media-type-not-allowed")))
|
||||
|
||||
(= (:code error) :invalid-image)
|
||||
(rx/of (msg/error (tr "errors.media-type-not-allowed")))
|
||||
|
||||
(= (:code error) :media-max-file-size-reached)
|
||||
(rx/of (msg/error (tr "errors.media-too-large")))
|
||||
|
||||
(= (:code error) :media-type-mismatch)
|
||||
(rx/of (msg/error (tr "errors.media-type-mismatch")))
|
||||
|
||||
(= (:code error) :unable-to-optimize)
|
||||
(rx/of (msg/error (:hint error)))
|
||||
|
||||
(fn? on-error)
|
||||
(on-error error)
|
||||
|
||||
:else
|
||||
(do
|
||||
(.error js/console "ERROR" error)
|
||||
(rx/of (msg/error (tr "errors.cannot-upload")))))))
|
||||
|
||||
(defn- process-media-objects
|
||||
[{:keys [uris on-error] :as params}]
|
||||
(dm/assert!
|
||||
(and (sm/valid? schema:process-media-objects params)
|
||||
(or (contains? params :blobs)
|
||||
(contains? params :uris))))
|
||||
(and (sm/valid? schema:process-media-objects params)
|
||||
(or (contains? params :blobs)
|
||||
(contains? params :uris))))
|
||||
|
||||
(letfn [(handle-error [error]
|
||||
(if (ex/ex-info? error)
|
||||
(handle-error (ex-data error))
|
||||
(cond
|
||||
(= (:code error) :invalid-svg-file)
|
||||
(rx/of (msg/error (tr "errors.media-type-not-allowed")))
|
||||
|
||||
(= (:code error) :media-type-not-allowed)
|
||||
(rx/of (msg/error (tr "errors.media-type-not-allowed")))
|
||||
|
||||
(= (:code error) :unable-to-access-to-url)
|
||||
(rx/of (msg/error (tr "errors.media-type-not-allowed")))
|
||||
|
||||
(= (:code error) :invalid-image)
|
||||
(rx/of (msg/error (tr "errors.media-type-not-allowed")))
|
||||
|
||||
(= (:code error) :media-max-file-size-reached)
|
||||
(rx/of (msg/error (tr "errors.media-too-large")))
|
||||
|
||||
(= (:code error) :media-type-mismatch)
|
||||
(rx/of (msg/error (tr "errors.media-type-mismatch")))
|
||||
|
||||
(= (:code error) :unable-to-optimize)
|
||||
(rx/of (msg/error (:hint error)))
|
||||
|
||||
(fn? on-error)
|
||||
(on-error error)
|
||||
|
||||
:else
|
||||
(do
|
||||
(.error js/console "ERROR" error)
|
||||
(rx/of (msg/error (tr "errors.cannot-upload")))))))]
|
||||
|
||||
(ptk/reify ::process-media-objects
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/concat
|
||||
(rx/of (msg/show {:content (tr "media.loading")
|
||||
:type :info
|
||||
:timeout nil
|
||||
:tag :media-loading}))
|
||||
(->> (if (seq uris)
|
||||
(ptk/reify ::process-media-objects
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/concat
|
||||
(rx/of (msg/show {:content (tr "media.loading")
|
||||
:type :info
|
||||
:timeout nil
|
||||
:tag :media-loading}))
|
||||
(->> (if (seq uris)
|
||||
;; Media objects is a list of URL's pointing to the path
|
||||
(process-uris params)
|
||||
(process-uris params)
|
||||
;; Media objects are blob of data to be upload
|
||||
(process-blobs params))
|
||||
(process-blobs params))
|
||||
|
||||
;; Every stream has its own sideeffect. We need to ignore the result
|
||||
(rx/ignore)
|
||||
(rx/catch handle-error)
|
||||
(rx/finalize #(st/emit! (msg/hide-tag :media-loading)))))))))
|
||||
(rx/ignore)
|
||||
(rx/catch #(handle-media-error % on-error))
|
||||
(rx/finalize #(st/emit! (msg/hide-tag :media-loading))))))))
|
||||
|
||||
;; Deprecated in components-v2
|
||||
(defn upload-media-asset
|
||||
|
@ -248,6 +250,35 @@
|
|||
(process-media-objects params)))
|
||||
|
||||
|
||||
|
||||
(defn upload-fill-image
|
||||
[file on-success]
|
||||
(dm/assert!
|
||||
"expected a valid blob for `file` param"
|
||||
(dmm/blob? file))
|
||||
(ptk/reify ::upload-fill-image
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [on-upload-success
|
||||
(fn [image]
|
||||
(on-success image)
|
||||
(dmm/notify-finished-loading))
|
||||
|
||||
prepare
|
||||
(fn [content]
|
||||
{:file-id (get-in state [:workspace-file :id])
|
||||
:name (if (dmm/file? content) (.-name content) (tr "media.image"))
|
||||
:is-local false
|
||||
:content content})]
|
||||
|
||||
(dmm/notify-start-loading)
|
||||
(->> (rx/of file)
|
||||
(rx/map dmm/validate-file)
|
||||
(rx/map prepare)
|
||||
(rx/mapcat #(rp/cmd! :upload-file-media-object %))
|
||||
(rx/do on-upload-success)
|
||||
(rx/catch handle-media-error))))))
|
||||
|
||||
;; --- Upload File Media objects
|
||||
|
||||
(defn load-and-parse-svg
|
||||
|
@ -283,7 +314,7 @@
|
|||
|
||||
(defn create-shapes-img
|
||||
"Convert a media object that contains a bitmap image into shapes,
|
||||
one shape of type :image and one group that contains it."
|
||||
one shape of type :rect containing an image fill and one group that contains it."
|
||||
[pos {:keys [name width height id mtype] :as media-obj} & {:keys [wrapper-type] :or {wrapper-type :group}}]
|
||||
(let [group-shape (cts/setup-shape
|
||||
{:type wrapper-type
|
||||
|
@ -296,15 +327,17 @@
|
|||
:parent-id uuid/zero})
|
||||
|
||||
img-shape (cts/setup-shape
|
||||
{:type :image
|
||||
{:type :rect
|
||||
:x (:x pos)
|
||||
:y (:y pos)
|
||||
:width width
|
||||
:height height
|
||||
:metadata {:id id
|
||||
:width width
|
||||
:height height
|
||||
:mtype mtype}
|
||||
:fills [{:fill-opacity 1
|
||||
:fill-image {:name name
|
||||
:id id
|
||||
:width width
|
||||
:height height
|
||||
:mtype mtype}}]
|
||||
:name name
|
||||
:frame-id uuid/zero
|
||||
:parent-id (:id group-shape)})]
|
||||
|
|
|
@ -6,8 +6,11 @@
|
|||
|
||||
(ns app.main.ui.components.color-bullet
|
||||
(:require
|
||||
[app.config :as cfg]
|
||||
[app.util.color :as uc]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc color-bullet
|
||||
|
@ -31,8 +34,15 @@
|
|||
:is-gradient (some? (:gradient color)))
|
||||
:on-click on-click
|
||||
:title (uc/get-color-name color)}
|
||||
(if (:gradient color)
|
||||
(cond
|
||||
(:gradient color)
|
||||
[:div.color-bullet-wrapper {:style {:background (uc/color->background color)}}]
|
||||
|
||||
(:image color)
|
||||
(let [uri (cfg/resolve-file-media (:image color))]
|
||||
[:div.color-bullet-wrapper {:style {:background-size "contain" :background-image (str/ffmt "url(%)" uri)}}])
|
||||
|
||||
:else
|
||||
[:div.color-bullet-wrapper
|
||||
[:div.color-bullet-left {:style {:background (uc/color->background (assoc color :opacity 1))}}]
|
||||
[:div.color-bullet-right {:style {:background (uc/color->background color)}}]])]))))
|
||||
|
@ -40,10 +50,12 @@
|
|||
(mf/defc color-name
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [color size on-click on-double-click]}]
|
||||
(let [{:keys [name color gradient]} (if (string? color) {:color color :opacity 1} color)]
|
||||
(let [{:keys [name color gradient image]} (if (string? color) {:color color :opacity 1} color)]
|
||||
(when (or (not size) (= size :big))
|
||||
[:span.color-text
|
||||
{:on-click on-click
|
||||
:on-double-click on-double-click
|
||||
:title name}
|
||||
(or name color (uc/gradient-type->string (:type gradient)))])))
|
||||
(if (some? image)
|
||||
(tr "media.image")
|
||||
(or name color (uc/gradient-type->string (:type gradient))))])))
|
||||
|
|
|
@ -7,7 +7,10 @@
|
|||
(ns app.main.ui.components.color-bullet-new
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.config :as cfg]
|
||||
[app.util.color :as uc]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc color-bullet
|
||||
|
@ -26,7 +29,8 @@
|
|||
(let [color (if (string? color) {:color color :opacity 1} color)
|
||||
id (:id color)
|
||||
gradient (:gradient color)
|
||||
opacity (:opacity color)]
|
||||
opacity (:opacity color)
|
||||
image (:image color)]
|
||||
[:div
|
||||
{:class (stl/css-case
|
||||
:color-bullet true
|
||||
|
@ -38,21 +42,27 @@
|
|||
:grid-area area)
|
||||
:on-click on-click}
|
||||
|
||||
(if (some? gradient)
|
||||
(cond
|
||||
(some? gradient)
|
||||
[:div {:class (stl/css :color-bullet-wrapper)
|
||||
:style {:background (uc/color->background color)}}]
|
||||
|
||||
(some? image)
|
||||
(let [uri (cfg/resolve-file-media image)]
|
||||
[:div {:class (stl/css :color-bullet-wrapper)
|
||||
:style {:background-image (str/ffmt "url(%)" uri)}}])
|
||||
|
||||
:else
|
||||
[:div {:class (stl/css :color-bullet-wrapper)}
|
||||
[:div {:class (stl/css :color-bullet-left)
|
||||
:style {:background (uc/color->background (assoc color :opacity 1))}}]
|
||||
[:div {:class (stl/css :color-bullet-right)
|
||||
:style {:background (uc/color->background color)}}]])]))))
|
||||
|
||||
|
||||
(mf/defc color-name
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [color size on-click on-double-click]}]
|
||||
(let [{:keys [name color gradient]} (if (string? color) {:color color :opacity 1} color)]
|
||||
(let [{:keys [name color gradient image]} (if (string? color) {:color color :opacity 1} color)]
|
||||
(when (or (not size) (> size 64))
|
||||
[:span {:class (stl/css-case
|
||||
:color-text (< size 72)
|
||||
|
@ -60,4 +70,6 @@
|
|||
:big-text (>= size 72))
|
||||
:on-click on-click
|
||||
:on-double-click on-double-click}
|
||||
(or name color (uc/gradient-type->string (:type gradient)))])))
|
||||
(if (some? image)
|
||||
(tr "media.image")
|
||||
(or name color (uc/gradient-type->string (:type gradient))))])))
|
||||
|
|
|
@ -55,6 +55,9 @@
|
|||
height: 100%;
|
||||
width: 100%;
|
||||
clip-path: circle(50%);
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
.color-bullet-wrapper > * {
|
||||
width: 100%;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
(ns app.main.ui.components.shape-icon
|
||||
(:require
|
||||
[app.common.types.component :as ctk]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.main.ui.icons :as i]
|
||||
[rumext.v2 :as mf]))
|
||||
|
@ -32,10 +33,10 @@
|
|||
:else
|
||||
i/artboard)
|
||||
:image i/image
|
||||
:line i/line
|
||||
:circle i/circle
|
||||
:path i/curve
|
||||
:rect i/box
|
||||
:line (if (cts/has-images? shape) i/image i/line)
|
||||
:circle (if (cts/has-images? shape) i/image i/circle)
|
||||
:path (if (cts/has-images? shape) i/image i/curve)
|
||||
:rect (if (cts/has-images? shape) i/image i/box)
|
||||
:text i/text
|
||||
:group (if (:masked-group shape)
|
||||
i/mask
|
||||
|
@ -47,39 +48,3 @@
|
|||
#_:default i/bool-union)
|
||||
:svg-raw i/file-svg
|
||||
nil)))
|
||||
|
||||
(mf/defc element-icon-refactor
|
||||
[{:keys [shape main-instance?] :as props}]
|
||||
(if (ctk/instance-head? shape)
|
||||
(if main-instance?
|
||||
i/component-refactor
|
||||
i/copy-refactor)
|
||||
(case (:type shape)
|
||||
:frame (cond
|
||||
(and (ctl/flex-layout? shape) (ctl/col? shape))
|
||||
i/flex-vertical-refactor
|
||||
|
||||
(and (ctl/flex-layout? shape) (ctl/row? shape))
|
||||
i/flex-horizontal-refactor
|
||||
|
||||
(ctl/grid-layout? shape)
|
||||
i/grid-refactor
|
||||
|
||||
:else
|
||||
i/board-refactor)
|
||||
:image i/img-refactor
|
||||
:line i/path-refactor
|
||||
:circle i/elipse-refactor
|
||||
:path i/curve-refactor
|
||||
:rect i/rectangle-refactor
|
||||
:text i/text-refactor
|
||||
:group (if (:masked-group shape)
|
||||
i/mask-refactor
|
||||
i/group-refactor)
|
||||
:bool (case (:bool-type shape)
|
||||
:difference i/boolean-difference-refactor
|
||||
:exclude i/boolean-exclude-refactor
|
||||
:intersection i/boolean-intersection-refactor
|
||||
#_:default i/boolean-union-refactor)
|
||||
:svg-raw i/svg-refactor
|
||||
nil)))
|
||||
|
|
|
@ -7,11 +7,11 @@
|
|||
(ns app.main.ui.components.shape-icon-refactor
|
||||
(:require
|
||||
[app.common.types.component :as ctk]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.main.ui.icons :as i]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
|
||||
(mf/defc element-icon-refactor
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [shape main-instance?]}]
|
||||
|
@ -33,10 +33,10 @@
|
|||
i/board-refactor)
|
||||
;; TODO -> THUMBNAIL ICON
|
||||
:image i/img-refactor
|
||||
:line i/path-refactor
|
||||
:circle i/elipse-refactor
|
||||
:path i/path-refactor
|
||||
:rect i/rectangle-refactor
|
||||
:line (if (cts/has-images? shape) i/img-refactor i/path-refactor)
|
||||
:circle (if (cts/has-images? shape) i/img-refactor i/elipse-refactor)
|
||||
:path (if (cts/has-images? shape) i/img-refactor i/curve-refactor)
|
||||
:rect (if (cts/has-images? shape) i/img-refactor i/rectangle-refactor)
|
||||
:text i/text-refactor
|
||||
:group (if (:masked-group shape)
|
||||
i/mask-refactor
|
||||
|
|
|
@ -62,14 +62,14 @@
|
|||
|
||||
(defn add-fill!
|
||||
[attrs fill-data render-id index type]
|
||||
(let [index (if (some? index) (dm/str "_" index) "")]
|
||||
(let [index (if (some? index) (dm/str "-" index) "")]
|
||||
(cond
|
||||
(contains? fill-data :fill-image)
|
||||
(let [id (dm/str "fill-image-" render-id)]
|
||||
(obj/set! attrs "fill" (dm/str "url(#" id ")")))
|
||||
|
||||
(some? (:fill-color-gradient fill-data))
|
||||
(let [id (dm/str "fill-color-gradient_" render-id index)]
|
||||
(let [id (dm/str "fill-color-gradient-" render-id index)]
|
||||
(obj/set! attrs "fill" (dm/str "url(#" id ")")))
|
||||
|
||||
(contains? fill-data :fill-color)
|
||||
|
@ -100,7 +100,7 @@
|
|||
(obj/set! attrs "strokeWidth" width)
|
||||
|
||||
(when (some? gradient)
|
||||
(let [gradient-id (dm/str "stroke-color-gradient_" render-id "_" index)]
|
||||
(let [gradient-id (dm/str "stroke-color-gradient-" render-id "-" index)]
|
||||
(obj/set! attrs "stroke" (str/ffmt "url(#%)" gradient-id))))
|
||||
|
||||
(when-not (some? gradient)
|
||||
|
|
|
@ -13,8 +13,10 @@
|
|||
[app.common.geom.shapes.bounds :as gsb]
|
||||
[app.common.geom.shapes.text :as gst]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.config :as cfg]
|
||||
[app.main.ui.context :as muc]
|
||||
[app.main.ui.shapes.attrs :as attrs]
|
||||
[app.main.ui.shapes.embed :as embed]
|
||||
[app.main.ui.shapes.gradients :as grad]
|
||||
[app.util.object :as obj]
|
||||
[cuerdas.core :as str]
|
||||
|
@ -83,11 +85,18 @@
|
|||
(let [id-prefix (dm/str "marker-" render-id)
|
||||
|
||||
gradient (:stroke-color-gradient stroke)
|
||||
image (:stroke-image stroke)
|
||||
cap-start (:stroke-cap-start stroke)
|
||||
cap-end (:stroke-cap-end stroke)
|
||||
|
||||
color (if (some? gradient)
|
||||
color (cond
|
||||
(some? gradient)
|
||||
(str/ffmt "url(#stroke-color-gradient-%s-%s)" render-id index)
|
||||
|
||||
(some? image)
|
||||
(str/ffmt "url(#stroke-fill-%-%)" render-id index)
|
||||
|
||||
:else
|
||||
(:stroke-color stroke))
|
||||
|
||||
opacity (when-not (some? gradient)
|
||||
|
@ -192,21 +201,57 @@
|
|||
(mf/defc stroke-defs
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [shape stroke render-id index]}]
|
||||
(let [open-path? (and ^boolean (cph/path-shape? shape)
|
||||
^boolean (gsh/open-path? shape))
|
||||
gradient (:stroke-color-gradient stroke)
|
||||
alignment (:stroke-alignment stroke :center)
|
||||
width (:stroke-width stroke 0)
|
||||
(let [open-path? (and ^boolean (cph/path-shape? shape)
|
||||
^boolean (gsh/open-path? shape))
|
||||
gradient (:stroke-color-gradient stroke)
|
||||
alignment (:stroke-alignment stroke :center)
|
||||
width (:stroke-width stroke 0)
|
||||
|
||||
props #js {:id (dm/str "stroke-color-gradient-" render-id "-" index)
|
||||
:gradient gradient
|
||||
:shape shape}]
|
||||
props #js {:id (dm/str "stroke-color-gradient-" render-id "-" index)
|
||||
:gradient gradient
|
||||
:shape shape}
|
||||
stroke-image (:stroke-image stroke)
|
||||
uri (when stroke-image (cfg/resolve-file-media stroke-image))
|
||||
embed (embed/use-data-uris [uri])
|
||||
|
||||
stroke-width (case (:stroke-alignment stroke :center)
|
||||
:center (/ (:stroke-width stroke 0) 2)
|
||||
:outer (:stroke-width stroke 0)
|
||||
0)
|
||||
margin (gsb/shape-stroke-margin stroke stroke-width)
|
||||
|
||||
selrect (mf/with-memo [shape]
|
||||
(if (cph/text-shape? shape)
|
||||
(gst/shape->rect shape)
|
||||
(grc/points->rect (:points shape))))
|
||||
|
||||
stroke-margin (+ stroke-width margin)
|
||||
|
||||
w (+ (dm/get-prop selrect :width) (* 2 stroke-margin))
|
||||
h (+ (dm/get-prop selrect :height) (* 2 stroke-margin))
|
||||
image-props #js {:href (get embed uri uri)
|
||||
:preserveAspectRatio "xMidYMid slice"
|
||||
:width 1
|
||||
:height 1
|
||||
:id (dm/str "stroke-image-" render-id "-" index)}]
|
||||
[:*
|
||||
(when (some? gradient)
|
||||
(case (:type gradient)
|
||||
:linear [:> grad/linear-gradient props]
|
||||
:radial [:> grad/radial-gradient props]))
|
||||
|
||||
(when (:stroke-image stroke)
|
||||
;; We need to make the pattern size and the image fit so it's not repeated
|
||||
[:pattern {:id (dm/str "stroke-fill-" render-id "-" index)
|
||||
:patternContentUnits "objectBoundingBox"
|
||||
:x (- (/ stroke-margin (dm/get-prop selrect :width)))
|
||||
:y (- (/ stroke-margin (dm/get-prop selrect :height)))
|
||||
:width (/ w (dm/get-prop selrect :width))
|
||||
:height (/ h (dm/get-prop selrect :height))
|
||||
:viewBox "0 0 1 1"
|
||||
:preserveAspectRatio "xMidYMid slice"}
|
||||
[:> :image image-props]])
|
||||
|
||||
(cond
|
||||
(and (not open-path?)
|
||||
(= :inner alignment)
|
||||
|
@ -345,6 +390,7 @@
|
|||
index (unchecked-get props "index")
|
||||
|
||||
render-id (mf/use-ctx muc/render-id)
|
||||
render-id (d/nilv (unchecked-get props "render-id") render-id)
|
||||
|
||||
stroke-width (:stroke-width stroke 0)
|
||||
stroke-style (:stroke-style stroke :none)
|
||||
|
@ -385,7 +431,8 @@
|
|||
url-fill? (or ^boolean (some? (:fill-image shape))
|
||||
^boolean (cph/image-shape? shape)
|
||||
^boolean (> (count shape-fills) 1)
|
||||
^boolean (some? (some :fill-color-gradient shape-fills)))
|
||||
^boolean (some? (some :fill-color-gradient shape-fills))
|
||||
^boolean (some? (some :fill-image shape-fills)))
|
||||
|
||||
props (if (cph/frame-shape? shape)
|
||||
props
|
||||
|
@ -447,6 +494,10 @@
|
|||
(obj/set! "fillOpacity" "none")
|
||||
(obj/merge! (attrs/get-stroke-style value position render-id)))
|
||||
|
||||
style (if (:stroke-image value)
|
||||
(obj/set! style "stroke" (dm/fmt "url(#stroke-fill-%-%)" render-id position))
|
||||
style)
|
||||
|
||||
props (-> (obj/clone props)
|
||||
(obj/unset! "fill")
|
||||
(obj/unset! "fillOpacity")
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
importation."
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.svg :as csvg]
|
||||
[app.main.ui.context :as muc]
|
||||
|
@ -282,36 +283,47 @@
|
|||
|
||||
|
||||
(defn- export-fills-data [{:keys [fills]}]
|
||||
(when-let [fills (seq fills)]
|
||||
(mf/html
|
||||
[:> "penpot:fills" #js {}
|
||||
(for [[index fill] (d/enumerate fills)]
|
||||
[:> "penpot:fill"
|
||||
#js {:penpot:fill-color (if (some? (:fill-color-gradient fill))
|
||||
(str/format "url(#%s)" (str "fill-color-gradient_" (mf/use-ctx muc/render-id) "_" index))
|
||||
(d/name (:fill-color fill)))
|
||||
:penpot:fill-color-ref-file (d/name (:fill-color-ref-file fill))
|
||||
:penpot:fill-color-ref-id (d/name (:fill-color-ref-id fill))
|
||||
:penpot:fill-opacity (d/name (:fill-opacity fill))}])])))
|
||||
(when-let [fills (seq fills)]
|
||||
(let [render-id (mf/use-ctx muc/render-id)]
|
||||
(mf/html
|
||||
[:> "penpot:fills" #js {}
|
||||
(for [[index fill] (d/enumerate fills)]
|
||||
(let [fill-image-id (dm/str "fill-image-" render-id "-" index)]
|
||||
[:> "penpot:fill"
|
||||
#js {:penpot:fill-color (cond
|
||||
(some? (:fill-color-gradient fill))
|
||||
(str/format "url(#%s)" (str "fill-color-gradient-" render-id "-" index))
|
||||
|
||||
:else
|
||||
(d/name (:fill-color fill)))
|
||||
:penpot:fill-image-id (when (:fill-image fill) fill-image-id)
|
||||
:penpot:fill-color-ref-file (d/name (:fill-color-ref-file fill))
|
||||
:penpot:fill-color-ref-id (d/name (:fill-color-ref-id fill))
|
||||
:penpot:fill-opacity (d/name (:fill-opacity fill))}]))]))))
|
||||
|
||||
(defn- export-strokes-data [{:keys [strokes]}]
|
||||
(when-let [strokes (seq strokes)]
|
||||
(mf/html
|
||||
[:> "penpot:strokes" #js {}
|
||||
(for [[index stroke] (d/enumerate strokes)]
|
||||
[:> "penpot:stroke"
|
||||
#js {:penpot:stroke-color (if (some? (:stroke-color-gradient stroke))
|
||||
(str/format "url(#%s)" (str "stroke-color-gradient_" (mf/use-ctx muc/render-id) "_" index))
|
||||
(d/name (:stroke-color stroke)))
|
||||
:penpot:stroke-color-ref-file (d/name (:stroke-color-ref-file stroke))
|
||||
:penpot:stroke-color-ref-id (d/name (:stroke-color-ref-id stroke))
|
||||
:penpot:stroke-opacity (d/name (:stroke-opacity stroke))
|
||||
:penpot:stroke-style (d/name (:stroke-style stroke))
|
||||
:penpot:stroke-width (d/name (:stroke-width stroke))
|
||||
:penpot:stroke-alignment (d/name (:stroke-alignment stroke))
|
||||
:penpot:stroke-cap-start (d/name (:stroke-cap-start stroke))
|
||||
:penpot:stroke-cap-end (d/name (:stroke-cap-end stroke))}])])))
|
||||
(let [render-id (mf/use-ctx muc/render-id)]
|
||||
(mf/html
|
||||
[:> "penpot:strokes" #js {}
|
||||
(for [[index stroke] (d/enumerate strokes)]
|
||||
(let [stroke-image-id (dm/str "stroke-image-" render-id "-" index)]
|
||||
[:> "penpot:stroke"
|
||||
#js {:penpot:stroke-color (cond
|
||||
(some? (:stroke-color-gradient stroke))
|
||||
(str/format "url(#%s)" (str "stroke-color-gradient-" render-id "-" index))
|
||||
|
||||
:else
|
||||
(d/name (:stroke-color stroke)))
|
||||
:penpot:stroke-image-id (when (:stroke-image stroke) stroke-image-id)
|
||||
:penpot:stroke-color-ref-file (d/name (:stroke-color-ref-file stroke))
|
||||
:penpot:stroke-color-ref-id (d/name (:stroke-color-ref-id stroke))
|
||||
:penpot:stroke-opacity (d/name (:stroke-opacity stroke))
|
||||
:penpot:stroke-style (d/name (:stroke-style stroke))
|
||||
:penpot:stroke-width (d/name (:stroke-width stroke))
|
||||
:penpot:stroke-alignment (d/name (:stroke-alignment stroke))
|
||||
:penpot:stroke-cap-start (d/name (:stroke-cap-start stroke))
|
||||
:penpot:stroke-cap-end (d/name (:stroke-cap-end stroke))}]))]))))
|
||||
|
||||
(defn- export-interactions-data [{:keys [interactions]}]
|
||||
(when-let [interactions (seq interactions)]
|
||||
|
@ -461,5 +473,5 @@
|
|||
(export-strokes-data shape)
|
||||
(export-grid-data shape)
|
||||
(export-layout-container-data shape)
|
||||
(export-layout-item-data shape)]))
|
||||
(export-layout-item-data shape)]))
|
||||
|
||||
|
|
|
@ -36,21 +36,26 @@
|
|||
height (dm/get-prop selrect :height)
|
||||
|
||||
has-image? (or (some? metadata)
|
||||
(some? image))
|
||||
(some? image))
|
||||
|
||||
uri (cond
|
||||
(some? metadata)
|
||||
(cfg/resolve-file-media metadata)
|
||||
uri (cond
|
||||
(some? metadata)
|
||||
(cfg/resolve-file-media metadata)
|
||||
|
||||
(some? image)
|
||||
(cfg/resolve-file-media image))
|
||||
(some? image)
|
||||
(cfg/resolve-file-media image))
|
||||
|
||||
embed (embed/use-data-uris [uri])
|
||||
uris (into [uri]
|
||||
(comp
|
||||
(keep :fill-image)
|
||||
(map cfg/resolve-file-media))
|
||||
fills)
|
||||
|
||||
embed (embed/use-data-uris uris)
|
||||
transform (gsh/transform-str shape)
|
||||
|
||||
;; When tru e the image has not loaded yet
|
||||
loading? (and (some? uri)
|
||||
(not (contains? embed uri)))
|
||||
;; When true the image has not loaded yet
|
||||
loading? (not-any? (partial contains? embed) uris)
|
||||
|
||||
pat-props #js {:patternUnits "userSpaceOnUse"
|
||||
:x x
|
||||
|
@ -65,10 +70,10 @@
|
|||
|
||||
(for [[shape-index shape] (d/enumerate (or (:position-data shape) [shape]))]
|
||||
[:* {:key (dm/str shape-index)}
|
||||
(for [[fill-index value] (reverse (d/enumerate fills))]
|
||||
(for [[fill-index value] (reverse (d/enumerate (get shape :fills [])))]
|
||||
(when (some? (:fill-color-gradient value))
|
||||
(let [gradient (:fill-color-gradient value)
|
||||
props #js {:id (dm/str "fill-color-gradient_" render-id "_" fill-index)
|
||||
props #js {:id (dm/str "fill-color-gradient-" render-id "-" fill-index)
|
||||
:key (dm/str fill-index)
|
||||
:gradient gradient
|
||||
:shape shape}]
|
||||
|
@ -84,13 +89,23 @@
|
|||
(-> (obj/set! "width" (* width no-repeat-padding))
|
||||
(obj/set! "height" (* height no-repeat-padding)))))
|
||||
[:g
|
||||
(for [[fill-index value] (reverse (d/enumerate fills))]
|
||||
(for [[fill-index value] (reverse (d/enumerate (get shape :fills [])))]
|
||||
(let [style (attrs/get-fill-style value fill-index render-id type)
|
||||
props #js {:key (dm/str fill-index)
|
||||
:width width
|
||||
:height height
|
||||
:style style}]
|
||||
[:> :rect props]))
|
||||
(if (:fill-image value)
|
||||
(let [uri (cfg/resolve-file-media (:fill-image value))
|
||||
image-props #js {:id (dm/str "fill-image-" render-id "-" fill-index)
|
||||
:href (get embed uri uri)
|
||||
:preserveAspectRatio "xMidYMid slice"
|
||||
:width width
|
||||
:height height
|
||||
:key (dm/str fill-index)
|
||||
:opacity (:fill-opacity value)}]
|
||||
[:> :image image-props])
|
||||
[:> :rect props])))
|
||||
|
||||
(when ^boolean has-image?
|
||||
[:g
|
||||
|
@ -121,5 +136,6 @@
|
|||
(or (= type :image)
|
||||
(= type :text))
|
||||
(> (count fills) 1)
|
||||
(some :fill-color-gradient fills))
|
||||
(some :fill-color-gradient fills)
|
||||
(some :fill-image fills))
|
||||
[:> fills* props])))
|
||||
|
|
|
@ -125,7 +125,7 @@
|
|||
|
||||
id (if (some? id)
|
||||
id
|
||||
(dm/str (name attr) "_" rid))
|
||||
(dm/str (name attr) "-" rid))
|
||||
|
||||
gradient (get shape attr)
|
||||
props #js {:id id
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
(attrs/add-border-props! shape))
|
||||
get-gradient-id
|
||||
(fn [index]
|
||||
(str render-id "_" (:id shape) "_" index))]
|
||||
(str render-id "-" (:id shape) "-" index))]
|
||||
|
||||
[:*
|
||||
;; Definition of gradients for partial elements
|
||||
|
@ -62,7 +62,7 @@
|
|||
[:defs
|
||||
(for [[index data] (d/enumerate position-data)]
|
||||
(when (some? (:fill-color-gradient data))
|
||||
(let [id (dm/str "fill-color-gradient_" (get-gradient-id index))]
|
||||
(let [id (dm/str "fill-color-gradient-" (get-gradient-id index))]
|
||||
[:& grad/gradient {:id id
|
||||
:key id
|
||||
:attr :fill-color-gradient
|
||||
|
@ -99,6 +99,6 @@
|
|||
(obj/merge! browser-props)))
|
||||
shape (assoc shape :fills (:fills data))]
|
||||
|
||||
[:& (mf/provider muc/render-id) {:key index :value (str render-id "_" (:id shape) "_" index)}
|
||||
[:& (mf/provider muc/render-id) {:key index :value render-id}
|
||||
[:& shape-custom-strokes {:shape shape :position index :render-id render-id}
|
||||
[:> :text props (:text data)]]]))]]))
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.colors :as cc]
|
||||
[app.common.media :as cm]
|
||||
[app.config :as cf]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.color-bullet :refer [color-bullet color-name]]
|
||||
|
@ -49,36 +51,64 @@
|
|||
colors-library (get-colors-library color)
|
||||
file-colors (get-file-colors)
|
||||
color-library-name (get-in (or colors-library file-colors) [(:id color) :name])
|
||||
color (assoc color :color-library-name color-library-name)]
|
||||
color (assoc color :color-library-name color-library-name)
|
||||
image (:image color)]
|
||||
|
||||
(if new-css-system
|
||||
[:div {:class (stl/css :attributes-color-row)}
|
||||
[:div {:class (stl/css :bullet-wrapper)
|
||||
:style #js {"--bullet-size" "16px"}}
|
||||
[:& cbn/color-bullet {:color color
|
||||
:mini? true}]]
|
||||
[:*
|
||||
[:div {:class (stl/css :attributes-color-row)}
|
||||
[:div {:class (stl/css :bullet-wrapper)
|
||||
:style #js {"--bullet-size" "16px"}}
|
||||
[:& cbn/color-bullet {:color color
|
||||
:mini? true}]]
|
||||
|
||||
[:div {:class (stl/css :format-wrapper)}
|
||||
(when-not (and on-change-format (:gradient color))
|
||||
(when-not image
|
||||
[:div {:class (stl/css :format-wrapper)}
|
||||
(when-not (and on-change-format (or (:gradient color) image))
|
||||
[:div {:class (stl/css :select-format-wrapper)}
|
||||
[:& select
|
||||
{:default-value format
|
||||
:options [{:value :hex :label (tr "inspect.attributes.color.hex")}
|
||||
{:value :rgba :label (tr "inspect.attributes.color.rgba")}
|
||||
{:value :hsla :label (tr "inspect.attributes.color.hsla")}]
|
||||
:on-change on-change-format}]])
|
||||
(when (:gradient color)
|
||||
[:div {:class (stl/css :format-info)} "rgba"])])
|
||||
|
||||
[:div {:class (stl/css :select-format-wrapper)}
|
||||
[:& select
|
||||
{:default-value format
|
||||
:options [{:value :hex :label (tr "inspect.attributes.color.hex")}
|
||||
{:value :rgba :label (tr "inspect.attributes.color.rgba")}
|
||||
{:value :hsla :label (tr "inspect.attributes.color.hsla")}]
|
||||
:on-change on-change-format}]])
|
||||
(when (:gradient color)
|
||||
[:div {:class (stl/css :format-info)} "rgba"])]
|
||||
(if (and copy-data (not image))
|
||||
[:& copy-button {:data copy-data
|
||||
:class (stl/css :color-row-copy-btn)}
|
||||
[:*
|
||||
[:div {:class (stl/css :first-row)}
|
||||
[:div {:class (stl/css :name-opacity)}
|
||||
[:span {:class (stl/css-case :color-value-wrapper true
|
||||
:gradient-name (:gradient color))}
|
||||
(if (:gradient color)
|
||||
[:& cbn/color-name {:color color
|
||||
:size 80}]
|
||||
(case format
|
||||
:hex [:& cbn/color-name {:color color
|
||||
:size 80}]
|
||||
:rgba (let [[r g b a] (cc/hex->rgba (:color color) (:opacity color))]
|
||||
[:* (str/fmt "%s, %s, %s, %s" r g b a)])
|
||||
:hsla (let [[h s l a] (cc/hex->hsla (:color color) (:opacity color))
|
||||
result (cc/format-hsla [h s l a])]
|
||||
[:* result])))]
|
||||
|
||||
(if copy-data
|
||||
[:& copy-button {:data copy-data
|
||||
:class (stl/css :color-row-copy-btn)}
|
||||
[:*
|
||||
(when-not (:gradient color)
|
||||
[:span {:class (stl/css :opacity-info)}
|
||||
(str (* 100 (:opacity color)) "%")])]]
|
||||
|
||||
(when color-library-name
|
||||
[:div {:class (stl/css :second-row)}
|
||||
[:div {:class (stl/css :color-name-library)}
|
||||
color-library-name]])]]
|
||||
|
||||
[:div {:class (stl/css :color-info)}
|
||||
[:div {:class (stl/css :first-row)}
|
||||
[:div {:class (stl/css :name-opacity)}
|
||||
[:span {:class (stl/css-case :color-value-wrapper true
|
||||
:gradient-name (:gradient color))}
|
||||
:gradient-name (:gradient color))}
|
||||
(if (:gradient color)
|
||||
[:& cbn/color-name {:color color
|
||||
:size 80}]
|
||||
|
@ -98,90 +128,66 @@
|
|||
(when color-library-name
|
||||
[:div {:class (stl/css :second-row)}
|
||||
[:div {:class (stl/css :color-name-library)}
|
||||
color-library-name]])]]
|
||||
color-library-name]])])]
|
||||
|
||||
[:div {:class (stl/css :color-info)}
|
||||
[:div {:class (stl/css :first-row)}
|
||||
[:div {:class (stl/css :name-opacity)}
|
||||
[:span {:class (stl/css-case :color-value-wrapper true
|
||||
:gradient-name (:gradient color))}
|
||||
(if (:gradient color)
|
||||
[:& cbn/color-name {:color color
|
||||
:size 80}]
|
||||
(case format
|
||||
:hex [:& cbn/color-name {:color color
|
||||
:size 80}]
|
||||
:rgba (let [[r g b a] (cc/hex->rgba (:color color) (:opacity color))]
|
||||
[:* (str/fmt "%s, %s, %s, %s" r g b a)])
|
||||
:hsla (let [[h s l a] (cc/hex->hsla (:color color) (:opacity color))
|
||||
result (cc/format-hsla [h s l a])]
|
||||
[:* result])))]
|
||||
(when image
|
||||
(let [mtype (-> image :mtype)
|
||||
name (or (:name image) (tr "media.image"))
|
||||
extension (cm/mtype->extension mtype)]
|
||||
[:a {:class (stl/css :download-button)
|
||||
:target "_blank"
|
||||
:download (cond-> name extension (str/concat extension))
|
||||
:href (cf/resolve-file-media image)}
|
||||
(tr "inspect.attributes.image.download")]))]
|
||||
|
||||
(when-not (:gradient color)
|
||||
[:span {:class (stl/css :opacity-info)}
|
||||
(str (* 100 (:opacity color)) "%")])]]
|
||||
[:*
|
||||
[:div.attributes-color-row
|
||||
(when color-library-name
|
||||
[:div.attributes-color-id
|
||||
[:& color-bullet {:color color}]
|
||||
[:div color-library-name]])
|
||||
|
||||
(when color-library-name
|
||||
[:div {:class (stl/css :second-row)}
|
||||
[:div {:class (stl/css :color-name-library)}
|
||||
color-library-name]])
|
||||
;; [:span {:class (stl/css-case :color-name-wrapper true
|
||||
;; :gradient-color (:gradient color))}
|
||||
[:div.attributes-color-value {:class (when color-library-name "hide-color")}
|
||||
[:& color-bullet {:color color}]
|
||||
|
||||
;; [:div {:class (stl/css :color-value-wrapper)}
|
||||
;; (if (:gradient color)
|
||||
;; [:& cbn/color-name {:color color
|
||||
;; :size 80}]
|
||||
;; (case format
|
||||
;; :hex [:& cbn/color-name {:color color
|
||||
;; :size 80}]
|
||||
;; :rgba (let [[r g b a] (cc/hex->rgba (:color color) (:opacity color))]
|
||||
;; [:* (str/fmt "%s, %s, %s, %s" r g b a)])
|
||||
;; :hsla (let [[h s l a] (cc/hex->hsla (:color color) (:opacity color))
|
||||
;; result (cc/format-hsla [h s l a])]
|
||||
;; [:* result])))]
|
||||
(cond
|
||||
(:gradient color)
|
||||
[:& color-name {:color color}]
|
||||
|
||||
;; (when color-library-name
|
||||
;; [:div {:class (stl/css :color-name-library)}
|
||||
;; color-library-name])]
|
||||
(= format :rgba)
|
||||
(let [[r g b a] (cc/hex->rgba (:color color) (:opacity color))]
|
||||
[:div (str/fmt "%s, %s, %s, %s" r g b a)])
|
||||
|
||||
;; (when-not (:gradient color)
|
||||
;; [:div {:class (stl/css :opacity-info)}
|
||||
;; (str (* 100 (:opacity color)) "%")])
|
||||
])]
|
||||
(= format :hsla)
|
||||
(let [[h s l a] (cc/hex->hsla (:color color) (:opacity color))
|
||||
result (cc/format-hsla [h s l a])]
|
||||
[:div result])
|
||||
|
||||
:else
|
||||
[:*
|
||||
[:& color-name {:color color}]
|
||||
(when-not (:gradient color) [:div (str (* 100 (:opacity color)) "%")])])
|
||||
|
||||
[:div.attributes-color-row
|
||||
(when color-library-name
|
||||
[:div.attributes-color-id
|
||||
[:& color-bullet {:color color}]
|
||||
[:div color-library-name]])
|
||||
(when-not (and on-change-format (or (:gradient color) image))
|
||||
[:select.color-format-select {:on-change #(-> (dom/get-target-val %) keyword on-change-format)}
|
||||
[:option {:value "hex"}
|
||||
(tr "inspect.attributes.color.hex")]
|
||||
|
||||
[:div.attributes-color-value {:class (when color-library-name "hide-color")}
|
||||
[:& color-bullet {:color color}]
|
||||
[:option {:value "rgba"}
|
||||
(tr "inspect.attributes.color.rgba")]
|
||||
|
||||
(if (:gradient color)
|
||||
[:& color-name {:color color}]
|
||||
(case format
|
||||
:rgba (let [[r g b a] (cc/hex->rgba (:color color) (:opacity color))]
|
||||
[:div (str/fmt "%s, %s, %s, %s" r g b a)])
|
||||
:hsla (let [[h s l a] (cc/hex->hsla (:color color) (:opacity color))
|
||||
result (cc/format-hsla [h s l a])]
|
||||
[:div result])
|
||||
[:*
|
||||
[:& color-name {:color color}]
|
||||
(when-not (:gradient color) [:div (str (* 100 (:opacity color)) "%")])]))
|
||||
[:option {:value "hsla"}
|
||||
(tr "inspect.attributes.color.hsla")]])]
|
||||
|
||||
(when-not (and on-change-format (:gradient color))
|
||||
[:select.color-format-select {:on-change #(-> (dom/get-target-val %) keyword on-change-format)}
|
||||
[:option {:value "hex"}
|
||||
(tr "inspect.attributes.color.hex")]
|
||||
(when (and copy-data (not image))
|
||||
[:& copy-button {:data copy-data}])]
|
||||
|
||||
[:option {:value "rgba"}
|
||||
(tr "inspect.attributes.color.rgba")]
|
||||
|
||||
[:option {:value "hsla"}
|
||||
(tr "inspect.attributes.color.hsla")]])]
|
||||
(when copy-data
|
||||
[:& copy-button {:data copy-data}])])))
|
||||
(when image
|
||||
(let [mtype (-> image :mtype)
|
||||
name (or (:name image) (tr "media.image"))
|
||||
extension (cm/mtype->extension mtype)]
|
||||
[:a.download-button {:target "_blank"
|
||||
:download (cond-> name extension (str/concat extension))
|
||||
:href (cf/resolve-file-media image)}
|
||||
(tr "inspect.attributes.image.download")]))])))
|
||||
|
||||
|
|
|
@ -72,7 +72,6 @@
|
|||
@include titleTipography;
|
||||
color: var(--menu-foreground-color);
|
||||
padding: $s-8 0;
|
||||
height: $s-32;
|
||||
}
|
||||
|
||||
button {
|
||||
|
@ -129,3 +128,10 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.download-button {
|
||||
@extend .button-secondary;
|
||||
@include tabTitleTipography;
|
||||
height: $s-32;
|
||||
margin-top: $s-4;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,8 @@
|
|||
:opacity (:fill-opacity shape)
|
||||
:gradient (:fill-color-gradient shape)
|
||||
:id (:fill-color-ref-id shape)
|
||||
:file-id (:fill-color-ref-file shape)})
|
||||
:file-id (:fill-color-ref-file shape)
|
||||
:image (:fill-image shape)})
|
||||
|
||||
(defn has-fill? [shape]
|
||||
(and
|
||||
|
|
|
@ -25,7 +25,8 @@
|
|||
:opacity (:stroke-opacity shape)
|
||||
:gradient (:stroke-color-gradient shape)
|
||||
:id (:stroke-color-ref-id shape)
|
||||
:file-id (:stroke-color-ref-file shape)})
|
||||
:file-id (:stroke-color-ref-file shape)
|
||||
:image (:stroke-image shape)})
|
||||
|
||||
(defn has-stroke? [shape]
|
||||
(seq (:strokes shape)))
|
||||
|
|
|
@ -35,12 +35,13 @@
|
|||
(get-in state [:viewer-libraries file-id :data :typographies]))]
|
||||
#(l/derived get-library st/state)))
|
||||
|
||||
(defn fill->color [{:keys [fill-color fill-opacity fill-color-gradient fill-color-ref-id fill-color-ref-file]}]
|
||||
(defn fill->color [{:keys [fill-color fill-opacity fill-color-gradient fill-color-ref-id fill-color-ref-file fill-image]}]
|
||||
{:color fill-color
|
||||
:opacity fill-opacity
|
||||
:gradient fill-color-gradient
|
||||
:id fill-color-ref-id
|
||||
:file-id fill-color-ref-file})
|
||||
:file-id fill-color-ref-file
|
||||
:image fill-image})
|
||||
|
||||
(defn copy-style-data
|
||||
[style & properties]
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
[app.common.types.component :as ctk]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.ui.components.shape-icon :as si]
|
||||
[app.main.ui.components.shape-icon-refactor :as sir]
|
||||
[app.main.ui.components.tab-container :refer [tab-container tab-element]]
|
||||
[app.main.ui.components.tabs-container :refer [tabs-container tabs-element]]
|
||||
[app.main.ui.context :as ctx]
|
||||
|
@ -97,7 +98,7 @@
|
|||
[:span {:class (stl/css :layer-title)} (tr "inspect.tabs.code.selected.multiple" (count shapes))]]
|
||||
[:*
|
||||
[:span {:class (stl/css :shape-icon)}
|
||||
[:& si/element-icon-refactor {:shape first-shape :main-instance? main-instance?}]]
|
||||
[:& sir/element-icon-refactor {:shape first-shape :main-instance? main-instance?}]]
|
||||
;; Execution time translation strings:
|
||||
;; inspect.tabs.code.selected.circle
|
||||
;; inspect.tabs.code.selected.component
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
(mf/defc palette
|
||||
[{:keys [current-colors size width]}]
|
||||
(let [;; We had to do this due to a bug that leave some bugged colors
|
||||
current-colors (h/use-equal-memo (filter #(or (:gradient %) (:color %)) current-colors))
|
||||
current-colors (h/use-equal-memo (filter #(or (:gradient %) (:color %) (:image %)) current-colors))
|
||||
state (mf/use-state {:show-menu false})
|
||||
offset-step (cond
|
||||
(<= size 64) 40
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
(mf/defc palette
|
||||
[{:keys [current-colors recent-colors file-colors shared-libs selected on-select]}]
|
||||
(let [;; We had to do this due to a bug that leave some bugged colors
|
||||
current-colors (h/use-equal-memo (filter #(or (:gradient %) (:color %)) current-colors))
|
||||
current-colors (h/use-equal-memo (filter #(or (:gradient %) (:color %) (:image %)) current-colors))
|
||||
state (mf/use-state {:show-menu false})
|
||||
|
||||
width (:width @state 0)
|
||||
|
|
|
@ -8,12 +8,17 @@
|
|||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.colors :as cc]
|
||||
[app.common.data :as d]
|
||||
[app.config :as cfg]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.workspace.colors :as dc]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.media :as dwm]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.file-uploader :refer [file-uploader]]
|
||||
[app.main.ui.components.select :refer [select]]
|
||||
[app.main.ui.components.tab-container :refer [tab-container tab-element]]
|
||||
[app.main.ui.context :as ctx]
|
||||
[app.main.ui.icons :as i]
|
||||
|
@ -46,22 +51,50 @@
|
|||
;; --- Color Picker Modal
|
||||
|
||||
(mf/defc colorpicker
|
||||
[{:keys [data disable-gradient disable-opacity on-change on-accept]}]
|
||||
(let [new-css-system (mf/use-ctx ctx/new-css-system)
|
||||
state (mf/deref refs/colorpicker)
|
||||
node-ref (mf/use-ref)
|
||||
[{:keys [data disable-gradient disable-opacity disable-image on-change on-accept]}]
|
||||
(let [new-css-system (mf/use-ctx ctx/new-css-system)
|
||||
state (mf/deref refs/colorpicker)
|
||||
node-ref (mf/use-ref)
|
||||
|
||||
;; TODO: I think we need to put all this picking state under
|
||||
;; the same object for avoid creating adhoc refs for each
|
||||
;; value
|
||||
picking-color? (mf/deref picking-color?)
|
||||
picked-color (mf/deref picked-color)
|
||||
picked-color-select (mf/deref picked-color-select)
|
||||
picking-color? (mf/deref picking-color?)
|
||||
picked-color (mf/deref picked-color)
|
||||
picked-color-select (mf/deref picked-color-select)
|
||||
|
||||
current-color (:current-color state)
|
||||
current-color (:current-color state)
|
||||
|
||||
active-tab (mf/use-state (dc/get-active-color-tab))
|
||||
drag? (mf/use-state false)
|
||||
active-fill-tab (if (:image data)
|
||||
:image
|
||||
(if-let [gradient (:gradient data)]
|
||||
(case (:type gradient)
|
||||
:linear :linear-gradient
|
||||
:radial :radial-gradient)
|
||||
:color))
|
||||
active-color-tab (mf/use-state (dc/get-active-color-tab))
|
||||
drag? (mf/use-state false)
|
||||
|
||||
fill-image-ref (mf/use-ref nil)
|
||||
|
||||
selected-mode (get state :type :color)
|
||||
|
||||
disabled-color-accept? (and
|
||||
(= selected-mode :image)
|
||||
(not (:image current-color)))
|
||||
|
||||
on-fill-image-success
|
||||
(mf/use-fn
|
||||
(fn [image]
|
||||
(st/emit! (dc/update-colorpicker-color {:image (select-keys image [:id :width :height :mtype :name])} (not @drag?)))))
|
||||
|
||||
on-fill-image-click
|
||||
(mf/use-callback #(dom/click (mf/ref-val fill-image-ref)))
|
||||
|
||||
on-fill-image-selected
|
||||
(mf/use-fn
|
||||
(fn [file]
|
||||
(st/emit! (dwm/upload-fill-image file on-fill-image-success))))
|
||||
|
||||
set-tab!
|
||||
(mf/use-fn
|
||||
|
@ -69,9 +102,18 @@
|
|||
(let [tab (-> (dom/get-current-target event)
|
||||
(dom/get-data "tab")
|
||||
(keyword))]
|
||||
(reset! active-tab tab)
|
||||
(reset! active-color-tab tab)
|
||||
(dc/set-active-color-tab! tab))))
|
||||
|
||||
handle-change-mode
|
||||
(mf/use-fn
|
||||
(fn [value]
|
||||
(case value
|
||||
:color (st/emit! (dc/activate-colorpicker-color))
|
||||
:linear-gradient (st/emit! (dc/activate-colorpicker-gradient :linear-gradient))
|
||||
:radial-gradient (st/emit! (dc/activate-colorpicker-gradient :radial-gradient))
|
||||
:image (st/emit! (dc/activate-colorpicker-image)))))
|
||||
|
||||
handle-change-color
|
||||
(mf/use-fn
|
||||
(mf/deps current-color @drag?)
|
||||
|
@ -105,7 +147,7 @@
|
|||
on-select-library-color
|
||||
(mf/use-fn
|
||||
(fn [state color]
|
||||
(let [type-origin (:type state)
|
||||
(let [type-origin selected-mode
|
||||
editig-stop-origin (:editing-stop state)
|
||||
is-gradient? (some? (:gradient color))
|
||||
change-to (fn [new-color]
|
||||
|
@ -149,12 +191,6 @@
|
|||
(fn [_]
|
||||
(st/emit! (dwl/add-color (dc/get-color-from-colorpicker-state state)))))
|
||||
|
||||
on-activate-linear-gradient
|
||||
(mf/use-fn #(st/emit! (dc/activate-colorpicker-gradient :linear-gradient)))
|
||||
|
||||
on-activate-radial-gradient
|
||||
(mf/use-fn #(st/emit! (dc/activate-colorpicker-gradient :radial-gradient)))
|
||||
|
||||
on-start-drag
|
||||
(mf/use-fn
|
||||
(mf/deps drag? node-ref)
|
||||
|
@ -174,11 +210,21 @@
|
|||
(mf/deps state)
|
||||
(fn []
|
||||
(on-accept (dc/get-color-from-colorpicker-state state))
|
||||
(modal/hide!)))]
|
||||
(modal/hide!)))
|
||||
|
||||
options
|
||||
(mf/with-memo [selected-mode disable-gradient disable-image]
|
||||
(d/concat-vec
|
||||
[{:value :color :label (tr "media.solid")}]
|
||||
(when (not disable-gradient)
|
||||
[{:value :linear-gradient :label (tr "media.linear")}
|
||||
{:value :radial-gradient :label (tr "media.radial")}])
|
||||
(when (not disable-image)
|
||||
[{:value :image :label (tr "media.image")}])))]
|
||||
|
||||
;; Initialize colorpicker state
|
||||
(mf/with-effect []
|
||||
(st/emit! (dc/initialize-colorpicker on-change))
|
||||
(st/emit! (dc/initialize-colorpicker on-change active-fill-tab))
|
||||
(partial st/emit! (dc/finalize-colorpicker)))
|
||||
|
||||
;; Update colorpicker with external color changes
|
||||
|
@ -220,181 +266,219 @@
|
|||
:ref node-ref
|
||||
:style {:touch-action "none"}}
|
||||
[:div {:class (stl/css :top-actions)}
|
||||
[:button {:class (stl/css-case :picker-btn true
|
||||
:selected picking-color?)
|
||||
:on-click handle-click-picker}
|
||||
i/picker-refactor]
|
||||
(when (not disable-gradient)
|
||||
[:div {:class (stl/css :gradient-buttons)}
|
||||
[:button
|
||||
{:on-click on-activate-linear-gradient
|
||||
:class (stl/css-case :gradient-btn true
|
||||
:linear-gradient-btn true
|
||||
:selected (= :linear-gradient (:type state)))}]
|
||||
(when (or (not disable-gradient) (not disable-image))
|
||||
[:div {:class (stl/css :select)}
|
||||
[:& select
|
||||
{:default-value selected-mode
|
||||
:options options
|
||||
:on-change handle-change-mode}]])
|
||||
(when (not= selected-mode :image)
|
||||
[:button {:class (stl/css-case :picker-btn true
|
||||
:selected picking-color?)
|
||||
:on-click handle-click-picker}
|
||||
i/picker-refactor])]
|
||||
|
||||
[:button
|
||||
{:on-click on-activate-radial-gradient
|
||||
:class (stl/css-case :gradient-btn true
|
||||
:radial-gradient-btn true
|
||||
:selected (= :radial-gradient (:type state)))}]])]
|
||||
|
||||
(when (or (= (:type state) :linear-gradient)
|
||||
(= (:type state) :radial-gradient))
|
||||
(when (or (= selected-mode :linear-gradient)
|
||||
(= selected-mode :radial-gradient))
|
||||
[:& gradients
|
||||
{:stops (:stops state)
|
||||
:editing-stop (:editing-stop state)
|
||||
:on-select-stop handle-change-stop}])
|
||||
|
||||
[:div {:class (stl/css :colorpicker-tabs)}
|
||||
[:& tab-container
|
||||
{:on-change-tab set-tab!
|
||||
:selected @active-tab
|
||||
:collapsable? false}
|
||||
(if (= selected-mode :image)
|
||||
(let [uri (cfg/resolve-file-media (:image current-color))]
|
||||
[:div {:class (stl/css :select-image)}
|
||||
[:div {:class (stl/css :content)}
|
||||
(when (:image current-color)
|
||||
[:img {:src uri}])]
|
||||
[:button
|
||||
{:class (stl/css :choose-image)
|
||||
:title (tr "media.choose-image")
|
||||
:aria-label (tr "media.choose-image")
|
||||
:on-click on-fill-image-click}
|
||||
(tr "media.choose-image")
|
||||
[:& file-uploader
|
||||
{:input-id "fill-image-upload"
|
||||
:accept "image/jpeg,image/png"
|
||||
:multi false
|
||||
:ref fill-image-ref
|
||||
:on-selected on-fill-image-selected}]]])
|
||||
[:*
|
||||
[:div {:class (stl/css :colorpicker-tabs)}
|
||||
[:& tab-container
|
||||
{:on-change-tab set-tab!
|
||||
:selected @active-color-tab
|
||||
:collapsable? false}
|
||||
|
||||
[:& tab-element {:id :ramp :title i/rgba-refactor}
|
||||
(if picking-color?
|
||||
[:div {:class (stl/css :picker-detail-wrapper)}
|
||||
[:div {:class (stl/css :center-circle)}]
|
||||
[:canvas#picker-detail {:width 256 :height 140}]]
|
||||
[:& ramp-selector
|
||||
{:color current-color
|
||||
:disable-opacity disable-opacity
|
||||
:on-change handle-change-color
|
||||
:on-start-drag on-start-drag
|
||||
:on-finish-drag on-finish-drag}])]
|
||||
[:& tab-element {:id :ramp :title i/rgba-refactor}
|
||||
(if picking-color?
|
||||
[:div {:class (stl/css :picker-detail-wrapper)}
|
||||
[:div {:class (stl/css :center-circle)}]
|
||||
[:canvas#picker-detail {:width 256 :height 140}]]
|
||||
[:& ramp-selector
|
||||
{:color current-color
|
||||
:disable-opacity disable-opacity
|
||||
:on-change handle-change-color
|
||||
:on-start-drag on-start-drag
|
||||
:on-finish-drag on-finish-drag}])]
|
||||
|
||||
[:& tab-element {:id :harmony :title i/rgba-complementary-refactor}
|
||||
(if picking-color?
|
||||
[:div {:class (stl/css :picker-detail-wrapper)}
|
||||
[:div {:class (stl/css :center-circle)}]
|
||||
[:canvas#picker-detail {:width 256 :height 140}]]
|
||||
[:& harmony-selector
|
||||
{:color current-color
|
||||
:disable-opacity disable-opacity
|
||||
:on-change handle-change-color
|
||||
:on-start-drag on-start-drag
|
||||
:on-finish-drag on-finish-drag}])]
|
||||
[:& tab-element {:id :harmony :title i/rgba-complementary-refactor}
|
||||
(if picking-color?
|
||||
[:div {:class (stl/css :picker-detail-wrapper)}
|
||||
[:div {:class (stl/css :center-circle)}]
|
||||
[:canvas#picker-detail {:width 256 :height 140}]]
|
||||
[:& harmony-selector
|
||||
{:color current-color
|
||||
:disable-opacity disable-opacity
|
||||
:on-change handle-change-color
|
||||
:on-start-drag on-start-drag
|
||||
:on-finish-drag on-finish-drag}])]
|
||||
|
||||
[:& tab-element {:id :hsva :title i/hsva-refactor}
|
||||
(if picking-color?
|
||||
[:div {:class (stl/css :picker-detail-wrapper)}
|
||||
[:div {:class (stl/css :center-circle)}]
|
||||
[:canvas#picker-detail {:width 256 :height 140}]]
|
||||
[:& hsva-selector
|
||||
{:color current-color
|
||||
:disable-opacity disable-opacity
|
||||
:on-change handle-change-color
|
||||
:on-start-drag on-start-drag
|
||||
:on-finish-drag on-finish-drag}])]]]
|
||||
[:& tab-element {:id :hsva :title i/hsva-refactor}
|
||||
(if picking-color?
|
||||
[:div {:class (stl/css :picker-detail-wrapper)}
|
||||
[:div {:class (stl/css :center-circle)}]
|
||||
[:canvas#picker-detail {:width 256 :height 140}]]
|
||||
[:& hsva-selector
|
||||
{:color current-color
|
||||
:disable-opacity disable-opacity
|
||||
:on-change handle-change-color
|
||||
:on-start-drag on-start-drag
|
||||
:on-finish-drag on-finish-drag}])]]]
|
||||
|
||||
[:& color-inputs
|
||||
{:type (if (= @active-tab :hsva) :hsv :rgb)
|
||||
:disable-opacity disable-opacity
|
||||
:color current-color
|
||||
:on-change handle-change-color}]
|
||||
[:& color-inputs
|
||||
{:type (if (= @active-color-tab :hsva) :hsv :rgb)
|
||||
:disable-opacity disable-opacity
|
||||
:color current-color
|
||||
:on-change handle-change-color}]
|
||||
|
||||
[:& libraries
|
||||
{:state state
|
||||
:current-color current-color
|
||||
:on-select-color on-select-library-color
|
||||
:on-add-library-color on-add-library-color}]
|
||||
[:& libraries
|
||||
{:state state
|
||||
:current-color current-color
|
||||
:disable-gradient disable-gradient
|
||||
:disable-opacity disable-opacity
|
||||
:disable-image disable-image
|
||||
:on-select-color on-select-library-color
|
||||
:on-add-library-color on-add-library-color}]])
|
||||
|
||||
(when on-accept
|
||||
[:div {:class (stl/css :actions)}
|
||||
[:button {:class (stl/css :accept-color)
|
||||
:on-click on-color-accept}
|
||||
[:button {:class (stl/css-case
|
||||
:accept-color true
|
||||
:btn-disabled disabled-color-accept?)
|
||||
:on-click on-color-accept
|
||||
:disabled disabled-color-accept?}
|
||||
(tr "workspace.libraries.colors.save-color")]])]
|
||||
|
||||
[:div.colorpicker {:ref node-ref
|
||||
:style {:touch-action "none"}}
|
||||
[:div.colorpicker-content
|
||||
[:div.top-actions
|
||||
[:button.picker-btn
|
||||
{:class (when picking-color? "active")
|
||||
:on-click handle-click-picker}
|
||||
i/picker]
|
||||
|
||||
(when (not disable-gradient)
|
||||
[:div.gradients-buttons
|
||||
[:button.gradient.linear-gradient
|
||||
{:on-click on-activate-linear-gradient
|
||||
:class (when (= :linear-gradient (:type state)) "active")}]
|
||||
|
||||
[:button.gradient.radial-gradient
|
||||
{:on-click on-activate-radial-gradient
|
||||
:class (when (= :radial-gradient (:type state)) "active")}]])]
|
||||
(when (or (not disable-gradient) (not disable-image))
|
||||
[:div.element-set-content
|
||||
[:& select
|
||||
{:default-value selected-mode
|
||||
:options options
|
||||
:on-change handle-change-mode}]])
|
||||
(when (not= selected-mode :image)
|
||||
[:button.picker-btn
|
||||
{:class (when picking-color? "active")
|
||||
:on-click handle-click-picker}
|
||||
i/picker])]
|
||||
|
||||
(when (or (= (:type state) :linear-gradient)
|
||||
(= (:type state) :radial-gradient))
|
||||
(= (:type state) :radial-gradient))
|
||||
|
||||
[:& gradients
|
||||
{:stops (:stops state)
|
||||
:editing-stop (:editing-stop state)
|
||||
:on-select-stop handle-change-stop}])
|
||||
|
||||
[:div.colorpicker-tabs
|
||||
[:div.colorpicker-tab.tooltip.tooltip-bottom.tooltip-expand
|
||||
{:class (when (= @active-tab :ramp) "active")
|
||||
:alt (tr "workspace.libraries.colors.rgba")
|
||||
:on-click set-tab!
|
||||
:data-tab "ramp"} i/picker-ramp]
|
||||
[:div.colorpicker-tab.tooltip.tooltip-bottom.tooltip-expand
|
||||
{:class (when (= @active-tab :harmony) "active")
|
||||
:alt (tr "workspace.libraries.colors.rgb-complementary")
|
||||
:on-click set-tab!
|
||||
:data-tab "harmony"} i/picker-harmony]
|
||||
[:div.colorpicker-tab.tooltip.tooltip-bottom.tooltip-expand
|
||||
{:class (when (= @active-tab :hsva) "active")
|
||||
:alt (tr "workspace.libraries.colors.hsv")
|
||||
:on-click set-tab!
|
||||
:data-tab "hsva"} i/picker-hsv]]
|
||||
(if (= selected-mode :image)
|
||||
(let [uri (cfg/resolve-file-media (:image current-color))]
|
||||
[:div.select-image
|
||||
[:div.content
|
||||
(when (:image current-color)
|
||||
[:img {:src uri}])]
|
||||
[:button.btn-secondary
|
||||
{:title (tr "media.choose-image")
|
||||
:aria-label (tr "media.choose-image")
|
||||
:on-click on-fill-image-click}
|
||||
(tr "media.choose-image")
|
||||
[:& file-uploader
|
||||
{:input-id "fill-image-upload"
|
||||
:accept "image/jpeg,image/png"
|
||||
:multi false
|
||||
:ref fill-image-ref
|
||||
:on-selected on-fill-image-selected}]]])
|
||||
[:*
|
||||
[:div.colorpicker-tabs
|
||||
[:div.colorpicker-tab.tooltip.tooltip-bottom.tooltip-expand
|
||||
{:class (when (= @active-color-tab :ramp) "active")
|
||||
:alt (tr "workspace.libraries.colors.rgba")
|
||||
:on-click set-tab!
|
||||
:data-tab "ramp"} i/picker-ramp]
|
||||
[:div.colorpicker-tab.tooltip.tooltip-bottom.tooltip-expand
|
||||
{:class (when (= @active-color-tab :harmony) "active")
|
||||
:alt (tr "workspace.libraries.colors.rgb-complementary")
|
||||
:on-click set-tab!
|
||||
:data-tab "harmony"} i/picker-harmony]
|
||||
[:div.colorpicker-tab.tooltip.tooltip-bottom.tooltip-expand
|
||||
{:class (when (= @active-color-tab :hsva) "active")
|
||||
:alt (tr "workspace.libraries.colors.hsv")
|
||||
:on-click set-tab!
|
||||
:data-tab "hsva"} i/picker-hsv]]
|
||||
|
||||
(if picking-color?
|
||||
[:div.picker-detail-wrapper
|
||||
[:div.center-circle]
|
||||
[:canvas#picker-detail {:width 200 :height 160}]]
|
||||
(case @active-tab
|
||||
:ramp
|
||||
[:& ramp-selector
|
||||
{:color current-color
|
||||
:disable-opacity disable-opacity
|
||||
:on-change handle-change-color
|
||||
:on-start-drag on-start-drag
|
||||
:on-finish-drag on-finish-drag}]
|
||||
:harmony
|
||||
[:& harmony-selector
|
||||
{:color current-color
|
||||
:disable-opacity disable-opacity
|
||||
:on-change handle-change-color
|
||||
:on-start-drag on-start-drag
|
||||
:on-finish-drag on-finish-drag}]
|
||||
:hsva
|
||||
[:& hsva-selector
|
||||
{:color current-color
|
||||
:disable-opacity disable-opacity
|
||||
:on-change handle-change-color
|
||||
:on-start-drag on-start-drag
|
||||
:on-finish-drag on-finish-drag}]
|
||||
nil))
|
||||
(if picking-color?
|
||||
[:div.picker-detail-wrapper
|
||||
[:div.center-circle]
|
||||
[:canvas#picker-detail {:width 200 :height 160}]]
|
||||
(case @active-color-tab
|
||||
:ramp
|
||||
[:& ramp-selector
|
||||
{:color current-color
|
||||
:disable-opacity disable-opacity
|
||||
:on-change handle-change-color
|
||||
:on-start-drag on-start-drag
|
||||
:on-finish-drag on-finish-drag}]
|
||||
:harmony
|
||||
[:& harmony-selector
|
||||
{:color current-color
|
||||
:disable-opacity disable-opacity
|
||||
:on-change handle-change-color
|
||||
:on-start-drag on-start-drag
|
||||
:on-finish-drag on-finish-drag}]
|
||||
:hsva
|
||||
[:& hsva-selector
|
||||
{:color current-color
|
||||
:disable-opacity disable-opacity
|
||||
:on-change handle-change-color
|
||||
:on-start-drag on-start-drag
|
||||
:on-finish-drag on-finish-drag}]
|
||||
nil))
|
||||
|
||||
[:& color-inputs
|
||||
{:type (if (= @active-tab :hsva) :hsv :rgb)
|
||||
:disable-opacity disable-opacity
|
||||
:color current-color
|
||||
:on-change handle-change-color}]
|
||||
[:& color-inputs
|
||||
{:type (if (= @active-color-tab :hsva) :hsv :rgb)
|
||||
:disable-opacity disable-opacity
|
||||
:color current-color
|
||||
:on-change handle-change-color}]
|
||||
|
||||
[:& libraries
|
||||
{:state state
|
||||
:current-color current-color
|
||||
:disable-gradient disable-gradient
|
||||
:disable-opacity disable-opacity
|
||||
:on-select-color on-select-library-color
|
||||
:on-add-library-color on-add-library-color}]
|
||||
[:& libraries
|
||||
{:state state
|
||||
:current-color current-color
|
||||
:disable-gradient disable-gradient
|
||||
:disable-opacity disable-opacity
|
||||
:disable-image disable-image
|
||||
:on-select-color on-select-library-color
|
||||
:on-add-library-color on-add-library-color}]])
|
||||
|
||||
(when on-accept
|
||||
[:div.actions
|
||||
[:button.btn-primary.btn-large
|
||||
{:on-click on-color-accept}
|
||||
{:on-click on-color-accept
|
||||
:disabled disabled-color-accept?
|
||||
:class (dom/classnames
|
||||
:btn-disabled disabled-color-accept?)}
|
||||
(tr "workspace.libraries.colors.save-color")]])]])))
|
||||
|
||||
(defn calculate-position
|
||||
|
@ -420,6 +504,7 @@
|
|||
[{:keys [x y data position
|
||||
disable-gradient
|
||||
disable-opacity
|
||||
disable-image
|
||||
on-change on-close on-accept] :as props}]
|
||||
(let [new-css-system (mf/use-ctx ctx/new-css-system)
|
||||
vport (mf/deref viewport)
|
||||
|
@ -445,6 +530,7 @@
|
|||
[:& colorpicker {:data data
|
||||
:disable-gradient disable-gradient
|
||||
:disable-opacity disable-opacity
|
||||
:disable-image disable-image
|
||||
:on-change handle-change
|
||||
:on-accept on-accept}]]))
|
||||
|
||||
|
|
|
@ -20,8 +20,9 @@
|
|||
.top-actions {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
flex-direction: row-reverse;
|
||||
justify-content: space-between;
|
||||
height: $s-28;
|
||||
height: $s-40;
|
||||
.picker-btn {
|
||||
@include buttonStyle;
|
||||
@include flexCenter;
|
||||
|
@ -32,6 +33,7 @@
|
|||
width: $s-20;
|
||||
border-radius: $br-4;
|
||||
padding: 0;
|
||||
margin-top: $s-4;
|
||||
svg {
|
||||
@extend .button-icon;
|
||||
stroke: var(--button-tertiary-foreground-color-rest);
|
||||
|
@ -96,7 +98,7 @@
|
|||
display: flex;
|
||||
gap: $s-4;
|
||||
.accept-color {
|
||||
@include titleTipography;
|
||||
@include tabTitleTipography;
|
||||
@extend .button-secondary;
|
||||
width: 100%;
|
||||
height: $s-32;
|
||||
|
@ -122,3 +124,36 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.select {
|
||||
width: $s-116;
|
||||
}
|
||||
|
||||
.select-image {
|
||||
margin-top: $s-4;
|
||||
.content {
|
||||
border-radius: $br-8;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
background-image: url("/images/colorpicker-no-image.png");
|
||||
background-position: center;
|
||||
background-size: auto $s-140;
|
||||
height: $s-140;
|
||||
margin-bottom: $s-6;
|
||||
margin-right: $s-1;
|
||||
img {
|
||||
height: fit-content;
|
||||
width: fit-content;
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
.choose-image {
|
||||
@extend .button-secondary;
|
||||
@include tabTitleTipography;
|
||||
width: 100%;
|
||||
margin-top: $s-12;
|
||||
height: $s-32;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc libraries
|
||||
[{:keys [state on-select-color on-add-library-color disable-gradient disable-opacity]}]
|
||||
[{:keys [state on-select-color on-add-library-color disable-gradient disable-opacity disable-image]}]
|
||||
(let [new-css-system (mf/use-ctx ctx/new-css-system)
|
||||
selected (h/use-shared-state mdc/colorpicker-selected-broadcast-key :recent)
|
||||
current-colors (mf/use-state [])
|
||||
|
@ -35,7 +35,7 @@
|
|||
shared-libs (mf/deref refs/workspace-libraries)
|
||||
file-colors (mf/deref refs/workspace-file-colors)
|
||||
recent-colors (mf/deref refs/workspace-recent-colors)
|
||||
recent-colors (h/use-equal-memo (filter #(or (:gradient %) (:color %)) recent-colors))
|
||||
recent-colors (h/use-equal-memo (filter #(or (:gradient %) (:color %) (:image %)) recent-colors))
|
||||
|
||||
on-library-change
|
||||
(mf/use-fn
|
||||
|
@ -52,7 +52,8 @@
|
|||
check-valid-color?
|
||||
(fn [color]
|
||||
(and (or (not disable-gradient) (not (:gradient color)))
|
||||
(or (not disable-opacity) (= 1 (:opacity color)))))
|
||||
(or (not disable-opacity) (= 1 (:opacity color)))
|
||||
(or (not disable-image) (not (:image color)))))
|
||||
|
||||
toggle-palette
|
||||
(mf/use-fn
|
||||
|
|
|
@ -32,10 +32,12 @@
|
|||
:opacity (:fill-opacity fill)
|
||||
:id color-id
|
||||
:file-id color-file-id
|
||||
:gradient (:fill-color-gradient fill)})
|
||||
:gradient (:fill-color-gradient fill)
|
||||
:image (:fill-image fill)})
|
||||
(d/without-nils {:color (str/lower (:fill-color fill))
|
||||
:opacity (:fill-opacity fill)
|
||||
:gradient (:fill-color-gradient fill)}))]
|
||||
:gradient (:fill-color-gradient fill)
|
||||
:image (:fill-image fill)}))]
|
||||
{:attrs attrs
|
||||
:prop :fill
|
||||
:shape-id (:shape-id fill)
|
||||
|
@ -47,16 +49,18 @@
|
|||
color-id (:stroke-color-ref-id stroke)
|
||||
shared-libs-colors (dm/get-in shared-libs [color-file-id :data :colors])
|
||||
is-shared? (contains? shared-libs-colors color-id)
|
||||
has-color? (not (nil? (:stroke-color stroke)))
|
||||
has-color? (or (not (nil? (:stroke-color stroke))) (not (nil? (:stroke-image stroke))) )
|
||||
attrs (if (or is-shared? (= color-file-id file-id))
|
||||
(d/without-nils {:color (str/lower (:stroke-color stroke))
|
||||
:opacity (:stroke-opacity stroke)
|
||||
:id color-id
|
||||
:file-id color-file-id
|
||||
:gradient (:stroke-color-gradient stroke)})
|
||||
:gradient (:stroke-color-gradient stroke)
|
||||
:image (:stroke-image stroke)})
|
||||
(d/without-nils {:color (str/lower (:stroke-color stroke))
|
||||
:opacity (:stroke-opacity stroke)
|
||||
:gradient (:stroke-color-gradient stroke)}))]
|
||||
:gradient (:stroke-color-gradient stroke)
|
||||
:image (:stroke-image stroke)}))]
|
||||
(when has-color?
|
||||
{:attrs attrs
|
||||
:prop :stroke
|
||||
|
|
|
@ -163,7 +163,8 @@
|
|||
:opacity (:fill-opacity value)
|
||||
:id (:fill-color-ref-id value)
|
||||
:file-id (:fill-color-ref-file value)
|
||||
:gradient (:fill-color-gradient value)}
|
||||
:gradient (:fill-color-gradient value)
|
||||
:image (:fill-image value)}
|
||||
:key index
|
||||
:index index
|
||||
:title (tr "workspace.options.fill")
|
||||
|
@ -215,7 +216,8 @@
|
|||
:opacity (:fill-opacity value)
|
||||
:id (:fill-color-ref-id value)
|
||||
:file-id (:fill-color-ref-file value)
|
||||
:gradient (:fill-color-gradient value)}
|
||||
:gradient (:fill-color-gradient value)
|
||||
:image (:fill-image value)}
|
||||
:key index
|
||||
:index index
|
||||
:title (tr "workspace.options.fill")
|
||||
|
|
|
@ -105,9 +105,10 @@
|
|||
(mf/use-fn
|
||||
(mf/deps grid)
|
||||
(fn [color]
|
||||
(-> grid
|
||||
(update :params assoc :color color)
|
||||
(on-change))))
|
||||
(let [color (dissoc color :id :file-id)]
|
||||
(-> grid
|
||||
(update :params assoc :color color)
|
||||
(on-change)))))
|
||||
|
||||
handle-detach-color
|
||||
(mf/use-fn
|
||||
|
@ -189,6 +190,7 @@
|
|||
[:& color-row {:color (:color params)
|
||||
:title (tr "workspace.options.grid.params.color")
|
||||
:disable-gradient true
|
||||
:disable-image true
|
||||
:on-change handle-change-color
|
||||
:on-detach handle-detach-color}]
|
||||
[:button {:class (stl/css :show-more-options)
|
||||
|
@ -228,6 +230,7 @@
|
|||
[:& color-row {:color (:color params)
|
||||
:title (tr "workspace.options.grid.params.color")
|
||||
:disable-gradient true
|
||||
:disable-image true
|
||||
:on-change handle-change-color
|
||||
:on-detach handle-detach-color}]]]
|
||||
|
||||
|
@ -384,6 +387,7 @@
|
|||
[:& color-row {:color (:color params)
|
||||
:title (tr "workspace.options.grid.params.color")
|
||||
:disable-gradient true
|
||||
:disable-image true
|
||||
:on-change handle-change-color
|
||||
:on-detach handle-detach-color}]
|
||||
[:div.row-flex
|
||||
|
|
|
@ -115,7 +115,8 @@
|
|||
(fn [color]
|
||||
(st/emit! (dch/update-shapes
|
||||
ids
|
||||
#(assoc-in % [:shadow index :color] color)))))
|
||||
#(assoc-in % [:shadow index :color]
|
||||
(dissoc color :id :file-id))))))
|
||||
|
||||
detach-color
|
||||
(mf/use-fn
|
||||
|
@ -242,6 +243,7 @@
|
|||
(:color value))
|
||||
:title (tr "workspace.options.shadow-options.color")
|
||||
:disable-gradient true
|
||||
:disable-image true
|
||||
:on-change update-color
|
||||
:on-detach detach-color
|
||||
:on-open manage-on-open
|
||||
|
@ -335,6 +337,7 @@
|
|||
(:color value))
|
||||
:title (tr "workspace.options.shadow-options.color")
|
||||
:disable-gradient true
|
||||
:disable-image true
|
||||
:on-change update-color
|
||||
:on-detach detach-color
|
||||
:on-open manage-on-open
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
[:& color-row
|
||||
{:disable-gradient true
|
||||
:disable-opacity true
|
||||
:disable-image true
|
||||
:title (tr "workspace.options.canvas-background")
|
||||
:color {:color (get options :background clr/canvas)
|
||||
:opacity 1}
|
||||
|
@ -51,6 +52,7 @@
|
|||
[:& color-row
|
||||
{:disable-gradient true
|
||||
:disable-opacity true
|
||||
:disable-image true
|
||||
:title (tr "workspace.options.canvas-background")
|
||||
:color {:color (get options :background clr/canvas)
|
||||
:opacity 1}
|
||||
|
|
|
@ -62,6 +62,8 @@
|
|||
gradient-color? (and (not multiple-colors?)
|
||||
(:gradient color)
|
||||
(get-in color [:gradient :type]))
|
||||
image-color? (and (not multiple-colors?)
|
||||
(:image color))
|
||||
|
||||
editing-text* (mf/use-state false)
|
||||
editing-text? (deref editing-text*)
|
||||
|
@ -218,6 +220,12 @@
|
|||
[:div {:class (stl/css :color-name)}
|
||||
(uc/gradient-type->string (get-in color [:gradient :type]))]]
|
||||
|
||||
;; Rendering an image
|
||||
image-color?
|
||||
[:*
|
||||
[:div {:class (stl/css :color-name)}
|
||||
(tr "media.image")]]
|
||||
|
||||
;; Rendering a plain color
|
||||
:else
|
||||
[:span {:class (stl/css :color-input-wrapper)}
|
||||
|
|
|
@ -188,12 +188,13 @@
|
|||
:dnd-over-top (= (:over dprops) :top)
|
||||
:dnd-over-bot (= (:over dprops) :bot))
|
||||
:ref dref}
|
||||
;; Stroke Color
|
||||
;; Stroke Color
|
||||
[:& color-row {:color {:color (:stroke-color stroke)
|
||||
:opacity (:stroke-opacity stroke)
|
||||
:id (:stroke-color-ref-id stroke)
|
||||
:file-id (:stroke-color-ref-file stroke)
|
||||
:gradient (:stroke-color-gradient stroke)}
|
||||
:gradient (:stroke-color-gradient stroke)
|
||||
:image (:stroke-image stroke)}
|
||||
:index index
|
||||
:title title
|
||||
:on-change on-color-change-refactor
|
||||
|
@ -263,7 +264,8 @@
|
|||
:opacity (:stroke-opacity stroke)
|
||||
:id (:stroke-color-ref-id stroke)
|
||||
:file-id (:stroke-color-ref-file stroke)
|
||||
:gradient (:stroke-color-gradient stroke)}
|
||||
:gradient (:stroke-color-gradient stroke)
|
||||
:image (:stroke-image stroke)}
|
||||
:index index
|
||||
:title title
|
||||
:on-change (on-color-change index)
|
||||
|
|
|
@ -74,7 +74,7 @@
|
|||
(= file-id :multiple)))
|
||||
|
||||
(def empty-color
|
||||
(into {} (map #(vector % nil)) [:color :id :file-id :gradient :opacity]))
|
||||
(into {} (map #(vector % nil)) [:color :id :file-id :gradient :opacity :image]))
|
||||
|
||||
(defn get-color-name
|
||||
[color]
|
||||
|
|
|
@ -101,6 +101,10 @@
|
|||
(get-in (get-data m) [:attrs ns-att]))]
|
||||
(when val (val-fn val)))))
|
||||
|
||||
(defn find-node-by-metadata-value
|
||||
[meta value coll]
|
||||
(->> coll (d/seek #(= value (get-meta % meta)))))
|
||||
|
||||
(defn get-children
|
||||
[node]
|
||||
(cond-> (:content node)
|
||||
|
@ -429,7 +433,7 @@
|
|||
fill-color-ref-file (get-meta node :fill-color-ref-file uuid/uuid)
|
||||
meta-fill-color (get-meta node :fill-color)
|
||||
meta-fill-opacity (get-meta node :fill-opacity)
|
||||
meta-fill-color-gradient (if (str/starts-with? meta-fill-color "url")
|
||||
meta-fill-color-gradient (if (str/starts-with? meta-fill-color "url#fill-color-gradient")
|
||||
(parse-gradient node meta-fill-color)
|
||||
(get-meta node :fill-color-gradient))
|
||||
gradient (when (str/starts-with? fill "url")
|
||||
|
@ -465,13 +469,14 @@
|
|||
(defn add-stroke
|
||||
[props node svg-data]
|
||||
|
||||
(let [stroke-style (get-meta node :stroke-style keyword)
|
||||
(let [stroke-style (get-meta node :stroke-style keyword)
|
||||
stroke-alignment (get-meta node :stroke-alignment keyword)
|
||||
stroke (:stroke svg-data)
|
||||
gradient (when (str/starts-with? stroke "url")
|
||||
(parse-gradient node stroke))
|
||||
stroke (:stroke svg-data)
|
||||
gradient (when (str/starts-with? stroke "url(#stroke-color-gradient")
|
||||
(parse-gradient node stroke))
|
||||
|
||||
stroke-cap-start (get-meta node :stroke-cap-start keyword)
|
||||
stroke-cap-end (get-meta node :stroke-cap-end keyword)]
|
||||
stroke-cap-end (get-meta node :stroke-cap-end keyword)]
|
||||
|
||||
(cond-> props
|
||||
:always
|
||||
|
@ -728,17 +733,22 @@
|
|||
(defn parse-fills
|
||||
[node svg-data]
|
||||
(let [fills-node (get-data node :penpot:fills)
|
||||
images (:images node)
|
||||
fills (->> (find-all-nodes fills-node :penpot:fill)
|
||||
(mapv (fn [fill-node]
|
||||
{:fill-color (when (not (str/starts-with? (get-meta fill-node :fill-color) "url"))
|
||||
(get-meta fill-node :fill-color))
|
||||
:fill-color-gradient (when (str/starts-with? (get-meta fill-node :fill-color) "url")
|
||||
(parse-gradient node (get-meta fill-node :fill-color)))
|
||||
:fill-color-ref-file (get-meta fill-node :fill-color-ref-file uuid/uuid)
|
||||
:fill-color-ref-id (get-meta fill-node :fill-color-ref-id uuid/uuid)
|
||||
:fill-opacity (get-meta fill-node :fill-opacity d/parse-double)}))
|
||||
(let [fill-image-id (get-meta fill-node :fill-image-id)]
|
||||
{:fill-color (when (not (str/starts-with? (get-meta fill-node :fill-color) "url"))
|
||||
(get-meta fill-node :fill-color))
|
||||
:fill-color-gradient (when (str/starts-with? (get-meta fill-node :fill-color) "url(#fill-color-gradient")
|
||||
(parse-gradient node (get-meta fill-node :fill-color)))
|
||||
:fill-image (when fill-image-id
|
||||
(get images fill-image-id))
|
||||
:fill-color-ref-file (get-meta fill-node :fill-color-ref-file uuid/uuid)
|
||||
:fill-color-ref-id (get-meta fill-node :fill-color-ref-id uuid/uuid)
|
||||
:fill-opacity (get-meta fill-node :fill-opacity d/parse-double)})))
|
||||
(mapv d/without-nils)
|
||||
(filterv #(not= (:fill-color %) "none")))]
|
||||
|
||||
(if (seq fills)
|
||||
fills
|
||||
(->> [(-> (add-fill {} node svg-data)
|
||||
|
@ -748,22 +758,27 @@
|
|||
(defn parse-strokes
|
||||
[node svg-data]
|
||||
(let [strokes-node (get-data node :penpot:strokes)
|
||||
images (:images node)
|
||||
strokes (->> (find-all-nodes strokes-node :penpot:stroke)
|
||||
(mapv (fn [stroke-node]
|
||||
{:stroke-color (when (not (str/starts-with? (get-meta stroke-node :stroke-color) "url"))
|
||||
(get-meta stroke-node :stroke-color))
|
||||
:stroke-color-gradient (when (str/starts-with? (get-meta stroke-node :stroke-color) "url")
|
||||
(parse-gradient node (get-meta stroke-node :stroke-color)))
|
||||
:stroke-color-ref-file (get-meta stroke-node :stroke-color-ref-file uuid/uuid)
|
||||
:stroke-color-ref-id (get-meta stroke-node :stroke-color-ref-id uuid/uuid)
|
||||
:stroke-opacity (get-meta stroke-node :stroke-opacity d/parse-double)
|
||||
:stroke-style (get-meta stroke-node :stroke-style keyword)
|
||||
:stroke-width (get-meta stroke-node :stroke-width d/parse-double)
|
||||
:stroke-alignment (get-meta stroke-node :stroke-alignment keyword)
|
||||
:stroke-cap-start (get-meta stroke-node :stroke-cap-start keyword)
|
||||
:stroke-cap-end (get-meta stroke-node :stroke-cap-end keyword)}))
|
||||
(let [stroke-image-id (get-meta stroke-node :stroke-image-id)]
|
||||
{:stroke-color (when (not (str/starts-with? (get-meta stroke-node :stroke-color) "url"))
|
||||
(get-meta stroke-node :stroke-color))
|
||||
:stroke-color-gradient (when (str/starts-with? (get-meta stroke-node :stroke-color) "url(#stroke-color-gradient")
|
||||
(parse-gradient node (get-meta stroke-node :stroke-color)))
|
||||
:stroke-image (when stroke-image-id
|
||||
(get images stroke-image-id))
|
||||
:stroke-color-ref-file (get-meta stroke-node :stroke-color-ref-file uuid/uuid)
|
||||
:stroke-color-ref-id (get-meta stroke-node :stroke-color-ref-id uuid/uuid)
|
||||
:stroke-opacity (get-meta stroke-node :stroke-opacity d/parse-double)
|
||||
:stroke-style (get-meta stroke-node :stroke-style keyword)
|
||||
:stroke-width (get-meta stroke-node :stroke-width d/parse-double)
|
||||
:stroke-alignment (get-meta stroke-node :stroke-alignment keyword)
|
||||
:stroke-cap-start (get-meta stroke-node :stroke-cap-start keyword)
|
||||
:stroke-cap-end (get-meta stroke-node :stroke-cap-end keyword)})))
|
||||
(mapv d/without-nils)
|
||||
(filterv #(not= (:stroke-color %) "none")))]
|
||||
|
||||
(if (seq strokes)
|
||||
strokes
|
||||
(->> [(-> (add-stroke {} node svg-data)
|
||||
|
@ -804,6 +819,25 @@
|
|||
(cond-> (d/not-empty? grids)
|
||||
(assoc :grids grids)))))
|
||||
|
||||
(defn get-stroke-images-data
|
||||
[node]
|
||||
(let [strokes
|
||||
(-> node
|
||||
(find-node :penpot:shape)
|
||||
(find-node :penpot:strokes))]
|
||||
(->> (find-all-nodes strokes :penpot:stroke)
|
||||
(mapv (fn [stroke-node]
|
||||
(let [id (get-in stroke-node [:attrs :penpot:stroke-image-id])
|
||||
image-node (->> node (node-seq) (find-node-by-id id))]
|
||||
{:id id
|
||||
:href (get-in image-node [:attrs :href])})))
|
||||
(filterv #(some? (:id %))))))
|
||||
|
||||
(defn has-stroke-images?
|
||||
[node]
|
||||
(let [stroke-images (get-stroke-images-data node)]
|
||||
(> (count stroke-images) 0)))
|
||||
|
||||
(defn has-image?
|
||||
[node]
|
||||
(let [type (get-type node)
|
||||
|
@ -812,7 +846,9 @@
|
|||
(find-node :defs)
|
||||
(find-node :pattern)
|
||||
(find-node :g)
|
||||
(find-node :g)
|
||||
(find-node :image))]
|
||||
|
||||
(or (= type :image)
|
||||
(some? pattern-image))))
|
||||
|
||||
|
@ -827,12 +863,32 @@
|
|||
(find-node :defs)
|
||||
(find-node :pattern)
|
||||
(find-node :g)
|
||||
(find-node :g)
|
||||
(find-node :image)
|
||||
:attrs)
|
||||
image-data (get-svg-data :image node)
|
||||
svg-data (or image-data pattern-data)]
|
||||
svg-data (or pattern-data image-data)]
|
||||
(or (:href svg-data) (:xlink:href svg-data))))
|
||||
|
||||
(defn get-fill-images-data
|
||||
[node]
|
||||
(let [fills
|
||||
(-> node
|
||||
(find-node :penpot:shape)
|
||||
(find-node :penpot:fills))]
|
||||
(->> (find-all-nodes fills :penpot:fill)
|
||||
(mapv (fn [fill-node]
|
||||
(let [id (get-in fill-node [:attrs :penpot:fill-image-id])
|
||||
image-node (->> node (node-seq) (find-node-by-id id))]
|
||||
{:id id
|
||||
:href (get-in image-node [:attrs :href])})))
|
||||
(filterv #(some? (:id %))))))
|
||||
|
||||
(defn has-fill-images?
|
||||
[node]
|
||||
(let [fill-images (get-fill-images-data node)]
|
||||
(> (count fill-images) 0)))
|
||||
|
||||
(defn get-image-fill
|
||||
[node]
|
||||
(let [linear-gradient-node (-> node
|
||||
|
|
|
@ -317,36 +317,59 @@
|
|||
|
||||
(defn resolve-media
|
||||
[context file-id node]
|
||||
(if (and (not (cip/close? node))
|
||||
(cip/has-image? node))
|
||||
(let [name (cip/get-image-name node)
|
||||
image-data (cip/get-image-data node)
|
||||
image-fill (cip/get-image-fill node)]
|
||||
(->> (upload-media-files context file-id name image-data)
|
||||
(rx/catch #(do (.error js/console "Error uploading media: " name)
|
||||
(rx/of node)))
|
||||
(if (or (and (not (cip/close? node))
|
||||
(cip/has-image? node))
|
||||
(cip/has-stroke-images? node)
|
||||
(cip/has-fill-images? node))
|
||||
(let [name (cip/get-image-name node)
|
||||
has-image (cip/has-image? node)
|
||||
image-data (cip/get-image-data node)
|
||||
image-fill (cip/get-image-fill node)
|
||||
fill-images-data (->> (cip/get-fill-images-data node)
|
||||
(map #(assoc % :type :fill)))
|
||||
stroke-images-data (->> (cip/get-stroke-images-data node)
|
||||
(map #(assoc % :type :stroke)))
|
||||
|
||||
images-data (concat
|
||||
fill-images-data
|
||||
stroke-images-data
|
||||
(when has-image
|
||||
[{:href image-data}]))]
|
||||
(->> (rx/from images-data)
|
||||
(rx/mapcat (fn [image-data]
|
||||
(->> (upload-media-files context file-id name (:href image-data))
|
||||
(rx/catch #(do (.error js/console "Error uploading media: " name)
|
||||
(rx/of node)))
|
||||
(rx/map #(vector (:id image-data) %)))))
|
||||
(rx/reduce (fn [acc [id data]] (assoc acc id data)) {})
|
||||
(rx/map
|
||||
(fn [media]
|
||||
(-> node
|
||||
(assoc-in [:attrs :penpot:media-id] (:id media))
|
||||
(assoc-in [:attrs :penpot:media-width] (:width media))
|
||||
(assoc-in [:attrs :penpot:media-height] (:height media))
|
||||
(assoc-in [:attrs :penpot:media-mtype] (:mtype media))
|
||||
(fn [images]
|
||||
(let [media (get images nil)]
|
||||
(-> node
|
||||
(assoc :images images)
|
||||
(cond-> (some? media)
|
||||
(->
|
||||
(assoc-in [:attrs :penpot:media-id] (:id media))
|
||||
(assoc-in [:attrs :penpot:media-width] (:width media))
|
||||
(assoc-in [:attrs :penpot:media-height] (:height media))
|
||||
(assoc-in [:attrs :penpot:media-mtype] (:mtype media))
|
||||
|
||||
(assoc-in [:attrs :penpot:fill-color] (:fill image-fill))
|
||||
(assoc-in [:attrs :penpot:fill-color-ref-file] (:fill-color-ref-file image-fill))
|
||||
(assoc-in [:attrs :penpot:fill-color-ref-id] (:fill-color-ref-id image-fill))
|
||||
(assoc-in [:attrs :penpot:fill-opacity] (:fill-opacity image-fill))
|
||||
(assoc-in [:attrs :penpot:fill-color-gradient] (:fill-color-gradient image-fill)))))))
|
||||
(assoc-in [:attrs :penpot:fill-color] (:fill image-fill))
|
||||
(assoc-in [:attrs :penpot:fill-color-ref-file] (:fill-color-ref-file image-fill))
|
||||
(assoc-in [:attrs :penpot:fill-color-ref-id] (:fill-color-ref-id image-fill))
|
||||
(assoc-in [:attrs :penpot:fill-opacity] (:fill-opacity image-fill))
|
||||
(assoc-in [:attrs :penpot:fill-color-gradient] (:fill-color-gradient image-fill))))))))))
|
||||
|
||||
;; If the node is not an image just return the node
|
||||
(->> (rx/of node)
|
||||
(rx/observe-on :async))))
|
||||
|
||||
(defn media-node? [node]
|
||||
(and (cip/shape? node)
|
||||
(cip/has-image? node)
|
||||
(not (cip/close? node))))
|
||||
(or (and (cip/shape? node)
|
||||
(cip/has-image? node)
|
||||
(not (cip/close? node)))
|
||||
(cip/has-stroke-images? node)
|
||||
(cip/has-fill-images? node)))
|
||||
|
||||
(defn import-page
|
||||
[context file [page-id page-name content]]
|
||||
|
@ -379,7 +402,8 @@
|
|||
(rx/mapcat
|
||||
(fn [node]
|
||||
(->> (resolve-media context file-id node)
|
||||
(rx/map (fn [result] [node result])))))
|
||||
(rx/map (fn [result]
|
||||
[node result])))))
|
||||
(rx/reduce conj {}))]
|
||||
|
||||
(->> pre-process-images
|
||||
|
|
|
@ -4993,3 +4993,21 @@ msgstr "Click to close the path"
|
|||
#, markdown
|
||||
msgid "workspace.top-bar.read-only"
|
||||
msgstr "**Inspect mode** (View Only)"
|
||||
|
||||
msgid "media.image"
|
||||
msgstr "Image"
|
||||
|
||||
msgid "media.solid"
|
||||
msgstr "Solid"
|
||||
|
||||
msgid "media.linear"
|
||||
msgstr "Linear"
|
||||
|
||||
msgid "media.radial"
|
||||
msgstr "Radial"
|
||||
|
||||
msgid "media.gradient"
|
||||
msgstr "Gradient"
|
||||
|
||||
msgid "media.choose-image"
|
||||
msgstr "Choose image"
|
||||
|
|
|
@ -5094,3 +5094,21 @@ msgstr "Pulsar para cerrar la ruta"
|
|||
#, markdown
|
||||
msgid "workspace.top-bar.read-only"
|
||||
msgstr "**Modo inspección** (View only)"
|
||||
|
||||
msgid "media.image"
|
||||
msgstr "Imagen"
|
||||
|
||||
msgid "media.solid"
|
||||
msgstr "Sólido"
|
||||
|
||||
msgid "media.linear"
|
||||
msgstr "Linear"
|
||||
|
||||
msgid "media.radial"
|
||||
msgstr "Radial"
|
||||
|
||||
msgid "media.gradient"
|
||||
msgstr "Gradiente"
|
||||
|
||||
msgid "media.choose-image"
|
||||
msgstr "Elegir imagen"
|
||||
|
|
Loading…
Add table
Reference in a new issue