Merge remote-tracking branch 'origin/staging' into develop

This commit is contained in:
Andrey Antukh 2025-03-20 14:27:37 +01:00
commit 22cd43b8a2
33 changed files with 634 additions and 655 deletions

View file

@ -12,6 +12,7 @@
### :bug: Bugs fixed ### :bug: Bugs fixed
## 2.6.0 (Unreleased) ## 2.6.0 (Unreleased)
### :rocket: Epics and highlights ### :rocket: Epics and highlights
@ -35,6 +36,9 @@
- Fix duplicate page with component over frame [Taiga #8151](https://tree.taiga.io/project/penpot/issue/8151) and [Taiga #9698](https://tree.taiga.io/project/penpot/issue/9698) - Fix duplicate page with component over frame [Taiga #8151](https://tree.taiga.io/project/penpot/issue/8151) and [Taiga #9698](https://tree.taiga.io/project/penpot/issue/9698)
- The plugin list in the navigation menu lacks scrolling, some plugins are not visible when a large number are installed [Taiga #9360](https://tree.taiga.io/project/penpot/us/9360) - The plugin list in the navigation menu lacks scrolling, some plugins are not visible when a large number are installed [Taiga #9360](https://tree.taiga.io/project/penpot/us/9360)
- Fix hidden toolbar click event still available [Taiga #10437](https://tree.taiga.io/project/penpot/us/10437) - Fix hidden toolbar click event still available [Taiga #10437](https://tree.taiga.io/project/penpot/us/10437)
- Fix hovering over templates [Taiga #10545](https://tree.taiga.io/project/penpot/issue/10545)
- Fix problem with default shadows value in plugins [Plugins #191](https://github.com/penpot/penpot-plugins/issues/191)
- Fix problem with constraints when creating group [Taiga #10455](https://tree.taiga.io/project/penpot/issue/10455)
## 2.5.4 ## 2.5.4

View file

@ -10,6 +10,7 @@
(:require (:require
[app.binfile.common :as bfc] [app.binfile.common :as bfc]
[app.common.data :as d] [app.common.data :as d]
[app.common.files.migrations :as fmg]
[app.common.files.validate :as cfv] [app.common.files.validate :as cfv]
[app.db :as db] [app.db :as db]
[app.features.components-v2 :as feat.comp-v2] [app.features.components-v2 :as feat.comp-v2]
@ -142,7 +143,9 @@
(update-fn file opts)))] (update-fn file opts)))]
(when (and (some? file') (when (and (some? file')
(not (identical? file file'))) (or (fmg/migrated? file)
(not (identical? file file'))))
(when validate? (when validate?
(cfv/validate-file-schema! file')) (cfv/validate-file-schema! file'))

View file

@ -61,7 +61,8 @@
"styles/v2" "styles/v2"
"layout/grid" "layout/grid"
"components/v2" "components/v2"
"plugins/runtime"}) "plugins/runtime"
"design-tokens/v1"})
;; A set of features which only affects on frontend and can be enabled ;; A set of features which only affects on frontend and can be enabled
;; and disabled freely by the user any time. This features does not ;; and disabled freely by the user any time. This features does not

View file

@ -382,13 +382,13 @@
[:set-group-path [:vector :string]] [:set-group-path [:vector :string]]
[:set-group-fname :string]]] [:set-group-fname :string]]]
[:move-token-set-before [:move-token-set
[:map {:title "MoveTokenSetBefore"} [:map {:title "MoveTokenSet"}
[:type [:= :move-token-set-before]] [:type [:= :move-token-set]]
[:from-path [:vector :string]] [:from-path [:vector :string]]
[:to-path [:vector :string]] [:to-path [:vector :string]]
[:before-path [:maybe [:vector :string]]] [:before-path [:maybe [:vector :string]]]
[:before-group? [:maybe :boolean]]]] [:before-group [:maybe :boolean]]]]
[:move-token-set-group-before [:move-token-set-group-before
[:map {:title "MoveTokenSetGroupBefore"} [:map {:title "MoveTokenSetGroupBefore"}
@ -1051,11 +1051,11 @@
(ctob/ensure-tokens-lib) (ctob/ensure-tokens-lib)
(ctob/rename-set-group set-group-path set-group-fname))))) (ctob/rename-set-group set-group-path set-group-fname)))))
(defmethod process-change :move-token-set-before (defmethod process-change :move-token-set
[data {:keys [from-path to-path before-path before-group?] :as changes}] [data {:keys [from-path to-path before-path before-group] :as changes}]
(update data :tokens-lib #(-> % (update data :tokens-lib #(-> %
(ctob/ensure-tokens-lib) (ctob/ensure-tokens-lib)
(ctob/move-set from-path to-path before-path before-group?)))) (ctob/move-set from-path to-path before-path before-group))))
(defmethod process-change :move-token-set-group-before (defmethod process-change :move-token-set-group-before
[data {:keys [from-path to-path before-path before-group?]}] [data {:keys [from-path to-path before-path before-group?]}]

View file

@ -809,19 +809,19 @@
(update :undo-changes conj {:type :rename-token-set-group :set-group-path undo-path :set-group-fname undo-fname}) (update :undo-changes conj {:type :rename-token-set-group :set-group-path undo-path :set-group-fname undo-fname})
(apply-changes-local)))) (apply-changes-local))))
(defn move-token-set-before (defn move-token-set
[changes {:keys [from-path to-path before-path before-group? prev-before-path prev-before-group?] :as opts}] [changes {:keys [from-path to-path before-path before-group? prev-before-path prev-before-group?] :as opts}]
(-> changes (-> changes
(update :redo-changes conj {:type :move-token-set-before (update :redo-changes conj {:type :move-token-set
:from-path from-path :from-path from-path
:to-path to-path :to-path to-path
:before-path before-path :before-path before-path
:before-group? before-group?}) :before-group before-group?})
(update :undo-changes conj {:type :move-token-set-before (update :undo-changes conj {:type :move-token-set
:from-path to-path :from-path to-path
:to-path from-path :to-path from-path
:before-path prev-before-path :before-path prev-before-path
:before-group? prev-before-group?}) :before-group prev-before-group?})
(apply-changes-local))) (apply-changes-local)))
(defn move-token-set-group-before (defn move-token-set-group-before

View file

@ -283,6 +283,22 @@
:else :else
(get-root-frame objects (:frame-id frame))))) (get-root-frame objects (:frame-id frame)))))
(defn get-parent-frame
"Similar to `get-frame, but always return the parent frame. When root
frame is provided, then itself is returned."
[objects shape-or-id]
(cond
(map? shape-or-id)
(get objects (dm/get-prop shape-or-id :frame-id))
(= uuid/zero shape-or-id)
(get objects uuid/zero)
:else
(some->> shape-or-id
(get objects)
(get-frame objects))))
(defn valid-frame-target? (defn valid-frame-target?
[objects parent-id shape-id] [objects parent-id shape-id]
(let [shape (get objects shape-id)] (let [shape (get objects shape-id)]

View file

@ -29,7 +29,6 @@
[app.common.types.file :as ctf] [app.common.types.file :as ctf]
[app.common.types.shape :as cts] [app.common.types.shape :as cts]
[app.common.types.shape.shadow :as ctss] [app.common.types.shape.shadow :as ctss]
[app.common.types.tokens-lib :as ctob]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[clojure.set :as set] [clojure.set :as set]
[cuerdas.core :as str])) [cuerdas.core :as str]))
@ -97,13 +96,13 @@
(if (nil? migrations) (if (nil? migrations)
(generate-migrations-from-version version) (generate-migrations-from-version version)
migrations))) migrations)))
(migrate)
(update :features (fnil into #{}) (deref cfeat/*new*)) (update :features (fnil into #{}) (deref cfeat/*new*))
;; NOTE: in some future we can consider to apply ;; NOTE: in some future we can consider to apply
;; a migration to the whole database and remove ;; a migration to the whole database and remove
;; this code from this function that executes on ;; this code from this function that executes on
;; each file migration operation ;; each file migration operation
(update :features cfeat/migrate-legacy-features))))) (update :features cfeat/migrate-legacy-features)
(migrate)))))
(defn migrated? (defn migrated?
[file] [file]
@ -1226,32 +1225,7 @@
(update :pages-index update-vals update-container) (update :pages-index update-vals update-container)
(update :components update-vals update-container)))) (update :components update-vals update-container))))
(defmethod migrate-data "Ensure hidden theme" (defmethod migrate-data "0001-remove-tokens-from-groups"
[data _]
(letfn [(update-tokens-lib [tokens-lib]
(let [hidden-theme (ctob/get-hidden-theme tokens-lib)]
(if (nil? hidden-theme)
(ctob/add-theme tokens-lib (ctob/make-hidden-token-theme))
tokens-lib)))]
(if (contains? data :tokens-lib)
(update data :tokens-lib update-tokens-lib)
data)))
(defmethod migrate-data "Add token theme id"
[data _]
(letfn [(update-tokens-lib [tokens-lib]
(let [themes (ctob/get-themes tokens-lib)]
(reduce (fn [lib theme]
(if (:id theme)
lib
(ctob/update-theme lib (:group theme) (:name theme) #(assoc % :id (str (uuid/next))))))
tokens-lib
themes)))]
(if (contains? data :tokens-lib)
(update data :tokens-lib update-tokens-lib)
data)))
(defmethod migrate-data "Remove tokens from groups"
[data _] [data _]
(letfn [(update-object [object] (letfn [(update-object [object]
(cond-> object (cond-> object
@ -1320,6 +1294,4 @@
"legacy-65" "legacy-65"
"legacy-66" "legacy-66"
"legacy-67" "legacy-67"
"Ensure hidden theme" "0001-remove-tokens-from-groups"]))
"Add token theme id"
"Remove tokens from groups"]))

View file

@ -1,11 +1,21 @@
;; 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.tokens (ns app.common.logic.tokens
(:require (:require
[app.common.files.changes-builder :as pcb] [app.common.files.changes-builder :as pcb]
[app.common.types.tokens-lib :as ctob])) [app.common.types.tokens-lib :as ctob]))
(defn generate-update-active-sets (defn generate-update-active-sets
"Copy the active sets from the currently active themes and move them to the hidden token theme and update the theme with `update-theme-fn`. "Copy the active sets from the currently active themes and move them
Use this for managing sets active state without having to modify a user created theme (\"no themes selected\" state in the ui)." to the hidden token theme and update the theme with
`update-theme-fn`.
Use this for managing sets active state without having to modify a
user created theme (\"no themes selected\" state in the ui)."
[changes tokens-lib update-theme-fn] [changes tokens-lib update-theme-fn]
(let [prev-active-token-themes (ctob/get-active-theme-paths tokens-lib) (let [prev-active-token-themes (ctob/get-active-theme-paths tokens-lib)
active-token-set-names (ctob/get-active-themes-set-names tokens-lib) active-token-set-names (ctob/get-active-themes-set-names tokens-lib)
@ -21,7 +31,8 @@
hidden-token-theme)))) hidden-token-theme))))
(defn generate-toggle-token-set (defn generate-toggle-token-set
"Toggle a token set at `set-name` in `tokens-lib` without modifying a user theme." "Toggle a token set at `set-name` in `tokens-lib` without modifying a
user theme."
[changes tokens-lib set-name] [changes tokens-lib set-name]
(generate-update-active-sets changes tokens-lib #(ctob/toggle-set % set-name))) (generate-update-active-sets changes tokens-lib #(ctob/toggle-set % set-name)))
@ -49,12 +60,20 @@
:or {collapsed-paths #{}}}] :or {collapsed-paths #{}}}]
(let [tree (-> (ctob/get-set-tree tokens-lib) (let [tree (-> (ctob/get-set-tree tokens-lib)
(ctob/walk-sets-tree-seq :skip-children-pred #(contains? collapsed-paths %))) (ctob/walk-sets-tree-seq :skip-children-pred #(contains? collapsed-paths %)))
from (nth tree from-index) from (nth tree from-index)
to (nth tree to-index) to (nth tree to-index)
before (case position before (case position
:top to :top to
:bot (nth tree (inc to-index) nil) :bot (let [v (nth tree (inc to-index) nil)]
;; if the next index is a group, we need to set it as
;; nil because if we set a path on different subpath,
;; the move algorightm will simply remove the set
(if (:group? v)
nil
v))
:center nil) :center nil)
prev-before (if (:group? from) prev-before (if (:group? from)
(->> (drop (inc from-index) tree) (->> (drop (inc from-index) tree)
(filter (fn [element] (filter (fn [element]
@ -113,9 +132,9 @@
(defn generate-move-token-set (defn generate-move-token-set
"Create changes for dropping a token set or token set. "Create changes for dropping a token set or token set.
Throws for impossible moves." Throws for impossible moves."
[changes tokens-lib drop-opts] [changes tokens-lib params]
(if-let [drop-opts' (calculate-move-token-set-or-set-group tokens-lib drop-opts)] (if-let [params (calculate-move-token-set-or-set-group tokens-lib params)]
(pcb/move-token-set-before changes drop-opts') (pcb/move-token-set changes params)
changes)) changes))
(defn generate-move-token-set-group (defn generate-move-token-set-group

View file

@ -1019,26 +1019,26 @@
(def valid-text? (def valid-text?
(validator ::text)) (validator ::text))
(def check-safe-int! (def check-safe-int
(check-fn ::safe-int)) (check-fn ::safe-int))
(def check-set-of-strings! (def check-set-of-strings
(check-fn ::set-of-strings)) (check-fn ::set-of-strings))
(def check-email! (def check-email
(check-fn ::email)) (check-fn ::email))
(def check-uuid! (def check-uuid
(check-fn ::uuid :hint "expected valid uuid instance")) (check-fn ::uuid :hint "expected valid uuid instance"))
(def check-string! (def check-string
(check-fn :string :hint "expected string")) (check-fn :string :hint "expected string"))
(def check-coll-of-uuid! (def check-coll-of-uuid
(check-fn ::coll-of-uuid)) (check-fn ::coll-of-uuid))
(def check-set-of-uuid! (def check-set-of-uuid
(check-fn ::set-of-uuid)) (check-fn ::set-of-uuid))
(def check-set-of-emails! (def check-set-of-emails
(check-fn [::set ::email])) (check-fn [::set ::email]))

View file

@ -7,7 +7,6 @@
(ns app.common.types.shape.interactions (ns app.common.types.shape.interactions
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.helpers :as cfh] [app.common.files.helpers :as cfh]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.geom.shapes.bounds :as gsb] [app.common.geom.shapes.bounds :as gsb]
@ -180,7 +179,7 @@
(sm/register! ::interaction schema:interaction) (sm/register! ::interaction schema:interaction)
(def check-interaction! (def check-interaction
(sm/check-fn schema:interaction)) (sm/check-fn schema:interaction))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -203,18 +202,13 @@
(defn set-event-type (defn set-event-type
[interaction event-type shape] [interaction event-type shape]
(dm/assert! (assert (check-interaction interaction))
"Should be an interraction map" (assert (contains? event-types event-type)
(check-interaction! interaction)) "should be a valid event type")
(dm/assert! (assert (or (not= event-type :after-delay)
"Should be a valid event type" (cfh/frame-shape? shape))
(contains? event-types event-type)) "the `:after-delay` event type incompatible with not frame shapes")
(dm/assert!
"The `:after-delay` event type incompatible with not frame shapes"
(or (not= event-type :after-delay)
(cfh/frame-shape? shape)))
(if (= (:event-type interaction) event-type) (if (= (:event-type interaction) event-type)
interaction interaction
@ -230,14 +224,9 @@
(defn set-action-type (defn set-action-type
[interaction action-type] [interaction action-type]
(assert (check-interaction interaction))
(dm/assert! (assert (contains? action-types action-type)
"Should be an interraction map" "Should be a valid event type")
(check-interaction! interaction))
(dm/assert!
"Should be a valid event type"
(contains? action-types action-type))
(let [new-interaction (let [new-interaction
(if (= (:action-type interaction) action-type) (if (= (:action-type interaction) action-type)
@ -284,18 +273,10 @@
(defn set-delay (defn set-delay
[interaction delay] [interaction delay]
(assert (check-interaction interaction))
(dm/assert! (assert (sm/check-safe-int delay))
"expected valid interaction map" (assert (has-delay interaction)
(check-interaction! interaction)) "expected compatible interaction event type")
(dm/assert!
"expected valid delay"
(sm/check-safe-int! delay))
(dm/assert!
"expected compatible interaction event type"
(has-delay interaction))
(assoc interaction :delay delay)) (assoc interaction :delay delay))
@ -315,14 +296,9 @@
(defn set-destination (defn set-destination
[interaction destination] [interaction destination]
(assert (check-interaction interaction))
(dm/assert! (assert (has-destination interaction)
"expected valid interaction map" "expected compatible interaction event type")
(check-interaction! interaction))
(dm/assert!
"expected compatible interaction event type"
(has-destination interaction))
(cond-> interaction (cond-> interaction
:always :always
@ -340,17 +316,11 @@
(defn set-preserve-scroll (defn set-preserve-scroll
[interaction preserve-scroll] [interaction preserve-scroll]
(dm/assert! (assert (check-interaction interaction))
"expected valid interaction map" (assert (boolean? preserve-scroll)
(check-interaction! interaction)) "expected boolean for `preserve-scroll`")
(assert (has-preserve-scroll interaction)
(dm/assert! "expected compatible interaction map with preserve-scroll")
"expected boolean for `preserve-scroll`"
(boolean? preserve-scroll))
(dm/assert!
"expected compatible interaction map with preserve-scroll"
(has-preserve-scroll interaction))
(assoc interaction :preserve-scroll preserve-scroll)) (assoc interaction :preserve-scroll preserve-scroll))
@ -361,17 +331,11 @@
(defn set-url (defn set-url
[interaction url] [interaction url]
(dm/assert! (assert (check-interaction interaction))
"expected valid interaction map" (assert (string? url)
(check-interaction! interaction)) "expected a string for `url`")
(assert (has-url interaction)
(dm/assert! "expected compatible interaction map with url param")
"expected a string for `url`"
(string? url))
(dm/assert!
"expected compatible interaction map with url param"
(has-url interaction))
(assoc interaction :url url)) (assoc interaction :url url))
@ -382,17 +346,12 @@
(defn set-overlay-pos-type (defn set-overlay-pos-type
[interaction overlay-pos-type shape objects] [interaction overlay-pos-type shape objects]
(dm/assert! (assert (check-interaction interaction))
"expected valid interaction map"
(check-interaction! interaction))
(dm/assert! (assert (contains? overlay-positioning-types overlay-pos-type)
"expected valid overlay positioning type" "expected valid overlay positioning type")
(contains? overlay-positioning-types overlay-pos-type)) (assert (has-overlay-opts interaction)
"expected compatible interaction map")
(dm/assert!
"expected compatible interaction map"
(has-overlay-opts interaction))
(assoc interaction (assoc interaction
:overlay-pos-type overlay-pos-type :overlay-pos-type overlay-pos-type
@ -403,17 +362,11 @@
(defn toggle-overlay-pos-type (defn toggle-overlay-pos-type
[interaction overlay-pos-type shape objects] [interaction overlay-pos-type shape objects]
(dm/assert! (assert (check-interaction interaction))
"expected valid interaction map" (assert (contains? overlay-positioning-types overlay-pos-type)
(check-interaction! interaction)) "expected valid overlay positioning type")
(assert (has-overlay-opts interaction)
(dm/assert! "expected compatible interaction map")
"expected valid overlay positioning type"
(contains? overlay-positioning-types overlay-pos-type))
(dm/assert!
"expected compatible interaction map"
(has-overlay-opts interaction))
(let [new-pos-type (if (= (:overlay-pos-type interaction) overlay-pos-type) (let [new-pos-type (if (= (:overlay-pos-type interaction) overlay-pos-type)
:manual :manual
@ -427,17 +380,12 @@
(defn set-overlay-position (defn set-overlay-position
[interaction overlay-position] [interaction overlay-position]
(dm/assert! (assert (check-interaction interaction))
"expected valid interaction map" (assert (gpt/point? overlay-position)
(check-interaction! interaction)) "expected valid overlay position")
(assert (has-overlay-opts interaction)
"expected compatible interaction map")
(dm/assert!
"expected valid overlay position"
(gpt/point? overlay-position))
(dm/assert!
"expected compatible interaction map"
(has-overlay-opts interaction))
(assoc interaction (assoc interaction
:overlay-pos-type :manual :overlay-pos-type :manual
@ -446,52 +394,34 @@
(defn set-close-click-outside (defn set-close-click-outside
[interaction close-click-outside] [interaction close-click-outside]
(dm/assert! (assert (check-interaction interaction))
"expected valid interaction map" (assert (boolean? close-click-outside)
(check-interaction! interaction)) "expected boolean value for `close-click-outside`")
(assert (has-overlay-opts interaction)
(dm/assert! "expected compatible interaction map")
"expected boolean value for `close-click-outside`"
(boolean? close-click-outside))
(dm/assert!
"expected compatible interaction map"
(has-overlay-opts interaction))
(assoc interaction :close-click-outside close-click-outside)) (assoc interaction :close-click-outside close-click-outside))
(defn set-background-overlay (defn set-background-overlay
[interaction background-overlay] [interaction background-overlay]
(dm/assert! (assert (check-interaction interaction))
"expected valid interaction map" (assert (boolean? background-overlay)
(check-interaction! interaction)) "expected boolean value for `background-overlay`")
(assert (has-overlay-opts interaction)
(dm/assert! "expected compatible interaction map")
"expected boolean value for `background-overlay`"
(boolean? background-overlay))
(dm/assert!
"expected compatible interaction map"
(has-overlay-opts interaction))
(assoc interaction :background-overlay background-overlay)) (assoc interaction :background-overlay background-overlay))
(defn set-position-relative-to (defn set-position-relative-to
[interaction position-relative-to] [interaction position-relative-to]
(dm/assert! (assert (check-interaction interaction))
"expected valid interaction map" (assert (or (nil? position-relative-to)
(check-interaction! interaction)) (uuid? position-relative-to))
"expected valid uuid for `position-relative-to`")
(dm/assert! (assert (has-overlay-opts interaction)
"expected valid uuid for `position-relative-to`" "expected compatible interaction map")
(or (nil? position-relative-to)
(uuid? position-relative-to)))
(dm/assert!
"expected compatible interaction map"
(has-overlay-opts interaction))
(assoc interaction :position-relative-to position-relative-to)) (assoc interaction :position-relative-to position-relative-to))
@ -519,13 +449,9 @@
frame-offset] ;; if this interaction starts in a frame opened frame-offset] ;; if this interaction starts in a frame opened
;; on another interaction, this is the position ;; on another interaction, this is the position
;; of that frame ;; of that frame
(dm/assert! (assert (check-interaction interaction))
"expected valid interaction map" (assert (has-overlay-opts interaction)
(check-interaction! interaction)) "expected compatible interaction map")
(dm/assert!
"expected compatible interaction map"
(has-overlay-opts interaction))
(let [;; When the interactive item is inside a nested frame we need to add to the offset the position (let [;; When the interactive item is inside a nested frame we need to add to the offset the position
;; of the parent-frame otherwise the position won't match ;; of the parent-frame otherwise the position won't match
@ -617,22 +543,15 @@
(defn set-animation-type (defn set-animation-type
[interaction animation-type] [interaction animation-type]
(dm/assert!
"expected valid interaction map"
(check-interaction! interaction))
(dm/assert! (assert (check-interaction interaction))
"expected valid value for `animation-type`" (assert (or (nil? animation-type)
(or (nil? animation-type) (contains? animation-types animation-type))
(contains? animation-types animation-type))) "expected valid value for `animation-type`")
(assert (has-animation? interaction)
(dm/assert! "expected interaction map compatible with animation")
"expected interaction map compatible with animation" (assert (allowed-animation? (:action-type interaction) animation-type)
(has-animation? interaction)) "expected allowed animation type")
(dm/assert!
"expected allowed animation type"
(allowed-animation? (:action-type interaction) animation-type))
(if (= (-> interaction :animation :animation-type) animation-type) (if (= (-> interaction :animation :animation-type) animation-type)
interaction interaction
@ -668,17 +587,10 @@
(defn set-duration (defn set-duration
[interaction duration] [interaction duration]
(dm/assert! (assert (check-interaction interaction))
"expected valid interaction map" (assert (sm/check-safe-int duration))
(check-interaction! interaction)) (assert (has-duration? interaction)
"expected compatible interaction map")
(dm/assert!
"expected valid duration"
(sm/check-safe-int! duration))
(dm/assert!
"expected compatible interaction map"
(has-duration? interaction))
(update interaction :animation assoc :duration duration)) (update interaction :animation assoc :duration duration))
@ -689,17 +601,11 @@
(defn set-easing (defn set-easing
[interaction easing] [interaction easing]
(dm/assert! (assert (check-interaction interaction))
"expected valid interaction map" (assert (contains? easing-types easing)
(check-interaction! interaction)) "expected valid easing")
(assert (has-easing? interaction)
(dm/assert! "expected compatible interaction map")
"expected valid easing"
(contains? easing-types easing))
(dm/assert!
"expected compatible interaction map"
(has-easing? interaction))
(update interaction :animation assoc :easing easing)) (update interaction :animation assoc :easing easing))
@ -712,17 +618,11 @@
(defn set-way (defn set-way
[interaction way] [interaction way]
(dm/assert! (assert (check-interaction interaction))
"expected valid interaction map" (assert (contains? way-types way)
(check-interaction! interaction)) "expected valid way")
(assert (has-way? interaction)
(dm/assert! "expected compatible interaction map")
"expected valid way"
(contains? way-types way))
(dm/assert!
"expected compatible interaction map"
(has-way? interaction))
(update interaction :animation assoc :way way)) (update interaction :animation assoc :way way))
@ -733,26 +633,20 @@
(defn set-direction (defn set-direction
[interaction direction] [interaction direction]
(dm/assert! (assert (check-interaction interaction))
"expected valid interaction map" (assert (contains? direction-types direction)
(check-interaction! interaction)) "expected valid direction")
(dm/assert! (assert (has-direction? interaction)
"expected valid direction" "expected compatible interaction map")
(contains? direction-types direction))
(dm/assert!
"expected compatible interaction map"
(has-direction? interaction))
(update interaction :animation assoc :direction direction)) (update interaction :animation assoc :direction direction))
(defn invert-direction (defn invert-direction
[animation] [animation]
(dm/assert! (assert (or (nil? animation)
"expected valid animation map" (check-animation! animation))
(or (nil? animation) "expected valid animation map")
(check-animation! animation)))
(case (:direction animation) (case (:direction animation)
:right :right
@ -768,24 +662,18 @@
(defn has-offset-effect? (defn has-offset-effect?
[interaction] [interaction]
; Offset-effect is ignored in slide animations of overlay actions ;; Offset-effect is ignored in slide animations of overlay actions
(and (= (:action-type interaction) :navigate) (and (= (:action-type interaction) :navigate)
(= (-> interaction :animation :animation-type) :slide))) (= (-> interaction :animation :animation-type) :slide)))
(defn set-offset-effect (defn set-offset-effect
[interaction offset-effect] [interaction offset-effect]
(dm/assert! (assert (check-interaction interaction))
"expected valid interaction map" (assert (boolean? offset-effect)
(check-interaction! interaction)) "expected valid boolean for `offset-effect`")
(assert (has-offset-effect? interaction)
(dm/assert! "expected compatible interaction map")
"expected valid boolean for `offset-effect`"
(boolean? offset-effect))
(dm/assert!
"expected compatible interaction map"
(has-offset-effect? interaction))
(update interaction :animation assoc :offset-effect offset-effect)) (update interaction :animation assoc :offset-effect offset-effect))

View file

@ -588,7 +588,7 @@
(update :group d/nilv top-level-theme-group-name) (update :group d/nilv top-level-theme-group-name)
(update :description d/nilv "") (update :description d/nilv "")
(update :is-source d/nilv false) (update :is-source d/nilv false)
(update :id d/nilv (str (uuid/next))) (update :id #(or % (str (uuid/next))))
(update :modified-at #(or % (dt/now))) (update :modified-at #(or % (dt/now)))
(update :sets set) (update :sets set)
(check-token-theme-attrs) (check-token-theme-attrs)
@ -612,7 +612,6 @@
(get-theme-tree [_] "get a nested tree of all themes in the library") (get-theme-tree [_] "get a nested tree of all themes in the library")
(get-themes [_] "get an ordered sequence of all themes in the library") (get-themes [_] "get an ordered sequence of all themes in the library")
(get-theme [_ group name] "get one theme looking for name") (get-theme [_ group name] "get one theme looking for name")
(get-hidden-theme [_] "get the theme hidden from the user, used for managing active sets without a user created theme.")
(get-theme-groups [_] "get a sequence of group names by order") (get-theme-groups [_] "get a sequence of group names by order")
(get-active-theme-paths [_] "get the active theme paths") (get-active-theme-paths [_] "get the active theme paths")
(get-active-themes [_] "get an ordered sequence of active themes in the library") (get-active-themes [_] "get an ordered sequence of active themes in the library")
@ -946,14 +945,21 @@ Will return a value that matches this schema:
(let [prefixed-from-path (set-full-path->set-prefixed-full-path from-path) (let [prefixed-from-path (set-full-path->set-prefixed-full-path from-path)
prev-set (get-in sets prefixed-from-path)] prev-set (get-in sets prefixed-from-path)]
(if (instance? TokenSet prev-set) (if (instance? TokenSet prev-set)
(let [prefixed-to-path (set-full-path->set-prefixed-full-path to-path) (let [prefixed-to-path
prefixed-before-path (when before-path (set-full-path->set-prefixed-full-path to-path)
prefixed-before-path
(when before-path
(if before-group? (if before-group?
(mapv add-set-path-group-prefix before-path) (mapv add-set-path-group-prefix before-path)
(set-full-path->set-prefixed-full-path before-path))) (set-full-path->set-prefixed-full-path before-path)))
set (assoc prev-set :name (join-set-path to-path)) set
reorder? (= prefixed-from-path prefixed-to-path) (assoc prev-set :name (join-set-path to-path))
reorder?
(= prefixed-from-path prefixed-to-path)
sets' sets'
(if reorder? (if reorder?
(d/oreorder-before sets (d/oreorder-before sets
@ -965,6 +971,7 @@ Will return a value that matches this schema:
(d/oassoc-in-before sets prefixed-before-path prefixed-to-path set) (d/oassoc-in-before sets prefixed-before-path prefixed-to-path set)
(d/oassoc-in sets prefixed-to-path set)) (d/oassoc-in sets prefixed-to-path set))
(d/dissoc-in prefixed-from-path)))] (d/dissoc-in prefixed-from-path)))]
(TokensLib. sets' (TokensLib. sets'
(if reorder? (if reorder?
themes themes
@ -1130,9 +1137,6 @@ Will return a value that matches this schema:
(get-theme [_ group name] (get-theme [_ group name]
(dm/get-in themes [group name])) (dm/get-in themes [group name]))
(get-hidden-theme [this]
(get-theme this hidden-token-theme-group hidden-token-theme-name))
(set-active-themes [_ active-themes] (set-active-themes [_ active-themes]
(TokensLib. sets (TokensLib. sets
themes themes
@ -1368,6 +1372,10 @@ Will return a value that matches this schema:
(valid-token-themes? themes) (valid-token-themes? themes)
(valid-active-token-themes? active-themes)))) (valid-active-token-themes? active-themes))))
(defn get-hidden-theme
[tokens-lib]
(get-theme tokens-lib hidden-token-theme-group hidden-token-theme-name))
(defn valid-tokens-lib? (defn valid-tokens-lib?
[o] [o]
(and (instance? TokensLib o) (and (instance? TokensLib o)
@ -1382,27 +1390,32 @@ Will return a value that matches this schema:
(def ^:private check-active-themes (def ^:private check-active-themes
(sm/check-fn schema:active-themes :hint "expected valid active themes")) (sm/check-fn schema:active-themes :hint "expected valid active themes"))
(defn make-tokens-lib (defn- ensure-hidden-theme
"Create an empty or prepopulated tokens library." "A helper that is responsible to ensure that the hidden theme always
([] exists on the themes data structure"
[themes]
(update themes hidden-token-theme-group
(fn [data]
(if (contains? data hidden-token-theme-name)
data
(d/oassoc data hidden-token-theme-name (make-hidden-token-theme))))))
;; NOTE: is possible that ordered map is not the most apropriate ;; NOTE: is possible that ordered map is not the most apropriate
;; data structure and maybe we need a specific that allows us an ;; data structure and maybe we need a specific that allows us an
;; easy way to reorder it, or just store inside Tokens data ;; easy way to reorder it, or just store inside Tokens data
;; structure the data and the order separately as we already do ;; structure the data and the order separately as we already do
;; with pages and pages-index. ;; with pages and pages-index.
(make-tokens-lib :sets (d/ordered-map) (defn make-tokens-lib
:themes (d/ordered-map) "Create an empty or prepopulated tokens library."
:active-themes #{hidden-token-theme-path})) [& {:keys [sets themes active-themes]}]
(let [sets (or sets (d/ordered-map))
([& {:keys [sets themes active-themes]}] themes (-> (or themes (d/ordered-map))
(let [active-themes (d/nilv active-themes #{hidden-token-theme-path}) (ensure-hidden-theme))
themes (if (empty? themes) active-themes (or active-themes #{hidden-token-theme-path})]
(update themes hidden-token-theme-group d/oassoc hidden-token-theme-name (make-hidden-token-theme))
themes)]
(TokensLib. (TokensLib.
(check-token-sets sets) (check-token-sets sets)
(check-token-themes themes) (check-token-themes themes)
(check-active-themes active-themes))))) (check-active-themes active-themes))))
(defn ensure-tokens-lib (defn ensure-tokens-lib
[tokens-lib] [tokens-lib]
@ -1445,6 +1458,71 @@ Will return a value that matches this schema:
:wfn #(into {} %) :wfn #(into {} %)
:rfn #(map->Token %)}) :rfn #(map->Token %)})
#?(:clj
(defn- read-tokens-lib-v1-0
"Reads the first version of tokens lib, now completly obsolete"
[r]
(let [;; Migrate sets tree without prefix to new format
prev-sets (->> (fres/read-object! r)
(tree-seq d/ordered-map? vals)
(filter (partial instance? TokenSet)))
sets (-> (reduce add-set (make-tokens-lib) prev-sets)
(deref)
(:sets))
_set-groups (fres/read-object! r)
themes (fres/read-object! r)
active-themes (fres/read-object! r)]
(->TokensLib sets themes active-themes))))
#?(:clj
(defn- read-tokens-lib-v1-1
"Reads the tokens lib data structure and ensures that hidden
theme exists and adds missing ID on themes"
[r]
(let [sets (fres/read-object! r)
themes (fres/read-object! r)
active-themes (fres/read-object! r)
;; Ensure we have at least a hidden theme
themes
(ensure-hidden-theme themes)
;; Ensure we add an :id field for each existing theme
themes
(reduce (fn [result group-id]
(update result group-id
(fn [themes]
(reduce (fn [themes theme-id]
(update themes theme-id
(fn [theme]
(if (get theme :id)
theme
(assoc theme :id (str (uuid/next)))))))
themes
(keys themes)))))
themes
(keys themes))]
(->TokensLib sets themes active-themes))))
#?(:clj
(defn- write-tokens-lib
[n w ^TokensLib o]
(fres/write-tag! w n 3)
(fres/write-object! w (.-sets o))
(fres/write-object! w (.-themes o))
(fres/write-object! w (.-active-themes o))))
#?(:clj
(defn- read-tokens-lib
[r]
(let [sets (fres/read-object! r)
themes (fres/read-object! r)
active-themes (fres/read-object! r)]
(->TokensLib sets themes active-themes))))
#?(:clj #?(:clj
(fres/add-handlers! (fres/add-handlers!
{:name "penpot/token/v1" {:name "penpot/token/v1"
@ -1474,32 +1552,15 @@ Will return a value that matches this schema:
(let [obj (fres/read-object! r)] (let [obj (fres/read-object! r)]
(map->TokenTheme obj)))} (map->TokenTheme obj)))}
;; LEGACY TOKENS LIB READERS (with migrations)
{:name "penpot/tokens-lib/v1" {:name "penpot/tokens-lib/v1"
:rfn (fn [r] :rfn read-tokens-lib-v1-0}
(let [;; Migrate sets tree without prefix to new format
prev-sets (->> (fres/read-object! r)
(tree-seq d/ordered-map? vals)
(filter (partial instance? TokenSet)))
;; FIXME: wtf we usind deref here?
sets (-> (reduce add-set (make-tokens-lib) prev-sets)
(deref)
(:sets))
_set-groups (fres/read-object! r)
themes (fres/read-object! r)
active-themes (fres/read-object! r)]
(->TokensLib sets themes active-themes)))}
{:name "penpot/tokens-lib/v1.1" {:name "penpot/tokens-lib/v1.1"
:rfn read-tokens-lib-v1-1}
;; CURRENT TOKENS LIB READER & WRITTER
{:name "penpot/tokens-lib/v1.2"
:class TokensLib :class TokensLib
:wfn (fn [n w o] :wfn write-tokens-lib
(fres/write-tag! w n 3) :rfn read-tokens-lib}))
(fres/write-object! w (.-sets o))
(fres/write-object! w (.-themes o))
(fres/write-object! w (.-active-themes o)))
:rfn (fn [r]
(let [sets (fres/read-object! r)
themes (fres/read-object! r)
active-themes (fres/read-object! r)]
(->TokensLib sets themes active-themes)))}))

View file

@ -84,8 +84,7 @@
(t/is (thrown-with-msg? #?(:cljs js/Error :clj Exception) #"expected valid params for token-set" (t/is (thrown-with-msg? #?(:cljs js/Error :clj Exception) #"expected valid params for token-set"
(ctob/make-token-set params))))) (ctob/make-token-set params)))))
(t/deftest move-token-set (t/deftest move-token-set-flat
(t/testing "flat"
(let [tokens-lib (-> (ctob/make-tokens-lib) (let [tokens-lib (-> (ctob/make-tokens-lib)
(ctob/add-set (ctob/make-token-set :name "A")) (ctob/add-set (ctob/make-token-set :name "A"))
(ctob/add-set (ctob/make-token-set :name "B")) (ctob/add-set (ctob/make-token-set :name "B"))
@ -103,7 +102,7 @@
(t/testing "move to bottom" (t/testing "move to bottom"
(t/is (= ["A" "B" "Move"] (move ["Move"] ["Move"] nil false)))))) (t/is (= ["A" "B" "Move"] (move ["Move"] ["Move"] nil false))))))
(t/testing "nested" (t/deftest move-token-set-nested
(let [tokens-lib (-> (ctob/make-tokens-lib) (let [tokens-lib (-> (ctob/make-tokens-lib)
(ctob/add-set (ctob/make-token-set :name "Foo/Baz")) (ctob/add-set (ctob/make-token-set :name "Foo/Baz"))
(ctob/add-set (ctob/make-token-set :name "Foo/Bar")) (ctob/add-set (ctob/make-token-set :name "Foo/Bar"))
@ -121,15 +120,29 @@
(t/is (= ["Foo/Foo" "Foo/Baz" "Foo/Bar"] (move ["Foo"] ["Foo" "Foo"] ["Foo" "Baz"] false))) (t/is (= ["Foo/Foo" "Foo/Baz" "Foo/Bar"] (move ["Foo"] ["Foo" "Foo"] ["Foo" "Baz"] false)))
(t/is (= ["Foo/Baz" "Foo/Bar" "Foo/Foo"] (move ["Foo"] ["Foo" "Foo"] nil false)))))) (t/is (= ["Foo/Baz" "Foo/Bar" "Foo/Foo"] (move ["Foo"] ["Foo" "Foo"] nil false))))))
;; FIXME
(t/testing "updates theme set names" (t/deftest move-token-set-nested-2
(let [tokens-lib (-> (ctob/make-tokens-lib)
(ctob/add-set (ctob/make-token-set :name "a/b"))
(ctob/add-set (ctob/make-token-set :name "a/a"))
(ctob/add-set (ctob/make-token-set :name "b/a"))
(ctob/add-set (ctob/make-token-set :name "b/b")))
move (fn [from-path to-path before-path before-group?]
(->> (ctob/move-set tokens-lib from-path to-path before-path before-group?)
(ctob/get-ordered-set-names)
(vec)))]
(t/testing "move within group"
(t/is (= ["a/b" "a/a" "b/a" "b/b"] (vec (ctob/get-ordered-set-names tokens-lib))))
(t/is (= ["a/a" "a/b" "b/a" "b/b"] (move ["a" "b"] ["a" "b"] nil true))))))
(t/deftest move-token-set-nested-3
(let [tokens-lib (-> (ctob/make-tokens-lib) (let [tokens-lib (-> (ctob/make-tokens-lib)
(ctob/add-set (ctob/make-token-set :name "Foo/Bar/Baz")) (ctob/add-set (ctob/make-token-set :name "Foo/Bar/Baz"))
(ctob/add-set (ctob/make-token-set :name "Other")) (ctob/add-set (ctob/make-token-set :name "Other"))
(ctob/add-theme (ctob/make-token-theme :name "Theme" (ctob/add-theme (ctob/make-token-theme :name "Theme"
:sets #{"Foo/Bar/Baz"})) :sets #{"Foo/Bar/Baz"}))
(ctob/move-set ["Foo" "Bar" "Baz"] ["Other/Baz"] nil nil))] (ctob/move-set ["Foo" "Bar" "Baz"] ["Other/Baz"] nil nil))]
(t/is (= #{"Other/Baz"} (:sets (ctob/get-theme tokens-lib "" "Theme"))))))) (t/is (= #{"Other/Baz"} (:sets (ctob/get-theme tokens-lib "" "Theme"))))))
(t/deftest move-token-set-group (t/deftest move-token-set-group
(t/testing "reordering" (t/testing "reordering"
@ -213,7 +226,7 @@
(t/is (= (ctob/set-count tokens-lib) 0)))) (t/is (= (ctob/set-count tokens-lib) 0))))
(t/deftest make-invalid-tokens-lib (t/deftest make-invalid-tokens-lib
(let [params {:sets nil :themes nil}] (let [params {:sets {} :themes {}}]
(t/is (thrown-with-msg? #?(:cljs js/Error :clj Exception) #"expected valid token sets" (t/is (thrown-with-msg? #?(:cljs js/Error :clj Exception) #"expected valid token sets"
(ctob/make-tokens-lib params))))) (ctob/make-tokens-lib params)))))

View file

@ -2,6 +2,14 @@
title: 1.3 Install with Docker title: 1.3 Install with Docker
--- ---
<p class="advice">
Installing and maintaining a self-hosted Penpot instance requires some technical knowledge:
Docker and Docker Compose, basic DNS management, and proxy configuration.
If you're not comfortable with this stack, we encourage you to try
more straight-forward installations with <a href="https://help.penpot.app/technical-guide/getting-started/elestio/" target="_blank">Elestio</a>
or use the SAAS at <a href="https://design.penpot.app" targret="_blank">https://design.penpot.app</a>.
</p>
# Install with Docker # Install with Docker
This section details everything you need to know to get Penpot up and running in This section details everything you need to know to get Penpot up and running in
@ -10,42 +18,10 @@ production environments using Docker. For this, we provide a series of *Dockerfi
## Install Docker ## Install Docker
<p class="advice"> To host a Penpot instance with Docker, it's necessary to have
Skip this section if you already have docker installed, up and running. <code class="language-bash">docker</code> and <code class="language-bash">docker compose</code>
</p> installed. Check the comprehensive <a href="https://docs.docker.com/" target="_blank">official documentation</a>
to install and maintain docker.
Currently, Docker comes into two different flavours:
### Docker Desktop
This is the only option to have Docker in a Windows or MacOS. Recently it's also available
for Linux, in the most popular distributions (Debian, Ubuntu and Fedora).
You can install it following the <a href="https://docs.docker.com/desktop/"
target="_blank">official guide</a>.
Docker Desktop has a graphical control panel (GUI) to manage the service and view the
containers, images and volumes. But you need the command line (Terminal in Linux and Mac, or
PowerShell in Windows) to build and run the containers, and execute other operations.
It already includes **docker compose** utility, needed by Penpot.
### Docker Engine
This is the classic and default Docker setup for Linux machines, and the only option for a
Linux VPS without graphical interface.
You can install it following the <a href="https://docs.docker.com/engine/"
target="_blank">official guide</a>.
And you also need the [docker
compose](https://docs.docker.com/compose/cli-command/#installing-compose-v2) (V2)
plugin. You can use the old **docker-compose** tool, but all the documentation supposes
you are using the V2.
You can easily check which version of **docker compose** you have. If you can execute
<code class="language-bash">docker compose</code> command, then you have V2. If you need to write <code class="language-bash">docker-compose</code> (with a
<code class="language-bash">-</code>) for it to work, you have the old version.
## Start Penpot ## Start Penpot

View file

@ -648,9 +648,7 @@
(defn detach-comment-thread (defn detach-comment-thread
"Detach comment threads that are inside a frame when that frame is deleted" "Detach comment threads that are inside a frame when that frame is deleted"
[ids] [ids]
(dm/assert! (assert (sm/check-coll-of-uuid ids))
"expected a valid coll of uuid's"
(sm/check-coll-of-uuid! ids))
(ptk/reify ::detach-comment-thread (ptk/reify ::detach-comment-thread
ptk/WatchEvent ptk/WatchEvent

View file

@ -536,11 +536,8 @@
(defn move-files (defn move-files
[{:keys [ids project-id] :as params}] [{:keys [ids project-id] :as params}]
(dm/assert! (uuid? project-id)) (assert (uuid? project-id))
(assert (sm/check-set-of-uuid ids))
(dm/assert!
"expected a valid set of uuids"
(sm/check-set-of-uuid! ids))
(ptk/reify ::move-files (ptk/reify ::move-files
ev/Event ev/Event

View file

@ -350,12 +350,10 @@
(defn create-invitations (defn create-invitations
[{:keys [emails role team-id resend?] :as params}] [{:keys [emails role team-id resend?] :as params}]
(dm/assert! (keyword? role))
(dm/assert! (uuid? team-id))
(dm/assert! (assert (keyword? role))
"expected a valid set of emails" (assert (uuid? team-id))
(sm/check-set-of-emails! emails)) (assert (sm/check-set-of-emails emails))
(ptk/reify ::create-invitations (ptk/reify ::create-invitations
ev/Event ev/Event
@ -376,11 +374,8 @@
(defn copy-invitation-link (defn copy-invitation-link
[{:keys [email team-id] :as params}] [{:keys [email team-id] :as params}]
(dm/assert! (assert (sm/check-email email))
"expected a valid email" (assert (uuid? team-id))
(sm/check-email! email))
(dm/assert! (uuid? team-id))
(ptk/reify ::copy-invitation-link (ptk/reify ::copy-invitation-link
IDeref IDeref
@ -406,12 +401,9 @@
(defn update-invitation-role (defn update-invitation-role
[{:keys [email team-id role] :as params}] [{:keys [email team-id role] :as params}]
(dm/assert! (assert (sm/check-email email))
"expected a valid email" (assert (uuid? team-id))
(sm/check-email! email)) (assert (contains? ctt/valid-roles role))
(dm/assert! (uuid? team-id))
(dm/assert! (contains? ctt/valid-roles role))
(ptk/reify ::update-invitation-role (ptk/reify ::update-invitation-role
IDeref IDeref
@ -428,8 +420,9 @@
(defn delete-invitation (defn delete-invitation
[{:keys [email team-id] :as params}] [{:keys [email team-id] :as params}]
(dm/assert! (sm/check-email! email)) (assert (sm/check-email email))
(dm/assert! (uuid? team-id)) (assert (uuid? team-id))
(ptk/reify ::delete-invitation (ptk/reify ::delete-invitation
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ _] (watch [_ _ _]

View file

@ -252,6 +252,8 @@
:level :error :level :error
:timeout 9000}))))))) :timeout 9000})))))))
;; FIXME: add schema for params
(defn drop-token-set-group [drop-opts] (defn drop-token-set-group [drop-opts]
(ptk/reify ::drop-token-set-group (ptk/reify ::drop-token-set-group
ptk/WatchEvent ptk/WatchEvent
@ -265,17 +267,21 @@
(rx/of (rx/of
(drop-error (ex-data e)))))))) (drop-error (ex-data e))))))))
(defn drop-token-set [drop-opts] ;; FIXME: add schema for params
(defn drop-token-set
[params]
(ptk/reify ::drop-token-set (ptk/reify ::drop-token-set
ptk/WatchEvent ptk/WatchEvent
(watch [it state _] (watch [it state _]
(try (try
(when-let [changes (clt/generate-move-token-set (pcb/empty-changes it) (get-tokens-lib state) drop-opts)] (let [tokens-lib (get-tokens-lib state)
changes (-> (pcb/empty-changes it)
(clt/generate-move-token-set tokens-lib params))]
(rx/of (dch/commit-changes changes) (rx/of (dch/commit-changes changes)
(wtu/update-workspace-tokens))) (wtu/update-workspace-tokens)))
(catch :default e (catch :default cause
(rx/of (rx/of (drop-error (ex-data cause))))))))
(drop-error (ex-data e))))))))
(defn- create-token-with-set (defn- create-token-with-set
"A special case when a first token is created and no set exists" "A special case when a first token is created and no set exists"

View file

@ -406,6 +406,7 @@
:workspace-media-objects :workspace-media-objects
:workspace-persistence :workspace-persistence
:workspace-presence :workspace-presence
:workspace-tokens
:workspace-undo) :workspace-undo)
(update :workspace-global dissoc :read-only?) (update :workspace-global dissoc :read-only?)
(assoc-in [:workspace-global :options-mode] :design))) (assoc-in [:workspace-global :options-mode] :design)))

View file

@ -134,9 +134,7 @@
;; Move comment threads that are inside a frame when that frame is moved" ;; Move comment threads that are inside a frame when that frame is moved"
(defmethod ptk/resolve ::move-frame-comment-threads (defmethod ptk/resolve ::move-frame-comment-threads
[_ ids] [_ ids]
(dm/assert! (assert (sm/check-coll-of-uuid ids))
"expected a valid coll of uuid's"
(sm/check-coll-of-uuid! ids))
(ptk/reify ::move-frame-comment-threads (ptk/reify ::move-frame-comment-threads
ptk/WatchEvent ptk/WatchEvent

View file

@ -120,6 +120,14 @@
(pcb/with-page-id page-id) (pcb/with-page-id page-id)
(pcb/with-objects objects) (pcb/with-objects objects)
(pcb/add-object group {:index group-idx}) (pcb/add-object group {:index group-idx})
;; Create a group needs to reset the constraints to scale/scale
(pcb/update-shapes
(map :id shapes)
(fn [shape]
(-> shape
(d/assoc-when :constraints-h :scale)
(d/assoc-when :constraints-v :scale))))
(pcb/update-shapes (map :id shapes) ctl/remove-layout-item-data) (pcb/update-shapes (map :id shapes) ctl/remove-layout-item-data)
(pcb/change-parent (:id group) (reverse shapes)) (pcb/change-parent (:id group) (reverse shapes))
(pcb/update-shapes (map :id shapes-to-detach) ctk/detach-shape) (pcb/update-shapes (map :id shapes-to-detach) ctk/detach-shape)

View file

@ -533,69 +533,11 @@
(assoc state :workspace-modifiers modif-tree))))) (assoc state :workspace-modifiers modif-tree)))))
(defn apply-modifiers (def ^:private xf:without-uuid-zero
([] (remove #(= % uuid/zero)))
(apply-modifiers nil))
([{:keys [modifiers undo-transation? stack-undo? ignore-constraints (def ^:private transform-attrs
ignore-snap-pixel ignore-touched undo-group page-id] #{:selrect
:or {undo-transation? true stack-undo? false ignore-constraints false
ignore-snap-pixel false ignore-touched false}}]
(ptk/reify ::apply-modifiers
ptk/WatchEvent
(watch [_ state _]
(let [text-modifiers (get state :workspace-text-modifier)
page-id (or page-id (:current-page-id state))
objects (dsh/lookup-page-objects state page-id)
object-modifiers
(if (some? modifiers)
(calculate-modifiers state ignore-constraints ignore-snap-pixel modifiers page-id)
(get state :workspace-modifiers))
ids
(into []
(remove #(= % uuid/zero))
(keys object-modifiers))
ids-with-children
(into ids
(mapcat (partial cfh/get-children-ids objects))
ids)
ignore-tree
(calculate-ignore-tree object-modifiers objects)
undo-id (js/Symbol)]
(rx/concat
(if undo-transation?
(rx/of (dwu/start-undo-transaction undo-id))
(rx/empty))
(rx/of (ptk/event ::dwg/move-frame-guides {:ids ids-with-children :modifiers object-modifiers})
(ptk/event ::dwcm/move-frame-comment-threads ids-with-children)
(dwsh/update-shapes
ids
(fn [shape]
(let [modif (get-in object-modifiers [(:id shape) :modifiers])
text-shape? (cfh/text-shape? shape)
position-data (when text-shape?
(dm/get-in text-modifiers [(:id shape) :position-data]))]
(-> shape
(gsh/transform-shape modif)
(cond-> (d/not-empty? position-data)
(assoc-position-data position-data shape))
(cond-> text-shape?
(update-grow-type shape)))))
{:reg-objects? true
:stack-undo? stack-undo?
:ignore-tree ignore-tree
:ignore-touched ignore-touched
:undo-group undo-group
:page-id page-id
;; Attributes that can change in the transform. This way we don't have to check
;; all the attributes
:attrs [:selrect
:points :points
:x :x
:y :y
@ -619,22 +561,93 @@
:layout-gap :layout-gap
:layout-padding :layout-padding
:layout-item-h-sizing :layout-item-h-sizing
:layout-item-margin
:layout-item-max-h :layout-item-max-h
:layout-item-max-w :layout-item-max-w
:layout-item-min-h :layout-item-min-h
:layout-item-min-w :layout-item-min-w
:layout-item-v-sizing :layout-item-v-sizing
:layout-padding-type :layout-padding-type
:layout-gap
:layout-item-margin :layout-item-margin
:layout-item-margin-type :layout-item-margin-type
:layout-grid-cells :layout-grid-cells
:layout-grid-columns :layout-grid-columns
:layout-grid-rows]}) :layout-grid-rows})
;; We've applied the text-modifier so we can dissoc the temporary data
(defn apply-modifiers*
"A lower-level version of apply-modifiers, that expects receive ready
to use objects, object-modifiers and text-modifiers."
[objects object-modifiers text-modifiers options]
(ptk/reify ::apply-modifiers*
ptk/WatchEvent
(watch [_ _ _]
(let [ids
(into [] xf:without-uuid-zero (keys object-modifiers))
ids-with-children
(into ids
(mapcat (partial cfh/get-children-ids objects))
ids)
ignore-tree
(calculate-ignore-tree object-modifiers objects)
options
(-> options
(assoc :reg-objects? true)
(assoc :ignore-tree ignore-tree)
;; Attributes that can change in the transform. This
;; way we don't have to check all the attributes
(assoc :attrs transform-attrs))
update-shape
(fn [shape]
(let [shape-id (dm/get-prop shape :id)
modifiers (dm/get-in object-modifiers [shape-id :modifiers])
text-shape? (cfh/text-shape? shape)
pos-data (when ^boolean text-shape?
(dm/get-in text-modifiers [shape-id :position-data]))]
(-> shape
(gsh/transform-shape modifiers)
(cond-> (d/not-empty? pos-data)
(assoc-position-data pos-data shape))
(cond-> text-shape?
(update-grow-type shape)))))]
(rx/of (ptk/event ::dwg/move-frame-guides {:ids ids-with-children :modifiers object-modifiers})
(ptk/event ::dwcm/move-frame-comment-threads ids-with-children)
(dwsh/update-shapes ids update-shape options))))))
(defn apply-modifiers
([]
(apply-modifiers nil))
([{:keys [modifiers undo-transation? ignore-constraints
ignore-snap-pixel page-id]
:or {undo-transation? true ignore-constraints false
ignore-snap-pixel false}
:as options}]
(ptk/reify ::apply-modifiers
ptk/WatchEvent
(watch [_ state _]
(let [text-modifiers (get state :workspace-text-modifier)
page-id (or page-id (:current-page-id state))
objects (dsh/lookup-page-objects state page-id)
object-modifiers
(if (some? modifiers)
(calculate-modifiers state ignore-constraints ignore-snap-pixel modifiers page-id)
(get state :workspace-modifiers))
undo-id
(js/Symbol)]
(rx/concat
(if undo-transation?
(rx/of (dwu/start-undo-transaction undo-id))
(rx/empty))
(rx/of (apply-modifiers* objects object-modifiers text-modifiers options)
(fn [state] (fn [state]
(update state :workspace-text-modifier #(apply dissoc % ids)))) (let [ids (into [] xf:without-uuid-zero (keys object-modifiers))]
(update state :workspace-text-modifier #(apply dissoc % ids)))))
(if (nil? modifiers) (if (nil? modifiers)
(rx/of (clear-local-transform)) (rx/of (clear-local-transform))
(rx/empty)) (rx/empty))

View file

@ -51,11 +51,8 @@
([ids update-fn {:keys [reg-objects? save-undo? stack-undo? attrs ignore-tree page-id ignore-touched undo-group with-objects? changed-sub-attr] ([ids update-fn {:keys [reg-objects? save-undo? stack-undo? attrs ignore-tree page-id ignore-touched undo-group with-objects? changed-sub-attr]
:or {reg-objects? false save-undo? true stack-undo? false ignore-touched false with-objects? false}}] :or {reg-objects? false save-undo? true stack-undo? false ignore-touched false with-objects? false}}]
(dm/assert! (assert (sm/check-coll-of-uuid ids))
"expected a valid coll of uuid's" (assert (fn? update-fn))
(sm/check-coll-of-uuid! ids))
(dm/assert! (fn? update-fn))
(ptk/reify ::update-shapes (ptk/reify ::update-shapes
ptk/WatchEvent ptk/WatchEvent
@ -162,9 +159,7 @@
([ids] (delete-shapes nil ids {})) ([ids] (delete-shapes nil ids {}))
([page-id ids] (delete-shapes page-id ids {})) ([page-id ids] (delete-shapes page-id ids {}))
([page-id ids options] ([page-id ids options]
(dm/assert! (assert (sm/check-set-of-uuid ids))
"expected a valid set of uuid's"
(sm/check-set-of-uuid! ids))
(ptk/reify ::delete-shapes (ptk/reify ::delete-shapes
ptk/WatchEvent ptk/WatchEvent

View file

@ -340,35 +340,35 @@
(rx/filter (ptk/type? ::trigger-bounding-box-cloaking) stream))))))) (rx/filter (ptk/type? ::trigger-bounding-box-cloaking) stream)))))))
(defn update-dimensions (defn update-dimensions
"Change size of shapes, from the sideber options form. "Change size of shapes, from the sidebar options form
Will ignore pixel snap used in the options side panel" (will ignore pixel snap)"
([ids attr value] (update-dimensions ids attr value nil)) ([ids attr value] (update-dimensions ids attr value nil))
([ids attr value options] ([ids attr value options]
(dm/assert! (number? value)) (assert (number? value))
(dm/assert! (assert (every? uuid? ids)
"expected valid coll of uuids" "expected valid coll of uuids")
(every? uuid? ids)) (assert (contains? #{:width :height} attr)
(dm/assert! "expected valid attr")
"expected valid attr"
(contains? #{:width :height} attr))
(ptk/reify ::update-dimensions (ptk/reify ::update-dimensions
ptk/UpdateEvent ptk/WatchEvent
(update [_ state] (watch [_ state _]
(let [page-id (or (get options :page-id) (let [page-id
(or (get options :page-id)
(get state :current-page-id)) (get state :current-page-id))
objects (dsh/lookup-page-objects state page-id) objects
(dsh/lookup-page-objects state page-id)
get-modifier get-modifier
(fn [shape] (ctm/change-dimensions-modifiers shape attr value)) (fn [shape]
(ctm/change-dimensions-modifiers shape attr value))
modif-tree modif-tree
(-> (dwm/build-modif-tree ids objects get-modifier) (-> (dwm/build-modif-tree ids objects get-modifier)
(gm/set-objects-modifiers objects))] (gm/set-objects-modifiers objects))]
(assoc state :workspace-modifiers modif-tree))) (rx/of (dwm/apply-modifiers* objects modif-tree nil options)))))))
ptk/WatchEvent
(watch [_ _ _]
(rx/of (dwm/apply-modifiers options))))))
(defn change-orientation (defn change-orientation
"Change orientation of shapes, from the sidebar options form. "Change orientation of shapes, from the sidebar options form.
@ -859,27 +859,27 @@
(rx/of (reorder-selected-layout-child direction)) (rx/of (reorder-selected-layout-child direction))
(rx/of (nudge-selected-shapes direction shift?))))))) (rx/of (nudge-selected-shapes direction shift?)))))))
(defn- get-delta [position bbox] (defn- calculate-delta
(let [cpos (gpt/point (:x bbox) (:y bbox)) [position bbox relative-to]
pos (gpt/point (or (:x position) (:x bbox)) (let [current (gpt/point (:x bbox) (:y bbox))
(or (:y position) (:y bbox)))] position (gpt/point (or (some-> (:x position) (+ (dm/get-prop relative-to :x)))
(gpt/subtract pos cpos))) (:x bbox))
(or (some-> (:y position) (+ (dm/get-prop relative-to :y)))
(defn- get-relative-delta [position bbox frame] (:y bbox)))]
(let [frame-bbox (-> frame :points grc/points->rect) (gpt/subtract position current)))
relative-cpos (gpt/subtract (gpt/point (:x bbox) (:y bbox))
(gpt/point (:x frame-bbox)
(:y frame-bbox)))
cpos (gpt/point (:x relative-cpos) (:y relative-cpos))
pos (gpt/point (or (:x position) (:x relative-cpos))
(or (:y position) (:y relative-cpos)))]
(gpt/subtract pos cpos)))
(defn update-position (defn update-position
"Move shapes to a new position" "Move shapes to a new position. It will resolve to the current frame
of the shape, unless given the absolute option. In this case it will
resolve to the root frame of the page.
The position is a map that can have a partial position (it means it
can receive {:x 10}."
([id position] (update-position id position nil)) ([id position] (update-position id position nil))
([id position options] ([id position options]
(dm/assert! (uuid? id)) (assert (uuid? id) "expected a valid uuid for `id`")
(assert (map? position) "expected a valid map for `position`")
(ptk/reify ::update-position (ptk/reify ::update-position
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
@ -887,14 +887,16 @@
(get state :current-page-id)) (get state :current-page-id))
objects (dsh/lookup-page-objects state page-id) objects (dsh/lookup-page-objects state page-id)
shape (get objects id) shape (get objects id)
;; FIXME: performance rect
bbox (-> shape :points grc/points->rect) bbox (-> shape :points grc/points->rect)
frame (cfh/get-frame objects shape) frame (if (:absolute? options)
delta (if (:absolute? options) (cfh/get-frame objects)
(get-delta position bbox) (cfh/get-parent-frame objects shape))
(get-relative-delta position bbox frame))
modif-tree (dwm/create-modif-tree [id] (ctm/move-modifiers delta))] delta (calculate-delta position bbox frame)
(rx/of (dwm/apply-modifiers {:modifiers modif-tree modifiers (dwm/create-modif-tree [id] (ctm/move-modifiers delta))]
(rx/of (dwm/apply-modifiers {:modifiers modifiers
:page-id page-id :page-id page-id
:ignore-constraints false :ignore-constraints false
:ignore-touched (:ignore-touched options) :ignore-touched (:ignore-touched options)

View file

@ -22,7 +22,8 @@
;; Change this to :info :debug or :trace to debug this module ;; Change this to :info :debug or :trace to debug this module
(log/set-level! :warn) (log/set-level! :warn)
(def discard-transaction-time-millis (* 20 1000)) (def ^:private
discard-transaction-time-millis (* 20 1000))
(def ^:private (def ^:private
schema:undo-entry schema:undo-entry
@ -30,7 +31,7 @@
[:undo-changes [:vector ::cpc/change]] [:undo-changes [:vector ::cpc/change]]
[:redo-changes [:vector ::cpc/change]]]) [:redo-changes [:vector ::cpc/change]]])
(def check-undo-entry! (def check-undo-entry
(sm/check-fn schema:undo-entry)) (sm/check-fn schema:undo-entry))
(def MAX-UNDO-SIZE 50) (def MAX-UNDO-SIZE 50)
@ -48,8 +49,7 @@
(ptk/reify ::materialize-undo (ptk/reify ::materialize-undo
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(-> state (update state :workspace-undo assoc :index index))))
(assoc-in [:workspace-undo :index] index)))))
(defn- add-undo-entry (defn- add-undo-entry
[state entry] [state entry]
@ -88,12 +88,9 @@
(defn append-undo (defn append-undo
[entry stack?] [entry stack?]
(dm/assert!
"expected valid undo entry"
(check-undo-entry! entry))
(dm/assert! (assert (check-undo-entry entry))
(boolean? stack?)) (assert (boolean? stack?))
(ptk/reify ::append-undo (ptk/reify ::append-undo
ptk/UpdateEvent ptk/UpdateEvent
@ -118,17 +115,11 @@
(defn start-undo-transaction (defn start-undo-transaction
"Start a transaction, so that every changes inside are added together in a single undo entry." "Start a transaction, so that every changes inside are added together in a single undo entry."
[id] [id & {:keys [timeout] :or {timeout discard-transaction-time-millis}}]
(ptk/reify ::start-undo-transaction (ptk/reify ::start-undo-transaction
ptk/WatchEvent
(watch [_ _ _]
(->> (rx/of (check-open-transactions))
;; Wait the configured time
(rx/delay discard-transaction-time-millis)))
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(log/info :msg "start-undo-transaction") (log/info :hint "start-undo-transaction")
;; We commit the old transaction before starting the new one ;; We commit the old transaction before starting the new one
(let [current-tx (get-in state [:workspace-undo :transaction]) (let [current-tx (get-in state [:workspace-undo :transaction])
pending-tx (get-in state [:workspace-undo :transactions-pending])] pending-tx (get-in state [:workspace-undo :transactions-pending])]
@ -136,20 +127,28 @@
(nil? current-tx) (assoc-in [:workspace-undo :transaction] empty-tx) (nil? current-tx) (assoc-in [:workspace-undo :transaction] empty-tx)
(nil? pending-tx) (assoc-in [:workspace-undo :transactions-pending] #{id}) (nil? pending-tx) (assoc-in [:workspace-undo :transactions-pending] #{id})
(some? pending-tx) (update-in [:workspace-undo :transactions-pending] conj id) (some? pending-tx) (update-in [:workspace-undo :transactions-pending] conj id)
:always (update-in [:workspace-undo :transactions-pending-ts] assoc id (dt/now))))))) :always (update-in [:workspace-undo :transactions-pending-ts] assoc id (dt/now)))))
ptk/WatchEvent
(watch [_ _ _]
(when (and timeout (pos? timeout))
(->> (rx/of (check-open-transactions timeout))
;; Wait the configured time
(rx/delay timeout))))))
(defn discard-undo-transaction [] (defn discard-undo-transaction []
(ptk/reify ::discard-undo-transaction (ptk/reify ::discard-undo-transaction
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(log/info :msg "discard-undo-transaction") (log/info :hint "discard-undo-transaction")
(update state :workspace-undo dissoc :transaction :transactions-pending :transactions-pending-ts)))) (update state :workspace-undo dissoc :transaction :transactions-pending :transactions-pending-ts))))
(defn commit-undo-transaction [id] (defn commit-undo-transaction [id]
(ptk/reify ::commit-undo-transaction (ptk/reify ::commit-undo-transaction
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(log/info :msg "commit-undo-transaction") (log/info :hint "commit-undo-transaction")
(let [state (-> state (let [state (-> state
(update-in [:workspace-undo :transactions-pending] disj id) (update-in [:workspace-undo :transactions-pending] disj id)
(update-in [:workspace-undo :transactions-pending-ts] dissoc id))] (update-in [:workspace-undo :transactions-pending-ts] dissoc id))]
@ -166,15 +165,15 @@
(assoc state :workspace-undo {})))) (assoc state :workspace-undo {}))))
(defn check-open-transactions (defn check-open-transactions
[] [timeout]
(ptk/reify ::check-open-transactions (ptk/reify ::check-open-transactions
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(log/info :msg "check-open-transactions") (log/info :hint "check-open-transactions" :timeout timeout)
(let [pending-ts (-> (dm/get-in state [:workspace-undo :transactions-pending-ts]) (let [pending-ts (-> (dm/get-in state [:workspace-undo :transactions-pending-ts])
(update-vals #(.toMillis (dt/diff (dt/now) %))))] (update-vals #(inst-ms (dt/diff (dt/now) %))))]
(->> pending-ts (->> pending-ts
(filter (fn [[_ ts]] (>= ts discard-transaction-time-millis))) (filter (fn [[_ ts]] (>= ts timeout)))
(rx/from) (rx/from)
(rx/tap #(js/console.warn (dm/str "FORCE COMMIT TRANSACTION AFTER " (second %) "MS"))) (rx/tap #(js/console.warn (dm/str "FORCE COMMIT TRANSACTION AFTER " (second %) "MS")))
(rx/map first) (rx/map first)

View file

@ -112,16 +112,16 @@
(dom/stop-propagation event) (dom/stop-propagation event)
(on-import item event))))] (on-import item event))))]
[:a {:class (stl/css :card-container) [:div {:class (stl/css :card-container)
:tab-index (if (or (not is-visible) collapsed) "-1" "0") :tab-index (if (or (not is-visible) collapsed) "-1" "0")
:id id :id id
:data-index index :data-index index}
[:a {:class (stl/css :template-card)
:on-click on-click :on-click on-click
:on-mouse-down dom/prevent-default :on-mouse-down dom/prevent-default
:on-mouse-enter #(reset! hover? true) :on-mouse-enter #(reset! hover? true)
:on-mouse-leave #(reset! hover? false) :on-mouse-leave #(reset! hover? false)
:on-key-down on-key-down} :on-key-down on-key-down}
[:div {:class (stl/css :template-card)}
[:div {:class (stl/css :img-container)} [:div {:class (stl/css :img-container)}
[:img {:src (dm/str thb) [:img {:src (dm/str thb)
:alt (:name item) :alt (:name item)

View file

@ -205,10 +205,10 @@
;; ORIENTATION ;; ORIENTATION
orientation (when (= type :frame) orientation
(cond (> (:width values) (:height values)) (when (= type :frame)
(if (> (:width values) (:height values))
:horiz :horiz
:else
:vert)) :vert))
on-orientation-change on-orientation-change
@ -235,10 +235,8 @@
(run! #(st/emit! (udw/set-shape-proportion-lock % new-lock)) ids)))) (run! #(st/emit! (udw/set-shape-proportion-lock % new-lock)) ids))))
;; POSITION ;; POSITION
do-position-change do-position-change
(mf/use-fn (mf/use-fn
(mf/deps ids)
(fn [shape' value attr] (fn [shape' value attr]
(st/emit! (udw/update-position (:id shape') {attr value})))) (st/emit! (udw/update-position (:id shape') {attr value}))))
@ -248,7 +246,7 @@
(fn [value attr] (fn [value attr]
(st/emit! (udw/trigger-bounding-box-cloaking ids)) (st/emit! (udw/trigger-bounding-box-cloaking ids))
(binding [cts/*wasm-sync* true] (binding [cts/*wasm-sync* true]
(doall (map #(do-position-change %1 value attr) shapes))))) (run! #(do-position-change %1 value attr) shapes))))
;; ROTATION ;; ROTATION

View file

@ -304,13 +304,11 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(when (number? value) (when (number? value)
(let [page-id' (or page-id (get state :current-page-id))] (let [page-id (or page-id (get state :current-page-id))]
(rx/concat (->> (rx/from shape-ids)
(map #(dwt/update-position % (zipmap attributes (repeat value)) (rx/map #(dwt/update-position % (zipmap attributes (repeat value))
{:ignore-touched true {:ignore-touched true
:page-id page-id'}) :page-id page-id})))))))))
shape-ids))))))))
(defn update-layout-sizing-limits (defn update-layout-sizing-limits
([value shape-ids attributes] (update-layout-sizing-limits value shape-ids attributes nil)) ([value shape-ids attributes] (update-layout-sizing-limits value shape-ids attributes nil))

View file

@ -140,24 +140,18 @@
[:div {:class (stl/css :theme-row-right)} [:div {:class (stl/css :theme-row-right)}
(if-let [sets-count (some-> theme :sets seq count)] (let [sets-count (some-> theme :sets seq count)]
[:> button* {:class (stl/css :sets-count-button) [:> button* {:class (stl/css-case :sets-count-button sets-count
:sets-count-empty-button (not sets-count))
:variant "secondary" :variant "secondary"
:type "button" :type "button"
:title (tr "workspace.token.sets-hint") :title (tr "workspace.token.sets-hint")
:on-click on-edit-theme} :on-click on-edit-theme}
[:div {:class (stl/css :label-wrapper)} [:div {:class (stl/css :label-wrapper)}
[:> text* {:as "span" :typography "body-medium"} [:> text* {:as "span" :typography "body-medium"}
(tr "workspace.token.num-active-sets" sets-count)] (if sets-count
[:> icon* {:icon-id "arrow-right"}]]] (tr "workspace.token.num-active-sets" sets-count)
(tr "workspace.token.no-active-sets"))]
[:> button* {:class (stl/css :sets-count-empty-button)
:type "button"
:variant "secondary"
:on-click on-edit-theme}
[:div {:class (stl/css :label-wrapper)}
[:> text* {:as "span" :typography "body-medium"}
(tr "workspace.token.no-active-sets")]
[:> icon* {:icon-id "arrow-right"}]]]) [:> icon* {:icon-id "arrow-right"}]]])
[:> icon-button* {:on-click delete-theme [:> icon-button* {:on-click delete-theme

View file

@ -368,13 +368,13 @@
(mf/use-fn (mf/use-fn
(mf/deps collapsed-paths) (mf/deps collapsed-paths)
(fn [tree-index position data] (fn [tree-index position data]
(let [props {:from-index (:index data) (let [params {:from-index (:index data)
:to-index tree-index :to-index tree-index
:position position :position position
:collapsed-paths collapsed-paths}] :collapsed-paths collapsed-paths}]
(if (:is-group data) (if (:is-group data)
(st/emit! (dt/drop-token-set-group props)) (st/emit! (dt/drop-token-set-group params))
(st/emit! (dt/drop-token-set props)))))) (st/emit! (dt/drop-token-set params))))))
on-toggle-collapse on-toggle-collapse
(mf/use-fn (mf/use-fn

View file

@ -103,8 +103,9 @@
(defn- generate-tooltip (defn- generate-tooltip
"Generates a tooltip for a given token" "Generates a tooltip for a given token"
[is-viewer shape theme-token token half-applied no-valid-value ref-not-in-active-set] [is-viewer shape theme-token token half-applied no-valid-value ref-not-in-active-set]
(let [{:keys [name value type]} token (let [{:keys [name value type resolved-value]} token
{:keys [resolved-value]} theme-token resolved-value-theme (:resolved-value theme-token)
resolved-value (or resolved-value-theme resolved-value)
{:keys [title] :as token-props} (wtch/get-token-properties theme-token) {:keys [title] :as token-props} (wtch/get-token-properties theme-token)
applied-tokens (:applied-tokens shape) applied-tokens (:applied-tokens shape)
app-token-vals (set (vals applied-tokens)) app-token-vals (set (vals applied-tokens))

View file

@ -1,6 +1,13 @@
;; 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.update (ns app.main.ui.workspace.tokens.update
(:require (:require
[app.common.files.helpers :as cfh] [app.common.files.helpers :as cfh]
[app.common.logging :as l]
[app.common.types.token :as ctt] [app.common.types.token :as ctt]
[app.main.data.helpers :as dsh] [app.main.data.helpers :as dsh]
[app.main.data.workspace.shapes :as dwsh] [app.main.data.workspace.shapes :as dwsh]
@ -9,6 +16,7 @@
[app.main.ui.workspace.tokens.changes :as wtch] [app.main.ui.workspace.tokens.changes :as wtch]
[app.main.ui.workspace.tokens.style-dictionary :as wtsd] [app.main.ui.workspace.tokens.style-dictionary :as wtsd]
[app.main.ui.workspace.tokens.token-set :as wtts] [app.main.ui.workspace.tokens.token-set :as wtts]
[app.util.time :as dt]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[clojure.data :as data] [clojure.data :as data]
[clojure.set :as set] [clojure.set :as set]
@ -70,7 +78,7 @@
(reduce (reduce
(fn [acc [attrs v]] (fn [acc [attrs v]]
(cond (cond
(some attrs #{:widht :height}) (let [[_ a b] (data/diff #{:width :height} attrs)] (some attrs #{:width :height}) (let [[_ a b] (data/diff #{:width :height} attrs)]
(cond-> (assoc acc b v) (cond-> (assoc acc b v)
;; Exact match in attrs ;; Exact match in attrs
a (assoc a v))) a (assoc a v)))
@ -127,8 +135,14 @@
[state resolved-tokens] [state resolved-tokens]
(let [file-id (get state :current-file-id) (let [file-id (get state :current-file-id)
current-page-id (get state :current-page-id) current-page-id (get state :current-page-id)
fdata (dsh/lookup-file-data state file-id)] fdata (dsh/lookup-file-data state file-id)
tpoint (dt/tpoint-ms)]
(l/inf :status "START" :hint "update-tokens")
(->> (rx/concat
(rx/of current-page-id)
(->> (rx/from (:pages fdata)) (->> (rx/from (:pages fdata))
(rx/filter (fn [id] (not= id current-page-id)))))
(rx/mapcat (rx/mapcat
(fn [page-id] (fn [page-id]
(let [page (let [page
@ -140,6 +154,12 @@
actions actions
(actionize-shapes-update-info page-id attrs)] (actionize-shapes-update-info page-id attrs)]
(l/inf :status "PROGRESS"
:hint "update-tokens"
:page-id (str page-id)
:elapsed (tpoint)
::l/sync? true)
(rx/merge (rx/merge
(rx/from actions) (rx/from actions)
(->> (rx/from frame-ids) (->> (rx/from frame-ids)
@ -151,7 +171,11 @@
(fn [shape] (fn [shape]
(dissoc shape :position-data)) (dissoc shape :position-data))
{:page-id page-id {:page-id page-id
:ignore-touched true})))))))))) :ignore-touched true})))))))
(rx/finalize
(fn [_]
(let [elapsed (tpoint)]
(l/inf :status "END" :hint "update-tokens" :elapsed elapsed)))))))
(defn update-workspace-tokens (defn update-workspace-tokens
[] []
@ -164,6 +188,6 @@
(rx/mapcat (fn [sd-tokens] (rx/mapcat (fn [sd-tokens]
(let [undo-id (js/Symbol)] (let [undo-id (js/Symbol)]
(rx/concat (rx/concat
(rx/of (dwu/start-undo-transaction undo-id)) (rx/of (dwu/start-undo-transaction undo-id :timeout false))
(update-tokens state sd-tokens) (update-tokens state sd-tokens)
(rx/of (dwu/commit-undo-transaction undo-id))))))))))) (rx/of (dwu/commit-undo-transaction undo-id)))))))))))

View file

@ -176,8 +176,9 @@
(defn format-shadows (defn format-shadows
[shadows] [shadows]
(when (some? shadows) (if (some? shadows)
(format-array format-shadow shadows))) (format-array format-shadow shadows)
(array)))
;;export interface Fill { ;;export interface Fill {
;; fillColor?: string; ;; fillColor?: string;

View file

@ -6738,7 +6738,7 @@ msgstr "%s sets activos"
#: src/app/main/ui/workspace/tokens/modals/themes.cljs:147 #: src/app/main/ui/workspace/tokens/modals/themes.cljs:147
msgid "workspace.token.sets-hint" msgid "workspace.token.sets-hint"
msgstr "Editar tema y gestionar set" msgstr "Editar tema y gestionar sets"
#: src/app/main/ui/workspace/tokens/token_pill.cljs:114 #: src/app/main/ui/workspace/tokens/token_pill.cljs:114
#, fuzzy #, fuzzy