mirror of
https://github.com/penpot/penpot.git
synced 2026-09-18 18:06:14 +00:00
🎉 Absorb colors and typographies
This commit is contained in:
parent
7da159d52a
commit
43e0b5cfa5
@ -17,10 +17,12 @@
|
|||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
[app.common.pages.changes-spec :as pcs]
|
[app.common.pages.changes-spec :as pcs]
|
||||||
[app.common.types.components-list :as ctkl]
|
[app.common.types.components-list :as ctkl]
|
||||||
|
[app.common.types.colors-list :as ctcl]
|
||||||
[app.common.types.page :as ctp]
|
[app.common.types.page :as ctp]
|
||||||
[app.common.types.pages-list :as ctpl]
|
[app.common.types.pages-list :as ctpl]
|
||||||
[app.common.types.shape :as cts]
|
[app.common.types.shape :as cts]
|
||||||
[app.common.types.shape-tree :as ctst]))
|
[app.common.types.shape-tree :as ctst]
|
||||||
|
[app.common.types.typographies-list :as ctyl]))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; Specific helpers
|
;; Specific helpers
|
||||||
@ -309,7 +311,7 @@
|
|||||||
|
|
||||||
(defmethod process-change :add-color
|
(defmethod process-change :add-color
|
||||||
[data {:keys [color]}]
|
[data {:keys [color]}]
|
||||||
(update data :colors assoc (:id color) color))
|
(ctcl/add-color data color))
|
||||||
|
|
||||||
(defmethod process-change :mod-color
|
(defmethod process-change :mod-color
|
||||||
[data {:keys [color]}]
|
[data {:keys [color]}]
|
||||||
@ -375,7 +377,7 @@
|
|||||||
|
|
||||||
(defmethod process-change :add-typography
|
(defmethod process-change :add-typography
|
||||||
[data {:keys [typography]}]
|
[data {:keys [typography]}]
|
||||||
(update data :typographies assoc (:id typography) typography))
|
(ctyl/add-typography data typography))
|
||||||
|
|
||||||
(defmethod process-change :mod-typography
|
(defmethod process-change :mod-typography
|
||||||
[data {:keys [typography]}]
|
[data {:keys [typography]}]
|
||||||
|
|||||||
@ -205,21 +205,6 @@
|
|||||||
([libraries library-id component-id]
|
([libraries library-id component-id]
|
||||||
(get-in libraries [library-id :data :components component-id])))
|
(get-in libraries [library-id :data :components component-id])))
|
||||||
|
|
||||||
(defn is-main-of?
|
|
||||||
[shape-main shape-inst]
|
|
||||||
(and (:shape-ref shape-inst)
|
|
||||||
(or (= (:shape-ref shape-inst) (:id shape-main))
|
|
||||||
(= (:shape-ref shape-inst) (:shape-ref shape-main)))))
|
|
||||||
|
|
||||||
(defn is-main-instance?
|
|
||||||
[shape-id page-id component]
|
|
||||||
(and (= shape-id (:main-instance-id component))
|
|
||||||
(= page-id (:main-instance-page component))))
|
|
||||||
|
|
||||||
(defn get-component-root
|
|
||||||
[component]
|
|
||||||
(get-in component [:objects (:id component)]))
|
|
||||||
|
|
||||||
(defn get-component-shape
|
(defn get-component-shape
|
||||||
"Get the parent shape linked to a component for this shape, if any"
|
"Get the parent shape linked to a component for this shape, if any"
|
||||||
[objects shape]
|
[objects shape]
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
[app.common.math :as mth]
|
[app.common.math :as mth]
|
||||||
[app.common.pages :as cp]
|
[app.common.pages :as cp]
|
||||||
[app.common.pages.helpers :as cph]
|
[app.common.pages.helpers :as cph]
|
||||||
|
[app.common.types.component :as ctk]
|
||||||
[app.common.types.components-list :as ctkl]
|
[app.common.types.components-list :as ctkl]
|
||||||
[app.common.types.container :as ctn]
|
[app.common.types.container :as ctn]
|
||||||
[app.common.types.file :as ctf]
|
[app.common.types.file :as ctf]
|
||||||
@ -485,7 +486,7 @@
|
|||||||
add-instance-grid
|
add-instance-grid
|
||||||
(fn [data components]
|
(fn [data components]
|
||||||
(let [position-seq (ctst/generate-shape-grid
|
(let [position-seq (ctst/generate-shape-grid
|
||||||
(map cph/get-component-root components)
|
(map ctk/get-component-root components)
|
||||||
start-pos
|
start-pos
|
||||||
grid-gap)]
|
grid-gap)]
|
||||||
(loop [data data
|
(loop [data data
|
||||||
|
|||||||
@ -102,6 +102,12 @@
|
|||||||
:fill-opacity opacity
|
:fill-opacity opacity
|
||||||
:fill-color-gradient gradient)))))
|
:fill-color-gradient gradient)))))
|
||||||
|
|
||||||
|
(defn attach-fill-color
|
||||||
|
[shape position ref-id ref-file]
|
||||||
|
(-> shape
|
||||||
|
(assoc-in [:fills position :fill-color-ref-id] ref-id)
|
||||||
|
(assoc-in [:fills position :fill-color-ref-file] ref-file)))
|
||||||
|
|
||||||
(defn detach-fill-color
|
(defn detach-fill-color
|
||||||
[shape position]
|
[shape position]
|
||||||
(-> shape
|
(-> shape
|
||||||
@ -127,6 +133,12 @@
|
|||||||
:stroke-opacity opacity
|
:stroke-opacity opacity
|
||||||
:stroke-color-gradient gradient)))))
|
:stroke-color-gradient gradient)))))
|
||||||
|
|
||||||
|
(defn attach-stroke-color
|
||||||
|
[shape position ref-id ref-file]
|
||||||
|
(-> shape
|
||||||
|
(assoc-in [:strokes position :stroke-color-ref-id] ref-id)
|
||||||
|
(assoc-in [:strokes position :stroke-color-ref-file] ref-file)))
|
||||||
|
|
||||||
(defn detach-stroke-color
|
(defn detach-stroke-color
|
||||||
[shape position]
|
[shape position]
|
||||||
(-> shape
|
(-> shape
|
||||||
@ -152,6 +164,12 @@
|
|||||||
:opacity opacity
|
:opacity opacity
|
||||||
:gradient gradient)))))
|
:gradient gradient)))))
|
||||||
|
|
||||||
|
(defn attach-shadow-color
|
||||||
|
[shape position ref-id ref-file]
|
||||||
|
(-> shape
|
||||||
|
(assoc-in [:shadow position :color :id] ref-id)
|
||||||
|
(assoc-in [:shadow position :color :file-id] ref-file)))
|
||||||
|
|
||||||
(defn detach-shadow-color
|
(defn detach-shadow-color
|
||||||
[shape position]
|
[shape position]
|
||||||
(-> shape
|
(-> shape
|
||||||
@ -176,6 +194,11 @@
|
|||||||
:color color
|
:color color
|
||||||
:opacity opacity
|
:opacity opacity
|
||||||
:gradient gradient)))))
|
:gradient gradient)))))
|
||||||
|
(defn attach-grid-color
|
||||||
|
[shape position ref-id ref-file]
|
||||||
|
(-> shape
|
||||||
|
(assoc-in [:grids position :params :color :id] ref-id)
|
||||||
|
(assoc-in [:grids position :params :color :file-id] ref-file)))
|
||||||
|
|
||||||
(defn detach-grid-color
|
(defn detach-grid-color
|
||||||
[shape position]
|
[shape position]
|
||||||
@ -213,11 +236,87 @@
|
|||||||
(= (:ref-file %) library-id))
|
(= (:ref-file %) library-id))
|
||||||
all-colors)))
|
all-colors)))
|
||||||
|
|
||||||
|
(defn uses-library-color?
|
||||||
|
"Check if the shape uses the given library color."
|
||||||
|
[shape library-id color]
|
||||||
|
(let [all-colors (get-all-colors shape)]
|
||||||
|
(some #(and (= (:ref-id %) (:id color))
|
||||||
|
(= (:ref-file %) library-id))
|
||||||
|
all-colors)))
|
||||||
|
|
||||||
|
(defn- process-shape-colors
|
||||||
|
"Execute an update function on all colors of a shape."
|
||||||
|
[shape func]
|
||||||
|
(let [process-fill (fn [shape [position fill]]
|
||||||
|
(func shape
|
||||||
|
position
|
||||||
|
(fill->shape-color fill)
|
||||||
|
set-fill-color
|
||||||
|
attach-fill-color
|
||||||
|
detach-fill-color))
|
||||||
|
|
||||||
|
process-stroke (fn [shape [position stroke]]
|
||||||
|
(func shape
|
||||||
|
position
|
||||||
|
(stroke->shape-color stroke)
|
||||||
|
set-stroke-color
|
||||||
|
attach-stroke-color
|
||||||
|
detach-stroke-color))
|
||||||
|
|
||||||
|
process-shadow (fn [shape [position shadow]]
|
||||||
|
(func shape
|
||||||
|
position
|
||||||
|
(shadow->shape-color shadow)
|
||||||
|
set-shadow-color
|
||||||
|
attach-shadow-color
|
||||||
|
detach-shadow-color))
|
||||||
|
|
||||||
|
process-grid (fn [shape [position grid]]
|
||||||
|
(func shape
|
||||||
|
position
|
||||||
|
(grid->shape-color grid)
|
||||||
|
set-grid-color
|
||||||
|
attach-grid-color
|
||||||
|
detach-grid-color))
|
||||||
|
|
||||||
|
process-text-node (fn [node]
|
||||||
|
(as-> node $
|
||||||
|
(reduce process-fill $ (d/enumerate (:fills $)))
|
||||||
|
(reduce process-stroke $ (d/enumerate (:strokes $)))))
|
||||||
|
|
||||||
|
process-text (fn [shape]
|
||||||
|
(let [content (:content shape)
|
||||||
|
new-content (txt/transform-nodes process-text-node content)]
|
||||||
|
(if (not= content new-content)
|
||||||
|
(assoc shape :content new-content)
|
||||||
|
shape)))]
|
||||||
|
|
||||||
|
(as-> shape $
|
||||||
|
(reduce process-fill $ (d/enumerate (:fills $)))
|
||||||
|
(reduce process-stroke $ (d/enumerate (:strokes $)))
|
||||||
|
(reduce process-shadow $ (d/enumerate (:shadow $)))
|
||||||
|
(reduce process-grid $ (d/enumerate (:grids $)))
|
||||||
|
(process-text $))))
|
||||||
|
|
||||||
|
(defn remap-colors
|
||||||
|
"Change the shape so that any use of the given color now points to
|
||||||
|
the given library."
|
||||||
|
[shape library-id color]
|
||||||
|
(let [remap-color (fn [shape position shape-color _ attach-fn _]
|
||||||
|
(if (= (:ref-id shape-color) (:id color))
|
||||||
|
(attach-fn shape
|
||||||
|
position
|
||||||
|
(:id color)
|
||||||
|
library-id)
|
||||||
|
shape))]
|
||||||
|
|
||||||
|
(process-shape-colors shape remap-color)))
|
||||||
|
|
||||||
(defn sync-shape-colors
|
(defn sync-shape-colors
|
||||||
"Look for usage of any color of the given library inside the shape,
|
"Look for usage of any color of the given library inside the shape,
|
||||||
and, in this case, copy the library color into the shape."
|
and, in this case, copy the library color into the shape."
|
||||||
[shape library-id library-colors]
|
[shape library-id library-colors]
|
||||||
(let [sync-color (fn [shape position shape-color set-fn detach-fn]
|
(let [sync-color (fn [shape position shape-color set-fn _ detach-fn]
|
||||||
(if (= (:ref-file shape-color) library-id)
|
(if (= (:ref-file shape-color) library-id)
|
||||||
(let [library-color (get library-colors (:ref-id shape-color))]
|
(let [library-color (get library-colors (:ref-id shape-color))]
|
||||||
(if (some? library-color)
|
(if (some? library-color)
|
||||||
@ -227,51 +326,7 @@
|
|||||||
(:opacity library-color)
|
(:opacity library-color)
|
||||||
(:gradient library-color))
|
(:gradient library-color))
|
||||||
(detach-fn shape position)))
|
(detach-fn shape position)))
|
||||||
shape))
|
shape))]
|
||||||
|
|
||||||
sync-fill (fn [shape [position fill]]
|
(process-shape-colors shape sync-color)))
|
||||||
(sync-color shape
|
|
||||||
position
|
|
||||||
(fill->shape-color fill)
|
|
||||||
set-fill-color
|
|
||||||
detach-fill-color))
|
|
||||||
|
|
||||||
sync-stroke (fn [shape [position stroke]]
|
|
||||||
(sync-color shape
|
|
||||||
position
|
|
||||||
(stroke->shape-color stroke)
|
|
||||||
set-stroke-color
|
|
||||||
detach-stroke-color))
|
|
||||||
|
|
||||||
sync-shadow (fn [shape [position shadow]]
|
|
||||||
(sync-color shape
|
|
||||||
position
|
|
||||||
(shadow->shape-color shadow)
|
|
||||||
set-shadow-color
|
|
||||||
detach-shadow-color))
|
|
||||||
|
|
||||||
sync-grid (fn [shape [position grid]]
|
|
||||||
(sync-color shape
|
|
||||||
position
|
|
||||||
(grid->shape-color grid)
|
|
||||||
set-grid-color
|
|
||||||
detach-grid-color))
|
|
||||||
|
|
||||||
sync-text-node (fn [node]
|
|
||||||
(as-> node $
|
|
||||||
(reduce sync-fill $ (d/enumerate (:fills $)))
|
|
||||||
(reduce sync-stroke $ (d/enumerate (:strokes $)))))
|
|
||||||
|
|
||||||
sync-text (fn [shape]
|
|
||||||
(let [content (:content shape)
|
|
||||||
new-content (txt/transform-nodes sync-text-node content)]
|
|
||||||
(if (not= content new-content)
|
|
||||||
(assoc shape :content new-content)
|
|
||||||
shape)))]
|
|
||||||
|
|
||||||
(as-> shape $
|
|
||||||
(reduce sync-fill $ (d/enumerate (:fills $)))
|
|
||||||
(reduce sync-stroke $ (d/enumerate (:strokes $)))
|
|
||||||
(reduce sync-shadow $ (d/enumerate (:shadow $)))
|
|
||||||
(reduce sync-grid $ (d/enumerate (:grids $)))
|
|
||||||
(sync-text $))))
|
|
||||||
|
|||||||
26
common/src/app/common/types/colors_list.cljc
Normal file
26
common/src/app/common/types/colors_list.cljc
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) UXBOX Labs SL
|
||||||
|
|
||||||
|
(ns app.common.types.colors-list
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]))
|
||||||
|
|
||||||
|
(defn colors-seq
|
||||||
|
[file-data]
|
||||||
|
(vals (:colors file-data)))
|
||||||
|
|
||||||
|
(defn add-color
|
||||||
|
[file-data color]
|
||||||
|
(update file-data :colors assoc (:id color) color))
|
||||||
|
|
||||||
|
(defn get-color
|
||||||
|
[file-data color-id]
|
||||||
|
(get-in file-data [:colors color-id]))
|
||||||
|
|
||||||
|
(defn update-color
|
||||||
|
[file-data color-id f]
|
||||||
|
(update-in file-data [:colors color-id] f))
|
||||||
|
|
||||||
@ -7,7 +7,24 @@
|
|||||||
(ns app.common.types.component)
|
(ns app.common.types.component)
|
||||||
|
|
||||||
(defn instance-of?
|
(defn instance-of?
|
||||||
[shape component]
|
[shape file-id component]
|
||||||
(and (some? (:component-id shape))
|
(and (some? (:component-id shape))
|
||||||
(= (:component-id shape) (:id component))))
|
(some? (:component-file shape))
|
||||||
|
(= (:component-id shape) (:id component))
|
||||||
|
(= (:component-file shape) file-id)))
|
||||||
|
|
||||||
|
(defn is-main-of?
|
||||||
|
[shape-main shape-inst]
|
||||||
|
(and (:shape-ref shape-inst)
|
||||||
|
(or (= (:shape-ref shape-inst) (:id shape-main))
|
||||||
|
(= (:shape-ref shape-inst) (:shape-ref shape-main)))))
|
||||||
|
|
||||||
|
(defn is-main-instance?
|
||||||
|
[shape-id page-id component]
|
||||||
|
(and (= shape-id (:main-instance-id component))
|
||||||
|
(= page-id (:main-instance-page component))))
|
||||||
|
|
||||||
|
(defn get-component-root
|
||||||
|
[component]
|
||||||
|
(get-in component [:objects (:id component)]))
|
||||||
|
|
||||||
|
|||||||
@ -18,7 +18,8 @@
|
|||||||
(s/def ::path (s/nilable string?))
|
(s/def ::path (s/nilable string?))
|
||||||
|
|
||||||
(s/def ::container
|
(s/def ::container
|
||||||
(s/keys :req-un [::id ::name ::ctst/objects]
|
;; (s/keys :req-un [::id ::name ::ctst/objects]
|
||||||
|
(s/keys :req-un [::id ::name]
|
||||||
:opt-un [::type ::path]))
|
:opt-un [::type ::path]))
|
||||||
|
|
||||||
(defn make-container
|
(defn make-container
|
||||||
|
|||||||
@ -13,12 +13,15 @@
|
|||||||
[app.common.pages.helpers :as cph]
|
[app.common.pages.helpers :as cph]
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
[app.common.types.color :as ctc]
|
[app.common.types.color :as ctc]
|
||||||
|
[app.common.types.colors-list :as ctcl]
|
||||||
[app.common.types.component :as ctk]
|
[app.common.types.component :as ctk]
|
||||||
[app.common.types.components-list :as ctkl]
|
[app.common.types.components-list :as ctkl]
|
||||||
[app.common.types.container :as ctn]
|
[app.common.types.container :as ctn]
|
||||||
[app.common.types.page :as ctp]
|
[app.common.types.page :as ctp]
|
||||||
[app.common.types.pages-list :as ctpl]
|
[app.common.types.pages-list :as ctpl]
|
||||||
[app.common.types.shape-tree :as ctst]
|
[app.common.types.shape-tree :as ctst]
|
||||||
|
[app.common.types.typography :as cty]
|
||||||
|
[app.common.types.typographies-list :as ctyl]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[clojure.spec.alpha :as s]
|
[clojure.spec.alpha :as s]
|
||||||
[cuerdas.core :as str]))
|
[cuerdas.core :as str]))
|
||||||
@ -91,6 +94,10 @@
|
|||||||
|
|
||||||
;; Helpers
|
;; Helpers
|
||||||
|
|
||||||
|
(defn file-data
|
||||||
|
[file]
|
||||||
|
(:data file))
|
||||||
|
|
||||||
(defn update-file-data
|
(defn update-file-data
|
||||||
[file f]
|
[file f]
|
||||||
(update file :data f))
|
(update file :data f))
|
||||||
@ -108,19 +115,51 @@
|
|||||||
(ctpl/update-page file-data (:id container) f)
|
(ctpl/update-page file-data (:id container) f)
|
||||||
(ctkl/update-component file-data (:id container) f)))
|
(ctkl/update-component file-data (:id container) f)))
|
||||||
|
|
||||||
(defn find-instances
|
;; Asset helpers
|
||||||
"Find all uses of a component in a file (may be in pages or in the components
|
|
||||||
of the local library).
|
|
||||||
|
|
||||||
Returns a vector [[container shapes] [container shapes]...]"
|
|
||||||
[file-data component]
|
|
||||||
(let [find-instances-in-container
|
|
||||||
(fn [container component]
|
|
||||||
(let [instances (filter #(ctk/instance-of? % component) (ctn/shapes-seq container))]
|
|
||||||
(when (d/not-empty? instances)
|
|
||||||
[[container instances]])))]
|
|
||||||
|
|
||||||
(mapcat #(find-instances-in-container % component) (containers-seq file-data))))
|
(defmulti uses-asset?
|
||||||
|
"Checks if a shape uses the given asset."
|
||||||
|
(fn [asset-type _ _ _] asset-type))
|
||||||
|
|
||||||
|
(defmethod uses-asset? :component
|
||||||
|
[_ shape library-id component]
|
||||||
|
(ctk/instance-of? shape library-id component))
|
||||||
|
|
||||||
|
(defmethod uses-asset? :color
|
||||||
|
[_ shape library-id color]
|
||||||
|
(ctc/uses-library-color? shape library-id color))
|
||||||
|
|
||||||
|
(defmethod uses-asset? :typography
|
||||||
|
[_ shape library-id typography]
|
||||||
|
(cty/uses-library-typography? shape library-id typography))
|
||||||
|
|
||||||
|
(defn find-asset-type-usages
|
||||||
|
"Find all usages of an asset in a file (may be in pages or in the components
|
||||||
|
of the local library).
|
||||||
|
|
||||||
|
Returns a list ((asset ((container shapes) (container shapes)...))...)"
|
||||||
|
[file-data library-data asset-type]
|
||||||
|
(let [assets-seq (case asset-type
|
||||||
|
:component (ctkl/components-seq library-data)
|
||||||
|
:color (ctcl/colors-seq library-data)
|
||||||
|
:typography (ctyl/typographies-seq library-data))
|
||||||
|
|
||||||
|
find-usages-in-container
|
||||||
|
(fn [container asset]
|
||||||
|
(let [instances (filter #(uses-asset? asset-type % (:id library-data) asset)
|
||||||
|
(ctn/shapes-seq container))]
|
||||||
|
(when (d/not-empty? instances)
|
||||||
|
[[container instances]])))
|
||||||
|
|
||||||
|
find-asset-usages
|
||||||
|
(fn [file-data library-id asset-type asset]
|
||||||
|
(mapcat #(find-usages-in-container % asset) (containers-seq file-data)))]
|
||||||
|
|
||||||
|
(mapcat (fn [asset]
|
||||||
|
(let [instances (find-asset-usages file-data (:id library-data) asset-type asset)]
|
||||||
|
(when (d/not-empty? instances)
|
||||||
|
[[asset instances]])))
|
||||||
|
assets-seq)))
|
||||||
|
|
||||||
(defn get-or-add-library-page
|
(defn get-or-add-library-page
|
||||||
[file-data grid-gap]
|
[file-data grid-gap]
|
||||||
@ -276,95 +315,20 @@
|
|||||||
"Find all assets of a library that are used in the file, and
|
"Find all assets of a library that are used in the file, and
|
||||||
move them to the file local library."
|
move them to the file local library."
|
||||||
[file-data library-data]
|
[file-data library-data]
|
||||||
(let [; Build a list of all components in the library used in the file
|
(let [used-components (find-asset-type-usages file-data library-data :component)
|
||||||
; The list is in the form [[component [[container shapes] [container shapes]...]]...]
|
used-colors (find-asset-type-usages file-data library-data :color)
|
||||||
used-components ; A vector of pair [component instances], where instances is non-empty
|
used-typographies (find-asset-type-usages file-data library-data :typography)]
|
||||||
(mapcat (fn [component]
|
|
||||||
(let [instances (find-instances file-data component)]
|
|
||||||
(when (d/not-empty? instances)
|
|
||||||
[[component instances]])))
|
|
||||||
(ctkl/components-seq library-data))]
|
|
||||||
|
|
||||||
(if (empty? used-components)
|
(cond-> file-data
|
||||||
file-data
|
(d/not-empty? used-components)
|
||||||
(let [; Search for the library page. If not exists, create it.
|
(absorb-components library-data used-components)
|
||||||
[file-data page-id start-pos]
|
|
||||||
(get-or-add-library-page file-data)
|
|
||||||
|
|
||||||
absorb-component
|
(d/not-empty? used-colors)
|
||||||
(fn [file-data [component instances] position]
|
(absorb-colors library-data used-colors)
|
||||||
(let [page (ctpl/get-page file-data page-id)
|
|
||||||
|
|
||||||
; Make a new main instance for the component
|
(d/not-empty? used-typographies)
|
||||||
[main-instance-shape main-instance-shapes]
|
(absorb-typographies library-data used-typographies))))
|
||||||
(ctn/instantiate-component page
|
|
||||||
component
|
|
||||||
(:id file-data)
|
|
||||||
position)
|
|
||||||
|
|
||||||
; Add all shapes of the main instance to the library page
|
|
||||||
add-main-instance-shapes
|
|
||||||
(fn [page]
|
|
||||||
(reduce (fn [page shape]
|
|
||||||
(ctst/add-shape (:id shape)
|
|
||||||
shape
|
|
||||||
page
|
|
||||||
(:frame-id shape)
|
|
||||||
(:parent-id shape)
|
|
||||||
nil ; <- As shapes are ordered, we can safely add each
|
|
||||||
true)) ; one at the end of the parent's children list.
|
|
||||||
page
|
|
||||||
main-instance-shapes))
|
|
||||||
|
|
||||||
; Copy the component in the file local library
|
|
||||||
copy-component
|
|
||||||
(fn [file-data]
|
|
||||||
(ctkl/add-component file-data
|
|
||||||
(:id component)
|
|
||||||
(:name component)
|
|
||||||
(:path component)
|
|
||||||
(:id main-instance-shape)
|
|
||||||
page-id
|
|
||||||
(vals (:objects component))))
|
|
||||||
|
|
||||||
; Change all existing instances to point to the local file
|
|
||||||
redirect-instances
|
|
||||||
(fn [file-data [container shapes]]
|
|
||||||
(let [redirect-instance #(assoc % :component-file (:id file-data))]
|
|
||||||
(update-container file-data
|
|
||||||
container
|
|
||||||
#(reduce (fn [container shape]
|
|
||||||
(ctn/update-shape container
|
|
||||||
(:id shape)
|
|
||||||
redirect-instance))
|
|
||||||
%
|
|
||||||
shapes))))]
|
|
||||||
|
|
||||||
(as-> file-data $
|
|
||||||
(ctpl/update-page $ page-id add-main-instance-shapes)
|
|
||||||
(copy-component $)
|
|
||||||
(reduce redirect-instances $ instances))))
|
|
||||||
|
|
||||||
; Absorb all used components into the local library. Position
|
|
||||||
; the main instances in a grid in the library page.
|
|
||||||
add-component-grid
|
|
||||||
(fn [data used-components]
|
|
||||||
(let [position-seq (ctst/generate-shape-grid
|
|
||||||
(map #(cph/get-component-root (first %)) used-components)
|
|
||||||
start-pos
|
|
||||||
50)]
|
|
||||||
(loop [data data
|
|
||||||
components-seq (seq used-components)
|
|
||||||
position-seq position-seq]
|
|
||||||
(let [used-component (first components-seq)
|
|
||||||
position (first position-seq)]
|
|
||||||
(if (nil? used-component)
|
|
||||||
data
|
|
||||||
(recur (absorb-component data used-component position)
|
|
||||||
(rest components-seq)
|
|
||||||
(rest position-seq)))))))]
|
|
||||||
|
|
||||||
(add-component-grid file-data (sort-by #(:name (first %)) used-components))))))
|
|
||||||
|
|
||||||
;; Debug helpers
|
;; Debug helpers
|
||||||
|
|
||||||
|
|||||||
26
common/src/app/common/types/typographies_list.cljc
Normal file
26
common/src/app/common/types/typographies_list.cljc
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) UXBOX Labs SL
|
||||||
|
|
||||||
|
(ns app.common.types.typographies-list
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]))
|
||||||
|
|
||||||
|
(defn typographies-seq
|
||||||
|
[file-data]
|
||||||
|
(vals (:typographies file-data)))
|
||||||
|
|
||||||
|
(defn add-typography
|
||||||
|
[file-data typography]
|
||||||
|
(update file-data :typographies assoc (:id typography) typography))
|
||||||
|
|
||||||
|
(defn get-typography
|
||||||
|
[file-data typography-id]
|
||||||
|
(get-in file-data [:typographies typography-id]))
|
||||||
|
|
||||||
|
(defn update-typography
|
||||||
|
[file-data typography-id f]
|
||||||
|
(update-in file-data [:typographies typography-id] f))
|
||||||
|
|
||||||
@ -6,7 +6,8 @@
|
|||||||
|
|
||||||
(ns app.common.types.typography
|
(ns app.common.types.typography
|
||||||
(:require
|
(:require
|
||||||
[clojure.spec.alpha :as s]))
|
[app.common.text :as txt]
|
||||||
|
[clojure.spec.alpha :as s]))
|
||||||
|
|
||||||
(s/def ::id uuid?)
|
(s/def ::id uuid?)
|
||||||
(s/def ::name string?)
|
(s/def ::name string?)
|
||||||
@ -35,4 +36,37 @@
|
|||||||
::text-transform]
|
::text-transform]
|
||||||
:opt-un [::path]))
|
:opt-un [::path]))
|
||||||
|
|
||||||
|
(defn uses-library-typographies?
|
||||||
|
"Check if the shape uses any typography in the given library."
|
||||||
|
[shape library-id]
|
||||||
|
(and (= (:type shape) :text)
|
||||||
|
(->> shape
|
||||||
|
:content
|
||||||
|
;; Check if any node in the content has a reference for the library
|
||||||
|
(txt/node-seq
|
||||||
|
#(and (some? (:typography-ref-id %))
|
||||||
|
(= (:typography-ref-file %) library-id))))))
|
||||||
|
|
||||||
|
(defn uses-library-typography?
|
||||||
|
"Check if the shape uses the given library typography."
|
||||||
|
[shape library-id typography]
|
||||||
|
(and (= (:type shape) :text)
|
||||||
|
(->> shape
|
||||||
|
:content
|
||||||
|
;; Check if any node in the content has a reference for the library
|
||||||
|
(txt/node-seq
|
||||||
|
#(and (= (:typography-ref-id %) (:id typography))
|
||||||
|
(= (:typography-ref-file %) library-id))))))
|
||||||
|
|
||||||
|
(defn remap-typographies
|
||||||
|
"Change the shape so that any use of the given typography now points to
|
||||||
|
the given library."
|
||||||
|
[shape library-id typography]
|
||||||
|
(let [remap-typography #(assoc % :typography-ref-file library-id)]
|
||||||
|
|
||||||
|
(update shape :content
|
||||||
|
(fn [content]
|
||||||
|
(txt/transform-nodes #(= (:typography-ref-id %) (:id typography))
|
||||||
|
remap-typography
|
||||||
|
content)))))
|
||||||
|
|
||||||
|
|||||||
150
common/test/app/common/test_helpers/components.cljc
Normal file
150
common/test/app/common/test_helpers/components.cljc
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
(ns app.common.test-helpers.components
|
||||||
|
(:require
|
||||||
|
[cljs.test :as t :include-macros true]
|
||||||
|
[cljs.pprint :refer [pprint]]
|
||||||
|
[app.common.pages.helpers :as cph]
|
||||||
|
[app.common.types.component :as ctk]
|
||||||
|
[app.common.types.container :as ctn]))
|
||||||
|
|
||||||
|
;; ---- Helpers to manage libraries and synchronization
|
||||||
|
|
||||||
|
(defn check-instance-root
|
||||||
|
[shape]
|
||||||
|
(t/is (some? (:shape-ref shape)))
|
||||||
|
(t/is (some? (:component-id shape)))
|
||||||
|
(t/is (= (:component-root? shape) true)))
|
||||||
|
|
||||||
|
(defn check-instance-subroot
|
||||||
|
[shape]
|
||||||
|
(t/is (some? (:shape-ref shape)))
|
||||||
|
(t/is (some? (:component-id shape)))
|
||||||
|
(t/is (nil? (:component-root? shape))))
|
||||||
|
|
||||||
|
(defn check-instance-child
|
||||||
|
[shape]
|
||||||
|
(t/is (some? (:shape-ref shape)))
|
||||||
|
(t/is (nil? (:component-id shape)))
|
||||||
|
(t/is (nil? (:component-file shape)))
|
||||||
|
(t/is (nil? (:component-root? shape))))
|
||||||
|
|
||||||
|
(defn check-instance-inner
|
||||||
|
[shape]
|
||||||
|
(if (some? (:component-id shape))
|
||||||
|
(check-instance-subroot shape)
|
||||||
|
(check-instance-child shape)))
|
||||||
|
|
||||||
|
(defn check-noninstance
|
||||||
|
[shape]
|
||||||
|
(t/is (nil? (:shape-ref shape)))
|
||||||
|
(t/is (nil? (:component-id shape)))
|
||||||
|
(t/is (nil? (:component-file shape)))
|
||||||
|
(t/is (nil? (:component-root? shape)))
|
||||||
|
(t/is (nil? (:remote-synced? shape)))
|
||||||
|
(t/is (nil? (:touched shape))))
|
||||||
|
|
||||||
|
(defn check-from-file
|
||||||
|
[shape file]
|
||||||
|
(t/is (= (:component-file shape)
|
||||||
|
(:id file))))
|
||||||
|
|
||||||
|
(defn resolve-instance
|
||||||
|
"Get the shape with the given id and all its children, and
|
||||||
|
verify that they are a well constructed instance tree."
|
||||||
|
[page root-inst-id]
|
||||||
|
(let [root-inst (ctn/get-shape page root-inst-id)
|
||||||
|
shapes-inst (cph/get-children-with-self (:objects page)
|
||||||
|
root-inst-id)]
|
||||||
|
(check-instance-root (first shapes-inst))
|
||||||
|
(run! check-instance-inner (rest shapes-inst))
|
||||||
|
|
||||||
|
shapes-inst))
|
||||||
|
|
||||||
|
(defn resolve-noninstance
|
||||||
|
"Get the shape with the given id and all its children, and
|
||||||
|
verify that they are not a component instance."
|
||||||
|
[page root-inst-id]
|
||||||
|
(let [root-inst (ctn/get-shape page root-inst-id)
|
||||||
|
shapes-inst (cph/get-children-with-self (:objects page)
|
||||||
|
root-inst-id)]
|
||||||
|
(run! check-noninstance shapes-inst)
|
||||||
|
|
||||||
|
shapes-inst))
|
||||||
|
|
||||||
|
(defn resolve-instance-and-main
|
||||||
|
"Get the shape with the given id and all its children, and also
|
||||||
|
the main component and all its shapes."
|
||||||
|
[page root-inst-id libraries]
|
||||||
|
(let [root-inst (ctn/get-shape page root-inst-id)
|
||||||
|
|
||||||
|
component (cph/get-component libraries (:component-id root-inst))
|
||||||
|
|
||||||
|
shapes-inst (cph/get-children-with-self (:objects page) root-inst-id)
|
||||||
|
shapes-main (cph/get-children-with-self (:objects component) (:shape-ref root-inst))
|
||||||
|
|
||||||
|
unique-refs (into #{} (map :shape-ref) shapes-inst)
|
||||||
|
|
||||||
|
main-exists? (fn [shape]
|
||||||
|
(let [component-shape
|
||||||
|
(cph/get-component-shape (:objects page) shape)
|
||||||
|
|
||||||
|
component
|
||||||
|
(cph/get-component libraries (:component-id component-shape))
|
||||||
|
|
||||||
|
main-shape
|
||||||
|
(ctn/get-shape component (:shape-ref shape))]
|
||||||
|
|
||||||
|
(t/is (some? main-shape))))]
|
||||||
|
|
||||||
|
;; Validate that the instance tree is well constructed
|
||||||
|
(check-instance-root (first shapes-inst))
|
||||||
|
(run! check-instance-inner (rest shapes-inst))
|
||||||
|
(t/is (= (count shapes-inst)
|
||||||
|
(count shapes-main)
|
||||||
|
(count unique-refs)))
|
||||||
|
(run! main-exists? shapes-inst)
|
||||||
|
|
||||||
|
[shapes-inst shapes-main component]))
|
||||||
|
|
||||||
|
(defn resolve-instance-and-main-allow-dangling
|
||||||
|
"Get the shape with the given id and all its children, and also
|
||||||
|
the main component and all its shapes. Allows shapes with the
|
||||||
|
corresponding component shape missing."
|
||||||
|
[page root-inst-id libraries]
|
||||||
|
(let [root-inst (ctn/get-shape page root-inst-id)
|
||||||
|
|
||||||
|
component (cph/get-component libraries (:component-id root-inst))
|
||||||
|
|
||||||
|
shapes-inst (cph/get-children-with-self (:objects page) root-inst-id)
|
||||||
|
shapes-main (cph/get-children-with-self (:objects component) (:shape-ref root-inst))
|
||||||
|
|
||||||
|
unique-refs (into #{} (map :shape-ref) shapes-inst)
|
||||||
|
|
||||||
|
main-exists? (fn [shape]
|
||||||
|
(let [component-shape
|
||||||
|
(cph/get-component-shape (:objects page) shape)
|
||||||
|
|
||||||
|
component
|
||||||
|
(cph/get-component libraries (:component-id component-shape))
|
||||||
|
|
||||||
|
main-shape
|
||||||
|
(ctn/get-shape component (:shape-ref shape))]
|
||||||
|
|
||||||
|
(t/is (some? main-shape))))]
|
||||||
|
|
||||||
|
;; Validate that the instance tree is well constructed
|
||||||
|
(check-instance-root (first shapes-inst))
|
||||||
|
|
||||||
|
[shapes-inst shapes-main component]))
|
||||||
|
|
||||||
|
(defn resolve-component
|
||||||
|
"Get the component with the given id and all its shapes."
|
||||||
|
[page component-id libraries]
|
||||||
|
(let [component (cph/get-component libraries component-id)
|
||||||
|
root-main (ctk/get-component-root component)
|
||||||
|
shapes-main (cph/get-children-with-self (:objects component) (:id root-main))]
|
||||||
|
|
||||||
|
;; Validate that the component tree is well constructed
|
||||||
|
(run! check-noninstance shapes-main)
|
||||||
|
|
||||||
|
[shapes-main component]))
|
||||||
|
|
||||||
@ -6,14 +6,16 @@
|
|||||||
|
|
||||||
(ns app.common.test-helpers.files
|
(ns app.common.test-helpers.files
|
||||||
(:require
|
(:require
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.types.components-list :as ctkl]
|
[app.common.types.components-list :as ctkl]
|
||||||
[app.common.types.container :as ctn]
|
[app.common.types.colors-list :as ctcl]
|
||||||
[app.common.types.file :as ctf]
|
[app.common.types.container :as ctn]
|
||||||
[app.common.types.pages-list :as ctpl]
|
[app.common.types.file :as ctf]
|
||||||
[app.common.types.shape :as cts]
|
[app.common.types.pages-list :as ctpl]
|
||||||
[app.common.types.shape-tree :as ctst]
|
[app.common.types.shape :as cts]
|
||||||
[app.common.uuid :as uuid]))
|
[app.common.types.shape-tree :as ctst]
|
||||||
|
[app.common.types.typographies-list :as ctyl]
|
||||||
|
[app.common.uuid :as uuid]))
|
||||||
|
|
||||||
(def ^:private idmap (atom {}))
|
(def ^:private idmap (atom {}))
|
||||||
|
|
||||||
@ -108,3 +110,38 @@
|
|||||||
%
|
%
|
||||||
instance-shapes)))))))
|
instance-shapes)))))))
|
||||||
|
|
||||||
|
(defn sample-color
|
||||||
|
[file label props]
|
||||||
|
(ctf/update-file-data
|
||||||
|
file
|
||||||
|
(fn [file-data]
|
||||||
|
(let [id (uuid/next)
|
||||||
|
props (merge {:id id
|
||||||
|
:name "Color-1"
|
||||||
|
:color "#000000"
|
||||||
|
:opacity 1}
|
||||||
|
props)]
|
||||||
|
(swap! idmap assoc label id)
|
||||||
|
(ctcl/add-color file-data props)))))
|
||||||
|
|
||||||
|
(defn sample-typography
|
||||||
|
[file label props]
|
||||||
|
(ctf/update-file-data
|
||||||
|
file
|
||||||
|
(fn [file-data]
|
||||||
|
(let [id (uuid/next)
|
||||||
|
props (merge {:id id
|
||||||
|
:name "Typography-1"
|
||||||
|
:font-id "sourcesanspro"
|
||||||
|
:font-family "sourcesanspro"
|
||||||
|
:font-size "14"
|
||||||
|
:font-style "normal"
|
||||||
|
:font-variant-id "regular"
|
||||||
|
:font-weight "400"
|
||||||
|
:line-height "1.2"
|
||||||
|
:letter-spacing "0"
|
||||||
|
:text-transform "none"}
|
||||||
|
props)]
|
||||||
|
(swap! idmap assoc label id)
|
||||||
|
(ctyl/add-typography file-data props)))))
|
||||||
|
|
||||||
|
|||||||
@ -6,25 +6,30 @@
|
|||||||
|
|
||||||
(ns app.common.types.file-test
|
(ns app.common.types.file-test
|
||||||
(:require
|
(:require
|
||||||
[clojure.test :as t]
|
;; Uncomment to debug
|
||||||
[app.common.geom.point :as gpt]
|
;; [clojure.pprint :refer [pprint]]
|
||||||
[app.common.types.components-list :as ctkl]
|
;; [cuerdas.core :as str]
|
||||||
[app.common.types.file :as ctf]
|
[clojure.test :as t]
|
||||||
[app.common.types.pages-list :as ctpl]
|
[app.common.data :as d]
|
||||||
[app.common.types.shape :as cts]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.types.shape-tree :as ctst]
|
[app.common.text :as txt]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.types.colors-list :as ctcl]
|
||||||
[app.common.test-helpers.files :as thf]
|
[app.common.types.component :as ctk]
|
||||||
|
[app.common.types.components-list :as ctkl]
|
||||||
[app.common.data :as d]
|
[app.common.types.container :as ctn]
|
||||||
[app.common.pages.helpers :as cph]
|
[app.common.types.file :as ctf]
|
||||||
[cuerdas.core :as str]
|
[app.common.types.pages-list :as ctpl]
|
||||||
))
|
[app.common.types.shape :as cts]
|
||||||
|
[app.common.types.shape-tree :as ctst]
|
||||||
|
[app.common.types.typographies-list :as ctyl]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
|
[app.common.test-helpers.files :as thf]
|
||||||
|
[app.common.test-helpers.components :as thk]))
|
||||||
|
|
||||||
(t/use-fixtures :each
|
(t/use-fixtures :each
|
||||||
{:before thf/reset-idmap!})
|
{:before thf/reset-idmap!})
|
||||||
|
|
||||||
(t/deftest test-absorb-assets
|
(t/deftest test-absorb-components
|
||||||
(let [library-id (uuid/custom 1 1)
|
(let [library-id (uuid/custom 1 1)
|
||||||
library-page-id (uuid/custom 2 2)
|
library-page-id (uuid/custom 2 2)
|
||||||
file-id (uuid/custom 3 3)
|
file-id (uuid/custom 3 3)
|
||||||
@ -52,7 +57,26 @@
|
|||||||
|
|
||||||
absorbed-file (ctf/update-file-data
|
absorbed-file (ctf/update-file-data
|
||||||
file
|
file
|
||||||
#(ctf/absorb-assets % (:data library)))]
|
#(ctf/absorb-assets % (:data library)))
|
||||||
|
|
||||||
|
pages (ctpl/pages-seq (ctf/file-data absorbed-file))
|
||||||
|
components (ctkl/components-seq (ctf/file-data absorbed-file))
|
||||||
|
shapes-1 (ctn/shapes-seq (first pages))
|
||||||
|
shapes-2 (ctn/shapes-seq (second pages))
|
||||||
|
|
||||||
|
[[p-group p-shape] [c-group1 c-shape1] component1]
|
||||||
|
(thk/resolve-instance-and-main
|
||||||
|
(first pages)
|
||||||
|
(:id (second shapes-1))
|
||||||
|
{file-id absorbed-file})
|
||||||
|
|
||||||
|
[[lp-group lp-shape] [c-group2 c-shape2] component2]
|
||||||
|
(thk/resolve-instance-and-main
|
||||||
|
(second pages)
|
||||||
|
(:id (second shapes-2))
|
||||||
|
{file-id absorbed-file})]
|
||||||
|
|
||||||
|
;; Uncomment to debug
|
||||||
|
|
||||||
;; (println "\n===== library")
|
;; (println "\n===== library")
|
||||||
;; (ctf/dump-tree (:data library)
|
;; (ctf/dump-tree (:data library)
|
||||||
@ -67,11 +91,118 @@
|
|||||||
;; true)
|
;; true)
|
||||||
|
|
||||||
;; (println "\n===== absorbed file")
|
;; (println "\n===== absorbed file")
|
||||||
|
;; (println (str "\n<" (:name (first pages)) ">"))
|
||||||
;; (ctf/dump-tree (:data absorbed-file)
|
;; (ctf/dump-tree (:data absorbed-file)
|
||||||
;; file-page-id
|
;; (:id (first pages))
|
||||||
;; {}
|
;; {file-id absorbed-file}
|
||||||
;; true)
|
;; false)
|
||||||
|
;; (println (str "\n<" (:name (second pages)) ">"))
|
||||||
|
;; (ctf/dump-tree (:data absorbed-file)
|
||||||
|
;; (:id (second pages))
|
||||||
|
;; {file-id absorbed-file}
|
||||||
|
;; false)
|
||||||
|
|
||||||
(t/is (= library-id (:id library)))
|
(t/is (= (count pages) 2))
|
||||||
(t/is (= file-id (:id absorbed-file)))))
|
(t/is (= (:name (first pages)) "Page-1"))
|
||||||
|
(t/is (= (:name (second pages)) "Library page"))
|
||||||
|
|
||||||
|
(t/is (= (count components) 1))
|
||||||
|
|
||||||
|
(t/is (= (:name p-group) "Group1"))
|
||||||
|
(t/is (ctk/instance-of? p-group file-id component1))
|
||||||
|
(t/is (not (ctk/is-main-instance? (:id p-group) file-page-id component1)))
|
||||||
|
(t/is (ctk/is-main-of? c-group1 p-group))
|
||||||
|
|
||||||
|
(t/is (= (:name p-shape) "Rect1"))
|
||||||
|
(t/is (ctk/is-main-of? c-shape1 p-shape))))
|
||||||
|
|
||||||
|
|
||||||
|
(t/deftest test-absorb-colors
|
||||||
|
(let [library-id (uuid/custom 1 1)
|
||||||
|
library-page-id (uuid/custom 2 2)
|
||||||
|
file-id (uuid/custom 3 3)
|
||||||
|
file-page-id (uuid/custom 4 4)
|
||||||
|
|
||||||
|
library (-> (thf/sample-file library-id library-page-id {:is-shared true})
|
||||||
|
(thf/sample-color :color1 {:name "Test color"
|
||||||
|
:color "#abcdef"}))
|
||||||
|
|
||||||
|
file (-> (thf/sample-file file-id file-page-id)
|
||||||
|
(thf/sample-shape :shape1
|
||||||
|
:rect
|
||||||
|
file-page-id
|
||||||
|
{:name "Rect1"
|
||||||
|
:fills [{:fill-color "#abcdef"
|
||||||
|
:fill-opacity 1
|
||||||
|
:fill-color-ref-id (thf/id :color1)
|
||||||
|
:fill-color-ref-file library-id}]}))
|
||||||
|
|
||||||
|
absorbed-file (ctf/update-file-data
|
||||||
|
file
|
||||||
|
#(ctf/absorb-assets % (:data library)))
|
||||||
|
|
||||||
|
colors (ctcl/colors-seq (ctf/file-data absorbed-file))
|
||||||
|
page (ctpl/get-page (ctf/file-data absorbed-file) file-page-id)
|
||||||
|
shape1 (ctn/get-shape page (thf/id :shape1))
|
||||||
|
fill (first (:fills shape1))]
|
||||||
|
|
||||||
|
(t/is (= (count colors) 1))
|
||||||
|
(t/is (= (:id (first colors)) (thf/id :color1)))
|
||||||
|
(t/is (= (:name (first colors)) "Test color"))
|
||||||
|
(t/is (= (:color (first colors)) "#abcdef"))
|
||||||
|
|
||||||
|
(t/is (= (:fill-color fill) "#abcdef"))
|
||||||
|
(t/is (= (:fill-color-ref-id fill) (thf/id :color1)))
|
||||||
|
(t/is (= (:fill-color-ref-file fill) file-id))))
|
||||||
|
|
||||||
|
(t/deftest test-absorb-typographies
|
||||||
|
(let [library-id (uuid/custom 1 1)
|
||||||
|
library-page-id (uuid/custom 2 2)
|
||||||
|
file-id (uuid/custom 3 3)
|
||||||
|
file-page-id (uuid/custom 4 4)
|
||||||
|
|
||||||
|
library (-> (thf/sample-file library-id library-page-id {:is-shared true})
|
||||||
|
(thf/sample-typography :typography1 {:name "Test typography"}))
|
||||||
|
|
||||||
|
file (-> (thf/sample-file file-id file-page-id)
|
||||||
|
(thf/sample-shape :shape1
|
||||||
|
:text
|
||||||
|
file-page-id
|
||||||
|
{:name "Text1"
|
||||||
|
:content {:type "root"
|
||||||
|
:children [{:type "paragraph-set"
|
||||||
|
:children [{:type "paragraph"
|
||||||
|
:key "67uep"
|
||||||
|
:children [{:text "Example text"
|
||||||
|
:typography-ref-id (thf/id :typography1)
|
||||||
|
:typography-ref-file library-id
|
||||||
|
:line-height "1.2"
|
||||||
|
:font-style "normal"
|
||||||
|
:text-transform "none"
|
||||||
|
:text-align "left"
|
||||||
|
:font-id "sourcesanspro"
|
||||||
|
:font-family "sourcesanspro"
|
||||||
|
:font-size "14"
|
||||||
|
:font-weight "400"
|
||||||
|
:font-variant-id "regular"
|
||||||
|
:text-decoration "none"
|
||||||
|
:letter-spacing "0"
|
||||||
|
:fills [{:fill-color "#000000"
|
||||||
|
:fill-opacity 1}]}]
|
||||||
|
}]}]}}))
|
||||||
|
absorbed-file (ctf/update-file-data
|
||||||
|
file
|
||||||
|
#(ctf/absorb-assets % (:data library)))
|
||||||
|
|
||||||
|
typographies (ctyl/typographies-seq (ctf/file-data absorbed-file))
|
||||||
|
page (ctpl/get-page (ctf/file-data absorbed-file) file-page-id)
|
||||||
|
shape1 (ctn/get-shape page (thf/id :shape1))
|
||||||
|
text-node (d/seek #(some? (:text %)) (txt/node-seq (:content shape1)))]
|
||||||
|
|
||||||
|
(t/is (= (count typographies) 1))
|
||||||
|
(t/is (= (:id (first typographies)) (thf/id :typography1)))
|
||||||
|
(t/is (= (:name (first typographies)) "Test typography"))
|
||||||
|
|
||||||
|
(t/is (= (:typography-ref-id text-node) (thf/id :typography1)))
|
||||||
|
(t/is (= (:typography-ref-file text-node) file-id))))
|
||||||
|
|
||||||
|
|||||||
@ -16,8 +16,10 @@
|
|||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
[app.common.text :as txt]
|
[app.common.text :as txt]
|
||||||
[app.common.types.color :as ctc]
|
[app.common.types.color :as ctc]
|
||||||
|
[app.common.types.component :as ctk]
|
||||||
[app.common.types.container :as ctn]
|
[app.common.types.container :as ctn]
|
||||||
[app.common.types.shape-tree :as ctst]
|
[app.common.types.shape-tree :as ctst]
|
||||||
|
[app.common.types.typography :as cty]
|
||||||
[app.main.data.workspace.groups :as dwg]
|
[app.main.data.workspace.groups :as dwg]
|
||||||
[app.main.data.workspace.state-helpers :as wsh]
|
[app.main.data.workspace.state-helpers :as wsh]
|
||||||
[cljs.spec.alpha :as s]
|
[cljs.spec.alpha :as s]
|
||||||
@ -94,7 +96,7 @@
|
|||||||
(let [position (gpt/add (gpt/point (:x main-instance-shape) (:y main-instance-shape))
|
(let [position (gpt/add (gpt/point (:x main-instance-shape) (:y main-instance-shape))
|
||||||
(gpt/point (+ (:width main-instance-shape) 50) 0))
|
(gpt/point (+ (:width main-instance-shape) 50) 0))
|
||||||
|
|
||||||
component-root (cph/get-component-root component)
|
component-root (ctk/get-component-root component)
|
||||||
|
|
||||||
[new-component-shape new-component-shapes _]
|
[new-component-shape new-component-shapes _]
|
||||||
(ctst/clone-object component-root
|
(ctst/clone-object component-root
|
||||||
@ -243,13 +245,7 @@
|
|||||||
|
|
||||||
(defmethod uses-assets? :typographies
|
(defmethod uses-assets? :typographies
|
||||||
[_ shape library-id _]
|
[_ shape library-id _]
|
||||||
(and (= (:type shape) :text)
|
(cty/uses-library-typographies? shape library-id))
|
||||||
(->> shape
|
|
||||||
:content
|
|
||||||
;; Check if any node in the content has a reference for the library
|
|
||||||
(txt/node-seq
|
|
||||||
#(and (some? (:typography-ref-id %))
|
|
||||||
(= (:typography-ref-file %) library-id))))))
|
|
||||||
|
|
||||||
(defmulti generate-sync-shape
|
(defmulti generate-sync-shape
|
||||||
"Generate changes to synchronize one shape from all assets of the given type
|
"Generate changes to synchronize one shape from all assets of the given type
|
||||||
@ -433,7 +429,7 @@
|
|||||||
|
|
||||||
root-inst shape-inst
|
root-inst shape-inst
|
||||||
root-main (when component
|
root-main (when component
|
||||||
(cph/get-component-root component))]
|
(ctk/get-component-root component))]
|
||||||
|
|
||||||
(if component
|
(if component
|
||||||
(generate-sync-shape-direct-recursive changes
|
(generate-sync-shape-direct-recursive changes
|
||||||
@ -557,7 +553,7 @@
|
|||||||
initial-root? (:component-root? shape-inst)
|
initial-root? (:component-root? shape-inst)
|
||||||
|
|
||||||
root-inst shape-inst
|
root-inst shape-inst
|
||||||
root-main (cph/get-component-root component)]
|
root-main (ctk/get-component-root component)]
|
||||||
|
|
||||||
(if component
|
(if component
|
||||||
(generate-sync-shape-inverse-recursive changes
|
(generate-sync-shape-inverse-recursive changes
|
||||||
@ -691,13 +687,13 @@
|
|||||||
(reduce only-inst-cb changes children-inst)
|
(reduce only-inst-cb changes children-inst)
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(if (cph/is-main-of? child-main child-inst)
|
(if (ctk/is-main-of? child-main child-inst)
|
||||||
(recur (next children-inst)
|
(recur (next children-inst)
|
||||||
(next children-main)
|
(next children-main)
|
||||||
(both-cb changes child-inst child-main))
|
(both-cb changes child-inst child-main))
|
||||||
|
|
||||||
(let [child-inst' (d/seek #(cph/is-main-of? child-main %) children-inst)
|
(let [child-inst' (d/seek #(ctk/is-main-of? child-main %) children-inst)
|
||||||
child-main' (d/seek #(cph/is-main-of? % child-inst) children-main)]
|
child-main' (d/seek #(ctk/is-main-of? % child-inst) children-main)]
|
||||||
(cond
|
(cond
|
||||||
(nil? child-inst')
|
(nil? child-inst')
|
||||||
(recur children-inst
|
(recur children-inst
|
||||||
@ -726,7 +722,7 @@
|
|||||||
[changes component-shape index component container root-instance root-main omit-touched? set-remote-synced?]
|
[changes component-shape index component container root-instance root-main omit-touched? set-remote-synced?]
|
||||||
(log/info :msg (str "ADD [P] " (:name component-shape)))
|
(log/info :msg (str "ADD [P] " (:name component-shape)))
|
||||||
(let [component-parent-shape (ctn/get-shape component (:parent-id component-shape))
|
(let [component-parent-shape (ctn/get-shape component (:parent-id component-shape))
|
||||||
parent-shape (d/seek #(cph/is-main-of? component-parent-shape %)
|
parent-shape (d/seek #(ctk/is-main-of? component-parent-shape %)
|
||||||
(cph/get-children-with-self (:objects container)
|
(cph/get-children-with-self (:objects container)
|
||||||
(:id root-instance)))
|
(:id root-instance)))
|
||||||
all-parents (into [(:id parent-shape)]
|
all-parents (into [(:id parent-shape)]
|
||||||
@ -794,7 +790,7 @@
|
|||||||
[changes shape index component page root-instance root-main]
|
[changes shape index component page root-instance root-main]
|
||||||
(log/info :msg (str "ADD [C] " (:name shape)))
|
(log/info :msg (str "ADD [C] " (:name shape)))
|
||||||
(let [parent-shape (ctn/get-shape page (:parent-id shape))
|
(let [parent-shape (ctn/get-shape page (:parent-id shape))
|
||||||
component-parent-shape (d/seek #(cph/is-main-of? % parent-shape)
|
component-parent-shape (d/seek #(ctk/is-main-of? % parent-shape)
|
||||||
(cph/get-children-with-self (:objects component)
|
(cph/get-children-with-self (:objects component)
|
||||||
(:id root-main)))
|
(:id root-main)))
|
||||||
all-parents (into [(:id component-parent-shape)]
|
all-parents (into [(:id component-parent-shape)]
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
[app.common.pages.changes-builder :as pcb]
|
[app.common.pages.changes-builder :as pcb]
|
||||||
[app.common.pages.helpers :as cph]
|
[app.common.pages.helpers :as cph]
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
|
[app.common.types.component :as ctk]
|
||||||
[app.common.types.page :as ctp]
|
[app.common.types.page :as ctp]
|
||||||
[app.common.types.shape :as cts]
|
[app.common.types.shape :as cts]
|
||||||
[app.common.types.shape.interactions :as ctsi]
|
[app.common.types.shape.interactions :as ctsi]
|
||||||
@ -230,7 +231,7 @@
|
|||||||
|
|
||||||
main-instance?
|
main-instance?
|
||||||
(when component
|
(when component
|
||||||
(cph/is-main-instance? (:id shape) (:id page) component))]
|
(ctk/is-main-instance? (:id shape) (:id page) component))]
|
||||||
|
|
||||||
(if main-instance?
|
(if main-instance?
|
||||||
(conj components (:component-id shape))
|
(conj components (:component-id shape))
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.pages.helpers :as cph]
|
[app.common.pages.helpers :as cph]
|
||||||
|
[app.common.types.component :as ctk]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.main.data.workspace :as dw]
|
[app.main.data.workspace :as dw]
|
||||||
[app.main.data.workspace.collapse :as dwc]
|
[app.main.data.workspace.collapse :as dwc]
|
||||||
@ -106,7 +107,7 @@
|
|||||||
component (when (and (:component-id item) (:component-file item))
|
component (when (and (:component-id item) (:component-file item))
|
||||||
(cph/get-component libraries (:component-file item) (:component-id item)))
|
(cph/get-component libraries (:component-file item) (:component-id item)))
|
||||||
main-instance? (when component
|
main-instance? (when component
|
||||||
(cph/is-main-instance? (:id item) (:id page) component))
|
(ctk/is-main-instance? (:id item) (:id page) component))
|
||||||
|
|
||||||
toggle-collapse
|
toggle-collapse
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
(ns app.main.ui.workspace.sidebar.options.menus.component
|
(ns app.main.ui.workspace.sidebar.options.menus.component
|
||||||
(:require
|
(:require
|
||||||
[app.common.pages.helpers :as cph]
|
[app.common.pages.helpers :as cph]
|
||||||
|
[app.common.types.component :as ctk]
|
||||||
[app.main.data.modal :as modal]
|
[app.main.data.modal :as modal]
|
||||||
[app.main.data.workspace :as dw]
|
[app.main.data.workspace :as dw]
|
||||||
[app.main.data.workspace.libraries :as dwl]
|
[app.main.data.workspace.libraries :as dwl]
|
||||||
@ -35,7 +36,7 @@
|
|||||||
|
|
||||||
component (when (and component-id library-id)
|
component (when (and component-id library-id)
|
||||||
(cph/get-component libraries library-id component-id))
|
(cph/get-component libraries library-id component-id))
|
||||||
main-instance? (cph/is-main-instance? id current-page-id component)
|
main-instance? (ctk/is-main-instance? id current-page-id component)
|
||||||
|
|
||||||
on-menu-click
|
on-menu-click
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
|
|||||||
@ -2,16 +2,9 @@
|
|||||||
(:require
|
(:require
|
||||||
[cljs.test :as t :include-macros true]
|
[cljs.test :as t :include-macros true]
|
||||||
[cljs.pprint :refer [pprint]]
|
[cljs.pprint :refer [pprint]]
|
||||||
[beicon.core :as rx]
|
|
||||||
[potok.core :as ptk]
|
|
||||||
[app.common.uuid :as uuid]
|
|
||||||
[app.common.geom.point :as gpt]
|
|
||||||
[app.common.geom.shapes :as gsh]
|
|
||||||
[app.common.pages.helpers :as cph]
|
[app.common.pages.helpers :as cph]
|
||||||
|
[app.common.types.component :as ctk]
|
||||||
[app.common.types.container :as ctn]
|
[app.common.types.container :as ctn]
|
||||||
[app.main.data.workspace :as dw]
|
|
||||||
[app.main.data.workspace.libraries-helpers :as dwlh]
|
|
||||||
[app.main.data.workspace.state-helpers :as wsh]
|
|
||||||
[app.test-helpers.pages :as thp]))
|
[app.test-helpers.pages :as thp]))
|
||||||
|
|
||||||
;; ---- Helpers to manage libraries and synchronization
|
;; ---- Helpers to manage libraries and synchronization
|
||||||
@ -156,7 +149,7 @@
|
|||||||
(let [page (thp/current-page state)
|
(let [page (thp/current-page state)
|
||||||
libs (wsh/get-libraries state)
|
libs (wsh/get-libraries state)
|
||||||
component (cph/get-component libs component-id)
|
component (cph/get-component libs component-id)
|
||||||
root-main (cph/get-component-root component)
|
root-main (ctk/get-component-root component)
|
||||||
shapes-main (cph/get-children-with-self (:objects component) (:id root-main))]
|
shapes-main (cph/get-children-with-self (:objects component) (:id root-main))]
|
||||||
|
|
||||||
;; Validate that the component tree is well constructed
|
;; Validate that the component tree is well constructed
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user