penpot/frontend/src/app/main/data/tokens.cljs
Florian Schroedl b7cedf219b Cleanup
2024-09-27 11:21:30 +02:00

373 lines
13 KiB
Clojure

;; 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.main.data.tokens
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.changes-builder :as pcb]
[app.common.geom.point :as gpt]
[app.common.types.shape :as cts]
[app.common.types.tokens-lib :as ctob]
[app.common.uuid :as uuid]
[app.main.data.changes :as dch]
[app.main.data.workspace.shapes :as dwsh]
[app.main.refs :as refs]
[app.main.ui.workspace.tokens.token :as wtt]
[app.main.ui.workspace.tokens.token-set :as wtts]
[app.main.ui.workspace.tokens.update :as wtu]
[beicon.v2.core :as rx]
[clojure.data :as data]
[cuerdas.core :as str]
[potok.v2.core :as ptk]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Helpers
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TODO HYMA: Copied over from workspace.cljs
(defn update-shape
[id attrs]
(dm/assert!
"expected valid parameters"
(and (cts/check-shape-attrs! attrs)
(uuid? id)))
(ptk/reify ::update-shape
ptk/WatchEvent
(watch [_ _ _]
(rx/of (dwsh/update-shapes [id] #(merge % attrs))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TOKENS Getters
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn get-tokens-lib [state]
(get-in state [:workspace-data :tokens-lib]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TOKENS Actions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn toggle-or-apply-token
"Remove any shape attributes from token if they exists.
Othewise apply token attributes."
[shape token]
(let [[shape-leftover token-leftover _matching] (data/diff (:applied-tokens shape) token)]
(merge {} shape-leftover token-leftover)))
(defn token-from-attributes [token attributes]
(->> (map (fn [attr] [attr (wtt/token-identifier token)]) attributes)
(into {})))
(defn unapply-token-id [shape attributes]
(update shape :applied-tokens d/without-keys attributes))
(defn apply-token-to-attributes [{:keys [shape token attributes]}]
(let [token (token-from-attributes token attributes)]
(toggle-or-apply-token shape token)))
(defn apply-token-to-shape
[{:keys [shape token attributes] :as _props}]
(let [applied-tokens (apply-token-to-attributes {:shape shape
:token token
:attributes attributes})]
(update shape :applied-tokens #(merge % applied-tokens))))
(defn maybe-apply-token-to-shape
"When the passed `:token` is non-nil apply it to the `:applied-tokens` on a shape."
[{:keys [shape token _attributes] :as props}]
(if token
(apply-token-to-shape props)
shape))
(defn get-token-data-from-token-id
[id]
(let [workspace-data (deref refs/workspace-data)]
(get (:tokens workspace-data) id)))
(defn get-token-set-data-from-token-set-id
[id]
(let [workspace-data (deref refs/workspace-data)]
(get (:token-sets-index workspace-data) id)))
(defn set-selected-token-set-id
[id]
(ptk/reify ::set-selected-token-set-id
ptk/UpdateEvent
(update [_ state]
(wtts/assoc-selected-token-set-id state id))))
(defn get-token-set-tokens
[token-set file]
(map #(get-in file [:tokens %]) (:tokens token-set)))
(defn create-token-theme [token-theme]
(let [new-token-theme token-theme]
(ptk/reify ::create-token-theme
ptk/WatchEvent
(watch [it _ _]
(let [changes (-> (pcb/empty-changes it)
(pcb/add-token-theme new-token-theme))]
(rx/of
(dch/commit-changes changes)))))))
(defn update-token-theme [[group name] token-theme]
(ptk/reify ::update-token-theme
ptk/WatchEvent
(watch [it state _]
(let [tokens-lib (get-tokens-lib state)
prev-token-theme (some-> tokens-lib (ctob/get-theme group name))
changes (pcb/update-token-theme (pcb/empty-changes it) token-theme prev-token-theme)]
(rx/of
(dch/commit-changes changes))))))
(defn ensure-token-theme-changes [changes state {:keys [id new-set?]}]
(let [theme-id (wtts/update-theme-id state)
theme (some-> theme-id (wtts/get-workspace-token-theme state))]
(cond
(not theme-id) (-> changes
(pcb/add-temporary-token-theme
{:id (uuid/next)
:name "Test theme"
:sets #{id}}))
new-set? (-> changes
(pcb/update-token-theme
(wtts/add-token-set-to-token-theme id theme)
theme))
:else changes)))
(defn toggle-token-theme-active? [group name]
(ptk/reify ::toggle-token-theme-active?
ptk/WatchEvent
(watch [it state _]
(let [tokens-lib (get-tokens-lib state)
prev-active-token-themes (some-> tokens-lib
(ctob/get-active-theme-paths))
active-token-themes (some-> tokens-lib
(ctob/toggle-theme-active? group name)
(ctob/get-active-theme-paths))
changes (pcb/update-active-token-themes (pcb/empty-changes it) active-token-themes prev-active-token-themes)]
(rx/of
(dch/commit-changes changes))))))
(defn delete-token-theme [group name]
(ptk/reify ::delete-token-theme
ptk/WatchEvent
(watch [it state _]
(let [data (get state :workspace-data)
changes (-> (pcb/empty-changes it)
(pcb/with-library-data data)
(pcb/delete-token-theme group name))]
(rx/of
(dch/commit-changes changes)
(wtu/update-workspace-tokens))))))
(defn create-token-set [token-set]
(let [new-token-set (merge
{:name "Token Set"
:tokens []}
token-set)]
(ptk/reify ::create-token-set
ptk/WatchEvent
(watch [it state _]
(let [changes (-> (pcb/empty-changes it)
(pcb/add-token-set new-token-set)
#_(ensure-token-theme-changes state {:id (:id new-token-set)
:new-set? true}))]
(rx/of
(set-selected-token-set-id (:name new-token-set))
(dch/commit-changes changes)))))))
(defn update-token-set [set-name token-set]
(ptk/reify ::update-token-set
ptk/WatchEvent
(watch [it state _]
(let [prev-token-set (some-> (get-tokens-lib state)
(ctob/get-set set-name))
changes (-> (pcb/empty-changes it)
(pcb/update-token-set token-set prev-token-set))]
(rx/of
(dch/commit-changes changes))))))
#_[target-theme-id (wtts/get-temp-theme-id state)
active-set-ids (wtts/get-active-set-ids state)
theme (-> (wtts/get-workspace-token-theme target-theme-id state)
(assoc :sets active-set-ids))
changes (-> (pcb/empty-changes it)
(pcb/update-token-theme
(wtts/toggle-token-set-to-token-theme token-set-id theme)
theme)
(pcb/update-active-token-themes #{target-theme-id} (wtts/get-active-theme-ids state)))]
(comment
(-> (ctob/make-token-theme
:group ""
:name "bar")
(ctob/toggle-set "foo"))
nil)
(defn toggle-token-set [{:keys [token-set-name]}]
(ptk/reify ::toggle-token-set
ptk/WatchEvent
(watch [it state _]
(let [tokens-lib (get-tokens-lib state)
prev-theme (ctob/get-theme tokens-lib ctob/hidden-token-theme-group ctob/hidden-token-theme-name)
active-token-set-names (ctob/get-active-themes-set-names tokens-lib)
theme (-> (or (some-> prev-theme
(ctob/set-sets active-token-set-names))
(ctob/make-token-theme
:group ctob/hidden-token-theme-group
:name ctob/hidden-token-theme-name
:sets active-token-set-names))
(ctob/toggle-set token-set-name))
prev-active-token-themes (ctob/get-active-theme-paths tokens-lib)
changes (-> (pcb/empty-changes it)
(pcb/update-active-token-themes #{(ctob/token-theme-path ctob/hidden-token-theme-group ctob/hidden-token-theme-name)} prev-active-token-themes))
changes' (if prev-theme
(pcb/update-token-theme changes theme prev-theme)
(pcb/add-token-theme changes theme))]
(rx/of
(dch/commit-changes changes')
(wtu/update-workspace-tokens))))))
(defn delete-token-set [token-set-name]
(ptk/reify ::delete-token-set
ptk/WatchEvent
(watch [it state _]
(let [data (get state :workspace-data)
changes (-> (pcb/empty-changes it)
(pcb/with-library-data data)
(pcb/delete-token-set token-set-name))]
(rx/of
(dch/commit-changes changes)
(wtu/update-workspace-tokens))))))
(defn update-create-token
[token]
(let [token (update token :id #(or % (uuid/next)))]
(ptk/reify ::update-create-token
ptk/WatchEvent
(watch [it state _]
(let [token-set (wtts/get-selected-token-set state)
create-set? (not token-set)
token-set (or token-set
{:id (uuid/next)
:name "Global"
:tokens []})
changes (cond-> (pcb/empty-changes it)
create-set?
(pcb/add-token-set token-set))
prev-token-id (d/seek #(= % (:id token)) (:tokens token-set))
prev-token (get-token-data-from-token-id prev-token-id)
create-token? (not prev-token)
changes (if create-token?
(pcb/add-token changes (:id token-set) (:name token-set) token)
(pcb/update-token changes (:id token-set) (:name token-set) token prev-token))
changes (-> changes
(ensure-token-theme-changes state {:new-set? create-set?
:id (:id token-set)}))]
(rx/of
(set-selected-token-set-id (:name token-set))
(dch/commit-changes changes)))))))
(defn delete-token
[set-name id name]
(dm/assert! (string? set-name))
(dm/assert! (uuid? id))
(dm/assert! (string? name))
(ptk/reify ::delete-token
ptk/WatchEvent
(watch [it state _]
(let [data (get state :workspace-data)
changes (-> (pcb/empty-changes it)
(pcb/with-library-data data)
(pcb/delete-token set-name id name))]
(rx/of (dch/commit-changes changes))))))
(defn duplicate-token
[id]
(let [new-token (-> (get-token-data-from-token-id id)
(dissoc :id)
(update :name #(str/concat % "-copy")))]
(update-create-token new-token)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TEMP (Move to test)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(comment
(def shape-1 {:r3 3})
(def token-1 {:rx 1
:ry 1})
(def shape-after-token-1-is-applied {:rx 1
:ry 1
:r3 3})
(def token-2 {:r3 1})
(def shape-after-token-2-is-applied {:rx 1
:ry 1
:r3 1})
(def token-3 {:r3 1})
(def shape-after-token-3-is-applied {:rx 1
:ry 1})
(= (toggle-or-apply-token shape-1 token-1)
shape-after-token-1-is-applied)
(= (toggle-or-apply-token shape-after-token-1-is-applied token-2)
shape-after-token-2-is-applied)
(= (toggle-or-apply-token shape-after-token-2-is-applied token-3)
shape-after-token-3-is-applied)
nil)
(defn set-token-type-section-open
[token-type open?]
(ptk/reify ::set-token-type-section-open
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:workspace-tokens :open-status token-type] open?))))
;; Token Context Menu Functions -------------------------------------------------
(defn show-token-context-menu
[{:keys [position _token-id] :as params}]
(dm/assert! (gpt/point? position))
(ptk/reify ::show-token-context-menu
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:workspace-local :token-context-menu] params))))
(def hide-token-context-menu
(ptk/reify ::hide-token-context-menu
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:workspace-local :token-context-menu] nil))))
(defn show-token-set-context-menu
[{:keys [position _token-set-id] :as params}]
(dm/assert! (gpt/point? position))
(ptk/reify ::show-token-set-context-menu
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:workspace-local :token-set-context-menu] params))))
(def hide-token-set-context-menu
(ptk/reify ::hide-token-set-context-menu
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:workspace-local :token-set-context-menu] nil))))