mirror of
https://github.com/penpot/penpot.git
synced 2025-05-29 00:56:10 +02:00
✨ Multiple fills in text shapes
This commit is contained in:
parent
a3063eb46d
commit
ec63d23666
19 changed files with 230 additions and 211 deletions
|
@ -202,9 +202,9 @@
|
||||||
(s/def :internal.shape.text/content
|
(s/def :internal.shape.text/content
|
||||||
(s/nilable
|
(s/nilable
|
||||||
(s/or :text-container
|
(s/or :text-container
|
||||||
(s/keys :req-un [:internal.shape.text/type
|
(s/keys :req-un [:internal.shape.text/type]
|
||||||
:internal.shape.text/children]
|
:opt-un [:internal.shape.text/key
|
||||||
:opt-un [:internal.shape.text/key])
|
:internal.shape.text/children])
|
||||||
:text-content
|
:text-content
|
||||||
(s/keys :req-un [:internal.shape.text/text]))))
|
(s/keys :req-un [:internal.shape.text/text]))))
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,8 @@
|
||||||
:text-transform "none"
|
:text-transform "none"
|
||||||
:text-align "left"
|
:text-align "left"
|
||||||
:text-decoration "none"
|
:text-decoration "none"
|
||||||
:fill-color clr/black
|
:fills [{:fill-color clr/black
|
||||||
:fill-opacity 1})
|
:fill-opacity 1}]})
|
||||||
|
|
||||||
(def typography-fields
|
(def typography-fields
|
||||||
[:font-id
|
[:font-id
|
||||||
|
|
|
@ -110,7 +110,7 @@
|
||||||
|
|
||||||
.dashboard-buttons {
|
.dashboard-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: end;
|
justify-content: flex-end;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -113,81 +113,89 @@
|
||||||
|
|
||||||
(defn transform-fill
|
(defn transform-fill
|
||||||
[state ids color transform]
|
[state ids color transform]
|
||||||
(let [page-id (:current-page-id state)
|
(let [objects (wsh/lookup-page-objects state)
|
||||||
objects (wsh/lookup-page-objects state page-id)
|
|
||||||
|
|
||||||
is-text? #(= :text (:type (get objects %)))
|
is-text? #(= :text (:type (get objects %)))
|
||||||
text-ids (filter is-text? ids)
|
text-ids (filter is-text? ids)
|
||||||
shape-ids (filter (comp not is-text?) ids)
|
shape-ids (remove is-text? ids)
|
||||||
|
|
||||||
attrs (cond-> {:fill-color nil
|
attrs
|
||||||
:fill-color-gradient nil
|
(cond-> {}
|
||||||
:fill-color-ref-file nil
|
(contains? color :color)
|
||||||
:fill-color-ref-id nil
|
(assoc :fill-color (:color color))
|
||||||
:fill-opacity nil}
|
|
||||||
|
|
||||||
(contains? color :color)
|
(contains? color :id)
|
||||||
(assoc :fill-color (:color color))
|
(assoc :fill-color-ref-id (:id color))
|
||||||
|
|
||||||
(contains? color :id)
|
(contains? color :file-id)
|
||||||
(assoc :fill-color-ref-id (:id color))
|
(assoc :fill-color-ref-file (:file-id color))
|
||||||
|
|
||||||
(contains? color :file-id)
|
(contains? color :gradient)
|
||||||
(assoc :fill-color-ref-file (:file-id color))
|
(assoc :fill-color-gradient (:gradient color))
|
||||||
|
|
||||||
(contains? color :gradient)
|
(contains? color :opacity)
|
||||||
(assoc :fill-color-gradient (:gradient color))
|
(assoc :fill-opacity (:opacity color))
|
||||||
|
|
||||||
(contains? color :opacity)
|
:always
|
||||||
(assoc :fill-opacity (:opacity color)))
|
(d/without-nils))
|
||||||
;; Not nil attrs
|
|
||||||
clean-attrs (d/without-nils attrs)]
|
transform-attrs #(transform % attrs)]
|
||||||
|
|
||||||
(rx/concat
|
(rx/concat
|
||||||
(rx/from (map #(dwt/update-text-attrs {:id % :attrs attrs}) text-ids))
|
(rx/from (map #(dwt/update-text-with-function % transform-attrs) text-ids))
|
||||||
(rx/of (dch/update-shapes
|
(rx/of (dch/update-shapes shape-ids transform-attrs)))))
|
||||||
shape-ids
|
|
||||||
#(transform % clean-attrs))))))
|
|
||||||
|
|
||||||
(defn swap-fills [shape index new-index]
|
(defn swap-fills [shape index new-index]
|
||||||
(let [first (get-in shape [:fills index])
|
(let [first (get-in shape [:fills index])
|
||||||
second (get-in shape [:fills new-index])]
|
second (get-in shape [:fills new-index])]
|
||||||
(-> shape
|
(-> shape
|
||||||
(assoc-in [:fills index] second)
|
(assoc-in [:fills index] second)
|
||||||
(assoc-in [:fills new-index] first))
|
(assoc-in [:fills new-index] first))))
|
||||||
))
|
|
||||||
|
|
||||||
(defn reorder-fills
|
(defn reorder-fills
|
||||||
[ids index new-index]
|
[ids index new-index]
|
||||||
(ptk/reify ::reorder-fills
|
(ptk/reify ::reorder-fills
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ _ _]
|
(watch [_ state _]
|
||||||
(rx/of (dch/update-shapes
|
(let [objects (wsh/lookup-page-objects state)
|
||||||
ids
|
|
||||||
#(swap-fills % index new-index))))))
|
is-text? #(= :text (:type (get objects %)))
|
||||||
|
text-ids (filter is-text? ids)
|
||||||
|
shape-ids (remove is-text? ids)
|
||||||
|
transform-attrs #(swap-fills % index new-index)]
|
||||||
|
|
||||||
|
(rx/concat
|
||||||
|
(rx/from (map #(dwt/update-text-with-function % transform-attrs) text-ids))
|
||||||
|
(rx/of (dch/update-shapes shape-ids transform-attrs)))))))
|
||||||
|
|
||||||
(defn change-fill
|
(defn change-fill
|
||||||
[ids color position]
|
[ids color position]
|
||||||
(ptk/reify ::change-fill
|
(ptk/reify ::change-fill
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(let [change (fn [shape attrs] (assoc-in shape [:fills position] (into {} attrs)))]
|
(let [change (fn [shape attrs]
|
||||||
(transform-fill state ids color change)))))
|
(-> shape
|
||||||
|
(cond-> (not (contains? shape :fills))
|
||||||
|
(assoc :fills []))
|
||||||
|
(assoc-in [:fills position] (into {} attrs))))]
|
||||||
|
(transform-fill state ids color change)))))
|
||||||
|
|
||||||
(defn change-fill-and-clear
|
(defn change-fill-and-clear
|
||||||
[ids color]
|
[ids color]
|
||||||
(ptk/reify ::change-fill-and-clear
|
(ptk/reify ::change-fill-and-clear
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(let [set (fn [shape attrs] (assoc shape :fills [attrs]))]
|
(let [set (fn [shape attrs] (assoc shape :fills [attrs]))]
|
||||||
(transform-fill state ids color set)))))
|
(transform-fill state ids color set)))))
|
||||||
|
|
||||||
(defn add-fill
|
(defn add-fill
|
||||||
[ids color]
|
[ids color]
|
||||||
(ptk/reify ::add-fill
|
(ptk/reify ::add-fill
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(let [add (fn [shape attrs] (assoc shape :fills (into [attrs] (:fills shape))))]
|
(let [add (fn [shape attrs]
|
||||||
|
(-> shape
|
||||||
|
(update :fills #(into [attrs] %))))]
|
||||||
(transform-fill state ids color add)))))
|
(transform-fill state ids color add)))))
|
||||||
|
|
||||||
(defn remove-fill
|
(defn remove-fill
|
||||||
|
|
|
@ -50,12 +50,14 @@
|
||||||
(update state :workspace-editor-state dissoc id)))))
|
(update state :workspace-editor-state dissoc id)))))
|
||||||
|
|
||||||
(defn finalize-editor-state
|
(defn finalize-editor-state
|
||||||
[{:keys [id] :as shape}]
|
[id]
|
||||||
(ptk/reify ::finalize-editor-state
|
(ptk/reify ::finalize-editor-state
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(when (dwc/initialized? state)
|
(when (dwc/initialized? state)
|
||||||
(let [content (-> (get-in state [:workspace-editor-state id])
|
(let [objects (wsh/lookup-page-objects state)
|
||||||
|
shape (get objects id)
|
||||||
|
content (-> (get-in state [:workspace-editor-state id])
|
||||||
(ted/get-editor-current-content))]
|
(ted/get-editor-current-content))]
|
||||||
(if (ted/content-has-text? content)
|
(if (ted/content-has-text? content)
|
||||||
(let [content (d/merge (ted/export-content content)
|
(let [content (d/merge (ted/export-content content)
|
||||||
|
@ -78,8 +80,8 @@
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [text-state (some->> content ted/import-content)
|
(let [text-state (some->> content ted/import-content)
|
||||||
attrs (get-in state [:workspace-local :defaults :font])
|
attrs (d/merge txt/default-text-attrs
|
||||||
|
(get-in state [:workspace-local :defaults :font]))
|
||||||
editor (cond-> (ted/create-editor-state text-state decorator)
|
editor (cond-> (ted/create-editor-state text-state decorator)
|
||||||
(and (nil? content) (some? attrs))
|
(and (nil? content) (some? attrs))
|
||||||
(ted/update-editor-current-block-data attrs))]
|
(ted/update-editor-current-block-data attrs))]
|
||||||
|
@ -95,7 +97,7 @@
|
||||||
(rx/filter (ptk/type? ::rt/navigate) stream)
|
(rx/filter (ptk/type? ::rt/navigate) stream)
|
||||||
(rx/filter #(= ::finalize-editor-state %) stream))
|
(rx/filter #(= ::finalize-editor-state %) stream))
|
||||||
(rx/take 1)
|
(rx/take 1)
|
||||||
(rx/map #(finalize-editor-state shape))))))
|
(rx/map #(finalize-editor-state id))))))
|
||||||
|
|
||||||
(defn select-all
|
(defn select-all
|
||||||
"Select all content of the current editor. When not editor found this
|
"Select all content of the current editor. When not editor found this
|
||||||
|
@ -145,10 +147,10 @@
|
||||||
|
|
||||||
;; --- TEXT EDITION IMPL
|
;; --- TEXT EDITION IMPL
|
||||||
|
|
||||||
(defn- update-shape
|
(defn- update-text-content
|
||||||
[shape pred-fn merge-fn attrs]
|
[shape pred-fn update-fn attrs]
|
||||||
(let [merge-attrs #(merge-fn % attrs)
|
(let [update-attrs #(update-fn % attrs)
|
||||||
transform #(txt/transform-nodes pred-fn merge-attrs %)]
|
transform #(txt/transform-nodes pred-fn update-attrs %)]
|
||||||
(-> shape
|
(-> shape
|
||||||
(update :content transform))))
|
(update :content transform))))
|
||||||
|
|
||||||
|
@ -160,7 +162,11 @@
|
||||||
(let [objects (wsh/lookup-page-objects state)
|
(let [objects (wsh/lookup-page-objects state)
|
||||||
shape (get objects id)
|
shape (get objects id)
|
||||||
|
|
||||||
update-fn #(update-shape % txt/is-root-node? attrs/merge attrs)
|
update-fn
|
||||||
|
(fn [shape]
|
||||||
|
(if (some? (:content shape))
|
||||||
|
(update-text-content shape txt/is-root-node? attrs/merge attrs)
|
||||||
|
(assoc shape :content (attrs/merge {:type "root"} attrs))))
|
||||||
|
|
||||||
shape-ids (cond (cph/text-shape? shape) [id]
|
shape-ids (cond (cph/text-shape? shape) [id]
|
||||||
(cph/group-shape? shape) (cph/get-children-ids objects id))]
|
(cph/group-shape? shape) (cph/get-children-ids objects id))]
|
||||||
|
@ -187,7 +193,7 @@
|
||||||
node
|
node
|
||||||
attrs))
|
attrs))
|
||||||
|
|
||||||
update-fn #(update-shape % txt/is-paragraph-node? merge-fn attrs)
|
update-fn #(update-text-content % txt/is-paragraph-node? merge-fn attrs)
|
||||||
shape-ids (cond
|
shape-ids (cond
|
||||||
(cph/text-shape? shape) [id]
|
(cph/text-shape? shape) [id]
|
||||||
(cph/group-shape? shape) (cph/get-children-ids objects id))]
|
(cph/group-shape? shape) (cph/get-children-ids objects id))]
|
||||||
|
@ -209,12 +215,57 @@
|
||||||
update-node? (fn [node]
|
update-node? (fn [node]
|
||||||
(or (txt/is-text-node? node)
|
(or (txt/is-text-node? node)
|
||||||
(txt/is-paragraph-node? node)))
|
(txt/is-paragraph-node? node)))
|
||||||
|
|
||||||
update-fn #(update-shape % update-node? attrs/merge attrs)
|
|
||||||
shape-ids (cond
|
shape-ids (cond
|
||||||
(cph/text-shape? shape) [id]
|
(cph/text-shape? shape) [id]
|
||||||
(cph/group-shape? shape) (cph/get-children-ids objects id))]
|
(cph/group-shape? shape) (cph/get-children-ids objects id))]
|
||||||
(rx/of (dch/update-shapes shape-ids update-fn)))))))
|
(rx/of (dch/update-shapes shape-ids #(update-text-content % update-node? attrs/merge attrs))))))))
|
||||||
|
|
||||||
|
(defn migrate-content
|
||||||
|
[content]
|
||||||
|
(txt/transform-nodes
|
||||||
|
#(or (txt/is-text-node? %) (txt/is-paragraph-node? %))
|
||||||
|
(fn [node]
|
||||||
|
(let [color-attrs (select-keys node [:fill-color :fill-opacity :fill-color-ref-id :fill-color-ref-file :fill-color-gradient])]
|
||||||
|
(cond-> node
|
||||||
|
(d/not-empty? color-attrs)
|
||||||
|
(-> (dissoc :fill-color :fill-opacity :fill-color-ref-id :fill-color-ref-file :fill-color-gradient)
|
||||||
|
(assoc :fills [color-attrs])))))
|
||||||
|
content))
|
||||||
|
|
||||||
|
(defn update-text-with-function
|
||||||
|
[id update-node-fn]
|
||||||
|
(ptk/reify ::update-text-with-function
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(d/update-in-when state [:workspace-editor-state id] ted/update-editor-current-inline-styles-fn update-node-fn))
|
||||||
|
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state _]
|
||||||
|
(when (nil? (get-in state [:workspace-editor-state id]))
|
||||||
|
(let [objects (wsh/lookup-page-objects state)
|
||||||
|
shape (get objects id)
|
||||||
|
|
||||||
|
update-node?
|
||||||
|
(fn [node]
|
||||||
|
(or (txt/is-text-node? node)
|
||||||
|
(txt/is-paragraph-node? node)))
|
||||||
|
|
||||||
|
shape-ids
|
||||||
|
(cond
|
||||||
|
(cph/text-shape? shape) [id]
|
||||||
|
(cph/group-shape? shape) (cph/get-children-ids objects id))
|
||||||
|
|
||||||
|
update-content
|
||||||
|
(fn [content]
|
||||||
|
(->> content
|
||||||
|
(migrate-content)
|
||||||
|
(txt/transform-nodes update-node? update-node-fn)))
|
||||||
|
|
||||||
|
update-shape
|
||||||
|
(fn [shape]
|
||||||
|
(d/update-when shape :content update-content))]
|
||||||
|
|
||||||
|
(rx/of (dch/update-shapes shape-ids update-shape)))))))
|
||||||
|
|
||||||
;; --- RESIZE UTILS
|
;; --- RESIZE UTILS
|
||||||
|
|
||||||
|
|
|
@ -205,7 +205,7 @@
|
||||||
(= :image (:type shape))
|
(= :image (:type shape))
|
||||||
(> (count (:fills shape)) 1)
|
(> (count (:fills shape)) 1)
|
||||||
(some #(some? (:fill-color-gradient %)) (:fills shape)))
|
(some #(some? (:fill-color-gradient %)) (:fills shape)))
|
||||||
(obj/set! styles "fill" (str "url(#fill-" render-id ")"))
|
(obj/set! styles "fill" (str "url(#fill-0-" render-id ")"))
|
||||||
|
|
||||||
;; imported svgs can have fill and fill-opacity attributes
|
;; imported svgs can have fill and fill-opacity attributes
|
||||||
(obj/contains? svg-styles "fill")
|
(obj/contains? svg-styles "fill")
|
||||||
|
@ -227,9 +227,8 @@
|
||||||
(add-style-attrs shape)))
|
(add-style-attrs shape)))
|
||||||
|
|
||||||
(defn extract-fill-attrs
|
(defn extract-fill-attrs
|
||||||
[shape index]
|
[shape render-id index]
|
||||||
(let [render-id (mf/use-ctx muc/render-ctx)
|
(let [fill-styles (-> (obj/get shape "style" (obj/new))
|
||||||
fill-styles (-> (obj/get shape "style" (obj/new))
|
|
||||||
(add-fill shape render-id index))]
|
(add-fill shape render-id index))]
|
||||||
(-> (obj/new)
|
(-> (obj/new)
|
||||||
(obj/set! "style" fill-styles))))
|
(obj/set! "style" fill-styles))))
|
||||||
|
|
|
@ -20,45 +20,54 @@
|
||||||
[props]
|
[props]
|
||||||
|
|
||||||
(let [shape (obj/get props "shape")
|
(let [shape (obj/get props "shape")
|
||||||
render-id (obj/get props "render-id")
|
render-id (obj/get props "render-id")]
|
||||||
{:keys [x y width height]} (:selrect shape)
|
|
||||||
{:keys [metadata]} shape
|
|
||||||
fill-id (str "fill-" render-id)
|
|
||||||
has-image (or metadata (:fill-image shape))
|
|
||||||
uri (if metadata
|
|
||||||
(cfg/resolve-file-media metadata)
|
|
||||||
(cfg/resolve-file-media (:fill-image shape)))
|
|
||||||
embed (embed/use-data-uris [uri])
|
|
||||||
transform (gsh/transform-matrix shape)
|
|
||||||
pattern-attrs (cond-> #js {:id fill-id
|
|
||||||
:patternUnits "userSpaceOnUse"
|
|
||||||
:x x
|
|
||||||
:y y
|
|
||||||
:height height
|
|
||||||
:width width
|
|
||||||
:data-loading (str (not (contains? embed uri)))}
|
|
||||||
(= :path (:type shape))
|
|
||||||
(obj/set! "patternTransform" transform))]
|
|
||||||
|
|
||||||
[:*
|
(when (or (some? (:fill-image shape))
|
||||||
(for [[index value] (-> (d/enumerate (:fills shape [])) reverse)]
|
(#{:image :text} (:type shape))
|
||||||
(cond (some? (:fill-color-gradient value))
|
(> (count (:fills shape)) 1)
|
||||||
(case (:type (:fill-color-gradient value))
|
(some :fill-color-gradient (:fills shape)))
|
||||||
:linear [:> grad/linear-gradient #js {:id (str (name :fill-color-gradient) "_" render-id "_" index)
|
|
||||||
:gradient (:fill-color-gradient value)
|
|
||||||
:shape shape}]
|
|
||||||
:radial [:> grad/radial-gradient #js {:id (str (name :fill-color-gradient) "_" render-id "_" index)
|
|
||||||
:gradient (:fill-color-gradient value)
|
|
||||||
:shape shape}])))
|
|
||||||
|
|
||||||
[:> :pattern pattern-attrs
|
(let [{:keys [x y width height]} (:selrect shape)
|
||||||
[:g
|
{:keys [metadata]} shape
|
||||||
(for [[index value] (-> (d/enumerate (:fills shape [])) reverse)]
|
|
||||||
[:> :rect (-> (attrs/extract-fill-attrs value index)
|
|
||||||
(obj/set! "width" width)
|
|
||||||
(obj/set! "height" height))])
|
|
||||||
|
|
||||||
(when has-image
|
has-image (or metadata (:fill-image shape))
|
||||||
[:image {:xlinkHref (get embed uri uri)
|
uri (if metadata
|
||||||
:width width
|
(cfg/resolve-file-media metadata)
|
||||||
:height height}])]]]))
|
(cfg/resolve-file-media (:fill-image shape)))
|
||||||
|
embed (embed/use-data-uris [uri])
|
||||||
|
transform (gsh/transform-matrix shape)
|
||||||
|
pattern-attrs (cond-> #js {:patternUnits "userSpaceOnUse"
|
||||||
|
:x x
|
||||||
|
:y y
|
||||||
|
:height height
|
||||||
|
:width width
|
||||||
|
:data-loading (str (not (contains? embed uri)))}
|
||||||
|
(= :path (:type shape))
|
||||||
|
(obj/set! "patternTransform" transform))]
|
||||||
|
|
||||||
|
[:*
|
||||||
|
(for [[_shape-index shape] (d/enumerate (or (:position-data shape) [shape]))]
|
||||||
|
(for [[fill-index value] (-> (d/enumerate (:fills shape [])) reverse)]
|
||||||
|
(cond (some? (:fill-color-gradient value))
|
||||||
|
(case (d/name (:type (:fill-color-gradient value)))
|
||||||
|
"linear" [:> grad/linear-gradient #js {:id (str "fill-color-gradient_" render-id "_" fill-index)
|
||||||
|
:gradient (:fill-color-gradient value)
|
||||||
|
:shape shape}]
|
||||||
|
"radial" [:> grad/radial-gradient #js {:id (str "fill-color-gradient_" render-id "_" fill-index)
|
||||||
|
:gradient (:fill-color-gradient value)
|
||||||
|
:shape shape}]))))
|
||||||
|
|
||||||
|
(for [[shape-index shape] (d/enumerate (or (:position-data shape) [shape]))]
|
||||||
|
(let [fill-id (str "fill-" shape-index "-" render-id)]
|
||||||
|
[:> :pattern (-> (obj/clone pattern-attrs)
|
||||||
|
(obj/set! "id" fill-id))
|
||||||
|
[:g
|
||||||
|
(for [[fill-index value] (-> (d/enumerate (:fills shape [])) reverse)]
|
||||||
|
[:> :rect (-> (attrs/extract-fill-attrs value render-id fill-index)
|
||||||
|
(obj/set! "width" width)
|
||||||
|
(obj/set! "height" height))])
|
||||||
|
|
||||||
|
(when has-image
|
||||||
|
[:image {:xlinkHref (get embed uri uri)
|
||||||
|
:width width
|
||||||
|
:height height}])]]))]))))
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
(fn [data]
|
(fn [data]
|
||||||
(-> data
|
(-> data
|
||||||
(dissoc :fill-color :fill-opacity :fill-color-gradient)
|
(dissoc :fill-color :fill-opacity :fill-color-gradient)
|
||||||
(assoc :fill-color "#FFFFFF" :fill-opacity 1)))]
|
(assoc :fills [{:fill-color "#FFFFFF" :fill-opacity 1}])))]
|
||||||
(-> shape
|
(-> shape
|
||||||
(d/update-when :position-data #(mapv update-color %))
|
(d/update-when :position-data #(mapv update-color %))
|
||||||
(assoc :stroke-color "#FFFFFF" :stroke-opacity 1))))
|
(assoc :stroke-color "#FFFFFF" :stroke-opacity 1))))
|
||||||
|
|
|
@ -63,10 +63,6 @@
|
||||||
[:& defs/svg-defs {:shape shape :render-id render-id}]
|
[:& defs/svg-defs {:shape shape :render-id render-id}]
|
||||||
[:& filters/filters {:shape shape :filter-id filter-id}]
|
[:& filters/filters {:shape shape :filter-id filter-id}]
|
||||||
[:& grad/gradient {:shape shape :attr :stroke-color-gradient}]
|
[:& grad/gradient {:shape shape :attr :stroke-color-gradient}]
|
||||||
(when (or (some? (:fill-image shape))
|
[:& fills/fills {:shape shape :render-id render-id}]
|
||||||
(= :image (:type shape))
|
|
||||||
(> (count (:fills shape)) 1)
|
|
||||||
(some :fill-color-gradient (:fills shape)))
|
|
||||||
[:& fills/fills {:shape shape :render-id render-id}])
|
|
||||||
[:& frame/frame-clip-def {:shape shape :render-id render-id}]]
|
[:& frame/frame-clip-def {:shape shape :render-id render-id}]]
|
||||||
children]]))
|
children]]))
|
||||||
|
|
|
@ -194,13 +194,16 @@
|
||||||
grow-type (obj/get props "grow-type") ;; This is only needed in workspace
|
grow-type (obj/get props "grow-type") ;; This is only needed in workspace
|
||||||
;; We add 8px to add a padding for the exporter
|
;; We add 8px to add a padding for the exporter
|
||||||
;; width (+ width 8)
|
;; width (+ width 8)
|
||||||
|
|
||||||
[colors color-mapping color-mapping-inverse] (retrieve-colors shape)
|
[colors color-mapping color-mapping-inverse] (retrieve-colors shape)
|
||||||
|
|
||||||
plain-colors? (mf/use-ctx muc/text-plain-colors-ctx)
|
plain-colors? (mf/use-ctx muc/text-plain-colors-ctx)
|
||||||
|
|
||||||
content (cond-> content
|
content (cond-> content
|
||||||
plain-colors?
|
plain-colors?
|
||||||
(remap-colors color-mapping))]
|
(remap-colors color-mapping))
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
[:foreignObject
|
[:foreignObject
|
||||||
{:x x
|
{:x x
|
||||||
|
|
|
@ -88,9 +88,10 @@
|
||||||
:overflowWrap "initial"}
|
:overflowWrap "initial"}
|
||||||
|
|
||||||
base (-> base
|
base (-> base
|
||||||
(obj/set! "--fill-color" fill-color)
|
(obj/set! "--fills" (transit/encode-str (:fills data)))
|
||||||
(obj/set! "--fill-color-gradient" (transit/encode-str (:fill-color-gradient data)))
|
#_(obj/set! "--fill-color" fill-color)
|
||||||
(obj/set! "--fill-opacity" fill-opacity))]
|
#_(obj/set! "--fill-color-gradient" (transit/encode-str (:fill-color-gradient data)))
|
||||||
|
#_(obj/set! "--fill-opacity" fill-opacity))]
|
||||||
|
|
||||||
(when (and (string? letter-spacing)
|
(when (and (string? letter-spacing)
|
||||||
(pos? (alength letter-spacing)))
|
(pos? (alength letter-spacing)))
|
||||||
|
|
|
@ -59,7 +59,8 @@
|
||||||
:fontStyle (:font-style data)
|
:fontStyle (:font-style data)
|
||||||
:direction (if (:rtl? data) "rtl" "ltr")
|
:direction (if (:rtl? data) "rtl" "ltr")
|
||||||
:whiteSpace "pre"}
|
:whiteSpace "pre"}
|
||||||
(attrs/add-fill data (get-gradient-id index)))})]
|
(obj/set! "fill" (str "url(#fill-" index "-" render-id ")"))
|
||||||
|
#_(attrs/add-fill data (get-gradient-id index)))})]
|
||||||
[:& shape-custom-stroke {:shape shape :index index}
|
[:& shape-custom-stroke {:shape shape :index index}
|
||||||
[:> :text props (:text data)]]))]]))
|
[:> :text props (:text data)]]))]]))
|
||||||
|
|
||||||
|
|
|
@ -155,6 +155,11 @@
|
||||||
|
|
||||||
show-svg-text? (or (some? position-data) (some? @local-position-data))
|
show-svg-text? (or (some? position-data) (some? @local-position-data))
|
||||||
|
|
||||||
|
shape
|
||||||
|
(cond-> shape
|
||||||
|
(some? @local-position-data)
|
||||||
|
(assoc :position-data @local-position-data))
|
||||||
|
|
||||||
update-position-data
|
update-position-data
|
||||||
(fn []
|
(fn []
|
||||||
(when (some? @local-position-data)
|
(when (some? @local-position-data)
|
||||||
|
@ -171,7 +176,7 @@
|
||||||
(fn []
|
(fn []
|
||||||
;; Timer to update the shape. We do this so a lot of changes won't produce
|
;; Timer to update the shape. We do this so a lot of changes won't produce
|
||||||
;; a lot of updates (kind of a debounce)
|
;; a lot of updates (kind of a debounce)
|
||||||
(let [sid (timers/schedule 100 update-position-data)]
|
(let [sid (timers/schedule 50 update-position-data)]
|
||||||
(fn []
|
(fn []
|
||||||
(rx/dispose! sid)))))
|
(rx/dispose! sid)))))
|
||||||
|
|
||||||
|
@ -217,9 +222,5 @@
|
||||||
:key (str id edition?)}]]
|
:key (str id edition?)}]]
|
||||||
|
|
||||||
(when show-svg-text?
|
(when show-svg-text?
|
||||||
(let [shape
|
[:g.text-svg {:pointer-events "none"}
|
||||||
(cond-> shape
|
[:& svg/text-shape {:shape shape}]])]]))
|
||||||
(some? @local-position-data)
|
|
||||||
(assoc :position-data @local-position-data))]
|
|
||||||
[:g.text-svg {:pointer-events "none"}
|
|
||||||
[:& svg/text-shape {:shape shape}]]))]]))
|
|
||||||
|
|
|
@ -210,7 +210,11 @@
|
||||||
{:ref self-ref
|
{:ref self-ref
|
||||||
:style {:cursor (cur/text (:rotation shape))
|
:style {:cursor (cur/text (:rotation shape))
|
||||||
:width (:width shape)
|
:width (:width shape)
|
||||||
:height (:height shape)}
|
:height (:height shape)
|
||||||
|
;; We hide the editor when is blurred because otherwise the selection won't let us see
|
||||||
|
;; the underlying text. Use opacity because display or visibility won't allow to recover
|
||||||
|
;; focus afterwards.
|
||||||
|
:opacity (when @blurred 0)}
|
||||||
:on-click on-click
|
:on-click on-click
|
||||||
:class (dom/classnames
|
:class (dom/classnames
|
||||||
:align-top (= (:vertical-align content "top") "top")
|
:align-top (= (:vertical-align content "top") "top")
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
(ns app.main.ui.workspace.sidebar.options.menus.fill
|
(ns app.main.ui.workspace.sidebar.options.menus.fill
|
||||||
(:require
|
(:require
|
||||||
[app.common.attrs :as attrs]
|
|
||||||
[app.common.colors :as clr]
|
[app.common.colors :as clr]
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.pages :as cp]
|
[app.common.pages :as cp]
|
||||||
|
@ -17,7 +16,6 @@
|
||||||
[app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]]
|
[app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
[cuerdas.core :as str]
|
|
||||||
[rumext.alpha :as mf]))
|
[rumext.alpha :as mf]))
|
||||||
|
|
||||||
(def fill-attrs
|
(def fill-attrs
|
||||||
|
@ -51,40 +49,6 @@
|
||||||
;; Excluding nil values
|
;; Excluding nil values
|
||||||
values (d/without-nils values)
|
values (d/without-nils values)
|
||||||
|
|
||||||
only-shapes? (and (contains? values :fills)
|
|
||||||
;; texts have :fill-* attributes, the rest of the shapes have :fills
|
|
||||||
(= (count (filter #(str/starts-with? (d/name %) "fill-") (keys values))) 0))
|
|
||||||
|
|
||||||
shapes-and-texts? (and (contains? values :fills)
|
|
||||||
;; texts have :fill-* attributes, the rest of the shapes have :fills
|
|
||||||
(> (count (filter #(str/starts-with? (d/name %) "fill-") (keys values))) 0))
|
|
||||||
|
|
||||||
;; Texts still have :fill-* attributes and the rest of the shapes just :fills so we need some extra calculation when multiple selection happens to detect them
|
|
||||||
plain-values (if (vector? (:fills values))
|
|
||||||
(concat (:fills values) [(dissoc values :fills)])
|
|
||||||
values)
|
|
||||||
|
|
||||||
plain-values (attrs/get-attrs-multi plain-values [:fill-color :fill-opacity :fill-color-ref-id :fill-color-ref-file :fill-color-gradient])
|
|
||||||
|
|
||||||
plain-values (if (empty? plain-values)
|
|
||||||
values
|
|
||||||
plain-values)
|
|
||||||
|
|
||||||
;; We must control some rare situations like
|
|
||||||
;; - Selecting texts and shapes with different fills
|
|
||||||
;; - Selecting a text and a shape with empty fills
|
|
||||||
plain-values (if (and shapes-and-texts?
|
|
||||||
(or
|
|
||||||
(= (:fills values) :multiple)
|
|
||||||
(= 0 (count (:fills values)))))
|
|
||||||
{:fills :multiple
|
|
||||||
:fill-color :multiple
|
|
||||||
:fill-opacity :multiple
|
|
||||||
:fill-color-ref-id :multiple
|
|
||||||
:fill-color-ref-file :multiple
|
|
||||||
:fill-color-gradient :multiple}
|
|
||||||
plain-values)
|
|
||||||
|
|
||||||
hide-fill-on-export? (:hide-fill-on-export values false)
|
hide-fill-on-export? (:hide-fill-on-export values false)
|
||||||
|
|
||||||
checkbox-ref (mf/use-ref)
|
checkbox-ref (mf/use-ref)
|
||||||
|
@ -110,12 +74,6 @@
|
||||||
(fn [index]
|
(fn [index]
|
||||||
(st/emit! (dc/reorder-fills ids index new-index)))))
|
(st/emit! (dc/reorder-fills ids index new-index)))))
|
||||||
|
|
||||||
on-change-mixed-shapes
|
|
||||||
(mf/use-callback
|
|
||||||
(mf/deps ids)
|
|
||||||
(fn [color]
|
|
||||||
(st/emit! (dc/change-fill-and-clear ids color))))
|
|
||||||
|
|
||||||
on-remove
|
on-remove
|
||||||
(fn [index]
|
(fn [index]
|
||||||
(fn []
|
(fn []
|
||||||
|
@ -155,43 +113,33 @@
|
||||||
[:div.element-set
|
[:div.element-set
|
||||||
[:div.element-set-title
|
[:div.element-set-title
|
||||||
[:span label]
|
[:span label]
|
||||||
(when (and (not disable-remove?) (not (= :multiple (:fills values))) only-shapes?)
|
(when (and (not disable-remove?) (not (= :multiple (:fills values))))
|
||||||
[:div.add-page {:on-click on-add} i/close])]
|
[:div.add-page {:on-click on-add} i/close])]
|
||||||
|
|
||||||
[:div.element-set-content
|
[:div.element-set-content
|
||||||
|
|
||||||
(if only-shapes?
|
(cond
|
||||||
(cond
|
(= :multiple (:fills values))
|
||||||
(= :multiple (:fills values))
|
[:div.element-set-options-group
|
||||||
[:div.element-set-options-group
|
[:div.element-set-label (tr "settings.multiple")]
|
||||||
[:div.element-set-label (tr "settings.multiple")]
|
[:div.element-set-actions
|
||||||
[:div.element-set-actions
|
[:div.element-set-actions-button {:on-click on-remove-all}
|
||||||
[:div.element-set-actions-button {:on-click on-remove-all}
|
i/minus]]]
|
||||||
i/minus]]]
|
|
||||||
|
|
||||||
(seq (:fills values))
|
(seq (:fills values))
|
||||||
[:& h/sortable-container {}
|
[:& h/sortable-container {}
|
||||||
(for [[index value] (d/enumerate (:fills values []))]
|
(for [[index value] (d/enumerate (:fills values []))]
|
||||||
[:& color-row {:color {:color (:fill-color value)
|
[:& color-row {:color {:color (:fill-color value)
|
||||||
:opacity (:fill-opacity value)
|
:opacity (:fill-opacity value)
|
||||||
:id (:fill-color-ref-id value)
|
:id (:fill-color-ref-id value)
|
||||||
:file-id (:fill-color-ref-file value)
|
:file-id (:fill-color-ref-file value)
|
||||||
:gradient (:fill-color-gradient value)}
|
:gradient (:fill-color-gradient value)}
|
||||||
:index index
|
:index index
|
||||||
:title (tr "workspace.options.fill")
|
:title (tr "workspace.options.fill")
|
||||||
:on-change (on-change index)
|
:on-change (on-change index)
|
||||||
:on-reorder (on-reorder index)
|
:on-reorder (on-reorder index)
|
||||||
:on-detach (on-detach index)
|
:on-detach (on-detach index)
|
||||||
:on-remove (on-remove index)}])])
|
:on-remove (on-remove index)}])])
|
||||||
|
|
||||||
[:& color-row {:color {:color (:fill-color plain-values)
|
|
||||||
:opacity (:fill-opacity plain-values)
|
|
||||||
:id (:fill-color-ref-id plain-values)
|
|
||||||
:file-id (:fill-color-ref-file plain-values)
|
|
||||||
:gradient (:fill-color-gradient plain-values)}
|
|
||||||
:title (tr "workspace.options.fill")
|
|
||||||
:on-change on-change-mixed-shapes
|
|
||||||
:on-detach (on-detach 0)}])
|
|
||||||
|
|
||||||
(when (or (= type :frame)
|
(when (or (= type :frame)
|
||||||
(and (= type :multiple) (some? hide-fill-on-export?)))
|
(and (= type :multiple) (some? hide-fill-on-export?)))
|
||||||
|
|
|
@ -347,5 +347,3 @@
|
||||||
[:div.row-flex
|
[:div.row-flex
|
||||||
[:> grow-options opts]
|
[:> grow-options opts]
|
||||||
[:div.align-icons]]]]))
|
[:div.align-icons]]]]))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
(ns app.main.ui.workspace.sidebar.options.shapes.text
|
(ns app.main.ui.workspace.sidebar.options.shapes.text
|
||||||
(:require
|
(:require
|
||||||
[app.common.colors :as clr]
|
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.main.data.workspace.texts :as dwt]
|
[app.main.data.workspace.texts :as dwt]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
|
@ -30,19 +29,16 @@
|
||||||
|
|
||||||
layer-values (select-keys shape layer-attrs)
|
layer-values (select-keys shape layer-attrs)
|
||||||
|
|
||||||
fill-values (dwt/current-text-values
|
fill-values (-> (dwt/current-text-values
|
||||||
{:editor-state editor-state
|
{:editor-state editor-state
|
||||||
:shape shape
|
:shape shape
|
||||||
:attrs text-fill-attrs})
|
:attrs (conj text-fill-attrs :fills)})
|
||||||
|
(d/update-in-when [:fill-color-gradient :type] keyword))
|
||||||
|
|
||||||
fill-values (d/update-in-when fill-values [:fill-color-gradient :type] keyword)
|
fill-values (if (not (contains? fill-values :fills))
|
||||||
|
;; Old fill format
|
||||||
fill-values (cond-> fill-values
|
{:fills [fill-values]}
|
||||||
(not (contains? fill-values :fill-color)) (assoc :fill-color clr/black)
|
fill-values)
|
||||||
(not (contains? fill-values :fill-opacity)) (assoc :fill-opacity 1)
|
|
||||||
;; Keep for backwards compatibility
|
|
||||||
(:fill fill-values) (assoc :fill-color (:fill fill-values))
|
|
||||||
(:opacity fill-values) (assoc :fill-opacity (:fill fill-values)))
|
|
||||||
|
|
||||||
stroke-values (select-keys shape stroke-attrs)
|
stroke-values (select-keys shape stroke-attrs)
|
||||||
|
|
||||||
|
@ -79,8 +75,7 @@
|
||||||
[:& fill-menu
|
[:& fill-menu
|
||||||
{:ids ids
|
{:ids ids
|
||||||
:type type
|
:type type
|
||||||
:values fill-values
|
:values fill-values}]
|
||||||
:disable-remove? true}]
|
|
||||||
|
|
||||||
[:& stroke-menu {:ids ids
|
[:& stroke-menu {:ids ids
|
||||||
:type type
|
:type type
|
||||||
|
|
|
@ -95,6 +95,13 @@
|
||||||
selected (impl/getSelectedBlocks state)]
|
selected (impl/getSelectedBlocks state)]
|
||||||
(reduce update-blocks state selected)))
|
(reduce update-blocks state selected)))
|
||||||
|
|
||||||
|
(defn update-editor-current-inline-styles-fn
|
||||||
|
[state update-fn]
|
||||||
|
(let [attrs (-> (.getCurrentInlineStyle ^js state)
|
||||||
|
(txt/styles-to-attrs)
|
||||||
|
(update-fn))]
|
||||||
|
(impl/applyInlineStyle state (txt/attrs-to-styles attrs))))
|
||||||
|
|
||||||
(defn editor-split-block
|
(defn editor-split-block
|
||||||
[state]
|
[state]
|
||||||
(impl/splitBlockPreservingData state))
|
(impl/splitBlockPreservingData state))
|
||||||
|
|
|
@ -119,7 +119,5 @@
|
||||||
:text-transform (str (get "text-transform"))
|
:text-transform (str (get "text-transform"))
|
||||||
:text-decoration (str (get "text-decoration"))
|
:text-decoration (str (get "text-decoration"))
|
||||||
:font-style (str (get "font-style"))
|
:font-style (str (get "font-style"))
|
||||||
:fill-color (or (get "--fill-color") "#000000")
|
:fills (transit/decode-str (get "--fills"))
|
||||||
:fill-color-gradient (transit/decode-str (get "--fill-color-gradient"))
|
|
||||||
:fill-opacity (d/parse-double (or (get "--fill-opacity") "1"))
|
|
||||||
:text text}))))))))))
|
:text text}))))))))))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue