mirror of
https://github.com/penpot/penpot.git
synced 2025-01-21 06:02:32 -05:00
🔧 add groups handling
This commit is contained in:
parent
316d333c96
commit
fa8f8ac54b
3 changed files with 200 additions and 9 deletions
|
@ -12,10 +12,9 @@
|
|||
[app.common.time :as dt]
|
||||
[app.common.transit :as t]
|
||||
[app.common.types.token :as cto]
|
||||
[cuerdas.core :as str]
|
||||
#?(:clj [app.common.fressian :as fres])))
|
||||
|
||||
;; #?(:clj (set! *warn-on-reflection* true))
|
||||
|
||||
;; === Token
|
||||
|
||||
(defrecord Token [name type value description modified-at])
|
||||
|
@ -56,7 +55,8 @@
|
|||
(defprotocol ITokenSet
|
||||
(add-token [_ token] "add a token at the end of the list")
|
||||
(update-token [_ token-name f] "update a token in the list")
|
||||
(delete-token [_ token-name] "delete a token from the list"))
|
||||
(delete-token [_ token-name] "delete a token from the list")
|
||||
(get-tokens [_] "return an ordered sequence of all tokens in the set"))
|
||||
|
||||
(defrecord TokenSet [name description modified-at tokens]
|
||||
ITokenSet
|
||||
|
@ -87,7 +87,10 @@
|
|||
(TokenSet. name
|
||||
description
|
||||
(dt/now)
|
||||
(dissoc tokens token-name))))
|
||||
(dissoc tokens token-name)))
|
||||
|
||||
(get-tokens [_]
|
||||
(vals tokens)))
|
||||
|
||||
(def schema:token-set
|
||||
[:and [:map {:title "TokenSet"}
|
||||
|
@ -442,3 +445,90 @@
|
|||
(let [sets (fres/read-object! r)
|
||||
themes (fres/read-object! r)]
|
||||
(->TokensLib sets themes)))}))
|
||||
|
||||
;; === Groups handling
|
||||
|
||||
(def schema:groupable-item
|
||||
[:map {:title "Groupable item"}
|
||||
[:name :string]])
|
||||
|
||||
(def valid-groupable-item?
|
||||
(sm/validator schema:groupable-item))
|
||||
|
||||
(defn split-path
|
||||
"Decompose a string in the form 'one.two.three' into a vector of strings, removing spaces."
|
||||
[path]
|
||||
(let [xf (comp (map str/trim)
|
||||
(remove str/empty?))]
|
||||
(->> (str/split path ".")
|
||||
(into [] xf))))
|
||||
|
||||
(defn join-path
|
||||
"Regenerate a path as a string, from a vector."
|
||||
[path-vec]
|
||||
(str/join "." path-vec))
|
||||
|
||||
(defn group-item
|
||||
"Add a group to the item name, in the form group.name."
|
||||
[item group-name]
|
||||
(dm/assert!
|
||||
"expected groupable item"
|
||||
(valid-groupable-item? item))
|
||||
(update item :name #(str group-name "." %)))
|
||||
|
||||
(defn ungroup-item
|
||||
"Remove the first group from the item name."
|
||||
[item]
|
||||
(dm/assert!
|
||||
"expected groupable item"
|
||||
(valid-groupable-item? item))
|
||||
(update item :name #(-> %
|
||||
(split-path)
|
||||
(rest)
|
||||
(join-path))))
|
||||
|
||||
(defn get-path
|
||||
"Get the groups part of the name. E.g. group.subgroup.name -> group.subrgoup"
|
||||
[item]
|
||||
(dm/assert!
|
||||
"expected groupable item"
|
||||
(valid-groupable-item? item))
|
||||
(-> (:name item)
|
||||
(split-path)
|
||||
(butlast)
|
||||
(join-path)))
|
||||
|
||||
(defn get-final-name
|
||||
"Get the final part of the name. E.g. group.subgroup.name -> name"
|
||||
[item]
|
||||
(dm/assert!
|
||||
"expected groupable item"
|
||||
(valid-groupable-item? item))
|
||||
(-> (:name item)
|
||||
(split-path)
|
||||
(last)))
|
||||
|
||||
(defn group-items
|
||||
"Convert a sequence of items in a nested structure like this:
|
||||
|
||||
{'': [itemA itemB]
|
||||
'group1': {'': [item1A item1B]
|
||||
'subgroup11': {'': [item11A item11B item11C]}
|
||||
'subgroup12': {'': [item12A]}}
|
||||
'group2': {'subgroup21': {'': [item21A]}}}
|
||||
"
|
||||
[items & {:keys [reverse-sort?]}]
|
||||
(when (seq items)
|
||||
(reduce (fn [groups item]
|
||||
(let [pathv (split-path (:name item))]
|
||||
(update-in groups
|
||||
pathv
|
||||
(fn [group]
|
||||
(if group
|
||||
(cons group item)
|
||||
item)))))
|
||||
(sorted-map-by (fn [key1 key2] ;; TODO: this does not work well with ordered-map
|
||||
(if reverse-sort? ;; we need to think a bit more of this
|
||||
(compare key2 key1)
|
||||
(compare key1 key2))))
|
||||
items)))
|
||||
|
|
|
@ -409,3 +409,104 @@
|
|||
(t/is (ctob/valid-tokens-lib? tokens-lib'))
|
||||
(t/is (= (ctob/set-count tokens-lib') 1))
|
||||
(t/is (= (ctob/theme-count tokens-lib') 1)))))
|
||||
|
||||
(t/testing "grouping"
|
||||
(t/deftest split-and-join
|
||||
(let [name "group.subgroup.name"
|
||||
path (ctob/split-path name)
|
||||
name' (ctob/join-path path)]
|
||||
(t/is (= (first path) "group"))
|
||||
(t/is (= (second path) "subgroup"))
|
||||
(t/is (= (nth path 2) "name"))
|
||||
(t/is (= name' name))))
|
||||
|
||||
(t/deftest remove-spaces
|
||||
(let [name "group . subgroup . name"
|
||||
path (ctob/split-path name)]
|
||||
(t/is (= (first path) "group"))
|
||||
(t/is (= (second path) "subgroup"))
|
||||
(t/is (= (nth path 2) "name"))))
|
||||
|
||||
(t/deftest group-and-ungroup
|
||||
(let [token1 (ctob/make-token :name "token1" :type :boolean :value true)
|
||||
token2 (ctob/make-token :name "some group.token2" :type :boolean :value true)
|
||||
|
||||
token1' (ctob/group-item token1 "big group")
|
||||
token2' (ctob/group-item token2 "big group")
|
||||
token1'' (ctob/ungroup-item token1')
|
||||
token2'' (ctob/ungroup-item token2')]
|
||||
(t/is (= (:name token1') "big group.token1"))
|
||||
(t/is (= (:name token2') "big group.some group.token2"))
|
||||
(t/is (= (:name token1'') "token1"))
|
||||
(t/is (= (:name token2'') "some group.token2"))))
|
||||
|
||||
(t/deftest get-path
|
||||
(let [token1 (ctob/make-token :name "token1" :type :boolean :value true)
|
||||
token2 (ctob/make-token :name "some-group.token2" :type :boolean :value true)
|
||||
token3 (ctob/make-token :name "some-group.some-subgroup.token3" :type :boolean :value true)]
|
||||
(t/is (= (ctob/get-path token1) ""))
|
||||
(t/is (= (ctob/get-path token2) "some-group"))
|
||||
(t/is (= (ctob/get-path token3) "some-group.some-subgroup"))))
|
||||
|
||||
(t/deftest get-final-name
|
||||
(let [token1 (ctob/make-token :name "token1" :type :boolean :value true)
|
||||
token2 (ctob/make-token :name "some-group.token2" :type :boolean :value true)
|
||||
token3 (ctob/make-token :name "some-group.some-subgroup.token3" :type :boolean :value true)]
|
||||
(t/is (= (ctob/get-final-name token1) "token1"))
|
||||
(t/is (= (ctob/get-final-name token2) "token2"))
|
||||
(t/is (= (ctob/get-final-name token3) "token3"))))
|
||||
|
||||
(t/deftest group-items
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-set (ctob/make-token-set :name "token-set1"))
|
||||
(ctob/add-set (ctob/make-token-set :name "sgroup1.token-set2"))
|
||||
(ctob/add-set (ctob/make-token-set :name "sgroup1.token-set3"))
|
||||
(ctob/add-set (ctob/make-token-set :name "sgroup1.ssubgroup1.token-set4"))
|
||||
(ctob/add-set (ctob/make-token-set :name "sgroup2.token-set5"))
|
||||
(ctob/add-token-in-set "sgroup2.token-set5"
|
||||
(ctob/make-token :name "tgroup1.tsubgroup1.token1"
|
||||
:type :boolean
|
||||
:value true))
|
||||
(ctob/add-token-in-set "sgroup2.token-set5"
|
||||
(ctob/make-token :name "tgroup1.tsubgroup1.token2"
|
||||
:type :boolean
|
||||
:value true)))
|
||||
sets (ctob/get-sets tokens-lib)
|
||||
set (ctob/get-set tokens-lib "sgroup2.token-set5")
|
||||
tokens (ctob/get-tokens set)
|
||||
|
||||
set-groups (ctob/group-items sets)
|
||||
|
||||
token-set1 (get set-groups "token-set1")
|
||||
sgroup1 (get set-groups "sgroup1")
|
||||
token-set2 (get sgroup1 "token-set2")
|
||||
token-set3 (get sgroup1 "token-set3")
|
||||
ssubgroup1 (get sgroup1 "ssubgroup1")
|
||||
token-set4 (get ssubgroup1 "token-set4")
|
||||
sgroup2 (get set-groups "sgroup2")
|
||||
token-set5 (get sgroup2 "token-set5")
|
||||
|
||||
|
||||
token-groups (ctob/group-items tokens)
|
||||
tgroup1 (get token-groups "tgroup1")
|
||||
tsubgroup1 (get tgroup1 "tsubgroup1")
|
||||
token1 (get tsubgroup1 "token1")
|
||||
token2 (get tsubgroup1 "token2")]
|
||||
|
||||
;; {"sgroup1"
|
||||
;; {"token-set2" {:name "sgroup1.token-set2" ...}
|
||||
;; "token-set3" {:name "sgroup1.token-set3" ...}
|
||||
;; "ssubgroup1"
|
||||
;; {"token-set4" {:name "sgroup1.ssubgroup1.token-set4" ...}}}
|
||||
;; "sgroup2"
|
||||
;; {"token-set5" {:name "sgroup2.token-set5" ...}}
|
||||
;; "token-set1" {:name "token-set1" ...}}
|
||||
|
||||
(t/is (= (:name token-set1) "token-set1"))
|
||||
(t/is (= (:name token-set2) "sgroup1.token-set2"))
|
||||
(t/is (= (:name token-set3) "sgroup1.token-set3"))
|
||||
(t/is (= (:name token-set4) "sgroup1.ssubgroup1.token-set4"))
|
||||
(t/is (= (:name token-set5) "sgroup2.token-set5"))
|
||||
|
||||
(t/is (= (:name token1) "tgroup1.tsubgroup1.token1"))
|
||||
(t/is (= (:name token2) "tgroup1.tsubgroup1.token2")))))
|
||||
|
|
|
@ -69,11 +69,11 @@
|
|||
(defn group-assets
|
||||
"Convert a list of assets in a nested structure like this:
|
||||
|
||||
{'': [{assetA} {assetB}]
|
||||
'group1': {'': [{asset1A} {asset1B}]
|
||||
'subgroup11': {'': [{asset11A} {asset11B} {asset11C}]}
|
||||
'subgroup12': {'': [{asset12A}]}}
|
||||
'group2': {'subgroup21': {'': [{asset21A}}}}
|
||||
{'': [assetA assetB]
|
||||
'group1': {'': [asset1A asset1B]
|
||||
'subgroup11': {'': [asset11A asset11B asset11C]}
|
||||
'subgroup12': {'': [asset12A]}}
|
||||
'group2': {'subgroup21': {'': [asset21A]}}}
|
||||
"
|
||||
[assets reverse-sort?]
|
||||
(when-not (empty? assets)
|
||||
|
|
Loading…
Add table
Reference in a new issue