0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-04-10 14:01:29 -05:00

♻️ Refactor variants

* ♻️ Refactor variants

*  Add MR changes
This commit is contained in:
Pablo Alba 2025-03-24 13:15:02 +01:00 committed by GitHub
parent d1d30e7eb5
commit 9653e72e47
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 496 additions and 252 deletions

View file

@ -31,6 +31,7 @@
[app.common.types.tokens-lib :as ctob]
[app.common.types.typographies-list :as ctyl]
[app.common.types.typography :as ctt]
[app.common.types.variant :as ctv]
[app.common.uuid :as uuid]
[clojure.set :as set]))
@ -336,7 +337,9 @@
[:type [:= :mod-component]]
[:id ::sm/uuid]
[:shapes {:optional true} [:vector {:gen/max 3} :any]]
[:name {:optional true} :string]]]
[:name {:optional true} :string]
[:variant-id {:optional true} ::sm/uuid]
[:variant-properties {:optional true} [:vector ::ctv/variant-property]]]]
[:del-component
[:map {:title "DelComponentChange"}

View file

@ -964,11 +964,13 @@
:name name
:main-instance-id main-instance-id
:main-instance-page main-instance-page
:annotation annotation
:variant-id variant-id
:variant-properties variant-properties}
:annotation annotation}
(some? new-shapes) ;; this will be null in components-v2
(assoc :shapes (vec new-shapes))))
(assoc :shapes (vec new-shapes))
(some? variant-id)
(assoc :variant-id variant-id)
(seq variant-properties)
(assoc :variant-properties variant-properties)))
(into (map mk-change) updated-shapes))))
(update :undo-changes
(fn [undo-changes]
@ -991,27 +993,39 @@
new-component (update-fn prev-component)]
(if prev-component
(-> changes
(update :redo-changes conj {:type :mod-component
:id id
:name (:name new-component)
:path (:path new-component)
:main-instance-id (:main-instance-id new-component)
:main-instance-page (:main-instance-page new-component)
:annotation (:annotation new-component)
:variant-id (:variant-id new-component)
:variant-properties (:variant-properties new-component)
:objects (:objects new-component) ;; this won't exist in components-v2 (except for deleted components)
:modified-at (:modified-at new-component)})
(update :undo-changes conj {:type :mod-component
:id id
:name (:name prev-component)
:path (:path prev-component)
:main-instance-id (:main-instance-id prev-component)
:main-instance-page (:main-instance-page prev-component)
:annotation (:annotation prev-component)
:variant-id (:variant-id prev-component)
:variant-properties (:variant-properties prev-component)
:objects (:objects prev-component)})
(update :redo-changes conj (cond-> {:type :mod-component
:id id
:name (:name new-component)
:path (:path new-component)
:main-instance-id (:main-instance-id new-component)
:main-instance-page (:main-instance-page new-component)
:annotation (:annotation new-component)
:objects (:objects new-component) ;; this won't exist in components-v2 (except for deleted components)
:modified-at (:modified-at new-component)}
(some? (:variant-id new-component))
(assoc :variant-id (:variant-id new-component))
(nil? (:variant-id new-component))
(dissoc :variant-id)
(seq (:variant-properties new-component))
(assoc :variant-properties (:variant-properties new-component))
(not (seq (:variant-properties new-component)))
(dissoc :variant-properties)))
(update :undo-changes conj (cond-> {:type :mod-component
:id id
:name (:name prev-component)
:path (:path prev-component)
:main-instance-id (:main-instance-id prev-component)
:main-instance-page (:main-instance-page prev-component)
:annotation (:annotation prev-component)
:objects (:objects prev-component)}
(some? (:variant-id prev-component))
(assoc :variant-id (:variant-id prev-component))
(nil? (:variant-id prev-component))
(dissoc :variant-id)
(seq (:variant-properties prev-component))
(assoc :variant-properties (:variant-properties prev-component))
(not (seq (:variant-properties prev-component)))
(dissoc :variant-properties)))
(cond-> apply-changes-local-library?
(apply-changes-local {:apply-to-library? true})))
changes)))

View file

@ -572,6 +572,41 @@
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
(defmethod repair-error :not-a-variant
[_ error file _]
(log/error :hint "Variant error code, we don't want to auto repair it for now" :code (:code error))
file)
(defmethod repair-error :invalid-variant-id
[_ error file _]
(log/error :hint "Variant error code, we don't want to auto repair it for now" :code (:code error))
file)
(defmethod repair-error :invalid-variant-properties
[_ error file _]
(log/error :hint "Variant error code, we don't want to auto repair it for now" :code (:code error))
file)
(defmethod repair-error :variant-not-main
[_ error file _]
(log/error :hint "Variant error code, we don't want to auto repair it for now" :code (:code error))
file)
(defmethod repair-error :parent-not-variant
[_ error file _]
(log/error :hint "Variant error code, we don't want to auto repair it for now" :code (:code error))
file)
(defmethod repair-error :variant-bad-name
[_ error file _]
(log/error :hint "Variant error code, we don't want to auto repair it for now" :code (:code error))
file)
(defmethod repair-error :variant-no-properties
[_ error file _]
(log/error :hint "Variant error code, we don't want to auto repair it for now" :code (:code error))
file)
(defmethod repair-error :default
[_ error file _]
(log/error :hint "Unknown error code, don't know how to repair" :code (:code error))

View file

@ -10,12 +10,15 @@
[app.common.data.macros :as dm]
[app.common.exceptions :as ex]
[app.common.files.helpers :as cfh]
[app.common.files.variant :as cfv]
[app.common.schema :as sm]
[app.common.types.component :as ctk]
[app.common.types.components-list :as ctkl]
[app.common.types.container :as ctn]
[app.common.types.file :as ctf]
[app.common.types.pages-list :as ctpl]
[app.common.types.shape-tree :as ctst]
[app.common.types.variant :as ctv]
[app.common.uuid :as uuid]
[cuerdas.core :as str]))
@ -56,7 +59,14 @@
:instance-head-not-frame
:misplaced-slot
:missing-slot
:shape-ref-cycle})
:shape-ref-cycle
:not-a-variant
:invalid-variant-id
:invalid-variant-properties
:variant-not-main
:parent-not-variant
:variant-bad-name
:variant-no-properties})
(def ^:private schema:error
[:map {:title "ValidationError"}
@ -401,6 +411,53 @@
(check-empty-swap-slot shape file page)
(run! #(check-shape % file page libraries :context :not-component) (:shapes shape)))
(defn- check-variant-container
"Shape is a variant container, so:
-all its children should be variants with variant-id equals to the shape-id
-all the components should have the same properties"
[shape file page]
(let [shape-id (:id shape)
shapes (:shapes shape)
children (map #(ctst/get-shape page %) shapes)
prop-names (cfv/extract-properties-names (first children) (:data file))]
(doseq [child children]
(if (not (ctk/is-variant? child))
(report-error :not-a-variant
(str/ffmt "Shape % should be a variant" (:id child))
child file page)
(do
(when (not= (:variant-id child) shape-id)
(report-error :invalid-variant-id
(str/ffmt "Variant % has invalid variant-id %" (:id child) (:variant-id child))
child file page))
(when (not= prop-names (cfv/extract-properties-names child (:data file)))
(report-error :invalid-variant-properties
(str/ffmt "Variant % has invalid properties" (:id child))
child file page)))))))
(defn- check-variant
"Shape is a variant, so
-it should be a main component
-its parent should be a variant-container
-its variant-name is derived from the properties"
[shape file page]
(let [parent (ctst/get-shape page (:parent-id shape))
component (ctkl/get-component (:data file) (:component-id shape) true)
name (ctv/properties-to-name (:variant-properties component))]
(when-not (ctk/main-instance? shape)
(report-error :variant-not-main
(str/ffmt "Variant % is not a main instance" (:id shape))
shape file page))
(when-not (ctk/is-variant-container? parent)
(report-error :parent-not-variant
(str/ffmt "Variant % has an invalid parent" (:id shape))
shape file page))
(when-not (= name (:variant-name shape))
(report-error :variant-bad-name
(str/ffmt "Variant % has an invalid variant-name" (:id shape))
shape file page))))
(defn- check-shape
"Validate referential integrity and semantic coherence of
a shape and all its children. Report all errors found.
@ -421,6 +478,12 @@
(check-parent-children shape file page)
(check-frame shape file page)
(when (ctk/is-variant-container? shape)
(check-variant-container shape file page))
(when (ctk/is-variant? shape)
(check-variant shape file page))
(if (ctk/instance-head? shape)
(if (not= :frame (:type shape))
(report-error :instance-head-not-frame
@ -496,6 +559,22 @@
"This deleted component has shapes with shape-ref pointing to self"
component file nil :cycles-ids cycles-ids))))
(defn- check-variant-component
"Component is a variant, so:
-Its main should be a variant
-It should have at least one variant property"
[component file]
(let [component-page (ctf/get-component-page (:data file) component)
main-component (ctst/get-shape component-page (:main-instance-id component))]
(when-not (ctk/is-variant? main-component)
(report-error :not-a-variant
(str/ffmt "Shape % should be a variant" (:id main-component))
main-component file component-page))
(when (< (count (:variant-properties component)) 1)
(report-error :variant-no-properties
(str/ffmt "Component variant % should have properties" (:id main-component))
main-component file nil))))
(defn- check-component
"Validate semantic coherence of a component. Report all errors found."
[component file]
@ -505,7 +584,10 @@
component file nil))
(when (:deleted component)
(check-component-duplicate-swap-slot component file)
(check-ref-cycles component file)))
(check-ref-cycles component file))
(when (ctk/is-variant? component)
(check-variant-component component file)))
(defn- get-orphan-shapes
[{:keys [objects] :as page}]

View file

@ -0,0 +1,63 @@
;; 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) KALEIDOS INC
(ns app.common.files.variant
(:require
[app.common.data.macros :as dm]
[app.common.types.components-list :as ctcl]
[app.common.types.variant :as ctv]
[cuerdas.core :as str]))
(defn find-variant-components
"Find a list of the components thet belongs to this variant-id"
[data objects variant-id]
(->> (dm/get-in objects [variant-id :shapes])
(map #(dm/get-in objects [% :component-id]))
(map #(ctcl/get-component data % true))
reverse))
(defn- dashes-to-end
[property-values]
(let [dashes (if (some #(= % "--") property-values) ["--"] [])]
(concat (remove #(= % "--") property-values) dashes)))
(defn extract-properties-names
[shape data]
(->> shape
(#(ctcl/get-component data (:component-id %) true))
:variant-properties
(map :name)))
(defn extract-properties-values
[data objects variant-id]
(->> (find-variant-components data objects variant-id)
(mapcat :variant-properties)
(group-by :name)
(map (fn [[k v]]
{:name k
:value (->> v
(map #(if (str/empty? (:value %)) "--" (:value %)))
distinct
dashes-to-end)}))))
(defn get-variant-mains
[component data]
(assert (ctv/valid-variant-component? component) "expected valid component variant")
(when-let [variant-id (:variant-id component)]
(let [page-id (:main-instance-page component)
objects (-> (dm/get-in data [:pages-index page-id])
(get :objects))]
(dm/get-in objects [variant-id :shapes]))))
(defn is-secondary-variant?
[component data]
(let [shapes (get-variant-mains component data)]
(and (seq shapes)
(not= (:main-instance-id component) (last shapes)))))

View file

@ -10,12 +10,13 @@
[app.common.files.changes-builder :as pcb]
[app.common.files.helpers :as cfh]
[app.common.geom.shapes :as gsh]
[app.common.logic.variants :as clv]
[app.common.logic.variant-properties :as clvp]
[app.common.types.component :as ctk]
[app.common.types.container :as ctn]
[app.common.types.shape.interactions :as ctsi]
[app.common.types.shape.layout :as ctl]
[app.common.types.token :as cto]
[app.common.types.variant :as ctv]
[app.common.uuid :as uuid]
[cuerdas.core :as str]))
@ -413,7 +414,7 @@
(- max-path-items num-props))
changes (nth
(iterate #(clv/generate-add-new-property % (:id parent)) changes)
(iterate #(clvp/generate-add-new-property % (:id parent)) changes)
num-new-props)]
(reduce
(fn [changes shape]
@ -424,11 +425,11 @@
;; we need to get the updated library data to have access to the current properties
data (pcb/get-library-data changes)
props (clv/path-to-properties
props (ctv/path-to-properties
base-name
(get-in data [:components first-comp-id :variant-properties]))
variant-name (clv/properties-to-name props)
variant-name (ctv/properties-to-name props)
[cpath cname] (cfh/parse-path-name (:name parent))]
(-> (pcb/update-component changes

View file

@ -0,0 +1,93 @@
;; 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) KALEIDOS INC
(ns app.common.logic.variant-properties
(:require
[app.common.data :as d]
[app.common.files.changes-builder :as pcb]
[app.common.files.variant :as cfv]
[app.common.types.components-list :as ctcl]
[app.common.types.variant :as ctv]
[cuerdas.core :as str]))
(defn generate-update-property-name
[changes variant-id pos new-name]
(let [data (pcb/get-library-data changes)
objects (pcb/get-objects changes)
related-components (cfv/find-variant-components data objects variant-id)]
(reduce (fn [changes component]
(pcb/update-component
changes (:id component)
#(assoc-in % [:variant-properties pos :name] new-name)
{:apply-changes-local-library? true}))
changes
related-components)))
(defn generate-remove-property
[changes variant-id pos]
(let [data (pcb/get-library-data changes)
objects (pcb/get-objects changes)
related-components (cfv/find-variant-components data objects variant-id)]
(reduce (fn [changes component]
(let [props (:variant-properties component)
props (d/remove-at-index props pos)
main-id (:main-instance-id component)
name (ctv/properties-to-name props)]
(-> changes
(pcb/update-component (:id component) #(assoc % :variant-properties props)
{:apply-changes-local-library? true})
(pcb/update-shapes [main-id] #(assoc % :variant-name name)))))
changes
related-components)))
(defn generate-update-property-value
[changes component-id pos value]
(let [data (pcb/get-library-data changes)
component (ctcl/get-component data component-id true)
main-id (:main-instance-id component)
name (-> (:variant-properties component)
(update pos assoc :value value)
ctv/properties-to-name)]
(-> changes
(pcb/update-component component-id #(assoc-in % [:variant-properties pos :value] value)
{:apply-changes-local-library? true})
(pcb/update-shapes [main-id] #(assoc % :variant-name name)))))
(defn generate-add-new-property
[changes variant-id & {:keys [fill-values?]}]
(let [data (pcb/get-library-data changes)
objects (pcb/get-objects changes)
related-components (cfv/find-variant-components data objects variant-id)
props (-> related-components first :variant-properties)
next-prop-num (ctv/next-property-number props)
property-name (str ctv/property-prefix next-prop-num)
[_ changes]
(reduce (fn [[num changes] component]
(let [main-id (:main-instance-id component)
update-props #(-> (d/nilv % [])
(conj {:name property-name
:value (if fill-values? (str ctv/value-prefix num) "")}))
update-name #(if fill-values?
(if (str/empty? %)
(str ctv/value-prefix num)
(str % ", " ctv/value-prefix num))
%)]
[(inc num)
(-> changes
(pcb/update-component (:id component)
#(update % :variant-properties update-props)
{:apply-changes-local-library? true})
(pcb/update-shapes [main-id] #(update % :variant-name update-name)))]))
[1 changes]
related-components)]
changes))

View file

@ -1,160 +1,31 @@
;; 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) KALEIDOS INC
(ns app.common.logic.variants
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.changes-builder :as pcb]
[app.common.files.helpers :as cfh]
[app.common.types.components-list :as ctcl]
[cuerdas.core :as str]))
[app.common.files.variant :as cfv]
[app.common.logic.libraries :as cll]
[app.common.logic.variant-properties :as clvp]
[app.common.types.variant :as ctv]))
(defn generate-add-new-variant
[changes shape variant-id new-component-id new-shape-id prop-num]
(let [data (pcb/get-library-data changes)
objects (pcb/get-objects changes)
component-id (:component-id shape)
value (str ctv/value-prefix
(-> (cfv/extract-properties-values data objects variant-id)
last
:value
count
inc))
(def property-prefix "Property")
(def property-regex (re-pattern (str property-prefix "(\\d+)")))
(def value-prefix "Value")
(defn find-related-components
"Find a list of the components thet belongs to this variant-id"
[data objects variant-id]
(->> (dm/get-in objects [variant-id :shapes])
(map #(dm/get-in objects [% :component-id]))
(map #(ctcl/get-component data % true))
reverse))
(defn properties-to-name
"Transform the properties into a name, with the values separated by comma"
[properties]
(->> properties
(map :value)
(remove str/empty?)
(str/join ", ")))
(defn next-property-number
"Returns the next property number, to avoid duplicates on the property names"
[properties]
(let [numbers (keep
#(some->> (:name %) (re-find property-regex) second d/parse-integer)
properties)
max-num (if (seq numbers)
(apply max numbers)
0)]
(inc (max max-num (count properties)))))
(defn path-to-properties
"From a list of properties and a name with path, assign each token of the
path as value of a different property"
[path properties]
(let [next-prop-num (next-property-number properties)
cpath (cfh/split-path path)
assigned (mapv #(assoc % :value (nth cpath %2 "")) properties (range))
remaining (drop (count properties) cpath)
new-properties (map-indexed (fn [i v] {:name (str property-prefix (+ next-prop-num i))
:value v}) remaining)]
(into assigned new-properties)))
(defn- dashes-to-end
[property-values]
(let [dashes (if (some #(= % "--") property-values) ["--"] [])]
(concat (remove #(= % "--") property-values) dashes)))
(defn extract-properties-values
[data objects variant-id]
(->> (find-related-components data objects variant-id)
(mapcat :variant-properties)
(group-by :name)
(map (fn [[k v]]
{:name k
:value (->> v
(map #(if (str/empty? (:value %)) "--" (:value %)))
distinct
dashes-to-end)}))))
(defn generate-update-property-name
[changes variant-id pos new-name]
(let [data (pcb/get-library-data changes)
objects (pcb/get-objects changes)
related-components (find-related-components data objects variant-id)]
(reduce (fn [changes component]
(pcb/update-component
changes (:id component)
#(assoc-in % [:variant-properties pos :name] new-name)
{:apply-changes-local-library? true}))
changes
related-components)))
(defn generate-remove-property
[changes variant-id pos]
(let [data (pcb/get-library-data changes)
objects (pcb/get-objects changes)
related-components (find-related-components data objects variant-id)]
(reduce (fn [changes component]
(let [props (:variant-properties component)
props (d/remove-at-index props pos)
main-id (:main-instance-id component)
name (properties-to-name props)]
(-> changes
(pcb/update-component (:id component) #(assoc % :variant-properties props)
{:apply-changes-local-library? true})
(pcb/update-shapes [main-id] #(assoc % :variant-name name)))))
changes
related-components)))
(defn generate-update-property-value
[changes component-id pos value]
(let [data (pcb/get-library-data changes)
component (ctcl/get-component data component-id true)
main-id (:main-instance-id component)
name (-> (:variant-properties component)
(update pos assoc :value value)
properties-to-name)]
[new-shape changes] (-> changes
(cll/generate-duplicate-component
{:data data}
component-id
new-component-id
true
{:new-shape-id new-shape-id :apply-changes-local-library? true}))]
(-> changes
(pcb/update-component component-id #(assoc-in % [:variant-properties pos :value] value)
{:apply-changes-local-library? true})
(pcb/update-shapes [main-id] #(assoc % :variant-name name)))))
(defn generate-add-new-property
[changes variant-id & {:keys [fill-values?]}]
(let [data (pcb/get-library-data changes)
objects (pcb/get-objects changes)
related-components (find-related-components data objects variant-id)
props (-> related-components first :variant-properties)
next-prop-num (next-property-number props)
property-name (str property-prefix next-prop-num)
[_ changes]
(reduce (fn [[num changes] component]
(let [main-id (:main-instance-id component)
update-props #(-> (d/nilv % [])
(conj {:name property-name
:value (if fill-values? (str value-prefix num) "")}))
update-name #(if fill-values?
(if (str/empty? %)
(str value-prefix num)
(str % ", " value-prefix num))
%)]
[(inc num)
(-> changes
(pcb/update-component (:id component)
#(update % :variant-properties update-props)
{:apply-changes-local-library? true})
(pcb/update-shapes [main-id] #(update % :variant-name update-name)))]))
[1 changes]
related-components)]
changes))
(clvp/generate-update-property-value new-component-id prop-num value)
(pcb/change-parent (:parent-id shape) [new-shape] 0))))

View file

@ -22,4 +22,18 @@
(thc/make-component component1-label root1-label)
(thc/update-component component1-label {:variant-id variant-id :variant-properties [{:name "Property1" :value "Value1"}]})
(thc/make-component component2-label root2-label)
(thc/update-component component2-label {:variant-id variant-id :variant-properties [{:name "Property1" :value "Value1"}]}))))
(thc/update-component component2-label {:variant-id variant-id :variant-properties [{:name "Property1" :value "Value2"}]}))))
(defn add-variant-two-properties
[file variant-label component1-label root1-label component2-label root2-label
& {:keys []}]
(let [file (ths/add-sample-shape file variant-label :type :frame :is-variant-container true)
variant-id (thi/id variant-label)]
(-> file
(ths/add-sample-shape root2-label :type :frame :parent-label variant-label :variant-id variant-id :variant-name "p1v2, p2v2")
(ths/add-sample-shape root1-label :type :frame :parent-label variant-label :variant-id variant-id :variant-name "p1v1, p2v1")
(thc/make-component component1-label root1-label)
(thc/update-component component1-label {:variant-id variant-id :variant-properties [{:name "Property1" :value "p1v1"} {:name "Property2" :value "p2v1"}]})
(thc/make-component component2-label root2-label)
(thc/update-component component2-label {:variant-id variant-id :variant-properties [{:name "Property1" :value "p1v2"} {:name "Property2" :value "p2v2"}]}))))

View file

@ -10,6 +10,7 @@
[app.common.schema :as sm]
[app.common.types.page :as ctp]
[app.common.types.plugins :as ctpg]
[app.common.types.variant :as ctv]
[cuerdas.core :as str]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -17,15 +18,17 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def schema:component
[:map
[:id ::sm/uuid]
[:name :string]
[:path {:optional true} [:maybe :string]]
[:modified-at {:optional true} ::sm/inst]
[:objects {:gen/max 10 :optional true} ::ctp/objects]
[:main-instance-id ::sm/uuid]
[:main-instance-page ::sm/uuid]
[:plugin-data {:optional true} ::ctpg/plugin-data]])
[:merge
[:map
[:id ::sm/uuid]
[:name :string]
[:path {:optional true} [:maybe :string]]
[:modified-at {:optional true} ::sm/inst]
[:objects {:gen/max 10 :optional true} ::ctp/objects]
[:main-instance-id ::sm/uuid]
[:main-instance-page ::sm/uuid]
[:plugin-data {:optional true} ::ctpg/plugin-data]]
::ctv/variant-component])
(sm/register! ::component schema:component)

View file

@ -32,6 +32,7 @@
[app.common.types.shape.shadow :as ctss]
[app.common.types.shape.text :as ctsx]
[app.common.types.token :as cto]
[app.common.types.variant :as ctv]
[app.common.uuid :as uuid]
[clojure.set :as set]))
@ -317,7 +318,9 @@
schema:frame-attrs
schema:shape-attrs
schema:shape-geom-attrs
schema:shape-base-attrs]]
schema:shape-base-attrs
::ctv/variant-shape
::ctv/variant-container]]
[:bool
[:merge {:title "BoolShape"}

View file

@ -0,0 +1,88 @@
;; 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) KALEIDOS INC
(ns app.common.types.variant
(:require
[app.common.data :as d]
[app.common.files.helpers :as cfh]
[app.common.schema :as sm]
[cuerdas.core :as str]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SCHEMA
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def schema:variant-property
[:map
[:name :string]
[:value :string]])
(def schema:variant-component
;; A component that is part of a variant set.
[:map
[:variant-id {:optional true} ::sm/uuid]
[:variant-properties {:optional true} [:vector schema:variant-property]]])
(def schema:variant-shape
;; The root shape of the main instance of a variant component.
[:map
[:variant-id {:optional true} ::sm/uuid]
[:variant-name {:optional true} :string]])
(def schema:variant-container
;; is a board that contains all variant components of a variant set,
;; for grouping them visually in the workspace.
[:map
[:is-variant-container {:optional true} :boolean]])
(sm/register! ::variant-property schema:variant-property)
(sm/register! ::variant-component schema:variant-component)
(sm/register! ::variant-shape schema:variant-shape)
(sm/register! ::variant-container schema:variant-container)
(def valid-variant-component?
(sm/check-fn schema:variant-component))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def property-prefix "Property")
(def property-regex (re-pattern (str property-prefix "(\\d+)")))
(def value-prefix "Value")
(defn properties-to-name
"Transform the properties into a name, with the values separated by comma"
[properties]
(->> properties
(map :value)
(remove str/empty?)
(str/join ", ")))
(defn next-property-number
"Returns the next property number, to avoid duplicates on the property names"
[properties]
(let [numbers (keep
#(some->> (:name %) (re-find property-regex) second d/parse-integer)
properties)
max-num (if (seq numbers)
(apply max numbers)
0)]
(inc (max max-num (count properties)))))
(defn path-to-properties
"From a list of properties and a name with path, assign each token of the
path as value of a different property"
[path properties]
(let [next-prop-num (next-property-number properties)
cpath (cfh/split-path path)
assigned (mapv #(assoc % :value (nth cpath %2 "")) properties (range))
remaining (drop (count properties) cpath)
new-properties (map-indexed (fn [i v] {:name (str property-prefix (+ next-prop-num i))
:value v}) remaining)]
(into assigned new-properties)))

View file

@ -7,7 +7,7 @@
(ns common-tests.logic.variants-test
(:require
[app.common.files.changes-builder :as pcb]
[app.common.logic.variants :as clv]
[app.common.logic.variant-properties :as clvp]
[app.common.test-helpers.components :as thc]
[app.common.test-helpers.files :as thf]
[app.common.test-helpers.ids-map :as thi]
@ -20,7 +20,7 @@
(t/deftest test-update-property-name
(let [;; ==== Setup
file (-> (thf/sample-file :file1)
(thv/add-variant :v01 :c01 :m01 :c02 :m02))
(thv/add-variant-two-properties :v01 :c01 :m01 :c02 :m02))
v-id (-> (ths/get-shape file :v01) :id)
page (thf/current-page file)
@ -29,8 +29,8 @@
(pcb/with-page-id (:id page))
(pcb/with-library-data (:data file))
(pcb/with-objects (:objects page))
(clv/generate-update-property-name v-id 0 "NewName1")
(clv/generate-update-property-name v-id 1 "NewName2"))
(clvp/generate-update-property-name v-id 0 "NewName1")
(clvp/generate-update-property-name v-id 1 "NewName2"))
file' (thf/apply-changes file changes)
@ -65,7 +65,7 @@
(pcb/with-page-id (:id page))
(pcb/with-library-data (:data file))
(pcb/with-objects (:objects page))
(clv/generate-add-new-property v-id))
(clvp/generate-add-new-property v-id))
file' (thf/apply-changes file changes)
@ -101,7 +101,7 @@
(pcb/with-page-id (:id page))
(pcb/with-library-data (:data file))
(pcb/with-objects (:objects page))
(clv/generate-add-new-property v-id {:fill-values? true}))
(clvp/generate-add-new-property v-id {:fill-values? true}))
file' (thf/apply-changes file changes)
@ -132,7 +132,7 @@
(pcb/with-page-id (:id page))
(pcb/with-library-data (:data file))
(pcb/with-objects (:objects page))
(clv/generate-add-new-property v-id))
(clvp/generate-add-new-property v-id))
file (thf/apply-changes file changes)
@ -147,7 +147,7 @@
(pcb/with-page-id (:id page))
(pcb/with-library-data (:data file))
(pcb/with-objects (:objects page))
(clv/generate-remove-property v-id 0))
(clvp/generate-remove-property v-id 0))
file' (thf/apply-changes file changes)
@ -180,8 +180,8 @@
(pcb/with-page-id (:id page))
(pcb/with-library-data (:data file))
(pcb/with-objects (:objects page))
(clv/generate-update-property-value (:id comp01) 0 "NewValue1")
(clv/generate-update-property-value (:id comp02) 0 "NewValue2"))
(clvp/generate-update-property-value (:id comp01) 0 "NewValue1")
(clvp/generate-update-property-value (:id comp02) 0 "NewValue2"))
file' (thf/apply-changes file changes)

View file

@ -8,10 +8,9 @@
(:require
[app.common.colors :as clr]
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.changes-builder :as pcb]
[app.common.files.helpers :as cfh]
[app.common.logic.libraries :as cll]
[app.common.logic.variant-properties :as clvp]
[app.common.logic.variants :as clv]
[app.common.types.component :as ctc]
[app.common.types.components-list :as ctkl]
@ -29,18 +28,6 @@
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
(dm/export clv/find-related-components)
(defn is-secondary-variant?
[component data]
(if-let [variant-id (:variant-id component)]
(let [page-id (:main-instance-page component)
objects (-> (dsh/get-page data page-id)
(get :objects))
shapes (dm/get-in objects [variant-id :shapes])]
(not= (:main-instance-id component) (last shapes)))
false))
(defn update-property-name
"Update the variant property name on the position pos
in all the components with this variant-id"
@ -56,7 +43,7 @@
changes (-> (pcb/empty-changes it page-id)
(pcb/with-objects objects)
(pcb/with-library-data data)
(clv/generate-update-property-name variant-id pos new-name))
(clvp/generate-update-property-name variant-id pos new-name))
undo-id (js/Symbol)]
(rx/of
(dwu/start-undo-transaction undo-id)
@ -77,7 +64,7 @@
changes (-> (pcb/empty-changes it page-id)
(pcb/with-library-data data)
(pcb/with-objects objects)
(clv/generate-update-property-value component-id pos value))
(clvp/generate-update-property-value component-id pos value))
undo-id (js/Symbol)]
(rx/of
(dwu/start-undo-transaction undo-id)
@ -100,7 +87,7 @@
changes (-> (pcb/empty-changes it page-id)
(pcb/with-library-data data)
(pcb/with-objects objects)
(clv/generate-remove-property variant-id pos))
(clvp/generate-remove-property variant-id pos))
undo-id (js/Symbol)]
(rx/of
@ -124,7 +111,7 @@
changes (-> (pcb/empty-changes it page-id)
(pcb/with-library-data data)
(pcb/with-objects objects)
(clv/generate-add-new-property variant-id options))
(clvp/generate-add-new-property variant-id options))
undo-id (js/Symbol)]
(rx/of
@ -132,7 +119,7 @@
(dch/commit-changes changes)
(dwu/commit-undo-transaction undo-id))))))
(defn set-variant-id
(defn- set-variant-id
"Sets the variant-id on a component"
[component-id variant-id]
(ptk/reify ::set-variant-id
@ -149,7 +136,7 @@
(dch/commit-changes changes)
(dwu/commit-undo-transaction undo-id))))))
(defn focus-property
(defn- focus-property
[shape-id prop-num]
(ptk/reify ::focus-property
ptk/EffectEvent
@ -177,30 +164,13 @@
new-component-id (uuid/next)
new-shape-id (uuid/next)
value (str clv/value-prefix
(-> (clv/extract-properties-values data objects (:variant-id component))
last
:value
count
inc))
prop-num (dec (count (:variant-properties component)))
[new-shape changes] (-> (pcb/empty-changes it page-id)
changes (-> (pcb/empty-changes it page-id)
(pcb/with-library-data data)
(pcb/with-objects objects)
(pcb/with-page-id page-id)
(cll/generate-duplicate-component
{:data data}
component-id
new-component-id
true
{:new-shape-id new-shape-id :apply-changes-local-library? true}))
changes (-> changes
(clv/generate-update-property-value new-component-id prop-num value)
(pcb/change-parent (:parent-id shape) [new-shape] 0))
(clv/generate-add-new-variant shape (:variant-id component) new-component-id new-shape-id prop-num))
undo-id (js/Symbol)]
(rx/concat
@ -253,6 +223,8 @@
undo-id (js/Symbol)]
;;TODO Refactor all called methods in order to be able to
;;generate changes instead of call the events
(rx/concat
(rx/of
(dwu/start-undo-transaction undo-id)
@ -286,6 +258,7 @@
(dwu/commit-undo-transaction undo-id)))))))
(defn add-component-or-variant
"Manage the shared shortcut, and do the pertinent action"
[]
(ptk/reify ::add-component-or-variant
@ -319,6 +292,7 @@
(rx/of (dwl/add-component)))))))
(defn duplicate-or-add-variant
"Manage the shared shortcut, and do the pertinent action"
[]
(ptk/reify ::duplicate-or-add-variant
ptk/WatchEvent

View file

@ -8,7 +8,7 @@
(:require-macros [app.main.style :as stl])
(:require
[app.common.data.macros :as dm]
[app.common.logic.variants :as clv]
[app.common.files.variant :as cfv]
[app.common.types.component :as ctc]
[app.common.types.components-list :as ctkl]
[app.main.ui.components.copy-button :refer [copy-button*]]
@ -35,7 +35,7 @@
is-container? (ctc/is-variant-container? shape)
component (when-not is-container? (ctkl/get-component data (:component-id shape)))]
(if is-container?
(->> (clv/extract-properties-values data objects (:id shape))
(->> (cfv/extract-properties-values data objects (:id shape))
(map #(update % :value (partial str/join ", "))))
(->> (:variant-properties component)
(map #(update % :value (fn [v] (if (str/blank? v) "--" v))))))))]

View file

@ -9,12 +9,12 @@
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.variant :as cfv]
[app.common.types.components-list :as ctkl]
[app.main.data.event :as ev]
[app.main.data.workspace :as dw]
[app.main.data.workspace.libraries :as dwl]
[app.main.data.workspace.undo :as dwu]
[app.main.data.workspace.variants :as dwv]
[app.main.refs :as refs]
[app.main.router :as rt]
[app.main.store :as st]
@ -346,7 +346,7 @@
(mf/with-memo [filters library]
(as-> (into [] (ctkl/components-seq library)) $
(cmm/apply-filters $ filters)
(remove #(dwv/is-secondary-variant? % library) $)))
(remove #(cfv/is-secondary-variant? % library) $)))
filtered-media
(mf/with-memo [filters media]

View file

@ -9,7 +9,7 @@
(:require
[app.common.data.macros :as dm]
[app.common.files.helpers :as cfh]
[app.common.logic.variants :as clv]
[app.common.files.variant :as cfv]
[app.common.types.component :as ctk]
[app.common.types.file :as ctf]
[app.main.data.helpers :as dsh]
@ -241,7 +241,7 @@
objects (-> (dsh/get-page data page-id)
(get :objects))
related-components (dwv/find-related-components data objects variant-id)
related-components (cfv/find-variant-components data objects variant-id)
flat-comps ;; Get a list like [{:id 0 :prop1 "v1" :prop2 "v2"} {:id 1, :prop1 "v3" :prop2 "v4"}]
(map (fn [{:keys [id variant-properties]}]
@ -760,7 +760,7 @@
variant-id (:variant-id first-variant)
properties (mf/with-memo [data objects variant-id]
(clv/extract-properties-values data objects (:id shape)))
(cfv/extract-properties-values data objects (:id shape)))
menu-open* (mf/use-state false)
menu-open? (deref menu-open*)