0
Fork 0
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:
Florian Schroedl 2024-04-25 19:00:00 +02:00
parent d1a8427563
commit e5c5640413
19 changed files with 880 additions and 3 deletions

View file

@ -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

View file

@ -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))

View file

@ -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]]])

View file

@ -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"}

View file

@ -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

View 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))

View 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))

View 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)

View file

@ -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})

View file

@ -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]

View file

@ -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)

View 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}]])

View 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;
}
}

View 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}]}}]))

View 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"]]]]))

View 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;
}
}
}

View 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])

View 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]])

View 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);
}
}