mirror of
https://github.com/penpot/penpot.git
synced 2025-01-21 06:02:32 -05:00
Start tokens studio plugin base
This commit is contained in:
parent
d1a8427563
commit
e5c5640413
19 changed files with 880 additions and 3 deletions
|
@ -23,6 +23,8 @@
|
|||
[app.common.types.pages-list :as ctpl]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[app.common.types.token :as cto]
|
||||
[app.common.types.tokens-list :as ctol]
|
||||
[app.common.types.typographies-list :as ctyl]
|
||||
[app.common.types.typography :as ctt]
|
||||
[clojure.set :as set]))
|
||||
|
@ -232,6 +234,22 @@
|
|||
[:del-typography
|
||||
[:map {:title "DelTypogrphyChange"}
|
||||
[:type [:= :del-typography]]
|
||||
[:id ::sm/uuid]]]
|
||||
|
||||
[:add-token
|
||||
[:map {:title "AddTokenChange"}
|
||||
[:type [:= :add-token]]
|
||||
[:token ::cto/token]]]
|
||||
|
||||
[:mod-token
|
||||
[:map {:title "ModTokenChange"}
|
||||
[:type [:= :mod-token]]
|
||||
[:id ::sm/uuid]
|
||||
[:token ::cto/token]]]
|
||||
|
||||
[:del-token
|
||||
[:map {:title "DelTokenChange"}
|
||||
[:type [:= :del-token]]
|
||||
[:id ::sm/uuid]]]]])
|
||||
|
||||
(sm/define! ::changes
|
||||
|
@ -669,6 +687,20 @@
|
|||
[data {:keys [id]}]
|
||||
(ctyl/delete-typography data id))
|
||||
|
||||
;; -- Tokens
|
||||
|
||||
(defmethod process-change :add-token
|
||||
[data {:keys [token]}]
|
||||
(ctol/add-token data token))
|
||||
|
||||
(defmethod process-change :mod-token
|
||||
[data {:keys [id token]}]
|
||||
(ctol/update-token data id merge token))
|
||||
|
||||
(defmethod process-change :del-token
|
||||
[data {:keys [id]}]
|
||||
(ctol/delete-token data id))
|
||||
|
||||
;; === Operations
|
||||
|
||||
(defmethod process-operation :set
|
||||
|
|
|
@ -664,6 +664,30 @@
|
|||
(update :undo-changes conj {:type :add-typography :typography prev-typography})
|
||||
(apply-changes-local))))
|
||||
|
||||
(defn add-token
|
||||
[changes token]
|
||||
(-> changes
|
||||
(update :redo-changes conj {:type :add-token :token token})
|
||||
(update :undo-changes conj {:type :del-token :id (:id token)})
|
||||
(apply-changes-local)))
|
||||
|
||||
(defn update-token
|
||||
[changes token]
|
||||
(let [token-id (:id token)]
|
||||
(-> changes
|
||||
(update :redo-changes conj {:type :mod-token :id token-id :token token})
|
||||
(apply-changes-local))))
|
||||
|
||||
(defn delete-token
|
||||
[changes token-id]
|
||||
(assert-library! changes)
|
||||
(let [library-data (::library-data (meta changes))
|
||||
prev-token (get-in library-data [:tokens token-id])]
|
||||
(-> changes
|
||||
(update :redo-changes conj {:type :del-token :id token-id})
|
||||
(update :undo-changes conj {:type :add-token :token prev-token})
|
||||
(apply-changes-local))))
|
||||
|
||||
(defn add-component
|
||||
([changes id path name new-shapes updated-shapes main-instance-id main-instance-page]
|
||||
(add-component changes id path name new-shapes updated-shapes main-instance-id main-instance-page nil))
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
[app.common.types.page :as ctp]
|
||||
[app.common.types.pages-list :as ctpl]
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[app.common.types.token :as cto]
|
||||
[app.common.types.typographies-list :as ctyl]
|
||||
[app.common.types.typography :as cty]
|
||||
[app.common.uuid :as uuid]
|
||||
|
@ -55,6 +56,8 @@
|
|||
[:vector {:gen/max 3} ::ctc/recent-color]]
|
||||
[:typographies {:optional true}
|
||||
[:map-of {:gen/max 2} ::sm/uuid ::cty/typography]]
|
||||
[:tokens {:optional true}
|
||||
[:map-of {:gen/max 100} ::sm/uuid ::cto/token]]
|
||||
[:media {:optional true}
|
||||
[:map-of {:gen/max 5} ::sm/uuid ::media-object]]])
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
[app.common.types.shape.path :as ctsp]
|
||||
[app.common.types.shape.shadow :as ctss]
|
||||
[app.common.types.shape.text :as ctsx]
|
||||
[app.common.types.token :as cto]
|
||||
[app.common.uuid :as uuid]
|
||||
[clojure.set :as set]))
|
||||
|
||||
|
@ -180,7 +181,8 @@
|
|||
[:vector {:gen/max 1} ::ctss/shadow]]
|
||||
[:blur {:optional true} ::ctsb/blur]
|
||||
[:grow-type {:optional true}
|
||||
[::sm/one-of #{:auto-width :auto-height :fixed}]]])
|
||||
[::sm/one-of #{:auto-width :auto-height :fixed}]]
|
||||
[:applied-tokens {:optional true} ::cto/applied-tokens]])
|
||||
|
||||
(sm/define! ::group-attrs
|
||||
[:map {:title "GroupAttrs"}
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
:show-content
|
||||
:hide-in-viewer
|
||||
|
||||
:applied-tokens
|
||||
|
||||
:opacity
|
||||
:blend-mode
|
||||
:blocked
|
||||
|
@ -95,6 +97,8 @@
|
|||
:parent-id
|
||||
:frame-id
|
||||
|
||||
:applied-tokens
|
||||
|
||||
:opacity
|
||||
:blend-mode
|
||||
:blocked
|
||||
|
|
80
common/src/app/common/types/token.cljc
Normal file
80
common/src/app/common/types/token.cljc
Normal file
|
@ -0,0 +1,80 @@
|
|||
;; 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.token
|
||||
(:require
|
||||
[app.common.schema :as sm]
|
||||
[app.common.schema.registry :as sr]))
|
||||
|
||||
(defn merge-schemas [& schema-keys]
|
||||
(let [schemas (map #(get @sr/registry %) schema-keys)]
|
||||
(reduce sm/merge schemas)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; SCHEMA
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def token-types
|
||||
#{:boolean
|
||||
:border-radius
|
||||
:box-shadow
|
||||
:dimension
|
||||
:numeric
|
||||
:opacity
|
||||
:other
|
||||
:rotation
|
||||
:sizing
|
||||
:spacing
|
||||
:string
|
||||
:typography})
|
||||
|
||||
(sm/def! ::token
|
||||
[:map {:title "Token"}
|
||||
[:id ::sm/uuid]
|
||||
[:name :string]
|
||||
[:type [::sm/one-of token-types]]
|
||||
[:value :any]
|
||||
[:description {:optional true} :string]
|
||||
[:modified-at {:optional true} ::sm/inst]])
|
||||
|
||||
(sm/def! ::border-radius
|
||||
[:map
|
||||
[:rx {:optional true} ::sm/uuid]
|
||||
[:ry {:optional true} ::sm/uuid]
|
||||
[:r1 {:optional true} ::sm/uuid]
|
||||
[:r2 {:optional true} ::sm/uuid]
|
||||
[:r3 {:optional true} ::sm/uuid]
|
||||
[:r4 {:optional true} ::sm/uuid]])
|
||||
|
||||
(sm/def! ::dimensions
|
||||
[:map
|
||||
[:width {:optional true} ::sm/uuid]
|
||||
[:height {:optional true} ::sm/uuid]
|
||||
[:min-height {:optional true} ::sm/uuid]
|
||||
[:max-height {:optional true} ::sm/uuid]
|
||||
[:min-width {:optional true} ::sm/uuid]
|
||||
[:max-width {:optional true} ::sm/uuid]])
|
||||
|
||||
(sm/def! ::spacing
|
||||
[:map
|
||||
[:spacing-column {:optional true} ::sm/uuid]
|
||||
[:spacing-row {:optional true} ::sm/uuid]
|
||||
[:padding-p1 {:optional true} ::sm/uuid]
|
||||
[:padding-p2 {:optional true} ::sm/uuid]
|
||||
[:padding-p3 {:optional true} ::sm/uuid]
|
||||
[:padding-p4 {:optional true} ::sm/uuid]
|
||||
[:padding-all {:optional true} ::sm/uuid]
|
||||
[:position-x {:optional true} ::sm/uuid]
|
||||
[:position-y {:optional true} ::sm/uuid]])
|
||||
|
||||
(sm/def! ::tokens
|
||||
[:map {:title "Applied Tokens"}])
|
||||
|
||||
(sm/def! ::applied-tokens
|
||||
(merge-schemas ::tokens
|
||||
::border-radius
|
||||
::dimensions
|
||||
::spacing))
|
45
common/src/app/common/types/tokens_list.cljc
Normal file
45
common/src/app/common/types/tokens_list.cljc
Normal file
|
@ -0,0 +1,45 @@
|
|||
;; 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.tokens-list
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.time :as dt]))
|
||||
|
||||
(defn tokens-seq
|
||||
"Returns a sequence of all tokens within the file data."
|
||||
[file-data]
|
||||
(vals (:tokens file-data)))
|
||||
|
||||
(defn- touch
|
||||
"Updates the `modified-at` timestamp of a token."
|
||||
[token]
|
||||
(assoc token :modified-at (dt/now)))
|
||||
|
||||
(defn add-token
|
||||
"Adds a new token to the file data, setting its `modified-at` timestamp."
|
||||
[file-data token]
|
||||
(update file-data :tokens assoc (:id token) (touch token)))
|
||||
|
||||
(defn get-token
|
||||
"Retrieves a token by its ID from the file data."
|
||||
[file-data token-id]
|
||||
(get-in file-data [:tokens token-id]))
|
||||
|
||||
(defn set-token
|
||||
"Sets or updates a token in the file data, updating its `modified-at` timestamp."
|
||||
[file-data token]
|
||||
(d/assoc-in-when file-data [:tokens (:id token)] (touch token)))
|
||||
|
||||
(defn update-token
|
||||
"Applies a function to update a token in the file data, then touches it."
|
||||
[file-data token-id f & args]
|
||||
(d/update-in-when file-data [:tokens token-id] #(-> (apply f % args) (touch))))
|
||||
|
||||
(defn delete-token
|
||||
"Removes a token from the file data by its ID."
|
||||
[file-data token-id]
|
||||
(update file-data :tokens dissoc token-id))
|
110
frontend/src/app/main/data/tokens.cljs
Normal file
110
frontend/src/app/main/data/tokens.cljs
Normal file
|
@ -0,0 +1,110 @@
|
|||
;; 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.macros :as dm]
|
||||
[app.common.files.changes-builder :as pcb]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.ui.workspace.tokens.common :refer [workspace-shapes]]
|
||||
[beicon.v2.core :as rx]
|
||||
[clojure.data :as data]
|
||||
[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 (dch/update-shapes [id] #(merge % attrs))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; 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 get-shape-from-state [shape-id state]
|
||||
(let [current-page-id (get state :current-page-id)
|
||||
shape (-> (workspace-shapes (:workspace-data state) current-page-id #{shape-id})
|
||||
(first))]
|
||||
shape))
|
||||
|
||||
(defn token-from-attributes [token-id attributes]
|
||||
(->> (map (fn [attr] [attr token-id]) attributes)
|
||||
(into {})))
|
||||
|
||||
(defn update-token-from-attributes
|
||||
[{:keys [token-id shape-id attributes]}]
|
||||
(ptk/reify ::update-token-from-attributes
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [shape (get-shape-from-state shape-id state)
|
||||
token (token-from-attributes token-id attributes)
|
||||
next-applied-tokens (toggle-or-apply-token shape token)]
|
||||
(rx/of (update-shape shape-id {:applied-tokens next-applied-tokens}))))))
|
||||
|
||||
(defn add-token
|
||||
[token]
|
||||
(let [token (update token :id #(or % (uuid/next)))]
|
||||
(ptk/reify ::add-token
|
||||
ptk/WatchEvent
|
||||
(watch [it _ _]
|
||||
(let [changes (-> (pcb/empty-changes it)
|
||||
(pcb/add-token token))]
|
||||
(rx/of (dch/commit-changes changes)))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; 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)
|
|
@ -43,7 +43,11 @@
|
|||
|
||||
:layers
|
||||
{:del #{:document-history :assets}
|
||||
:add #{:sitemap :layers}}})
|
||||
:add #{:sitemap :layers}}
|
||||
|
||||
:tokens
|
||||
{:del #{:sitemap :layers :document-history :assets}
|
||||
:add #{:tokens}}})
|
||||
|
||||
(def valid-options-mode
|
||||
#{:design :prototype :inspect})
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
[app.main.ui.workspace.sidebar :refer [left-sidebar right-sidebar]]
|
||||
[app.main.ui.workspace.sidebar.collapsable-button :refer [collapsed-button]]
|
||||
[app.main.ui.workspace.sidebar.history :refer [history-toolbox]]
|
||||
[app.main.ui.workspace.tokens.modals]
|
||||
[app.main.ui.workspace.viewport :refer [viewport]]
|
||||
[app.util.debug :as dbg]
|
||||
[app.util.dom :as dom]
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
[app.main.ui.workspace.sidebar.options :refer [options-toolbox]]
|
||||
[app.main.ui.workspace.sidebar.shortcuts :refer [shortcuts-container]]
|
||||
[app.main.ui.workspace.sidebar.sitemap :refer [sitemap]]
|
||||
[app.main.ui.workspace.tokens.sidebar :refer [tokens-sidebar-tab]]
|
||||
[app.util.debug :as dbg]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[rumext.v2 :as mf]))
|
||||
|
@ -42,6 +43,7 @@
|
|||
toggle-pages (mf/use-callback #(reset! show-pages? not))
|
||||
|
||||
section (cond (or mode-inspect? (contains? layout :layers)) :layers
|
||||
(contains? layout :tokens) :tokens
|
||||
(contains? layout :assets) :assets)
|
||||
|
||||
shortcuts? (contains? layout :shortcuts)
|
||||
|
@ -115,7 +117,11 @@
|
|||
(when-not ^boolean mode-inspect?
|
||||
[:& tab-element {:id :assets
|
||||
:title (tr "workspace.toolbar.assets")}
|
||||
[:& assets-toolbox {:size (- size 58)}]])]])]]))
|
||||
[:& assets-toolbox {:size (- size 58)}]])
|
||||
|
||||
[:& tab-element {:id :tokens
|
||||
:title "Tokens"}
|
||||
[:& tokens-sidebar-tab]]]])]]))
|
||||
|
||||
;; --- Right Sidebar (Component)
|
||||
|
||||
|
|
41
frontend/src/app/main/ui/workspace/tokens/common.cljs
Normal file
41
frontend/src/app/main/ui/workspace/tokens/common.cljs
Normal file
|
@ -0,0 +1,41 @@
|
|||
;; 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.ui.workspace.tokens.common
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
;; Helpers ---------------------------------------------------------------------
|
||||
|
||||
(defn workspace-shapes [workspace page-id shape-ids]
|
||||
(-> (get-in workspace [:pages-index page-id :objects])
|
||||
(keep shape-ids)))
|
||||
|
||||
(defn vec-remove
|
||||
"remove elem in coll"
|
||||
[pos coll]
|
||||
(into (subvec coll 0 pos) (subvec coll (inc pos))))
|
||||
|
||||
;; Components ------------------------------------------------------------------
|
||||
|
||||
(mf/defc input
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [type placeholder]
|
||||
:or {type "text"}}]
|
||||
[:input {:type type
|
||||
:class (stl/css :input)
|
||||
:placeholder placeholder}])
|
||||
|
||||
(mf/defc labeled-input
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [input-ref label default-value on-change auto-focus?]}]
|
||||
[:label {:class (stl/css :labeled-input)}
|
||||
[:span {:class (stl/css :label)} label]
|
||||
[:input {:ref input-ref
|
||||
:default-value default-value
|
||||
:autoFocus auto-focus?
|
||||
:on-change on-change}]])
|
32
frontend/src/app/main/ui/workspace/tokens/common.scss
Normal file
32
frontend/src/app/main/ui/workspace/tokens/common.scss
Normal file
|
@ -0,0 +1,32 @@
|
|||
// 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
|
||||
|
||||
@import "refactor/common-refactor.scss";
|
||||
|
||||
.input {
|
||||
@extend .input-element;
|
||||
}
|
||||
|
||||
.labeled-input {
|
||||
@extend .input-element;
|
||||
.label {
|
||||
width: auto;
|
||||
text-wrap: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
@extend .button-primary;
|
||||
}
|
||||
|
||||
.action-button {
|
||||
@extend .button-tertiary;
|
||||
height: $s-32;
|
||||
width: $s-28;
|
||||
svg {
|
||||
@extend .button-icon;
|
||||
}
|
||||
}
|
112
frontend/src/app/main/ui/workspace/tokens/core.cljs
Normal file
112
frontend/src/app/main/ui/workspace/tokens/core.cljs
Normal file
|
@ -0,0 +1,112 @@
|
|||
;; 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.ui.workspace.tokens.core
|
||||
(:require
|
||||
[app.common.data :as d :refer [ordered-map]]
|
||||
[app.common.types.shape.radius :as ctsr]
|
||||
[app.main.data.tokens :as dt]
|
||||
[app.main.data.workspace.changes :as dch]))
|
||||
|
||||
;; Helpers ---------------------------------------------------------------------
|
||||
|
||||
(defn token-applied?
|
||||
"Test if `token` is applied to a `shape` with the given `token-attributes`."
|
||||
[token shape token-attributes]
|
||||
(let [{:keys [id]} token
|
||||
applied-tokens (get shape :applied-tokens {})]
|
||||
(some (fn [attr]
|
||||
(= (get applied-tokens attr) id))
|
||||
token-attributes)))
|
||||
|
||||
(defn tokens-applied?
|
||||
"Test if `token` is applied to to any of `shapes` with the given `token-attributes`."
|
||||
[token shapes token-attributes]
|
||||
(some #(token-applied? token % token-attributes) shapes))
|
||||
|
||||
;; Update functions ------------------------------------------------------------
|
||||
|
||||
(defn update-shape-radius [value shape-ids]
|
||||
(let [parsed-value (d/parse-integer value)]
|
||||
(dch/update-shapes shape-ids
|
||||
(fn [shape]
|
||||
(when (ctsr/has-radius? shape)
|
||||
(ctsr/set-radius-1 shape parsed-value)))
|
||||
{:reg-objects? true
|
||||
:attrs [:rx :ry :r1 :r2 :r3 :r4]})))
|
||||
|
||||
;; Token types -----------------------------------------------------------------
|
||||
|
||||
(def token-types
|
||||
(ordered-map
|
||||
[:boolean {:title "Boolean"
|
||||
:modal {:key :tokens/boolean
|
||||
:fields [{:label "Boolean"}]}}]
|
||||
[:border-radius {:title "Border Radius"
|
||||
:attributes #{:rx :ry :r1 :r2 :r3 :r4}
|
||||
:on-apply dt/update-token-from-attributes
|
||||
:modal {:key :tokens/border-radius
|
||||
:fields [{:label "Border Radius"
|
||||
:key :border-radius}]}
|
||||
:on-update-shape update-shape-radius}]
|
||||
[:box-shadow
|
||||
{:title "Box Shadow"
|
||||
:modal {:key :tokens/box-shadow
|
||||
:fields [{:label "Box shadows"
|
||||
:key :box-shadow
|
||||
:type :box-shadow}]}}]
|
||||
[:sizing
|
||||
{:title "Sizing"
|
||||
:modal {:key :tokens/sizing
|
||||
:fields [{:label "Sizing"
|
||||
:key :sizing}]}}]
|
||||
[:dimension
|
||||
{:title "Dimension"
|
||||
:modal {:key :tokens/dimensions
|
||||
:fields [{:label "Dimensions"
|
||||
:key :dimensions}]}}]
|
||||
[:numeric
|
||||
{:title "Numeric"
|
||||
:modal {:key :tokens/numeric
|
||||
:fields [{:label "Numeric"
|
||||
:key :numeric}]}}]
|
||||
[:opacity
|
||||
{:title "Opacity"
|
||||
:modal {:key :tokens/opacity
|
||||
:fields [{:label "Opacity"
|
||||
:key :opacity}]}}]
|
||||
[:other
|
||||
{:title "Other"
|
||||
:modal {:key :tokens/other
|
||||
:fields [{:label "Other"
|
||||
:key :other}]}}]
|
||||
[:rotation
|
||||
{:title "Rotation"
|
||||
:modal {:key :tokens/rotation
|
||||
:fields [{:label "Rotation"
|
||||
:key :rotation}]}}]
|
||||
[:spacing
|
||||
{:title "Spacing"
|
||||
:modal {:key :tokens/spacing
|
||||
:fields [{:label "Spacing"
|
||||
:key :spacing}]}}]
|
||||
[:string
|
||||
{:title "String"
|
||||
:modal {:key :tokens/string
|
||||
:fields [{:label "String"
|
||||
:key :string}]}}]
|
||||
[:typography
|
||||
{:title "Typography"
|
||||
:modal {:key :tokens/typography
|
||||
:fields [{:label "Font" :key :font-family}
|
||||
{:label "Weight" :key :weight}
|
||||
{:label "Font Size" :key :font-size}
|
||||
{:label "Line Height" :key :line-height}
|
||||
{:label "Letter Spacing" :key :letter-spacing}
|
||||
{:label "Paragraph Spacing" :key :paragraph-spacing}
|
||||
{:label "Paragraph Indent" :key :paragraph-indent}
|
||||
{:label "Text Decoration" :key :text-decoration}
|
||||
{:label "Text Case" :key :text-case}]}}]))
|
96
frontend/src/app/main/ui/workspace/tokens/modal.cljs
Normal file
96
frontend/src/app/main/ui/workspace/tokens/modal.cljs
Normal file
|
@ -0,0 +1,96 @@
|
|||
;; 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.ui.workspace.tokens.modal
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.main.data.tokens :as dt]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.workspace.tokens.common :as tokens.common]
|
||||
[app.util.dom :as dom]
|
||||
[okulary.core :as l]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(defn calculate-position
|
||||
"Calculates the style properties for the given coordinates and position"
|
||||
[{vh :height} position x y]
|
||||
(let [;; picker height in pixels
|
||||
h 510
|
||||
;; Checks for overflow outside the viewport height
|
||||
overflow-fix (max 0 (+ y (- 50) h (- vh)))
|
||||
|
||||
x-pos 325]
|
||||
(cond
|
||||
(or (nil? x) (nil? y)) {:left "auto" :right "16rem" :top "4rem"}
|
||||
(= position :left) {:left (str (- x x-pos) "px")
|
||||
:top (str (- y 50 overflow-fix) "px")}
|
||||
:else {:left (str (+ x 80) "px")
|
||||
:top (str (- y 70 overflow-fix) "px")})))
|
||||
|
||||
(def viewport
|
||||
(l/derived :vport refs/workspace-local))
|
||||
|
||||
(defn fields->map [fields]
|
||||
(->> (map (fn [{:keys [key] :as field}]
|
||||
[key (:value field)]) fields)
|
||||
(into {})))
|
||||
|
||||
(mf/defc tokens-properties-form
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [token-type x y position fields]}]
|
||||
(let [vport (mf/deref viewport)
|
||||
style (calculate-position vport position x y)
|
||||
|
||||
name (mf/use-var nil)
|
||||
on-update-name #(reset! name (dom/get-target-val %))
|
||||
name-ref (mf/use-ref)
|
||||
|
||||
description (mf/use-var nil)
|
||||
on-update-description #(reset! description (dom/get-target-val %))
|
||||
|
||||
state (mf/use-state fields)
|
||||
on-update-state-field (fn [idx e]
|
||||
(->> (dom/get-target-val e)
|
||||
(assoc-in @state [idx :value])
|
||||
(reset! state)))
|
||||
|
||||
on-submit (fn [e]
|
||||
(dom/prevent-default e)
|
||||
(let [token-value (-> (fields->map @state)
|
||||
(first)
|
||||
(val))
|
||||
token (cond-> {:name @name
|
||||
:type token-type
|
||||
:value token-value}
|
||||
@description (assoc :description @description))]
|
||||
(st/emit! (dt/add-token token))))]
|
||||
|
||||
(mf/use-effect
|
||||
(fn []
|
||||
(dom/focus! (mf/ref-val name-ref))))
|
||||
|
||||
[:form
|
||||
{:class (stl/css :shadow)
|
||||
:style (clj->js style)
|
||||
:on-submit on-submit}
|
||||
[:div {:class (stl/css :token-rows)}
|
||||
[:& tokens.common/labeled-input {:label "Name"
|
||||
:on-change on-update-name
|
||||
:input-ref name-ref}]
|
||||
(for [[idx {:keys [type label]}] (d/enumerate @state)]
|
||||
[:* {:key (str "form-field-" idx)}
|
||||
(case type
|
||||
:box-shadow [:p "TODO BOX SHADOW"]
|
||||
[:& tokens.common/labeled-input {:label label
|
||||
:on-change #(on-update-state-field idx %)}])])
|
||||
[:& tokens.common/labeled-input {:label "Description"
|
||||
:on-change #(on-update-description %)}]
|
||||
[:div {:class (stl/css :button-row)}
|
||||
[:button {:class (stl/css :button)
|
||||
:type "submit"}
|
||||
"Save"]]]]))
|
66
frontend/src/app/main/ui/workspace/tokens/modal.scss
Normal file
66
frontend/src/app/main/ui/workspace/tokens/modal.scss
Normal file
|
@ -0,0 +1,66 @@
|
|||
// 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
|
||||
|
||||
@import "refactor/common-refactor.scss";
|
||||
@import "./common.scss";
|
||||
|
||||
.button-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: $s-16;
|
||||
}
|
||||
|
||||
.token-rows {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $s-8;
|
||||
}
|
||||
|
||||
.shadow {
|
||||
@extend .modal-container-base;
|
||||
@include menuShadow;
|
||||
position: absolute;
|
||||
z-index: 11;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
&-select-wrapper {
|
||||
display: flex;
|
||||
grid-gap: $s-4;
|
||||
}
|
||||
|
||||
&-properties {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
grid-gap: $s-4;
|
||||
}
|
||||
|
||||
.inputs-grid {
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
"x blur blur spread spread"
|
||||
"y color color color color";
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
grid-template-rows: repeat(2, 1fr);
|
||||
grid-gap: $s-4;
|
||||
|
||||
label:nth-child(1) {
|
||||
grid-area: x;
|
||||
}
|
||||
label:nth-child(2) {
|
||||
grid-area: y;
|
||||
}
|
||||
label:nth-child(3) {
|
||||
grid-area: blur;
|
||||
}
|
||||
label:nth-child(4) {
|
||||
grid-area: spread;
|
||||
}
|
||||
label:nth-child(5) {
|
||||
grid-area: color;
|
||||
}
|
||||
}
|
||||
}
|
83
frontend/src/app/main/ui/workspace/tokens/modals.cljs
Normal file
83
frontend/src/app/main/ui/workspace/tokens/modals.cljs
Normal file
|
@ -0,0 +1,83 @@
|
|||
;; 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.ui.workspace.tokens.modals
|
||||
(:require
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.ui.workspace.tokens.modal :refer [tokens-properties-form]]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc boolean-modal
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :tokens/boolean}
|
||||
[properties]
|
||||
[:& tokens-properties-form properties])
|
||||
|
||||
(mf/defc border-radius-modal
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :tokens/border-radius}
|
||||
[properties]
|
||||
[:& tokens-properties-form properties])
|
||||
|
||||
(mf/defc box-shadow-modal
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :tokens/box-shadow}
|
||||
[properties]
|
||||
[:& tokens-properties-form properties])
|
||||
|
||||
(mf/defc sizing-modal
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :tokens/sizing}
|
||||
[properties]
|
||||
[:& tokens-properties-form properties])
|
||||
|
||||
(mf/defc dimensions-modal
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :tokens/dimensions}
|
||||
[properties]
|
||||
[:& tokens-properties-form properties])
|
||||
|
||||
(mf/defc numeric-modal
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :tokens/numeric}
|
||||
[properties]
|
||||
[:& tokens-properties-form properties])
|
||||
|
||||
(mf/defc opacity-modal
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :tokens/opacity}
|
||||
[properties]
|
||||
[:& tokens-properties-form properties])
|
||||
|
||||
(mf/defc other-modal
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :tokens/other}
|
||||
[properties]
|
||||
[:& tokens-properties-form properties])
|
||||
|
||||
(mf/defc rotation-modal
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :tokens/rotation}
|
||||
[properties]
|
||||
[:& tokens-properties-form properties])
|
||||
|
||||
(mf/defc spacing-modal
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :tokens/spacing}
|
||||
[properties]
|
||||
[:& tokens-properties-form properties])
|
||||
|
||||
(mf/defc string-modal
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :tokens/string}
|
||||
[properties]
|
||||
[:& tokens-properties-form properties])
|
||||
|
||||
(mf/defc typography-modal
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :tokens/typography}
|
||||
[properties]
|
||||
[:& tokens-properties-form properties])
|
106
frontend/src/app/main/ui/workspace/tokens/sidebar.cljs
Normal file
106
frontend/src/app/main/ui/workspace/tokens/sidebar.cljs
Normal file
|
@ -0,0 +1,106 @@
|
|||
;; 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.ui.workspace.tokens.sidebar
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.search-bar :refer [search-bar]]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.workspace.sidebar.assets.common :as cmm]
|
||||
[app.main.ui.workspace.tokens.common :refer [workspace-shapes]]
|
||||
[app.main.ui.workspace.tokens.core :refer [token-types tokens-applied?]]
|
||||
[app.util.dom :as dom]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc token-pill
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [on-click token highlighted?]}]
|
||||
(let [{:keys [name value]} token]
|
||||
[:div {:class (stl/css-case :token-pill true
|
||||
:token-pill-highlighted highlighted?)
|
||||
:title (str "Token value: " value)
|
||||
:on-click on-click}
|
||||
name]))
|
||||
|
||||
(defn- on-apply-token [token attributes selected-shapes on-apply on-update-shape event]
|
||||
(let [shapes-to-apply-token (filter #(not (tokens-applied? token % attributes)) selected-shapes)
|
||||
shapes-to-apply-token-ids (map #(:id %) shapes-to-apply-token)]
|
||||
(dom/stop-propagation event)
|
||||
(doseq [shape selected-shapes]
|
||||
(st/emit! (on-apply {:token-id (:id token)
|
||||
:shape-id (:id shape)
|
||||
:attributes attributes}))
|
||||
(st/emit! (on-update-shape (:value token) shapes-to-apply-token-ids)))))
|
||||
|
||||
(mf/defc token-component
|
||||
[{:keys [type file tokens selected-shapes token-type-props]}]
|
||||
(let [open? (mf/use-state false)
|
||||
{:keys [modal attributes title on-apply on-update-shape]} token-type-props
|
||||
on-toggle-open-click (mf/use-fn
|
||||
(mf/deps open? tokens)
|
||||
#(when (seq tokens)
|
||||
(swap! open? not)))
|
||||
on-popover-open-click (mf/use-fn
|
||||
(fn [event]
|
||||
(let [{:keys [key fields]} modal]
|
||||
(dom/stop-propagation event)
|
||||
(modal/show! key {:x (.-clientX ^js event)
|
||||
:y (.-clientY ^js event)
|
||||
:position :right
|
||||
:fields fields
|
||||
:token-type type}))))
|
||||
tokens-count (count tokens)]
|
||||
[:div {:on-click on-toggle-open-click}
|
||||
[:& cmm/asset-section {:file-id (:id file)
|
||||
:title title
|
||||
:assets-count tokens-count
|
||||
:open? @open?}
|
||||
[:& cmm/asset-section-block {:role :title-button}
|
||||
[:button {:class (stl/css :action-button)
|
||||
:on-click on-popover-open-click}
|
||||
i/add]]
|
||||
(when open?
|
||||
[:& cmm/asset-section-block {:role :content}
|
||||
[:div {:class (stl/css :token-pills-wrapper)}
|
||||
(for [token tokens]
|
||||
[:& token-pill {:key (:id token)
|
||||
:token token
|
||||
:highlighted? (tokens-applied? token selected-shapes attributes)
|
||||
:on-click #(on-apply-token token attributes selected-shapes on-apply on-update-shape %1)}])]])]]))
|
||||
|
||||
(mf/defc tokens-explorer
|
||||
[_props]
|
||||
(let [file (mf/deref refs/workspace-file)
|
||||
current-page-id (:current-page-id @st/state)
|
||||
workspace-data (mf/deref refs/workspace-data)
|
||||
tokens (get workspace-data :tokens)
|
||||
tokens-by-group (->> (vals tokens)
|
||||
(group-by :type))
|
||||
selected-shape-ids (mf/deref refs/selected-shapes)
|
||||
selected-shapes (workspace-shapes workspace-data current-page-id selected-shape-ids)]
|
||||
(js/console.log "tokens" tokens)
|
||||
[:article
|
||||
[:& search-bar {:placeholder "Filter"
|
||||
:on-change js/console.log}]
|
||||
[:div.assets-bar
|
||||
(for [[token-key token-type-props] token-types
|
||||
:let [tokens (or (get tokens-by-group token-key) [])]]
|
||||
[:& token-component {:key token-key
|
||||
:type token-key
|
||||
:file file
|
||||
:selected-shapes selected-shapes
|
||||
:tokens tokens
|
||||
:token-type-props token-type-props}])]]))
|
||||
|
||||
(mf/defc tokens-sidebar-tab
|
||||
{::mf/wrap [mf/memo]
|
||||
::mf/wrap-props false}
|
||||
[_props]
|
||||
[:div {:class (stl/css :sidebar-tab-wrapper)}
|
||||
[:& tokens-explorer]])
|
30
frontend/src/app/main/ui/workspace/tokens/sidebar.scss
Normal file
30
frontend/src/app/main/ui/workspace/tokens/sidebar.scss
Normal file
|
@ -0,0 +1,30 @@
|
|||
// 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
|
||||
|
||||
@import "refactor/common-refactor.scss";
|
||||
|
||||
@import "./common.scss";
|
||||
|
||||
.sidebar-tab-wrapper {
|
||||
padding: $s-12;
|
||||
}
|
||||
|
||||
.token-pills-wrapper {
|
||||
display: flex;
|
||||
gap: $s-4;
|
||||
}
|
||||
|
||||
.token-pill {
|
||||
@extend .button-secondary;
|
||||
padding: $s-4 $s-8;
|
||||
border-radius: $br-6;
|
||||
font-size: $fs-14;
|
||||
|
||||
&.token-pill-highlighted {
|
||||
color: var(--button-primary-foreground-color-rest);
|
||||
background: var(--button-primary-background-color-rest);
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue