mirror of
https://github.com/penpot/penpot.git
synced 2025-05-18 14:56:12 +02:00
Merge remote-tracking branch 'origin/staging' into develop
This commit is contained in:
commit
22cd43b8a2
33 changed files with 634 additions and 655 deletions
|
@ -12,6 +12,7 @@
|
|||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
|
||||
## 2.6.0 (Unreleased)
|
||||
|
||||
### :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)
|
||||
- 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 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
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
(:require
|
||||
[app.binfile.common :as bfc]
|
||||
[app.common.data :as d]
|
||||
[app.common.files.migrations :as fmg]
|
||||
[app.common.files.validate :as cfv]
|
||||
[app.db :as db]
|
||||
[app.features.components-v2 :as feat.comp-v2]
|
||||
|
@ -142,7 +143,9 @@
|
|||
(update-fn file opts)))]
|
||||
|
||||
(when (and (some? file')
|
||||
(not (identical? file file')))
|
||||
(or (fmg/migrated? file)
|
||||
(not (identical? file file'))))
|
||||
|
||||
(when validate?
|
||||
(cfv/validate-file-schema! file'))
|
||||
|
||||
|
|
|
@ -61,7 +61,8 @@
|
|||
"styles/v2"
|
||||
"layout/grid"
|
||||
"components/v2"
|
||||
"plugins/runtime"})
|
||||
"plugins/runtime"
|
||||
"design-tokens/v1"})
|
||||
|
||||
;; 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
|
||||
|
|
|
@ -382,13 +382,13 @@
|
|||
[:set-group-path [:vector :string]]
|
||||
[:set-group-fname :string]]]
|
||||
|
||||
[:move-token-set-before
|
||||
[:map {:title "MoveTokenSetBefore"}
|
||||
[:type [:= :move-token-set-before]]
|
||||
[:move-token-set
|
||||
[:map {:title "MoveTokenSet"}
|
||||
[:type [:= :move-token-set]]
|
||||
[:from-path [:vector :string]]
|
||||
[:to-path [:vector :string]]
|
||||
[:before-path [:maybe [:vector :string]]]
|
||||
[:before-group? [:maybe :boolean]]]]
|
||||
[:before-group [:maybe :boolean]]]]
|
||||
|
||||
[:move-token-set-group-before
|
||||
[:map {:title "MoveTokenSetGroupBefore"}
|
||||
|
@ -1051,11 +1051,11 @@
|
|||
(ctob/ensure-tokens-lib)
|
||||
(ctob/rename-set-group set-group-path set-group-fname)))))
|
||||
|
||||
(defmethod process-change :move-token-set-before
|
||||
[data {:keys [from-path to-path before-path before-group?] :as changes}]
|
||||
(defmethod process-change :move-token-set
|
||||
[data {:keys [from-path to-path before-path before-group] :as changes}]
|
||||
(update data :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
|
||||
[data {:keys [from-path to-path before-path before-group?]}]
|
||||
|
|
|
@ -809,19 +809,19 @@
|
|||
(update :undo-changes conj {:type :rename-token-set-group :set-group-path undo-path :set-group-fname undo-fname})
|
||||
(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
|
||||
(update :redo-changes conj {:type :move-token-set-before
|
||||
(update :redo-changes conj {:type :move-token-set
|
||||
:from-path from-path
|
||||
:to-path to-path
|
||||
:before-path before-path
|
||||
:before-group? before-group?})
|
||||
(update :undo-changes conj {:type :move-token-set-before
|
||||
:before-group before-group?})
|
||||
(update :undo-changes conj {:type :move-token-set
|
||||
:from-path to-path
|
||||
:to-path from-path
|
||||
:before-path prev-before-path
|
||||
:before-group? prev-before-group?})
|
||||
:before-group prev-before-group?})
|
||||
(apply-changes-local)))
|
||||
|
||||
(defn move-token-set-group-before
|
||||
|
|
|
@ -283,6 +283,22 @@
|
|||
:else
|
||||
(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?
|
||||
[objects parent-id shape-id]
|
||||
(let [shape (get objects shape-id)]
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
[app.common.types.file :as ctf]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.common.types.shape.shadow :as ctss]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.common.uuid :as uuid]
|
||||
[clojure.set :as set]
|
||||
[cuerdas.core :as str]))
|
||||
|
@ -97,13 +96,13 @@
|
|||
(if (nil? migrations)
|
||||
(generate-migrations-from-version version)
|
||||
migrations)))
|
||||
(migrate)
|
||||
(update :features (fnil into #{}) (deref cfeat/*new*))
|
||||
;; NOTE: in some future we can consider to apply
|
||||
;; a migration to the whole database and remove
|
||||
;; this code from this function that executes on
|
||||
;; each file migration operation
|
||||
(update :features cfeat/migrate-legacy-features)))))
|
||||
(update :features cfeat/migrate-legacy-features)
|
||||
(migrate)))))
|
||||
|
||||
(defn migrated?
|
||||
[file]
|
||||
|
@ -1226,32 +1225,7 @@
|
|||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "Ensure hidden theme"
|
||||
[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"
|
||||
(defmethod migrate-data "0001-remove-tokens-from-groups"
|
||||
[data _]
|
||||
(letfn [(update-object [object]
|
||||
(cond-> object
|
||||
|
@ -1320,6 +1294,4 @@
|
|||
"legacy-65"
|
||||
"legacy-66"
|
||||
"legacy-67"
|
||||
"Ensure hidden theme"
|
||||
"Add token theme id"
|
||||
"Remove tokens from groups"]))
|
||||
"0001-remove-tokens-from-groups"]))
|
||||
|
|
|
@ -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
|
||||
(:require
|
||||
[app.common.files.changes-builder :as pcb]
|
||||
[app.common.types.tokens-lib :as ctob]))
|
||||
|
||||
(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`.
|
||||
Use this for managing sets active state without having to modify a user created theme (\"no themes selected\" state in the ui)."
|
||||
"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`.
|
||||
|
||||
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]
|
||||
(let [prev-active-token-themes (ctob/get-active-theme-paths tokens-lib)
|
||||
active-token-set-names (ctob/get-active-themes-set-names tokens-lib)
|
||||
|
@ -21,7 +31,8 @@
|
|||
hidden-token-theme))))
|
||||
|
||||
(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]
|
||||
(generate-update-active-sets changes tokens-lib #(ctob/toggle-set % set-name)))
|
||||
|
||||
|
@ -49,12 +60,20 @@
|
|||
:or {collapsed-paths #{}}}]
|
||||
(let [tree (-> (ctob/get-set-tree tokens-lib)
|
||||
(ctob/walk-sets-tree-seq :skip-children-pred #(contains? collapsed-paths %)))
|
||||
|
||||
from (nth tree from-index)
|
||||
to (nth tree to-index)
|
||||
before (case position
|
||||
: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)
|
||||
|
||||
prev-before (if (:group? from)
|
||||
(->> (drop (inc from-index) tree)
|
||||
(filter (fn [element]
|
||||
|
@ -113,9 +132,9 @@
|
|||
(defn generate-move-token-set
|
||||
"Create changes for dropping a token set or token set.
|
||||
Throws for impossible moves."
|
||||
[changes tokens-lib drop-opts]
|
||||
(if-let [drop-opts' (calculate-move-token-set-or-set-group tokens-lib drop-opts)]
|
||||
(pcb/move-token-set-before changes drop-opts')
|
||||
[changes tokens-lib params]
|
||||
(if-let [params (calculate-move-token-set-or-set-group tokens-lib params)]
|
||||
(pcb/move-token-set changes params)
|
||||
changes))
|
||||
|
||||
(defn generate-move-token-set-group
|
||||
|
|
|
@ -1019,26 +1019,26 @@
|
|||
(def valid-text?
|
||||
(validator ::text))
|
||||
|
||||
(def check-safe-int!
|
||||
(def check-safe-int
|
||||
(check-fn ::safe-int))
|
||||
|
||||
(def check-set-of-strings!
|
||||
(def check-set-of-strings
|
||||
(check-fn ::set-of-strings))
|
||||
|
||||
(def check-email!
|
||||
(def check-email
|
||||
(check-fn ::email))
|
||||
|
||||
(def check-uuid!
|
||||
(def check-uuid
|
||||
(check-fn ::uuid :hint "expected valid uuid instance"))
|
||||
|
||||
(def check-string!
|
||||
(def check-string
|
||||
(check-fn :string :hint "expected string"))
|
||||
|
||||
(def check-coll-of-uuid!
|
||||
(def check-coll-of-uuid
|
||||
(check-fn ::coll-of-uuid))
|
||||
|
||||
(def check-set-of-uuid!
|
||||
(def check-set-of-uuid
|
||||
(check-fn ::set-of-uuid))
|
||||
|
||||
(def check-set-of-emails!
|
||||
(def check-set-of-emails
|
||||
(check-fn [::set ::email]))
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
(ns app.common.types.shape.interactions
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.files.helpers :as cfh]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.bounds :as gsb]
|
||||
|
@ -180,7 +179,7 @@
|
|||
|
||||
(sm/register! ::interaction schema:interaction)
|
||||
|
||||
(def check-interaction!
|
||||
(def check-interaction
|
||||
(sm/check-fn schema:interaction))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -203,18 +202,13 @@
|
|||
|
||||
(defn set-event-type
|
||||
[interaction event-type shape]
|
||||
(dm/assert!
|
||||
"Should be an interraction map"
|
||||
(check-interaction! interaction))
|
||||
(assert (check-interaction interaction))
|
||||
(assert (contains? event-types event-type)
|
||||
"should be a valid event type")
|
||||
|
||||
(dm/assert!
|
||||
"Should be a valid event type"
|
||||
(contains? event-types event-type))
|
||||
|
||||
(dm/assert!
|
||||
"The `:after-delay` event type incompatible with not frame shapes"
|
||||
(or (not= event-type :after-delay)
|
||||
(cfh/frame-shape? shape)))
|
||||
(assert (or (not= event-type :after-delay)
|
||||
(cfh/frame-shape? shape))
|
||||
"the `:after-delay` event type incompatible with not frame shapes")
|
||||
|
||||
(if (= (:event-type interaction) event-type)
|
||||
interaction
|
||||
|
@ -230,14 +224,9 @@
|
|||
|
||||
(defn set-action-type
|
||||
[interaction action-type]
|
||||
|
||||
(dm/assert!
|
||||
"Should be an interraction map"
|
||||
(check-interaction! interaction))
|
||||
|
||||
(dm/assert!
|
||||
"Should be a valid event type"
|
||||
(contains? action-types action-type))
|
||||
(assert (check-interaction interaction))
|
||||
(assert (contains? action-types action-type)
|
||||
"Should be a valid event type")
|
||||
|
||||
(let [new-interaction
|
||||
(if (= (:action-type interaction) action-type)
|
||||
|
@ -284,18 +273,10 @@
|
|||
|
||||
(defn set-delay
|
||||
[interaction delay]
|
||||
|
||||
(dm/assert!
|
||||
"expected valid interaction map"
|
||||
(check-interaction! interaction))
|
||||
|
||||
(dm/assert!
|
||||
"expected valid delay"
|
||||
(sm/check-safe-int! delay))
|
||||
|
||||
(dm/assert!
|
||||
"expected compatible interaction event type"
|
||||
(has-delay interaction))
|
||||
(assert (check-interaction interaction))
|
||||
(assert (sm/check-safe-int delay))
|
||||
(assert (has-delay interaction)
|
||||
"expected compatible interaction event type")
|
||||
|
||||
(assoc interaction :delay delay))
|
||||
|
||||
|
@ -315,14 +296,9 @@
|
|||
|
||||
(defn set-destination
|
||||
[interaction destination]
|
||||
|
||||
(dm/assert!
|
||||
"expected valid interaction map"
|
||||
(check-interaction! interaction))
|
||||
|
||||
(dm/assert!
|
||||
"expected compatible interaction event type"
|
||||
(has-destination interaction))
|
||||
(assert (check-interaction interaction))
|
||||
(assert (has-destination interaction)
|
||||
"expected compatible interaction event type")
|
||||
|
||||
(cond-> interaction
|
||||
:always
|
||||
|
@ -340,17 +316,11 @@
|
|||
(defn set-preserve-scroll
|
||||
[interaction preserve-scroll]
|
||||
|
||||
(dm/assert!
|
||||
"expected valid interaction map"
|
||||
(check-interaction! interaction))
|
||||
|
||||
(dm/assert!
|
||||
"expected boolean for `preserve-scroll`"
|
||||
(boolean? preserve-scroll))
|
||||
|
||||
(dm/assert!
|
||||
"expected compatible interaction map with preserve-scroll"
|
||||
(has-preserve-scroll interaction))
|
||||
(assert (check-interaction interaction))
|
||||
(assert (boolean? preserve-scroll)
|
||||
"expected boolean for `preserve-scroll`")
|
||||
(assert (has-preserve-scroll interaction)
|
||||
"expected compatible interaction map with preserve-scroll")
|
||||
|
||||
(assoc interaction :preserve-scroll preserve-scroll))
|
||||
|
||||
|
@ -361,17 +331,11 @@
|
|||
(defn set-url
|
||||
[interaction url]
|
||||
|
||||
(dm/assert!
|
||||
"expected valid interaction map"
|
||||
(check-interaction! interaction))
|
||||
|
||||
(dm/assert!
|
||||
"expected a string for `url`"
|
||||
(string? url))
|
||||
|
||||
(dm/assert!
|
||||
"expected compatible interaction map with url param"
|
||||
(has-url interaction))
|
||||
(assert (check-interaction interaction))
|
||||
(assert (string? url)
|
||||
"expected a string for `url`")
|
||||
(assert (has-url interaction)
|
||||
"expected compatible interaction map with url param")
|
||||
|
||||
(assoc interaction :url url))
|
||||
|
||||
|
@ -382,17 +346,12 @@
|
|||
(defn set-overlay-pos-type
|
||||
[interaction overlay-pos-type shape objects]
|
||||
|
||||
(dm/assert!
|
||||
"expected valid interaction map"
|
||||
(check-interaction! interaction))
|
||||
(assert (check-interaction interaction))
|
||||
|
||||
(dm/assert!
|
||||
"expected valid overlay positioning type"
|
||||
(contains? overlay-positioning-types overlay-pos-type))
|
||||
|
||||
(dm/assert!
|
||||
"expected compatible interaction map"
|
||||
(has-overlay-opts interaction))
|
||||
(assert (contains? overlay-positioning-types overlay-pos-type)
|
||||
"expected valid overlay positioning type")
|
||||
(assert (has-overlay-opts interaction)
|
||||
"expected compatible interaction map")
|
||||
|
||||
(assoc interaction
|
||||
:overlay-pos-type overlay-pos-type
|
||||
|
@ -403,17 +362,11 @@
|
|||
(defn toggle-overlay-pos-type
|
||||
[interaction overlay-pos-type shape objects]
|
||||
|
||||
(dm/assert!
|
||||
"expected valid interaction map"
|
||||
(check-interaction! interaction))
|
||||
|
||||
(dm/assert!
|
||||
"expected valid overlay positioning type"
|
||||
(contains? overlay-positioning-types overlay-pos-type))
|
||||
|
||||
(dm/assert!
|
||||
"expected compatible interaction map"
|
||||
(has-overlay-opts interaction))
|
||||
(assert (check-interaction interaction))
|
||||
(assert (contains? overlay-positioning-types overlay-pos-type)
|
||||
"expected valid overlay positioning type")
|
||||
(assert (has-overlay-opts interaction)
|
||||
"expected compatible interaction map")
|
||||
|
||||
(let [new-pos-type (if (= (:overlay-pos-type interaction) overlay-pos-type)
|
||||
:manual
|
||||
|
@ -427,17 +380,12 @@
|
|||
(defn set-overlay-position
|
||||
[interaction overlay-position]
|
||||
|
||||
(dm/assert!
|
||||
"expected valid interaction map"
|
||||
(check-interaction! interaction))
|
||||
(assert (check-interaction interaction))
|
||||
(assert (gpt/point? overlay-position)
|
||||
"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
|
||||
:overlay-pos-type :manual
|
||||
|
@ -446,52 +394,34 @@
|
|||
(defn set-close-click-outside
|
||||
[interaction close-click-outside]
|
||||
|
||||
(dm/assert!
|
||||
"expected valid interaction map"
|
||||
(check-interaction! interaction))
|
||||
|
||||
(dm/assert!
|
||||
"expected boolean value for `close-click-outside`"
|
||||
(boolean? close-click-outside))
|
||||
|
||||
(dm/assert!
|
||||
"expected compatible interaction map"
|
||||
(has-overlay-opts interaction))
|
||||
(assert (check-interaction interaction))
|
||||
(assert (boolean? close-click-outside)
|
||||
"expected boolean value for `close-click-outside`")
|
||||
(assert (has-overlay-opts interaction)
|
||||
"expected compatible interaction map")
|
||||
|
||||
(assoc interaction :close-click-outside close-click-outside))
|
||||
|
||||
(defn set-background-overlay
|
||||
[interaction background-overlay]
|
||||
|
||||
(dm/assert!
|
||||
"expected valid interaction map"
|
||||
(check-interaction! interaction))
|
||||
|
||||
(dm/assert!
|
||||
"expected boolean value for `background-overlay`"
|
||||
(boolean? background-overlay))
|
||||
|
||||
(dm/assert!
|
||||
"expected compatible interaction map"
|
||||
(has-overlay-opts interaction))
|
||||
(assert (check-interaction interaction))
|
||||
(assert (boolean? background-overlay)
|
||||
"expected boolean value for `background-overlay`")
|
||||
(assert (has-overlay-opts interaction)
|
||||
"expected compatible interaction map")
|
||||
|
||||
(assoc interaction :background-overlay background-overlay))
|
||||
|
||||
(defn set-position-relative-to
|
||||
[interaction position-relative-to]
|
||||
|
||||
(dm/assert!
|
||||
"expected valid interaction map"
|
||||
(check-interaction! interaction))
|
||||
|
||||
(dm/assert!
|
||||
"expected valid uuid for `position-relative-to`"
|
||||
(or (nil? position-relative-to)
|
||||
(uuid? position-relative-to)))
|
||||
|
||||
(dm/assert!
|
||||
"expected compatible interaction map"
|
||||
(has-overlay-opts interaction))
|
||||
(assert (check-interaction interaction))
|
||||
(assert (or (nil? position-relative-to)
|
||||
(uuid? position-relative-to))
|
||||
"expected valid uuid for `position-relative-to`")
|
||||
(assert (has-overlay-opts interaction)
|
||||
"expected compatible interaction map")
|
||||
|
||||
(assoc interaction :position-relative-to position-relative-to))
|
||||
|
||||
|
@ -519,13 +449,9 @@
|
|||
frame-offset] ;; if this interaction starts in a frame opened
|
||||
;; on another interaction, this is the position
|
||||
;; of that frame
|
||||
(dm/assert!
|
||||
"expected valid interaction map"
|
||||
(check-interaction! interaction))
|
||||
|
||||
(dm/assert!
|
||||
"expected compatible interaction map"
|
||||
(has-overlay-opts interaction))
|
||||
(assert (check-interaction interaction))
|
||||
(assert (has-overlay-opts interaction)
|
||||
"expected compatible interaction map")
|
||||
|
||||
(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
|
||||
|
@ -617,22 +543,15 @@
|
|||
|
||||
(defn set-animation-type
|
||||
[interaction animation-type]
|
||||
(dm/assert!
|
||||
"expected valid interaction map"
|
||||
(check-interaction! interaction))
|
||||
|
||||
(dm/assert!
|
||||
"expected valid value for `animation-type`"
|
||||
(or (nil? animation-type)
|
||||
(contains? animation-types animation-type)))
|
||||
|
||||
(dm/assert!
|
||||
"expected interaction map compatible with animation"
|
||||
(has-animation? interaction))
|
||||
|
||||
(dm/assert!
|
||||
"expected allowed animation type"
|
||||
(allowed-animation? (:action-type interaction) animation-type))
|
||||
(assert (check-interaction interaction))
|
||||
(assert (or (nil? animation-type)
|
||||
(contains? animation-types animation-type))
|
||||
"expected valid value for `animation-type`")
|
||||
(assert (has-animation? interaction)
|
||||
"expected interaction map compatible with animation")
|
||||
(assert (allowed-animation? (:action-type interaction) animation-type)
|
||||
"expected allowed animation type")
|
||||
|
||||
(if (= (-> interaction :animation :animation-type) animation-type)
|
||||
interaction
|
||||
|
@ -668,17 +587,10 @@
|
|||
(defn set-duration
|
||||
[interaction duration]
|
||||
|
||||
(dm/assert!
|
||||
"expected valid interaction map"
|
||||
(check-interaction! interaction))
|
||||
|
||||
(dm/assert!
|
||||
"expected valid duration"
|
||||
(sm/check-safe-int! duration))
|
||||
|
||||
(dm/assert!
|
||||
"expected compatible interaction map"
|
||||
(has-duration? interaction))
|
||||
(assert (check-interaction interaction))
|
||||
(assert (sm/check-safe-int duration))
|
||||
(assert (has-duration? interaction)
|
||||
"expected compatible interaction map")
|
||||
|
||||
(update interaction :animation assoc :duration duration))
|
||||
|
||||
|
@ -689,17 +601,11 @@
|
|||
(defn set-easing
|
||||
[interaction easing]
|
||||
|
||||
(dm/assert!
|
||||
"expected valid interaction map"
|
||||
(check-interaction! interaction))
|
||||
|
||||
(dm/assert!
|
||||
"expected valid easing"
|
||||
(contains? easing-types easing))
|
||||
|
||||
(dm/assert!
|
||||
"expected compatible interaction map"
|
||||
(has-easing? interaction))
|
||||
(assert (check-interaction interaction))
|
||||
(assert (contains? easing-types easing)
|
||||
"expected valid easing")
|
||||
(assert (has-easing? interaction)
|
||||
"expected compatible interaction map")
|
||||
|
||||
(update interaction :animation assoc :easing easing))
|
||||
|
||||
|
@ -712,17 +618,11 @@
|
|||
(defn set-way
|
||||
[interaction way]
|
||||
|
||||
(dm/assert!
|
||||
"expected valid interaction map"
|
||||
(check-interaction! interaction))
|
||||
|
||||
(dm/assert!
|
||||
"expected valid way"
|
||||
(contains? way-types way))
|
||||
|
||||
(dm/assert!
|
||||
"expected compatible interaction map"
|
||||
(has-way? interaction))
|
||||
(assert (check-interaction interaction))
|
||||
(assert (contains? way-types way)
|
||||
"expected valid way")
|
||||
(assert (has-way? interaction)
|
||||
"expected compatible interaction map")
|
||||
|
||||
(update interaction :animation assoc :way way))
|
||||
|
||||
|
@ -733,26 +633,20 @@
|
|||
(defn set-direction
|
||||
[interaction direction]
|
||||
|
||||
(dm/assert!
|
||||
"expected valid interaction map"
|
||||
(check-interaction! interaction))
|
||||
(assert (check-interaction interaction))
|
||||
(assert (contains? direction-types direction)
|
||||
"expected valid direction")
|
||||
|
||||
(dm/assert!
|
||||
"expected valid direction"
|
||||
(contains? direction-types direction))
|
||||
|
||||
(dm/assert!
|
||||
"expected compatible interaction map"
|
||||
(has-direction? interaction))
|
||||
(assert (has-direction? interaction)
|
||||
"expected compatible interaction map")
|
||||
|
||||
(update interaction :animation assoc :direction direction))
|
||||
|
||||
(defn invert-direction
|
||||
[animation]
|
||||
(dm/assert!
|
||||
"expected valid animation map"
|
||||
(or (nil? animation)
|
||||
(check-animation! animation)))
|
||||
(assert (or (nil? animation)
|
||||
(check-animation! animation))
|
||||
"expected valid animation map")
|
||||
|
||||
(case (:direction animation)
|
||||
:right
|
||||
|
@ -768,24 +662,18 @@
|
|||
|
||||
(defn has-offset-effect?
|
||||
[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)
|
||||
(= (-> interaction :animation :animation-type) :slide)))
|
||||
|
||||
(defn set-offset-effect
|
||||
[interaction offset-effect]
|
||||
|
||||
(dm/assert!
|
||||
"expected valid interaction map"
|
||||
(check-interaction! interaction))
|
||||
|
||||
(dm/assert!
|
||||
"expected valid boolean for `offset-effect`"
|
||||
(boolean? offset-effect))
|
||||
|
||||
(dm/assert!
|
||||
"expected compatible interaction map"
|
||||
(has-offset-effect? interaction))
|
||||
(assert (check-interaction interaction))
|
||||
(assert (boolean? offset-effect)
|
||||
"expected valid boolean for `offset-effect`")
|
||||
(assert (has-offset-effect? interaction)
|
||||
"expected compatible interaction map")
|
||||
|
||||
(update interaction :animation assoc :offset-effect offset-effect))
|
||||
|
||||
|
|
|
@ -588,7 +588,7 @@
|
|||
(update :group d/nilv top-level-theme-group-name)
|
||||
(update :description d/nilv "")
|
||||
(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 :sets set)
|
||||
(check-token-theme-attrs)
|
||||
|
@ -612,7 +612,6 @@
|
|||
(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-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-active-theme-paths [_] "get the active theme paths")
|
||||
(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)
|
||||
prev-set (get-in sets prefixed-from-path)]
|
||||
(if (instance? TokenSet prev-set)
|
||||
(let [prefixed-to-path (set-full-path->set-prefixed-full-path to-path)
|
||||
prefixed-before-path (when before-path
|
||||
(if before-group?
|
||||
(mapv add-set-path-group-prefix before-path)
|
||||
(set-full-path->set-prefixed-full-path before-path)))
|
||||
(let [prefixed-to-path
|
||||
(set-full-path->set-prefixed-full-path to-path)
|
||||
|
||||
prefixed-before-path
|
||||
(when before-path
|
||||
(if before-group?
|
||||
(mapv add-set-path-group-prefix before-path)
|
||||
(set-full-path->set-prefixed-full-path before-path)))
|
||||
|
||||
set
|
||||
(assoc prev-set :name (join-set-path to-path))
|
||||
|
||||
reorder?
|
||||
(= prefixed-from-path prefixed-to-path)
|
||||
|
||||
set (assoc prev-set :name (join-set-path to-path))
|
||||
reorder? (= prefixed-from-path prefixed-to-path)
|
||||
sets'
|
||||
(if reorder?
|
||||
(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 sets prefixed-to-path set))
|
||||
(d/dissoc-in prefixed-from-path)))]
|
||||
|
||||
(TokensLib. sets'
|
||||
(if reorder?
|
||||
themes
|
||||
|
@ -1130,9 +1137,6 @@ Will return a value that matches this schema:
|
|||
(get-theme [_ 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]
|
||||
(TokensLib. sets
|
||||
themes
|
||||
|
@ -1368,6 +1372,10 @@ Will return a value that matches this schema:
|
|||
(valid-token-themes? 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?
|
||||
[o]
|
||||
(and (instance? TokensLib o)
|
||||
|
@ -1382,27 +1390,32 @@ Will return a value that matches this schema:
|
|||
(def ^:private check-active-themes
|
||||
(sm/check-fn schema:active-themes :hint "expected valid active themes"))
|
||||
|
||||
(defn- ensure-hidden-theme
|
||||
"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
|
||||
;; data structure and maybe we need a specific that allows us an
|
||||
;; easy way to reorder it, or just store inside Tokens data
|
||||
;; structure the data and the order separately as we already do
|
||||
;; with pages and pages-index.
|
||||
(defn make-tokens-lib
|
||||
"Create an empty or prepopulated tokens library."
|
||||
([]
|
||||
;; NOTE: is possible that ordered map is not the most apropriate
|
||||
;; data structure and maybe we need a specific that allows us an
|
||||
;; easy way to reorder it, or just store inside Tokens data
|
||||
;; structure the data and the order separately as we already do
|
||||
;; with pages and pages-index.
|
||||
(make-tokens-lib :sets (d/ordered-map)
|
||||
:themes (d/ordered-map)
|
||||
:active-themes #{hidden-token-theme-path}))
|
||||
|
||||
([& {:keys [sets themes active-themes]}]
|
||||
(let [active-themes (d/nilv active-themes #{hidden-token-theme-path})
|
||||
themes (if (empty? themes)
|
||||
(update themes hidden-token-theme-group d/oassoc hidden-token-theme-name (make-hidden-token-theme))
|
||||
themes)]
|
||||
(TokensLib.
|
||||
(check-token-sets sets)
|
||||
(check-token-themes themes)
|
||||
(check-active-themes active-themes)))))
|
||||
[& {:keys [sets themes active-themes]}]
|
||||
(let [sets (or sets (d/ordered-map))
|
||||
themes (-> (or themes (d/ordered-map))
|
||||
(ensure-hidden-theme))
|
||||
active-themes (or active-themes #{hidden-token-theme-path})]
|
||||
(TokensLib.
|
||||
(check-token-sets sets)
|
||||
(check-token-themes themes)
|
||||
(check-active-themes active-themes))))
|
||||
|
||||
(defn ensure-tokens-lib
|
||||
[tokens-lib]
|
||||
|
@ -1445,6 +1458,71 @@ Will return a value that matches this schema:
|
|||
:wfn #(into {} %)
|
||||
: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
|
||||
(fres/add-handlers!
|
||||
{:name "penpot/token/v1"
|
||||
|
@ -1474,32 +1552,15 @@ Will return a value that matches this schema:
|
|||
(let [obj (fres/read-object! r)]
|
||||
(map->TokenTheme obj)))}
|
||||
|
||||
;; LEGACY TOKENS LIB READERS (with migrations)
|
||||
{:name "penpot/tokens-lib/v1"
|
||||
:rfn (fn [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)))
|
||||
|
||||
;; 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)))}
|
||||
:rfn read-tokens-lib-v1-0}
|
||||
|
||||
{: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
|
||||
:wfn (fn [n w 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)))
|
||||
: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)))}))
|
||||
:wfn write-tokens-lib
|
||||
:rfn read-tokens-lib}))
|
||||
|
|
|
@ -84,52 +84,65 @@
|
|||
(t/is (thrown-with-msg? #?(:cljs js/Error :clj Exception) #"expected valid params for token-set"
|
||||
(ctob/make-token-set params)))))
|
||||
|
||||
(t/deftest move-token-set
|
||||
(t/testing "flat"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(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 "Move")))
|
||||
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)
|
||||
(into [])))]
|
||||
(t/testing "move to top"
|
||||
(t/is (= ["Move" "A" "B"] (move ["Move"] ["Move"] ["A"] false))))
|
||||
(t/deftest move-token-set-flat
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(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 "Move")))
|
||||
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)
|
||||
(into [])))]
|
||||
(t/testing "move to top"
|
||||
(t/is (= ["Move" "A" "B"] (move ["Move"] ["Move"] ["A"] false))))
|
||||
|
||||
(t/testing "move in-between"
|
||||
(t/is (= ["A" "Move" "B"] (move ["Move"] ["Move"] ["B"] false))))
|
||||
(t/testing "move in-between"
|
||||
(t/is (= ["A" "Move" "B"] (move ["Move"] ["Move"] ["B"] false))))
|
||||
|
||||
(t/testing "move to bottom"
|
||||
(t/is (= ["A" "B" "Move"] (move ["Move"] ["Move"] nil false))))))
|
||||
(t/testing "move to bottom"
|
||||
(t/is (= ["A" "B" "Move"] (move ["Move"] ["Move"] nil false))))))
|
||||
|
||||
(t/testing "nested"
|
||||
(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/Bar"))
|
||||
(ctob/add-set (ctob/make-token-set :name "Foo")))
|
||||
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)
|
||||
(into [])))]
|
||||
(t/testing "move outside of group"
|
||||
(t/is (= ["Foo/Baz" "Bar" "Foo"] (move ["Foo" "Bar"] ["Bar"] ["Foo"] false)))
|
||||
(t/is (= ["Bar" "Foo/Baz" "Foo"] (move ["Foo" "Bar"] ["Bar"] ["Foo" "Baz"] true)))
|
||||
(t/is (= ["Foo/Baz" "Foo" "Bar"] (move ["Foo" "Bar"] ["Bar"] nil false))))
|
||||
(t/deftest move-token-set-nested
|
||||
(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/Bar"))
|
||||
(ctob/add-set (ctob/make-token-set :name "Foo")))
|
||||
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)
|
||||
(into [])))]
|
||||
(t/testing "move outside of group"
|
||||
(t/is (= ["Foo/Baz" "Bar" "Foo"] (move ["Foo" "Bar"] ["Bar"] ["Foo"] false)))
|
||||
(t/is (= ["Bar" "Foo/Baz" "Foo"] (move ["Foo" "Bar"] ["Bar"] ["Foo" "Baz"] true)))
|
||||
(t/is (= ["Foo/Baz" "Foo" "Bar"] (move ["Foo" "Bar"] ["Bar"] nil false))))
|
||||
|
||||
(t/testing "move inside of group"
|
||||
(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/testing "move inside of group"
|
||||
(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))))))
|
||||
|
||||
;; FIXME
|
||||
(t/testing "updates theme set names"
|
||||
(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 "Other"))
|
||||
(ctob/add-theme (ctob/make-token-theme :name "Theme"
|
||||
:sets #{"Foo/Bar/Baz"}))
|
||||
(ctob/move-set ["Foo" "Bar" "Baz"] ["Other/Baz"] nil nil))]
|
||||
(t/is (= #{"Other/Baz"} (:sets (ctob/get-theme tokens-lib "" "Theme")))))))
|
||||
|
||||
(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)
|
||||
(ctob/add-set (ctob/make-token-set :name "Foo/Bar/Baz"))
|
||||
(ctob/add-set (ctob/make-token-set :name "Other"))
|
||||
(ctob/add-theme (ctob/make-token-theme :name "Theme"
|
||||
:sets #{"Foo/Bar/Baz"}))
|
||||
(ctob/move-set ["Foo" "Bar" "Baz"] ["Other/Baz"] nil nil))]
|
||||
(t/is (= #{"Other/Baz"} (:sets (ctob/get-theme tokens-lib "" "Theme"))))))
|
||||
|
||||
(t/deftest move-token-set-group
|
||||
(t/testing "reordering"
|
||||
|
@ -213,7 +226,7 @@
|
|||
(t/is (= (ctob/set-count tokens-lib) 0))))
|
||||
|
||||
(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"
|
||||
(ctob/make-tokens-lib params)))))
|
||||
|
||||
|
|
|
@ -2,6 +2,14 @@
|
|||
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
|
||||
|
||||
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
|
||||
|
||||
<p class="advice">
|
||||
Skip this section if you already have docker installed, up and running.
|
||||
</p>
|
||||
|
||||
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.
|
||||
To host a Penpot instance with Docker, it's necessary to have
|
||||
<code class="language-bash">docker</code> and <code class="language-bash">docker compose</code>
|
||||
installed. Check the comprehensive <a href="https://docs.docker.com/" target="_blank">official documentation</a>
|
||||
to install and maintain docker.
|
||||
|
||||
## Start Penpot
|
||||
|
||||
|
|
|
@ -648,9 +648,7 @@
|
|||
(defn detach-comment-thread
|
||||
"Detach comment threads that are inside a frame when that frame is deleted"
|
||||
[ids]
|
||||
(dm/assert!
|
||||
"expected a valid coll of uuid's"
|
||||
(sm/check-coll-of-uuid! ids))
|
||||
(assert (sm/check-coll-of-uuid ids))
|
||||
|
||||
(ptk/reify ::detach-comment-thread
|
||||
ptk/WatchEvent
|
||||
|
|
|
@ -536,11 +536,8 @@
|
|||
|
||||
(defn move-files
|
||||
[{:keys [ids project-id] :as params}]
|
||||
(dm/assert! (uuid? project-id))
|
||||
|
||||
(dm/assert!
|
||||
"expected a valid set of uuids"
|
||||
(sm/check-set-of-uuid! ids))
|
||||
(assert (uuid? project-id))
|
||||
(assert (sm/check-set-of-uuid ids))
|
||||
|
||||
(ptk/reify ::move-files
|
||||
ev/Event
|
||||
|
|
|
@ -350,12 +350,10 @@
|
|||
|
||||
(defn create-invitations
|
||||
[{:keys [emails role team-id resend?] :as params}]
|
||||
(dm/assert! (keyword? role))
|
||||
(dm/assert! (uuid? team-id))
|
||||
|
||||
(dm/assert!
|
||||
"expected a valid set of emails"
|
||||
(sm/check-set-of-emails! emails))
|
||||
(assert (keyword? role))
|
||||
(assert (uuid? team-id))
|
||||
(assert (sm/check-set-of-emails emails))
|
||||
|
||||
(ptk/reify ::create-invitations
|
||||
ev/Event
|
||||
|
@ -376,11 +374,8 @@
|
|||
|
||||
(defn copy-invitation-link
|
||||
[{:keys [email team-id] :as params}]
|
||||
(dm/assert!
|
||||
"expected a valid email"
|
||||
(sm/check-email! email))
|
||||
|
||||
(dm/assert! (uuid? team-id))
|
||||
(assert (sm/check-email email))
|
||||
(assert (uuid? team-id))
|
||||
|
||||
(ptk/reify ::copy-invitation-link
|
||||
IDeref
|
||||
|
@ -406,12 +401,9 @@
|
|||
|
||||
(defn update-invitation-role
|
||||
[{:keys [email team-id role] :as params}]
|
||||
(dm/assert!
|
||||
"expected a valid email"
|
||||
(sm/check-email! email))
|
||||
|
||||
(dm/assert! (uuid? team-id))
|
||||
(dm/assert! (contains? ctt/valid-roles role))
|
||||
(assert (sm/check-email email))
|
||||
(assert (uuid? team-id))
|
||||
(assert (contains? ctt/valid-roles role))
|
||||
|
||||
(ptk/reify ::update-invitation-role
|
||||
IDeref
|
||||
|
@ -428,8 +420,9 @@
|
|||
|
||||
(defn delete-invitation
|
||||
[{:keys [email team-id] :as params}]
|
||||
(dm/assert! (sm/check-email! email))
|
||||
(dm/assert! (uuid? team-id))
|
||||
(assert (sm/check-email email))
|
||||
(assert (uuid? team-id))
|
||||
|
||||
(ptk/reify ::delete-invitation
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
|
|
|
@ -252,6 +252,8 @@
|
|||
:level :error
|
||||
:timeout 9000})))))))
|
||||
|
||||
;; FIXME: add schema for params
|
||||
|
||||
(defn drop-token-set-group [drop-opts]
|
||||
(ptk/reify ::drop-token-set-group
|
||||
ptk/WatchEvent
|
||||
|
@ -265,17 +267,21 @@
|
|||
(rx/of
|
||||
(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/WatchEvent
|
||||
(watch [it state _]
|
||||
(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)
|
||||
(wtu/update-workspace-tokens)))
|
||||
(catch :default e
|
||||
(rx/of
|
||||
(drop-error (ex-data e))))))))
|
||||
(catch :default cause
|
||||
(rx/of (drop-error (ex-data cause))))))))
|
||||
|
||||
(defn- create-token-with-set
|
||||
"A special case when a first token is created and no set exists"
|
||||
|
|
|
@ -406,6 +406,7 @@
|
|||
:workspace-media-objects
|
||||
:workspace-persistence
|
||||
:workspace-presence
|
||||
:workspace-tokens
|
||||
:workspace-undo)
|
||||
(update :workspace-global dissoc :read-only?)
|
||||
(assoc-in [:workspace-global :options-mode] :design)))
|
||||
|
|
|
@ -134,9 +134,7 @@
|
|||
;; Move comment threads that are inside a frame when that frame is moved"
|
||||
(defmethod ptk/resolve ::move-frame-comment-threads
|
||||
[_ ids]
|
||||
(dm/assert!
|
||||
"expected a valid coll of uuid's"
|
||||
(sm/check-coll-of-uuid! ids))
|
||||
(assert (sm/check-coll-of-uuid ids))
|
||||
|
||||
(ptk/reify ::move-frame-comment-threads
|
||||
ptk/WatchEvent
|
||||
|
|
|
@ -120,6 +120,14 @@
|
|||
(pcb/with-page-id page-id)
|
||||
(pcb/with-objects objects)
|
||||
(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/change-parent (:id group) (reverse shapes))
|
||||
(pcb/update-shapes (map :id shapes-to-detach) ctk/detach-shape)
|
||||
|
|
|
@ -533,14 +533,98 @@
|
|||
|
||||
(assoc state :workspace-modifiers modif-tree)))))
|
||||
|
||||
(def ^:private xf:without-uuid-zero
|
||||
(remove #(= % uuid/zero)))
|
||||
|
||||
(def ^:private transform-attrs
|
||||
#{:selrect
|
||||
:points
|
||||
:x
|
||||
:y
|
||||
:r1
|
||||
:r2
|
||||
:r3
|
||||
:r4
|
||||
:shadow
|
||||
:blur
|
||||
:strokes
|
||||
:width
|
||||
:height
|
||||
:content
|
||||
:transform
|
||||
:transform-inverse
|
||||
:rotation
|
||||
:flip-x
|
||||
:flip-y
|
||||
:grow-type
|
||||
:position-data
|
||||
:layout-gap
|
||||
:layout-padding
|
||||
:layout-item-h-sizing
|
||||
:layout-item-max-h
|
||||
:layout-item-max-w
|
||||
:layout-item-min-h
|
||||
:layout-item-min-w
|
||||
:layout-item-v-sizing
|
||||
:layout-padding-type
|
||||
:layout-item-margin
|
||||
:layout-item-margin-type
|
||||
:layout-grid-cells
|
||||
:layout-grid-columns
|
||||
:layout-grid-rows})
|
||||
|
||||
(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? stack-undo? ignore-constraints
|
||||
ignore-snap-pixel ignore-touched undo-group page-id]
|
||||
:or {undo-transation? true stack-undo? false ignore-constraints false
|
||||
ignore-snap-pixel false ignore-touched false}}]
|
||||
([{: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 _]
|
||||
|
@ -553,88 +637,17 @@
|
|||
(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)]
|
||||
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
|
||||
:x
|
||||
:y
|
||||
:r1
|
||||
:r2
|
||||
:r3
|
||||
:r4
|
||||
:shadow
|
||||
:blur
|
||||
:strokes
|
||||
:width
|
||||
:height
|
||||
:content
|
||||
:transform
|
||||
:transform-inverse
|
||||
:rotation
|
||||
:flip-x
|
||||
:flip-y
|
||||
:grow-type
|
||||
:position-data
|
||||
:layout-gap
|
||||
:layout-padding
|
||||
:layout-item-h-sizing
|
||||
:layout-item-margin
|
||||
:layout-item-max-h
|
||||
:layout-item-max-w
|
||||
:layout-item-min-h
|
||||
:layout-item-min-w
|
||||
:layout-item-v-sizing
|
||||
:layout-padding-type
|
||||
:layout-gap
|
||||
:layout-item-margin
|
||||
:layout-item-margin-type
|
||||
:layout-grid-cells
|
||||
:layout-grid-columns
|
||||
:layout-grid-rows]})
|
||||
;; We've applied the text-modifier so we can dissoc the temporary data
|
||||
(rx/of (apply-modifiers* objects object-modifiers text-modifiers options)
|
||||
(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)
|
||||
(rx/of (clear-local-transform))
|
||||
(rx/empty))
|
||||
|
|
|
@ -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]
|
||||
:or {reg-objects? false save-undo? true stack-undo? false ignore-touched false with-objects? false}}]
|
||||
|
||||
(dm/assert!
|
||||
"expected a valid coll of uuid's"
|
||||
(sm/check-coll-of-uuid! ids))
|
||||
|
||||
(dm/assert! (fn? update-fn))
|
||||
(assert (sm/check-coll-of-uuid ids))
|
||||
(assert (fn? update-fn))
|
||||
|
||||
(ptk/reify ::update-shapes
|
||||
ptk/WatchEvent
|
||||
|
@ -162,9 +159,7 @@
|
|||
([ids] (delete-shapes nil ids {}))
|
||||
([page-id ids] (delete-shapes page-id ids {}))
|
||||
([page-id ids options]
|
||||
(dm/assert!
|
||||
"expected a valid set of uuid's"
|
||||
(sm/check-set-of-uuid! ids))
|
||||
(assert (sm/check-set-of-uuid ids))
|
||||
|
||||
(ptk/reify ::delete-shapes
|
||||
ptk/WatchEvent
|
||||
|
|
|
@ -340,35 +340,35 @@
|
|||
(rx/filter (ptk/type? ::trigger-bounding-box-cloaking) stream)))))))
|
||||
|
||||
(defn update-dimensions
|
||||
"Change size of shapes, from the sideber options form.
|
||||
Will ignore pixel snap used in the options side panel"
|
||||
"Change size of shapes, from the sidebar options form
|
||||
(will ignore pixel snap)"
|
||||
([ids attr value] (update-dimensions ids attr value nil))
|
||||
([ids attr value options]
|
||||
(dm/assert! (number? value))
|
||||
(dm/assert!
|
||||
"expected valid coll of uuids"
|
||||
(every? uuid? ids))
|
||||
(dm/assert!
|
||||
"expected valid attr"
|
||||
(contains? #{:width :height} attr))
|
||||
(ptk/reify ::update-dimensions
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [page-id (or (get options :page-id)
|
||||
(get state :current-page-id))
|
||||
(assert (number? value))
|
||||
(assert (every? uuid? ids)
|
||||
"expected valid coll of uuids")
|
||||
(assert (contains? #{:width :height} attr)
|
||||
"expected valid attr")
|
||||
|
||||
(ptk/reify ::update-dimensions
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [page-id
|
||||
(or (get options :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
|
||||
(fn [shape] (ctm/change-dimensions-modifiers shape attr value))
|
||||
(fn [shape]
|
||||
(ctm/change-dimensions-modifiers shape attr value))
|
||||
|
||||
modif-tree
|
||||
(-> (dwm/build-modif-tree ids objects get-modifier)
|
||||
(gm/set-objects-modifiers objects))]
|
||||
|
||||
(assoc state :workspace-modifiers modif-tree)))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of (dwm/apply-modifiers options))))))
|
||||
(rx/of (dwm/apply-modifiers* objects modif-tree nil options)))))))
|
||||
|
||||
(defn change-orientation
|
||||
"Change orientation of shapes, from the sidebar options form.
|
||||
|
@ -859,42 +859,44 @@
|
|||
(rx/of (reorder-selected-layout-child direction))
|
||||
(rx/of (nudge-selected-shapes direction shift?)))))))
|
||||
|
||||
(defn- get-delta [position bbox]
|
||||
(let [cpos (gpt/point (:x bbox) (:y bbox))
|
||||
pos (gpt/point (or (:x position) (:x bbox))
|
||||
(or (:y position) (:y bbox)))]
|
||||
(gpt/subtract pos cpos)))
|
||||
|
||||
(defn- get-relative-delta [position bbox frame]
|
||||
(let [frame-bbox (-> frame :points grc/points->rect)
|
||||
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- calculate-delta
|
||||
[position bbox relative-to]
|
||||
(let [current (gpt/point (:x bbox) (:y bbox))
|
||||
position (gpt/point (or (some-> (:x position) (+ (dm/get-prop relative-to :x)))
|
||||
(:x bbox))
|
||||
(or (some-> (:y position) (+ (dm/get-prop relative-to :y)))
|
||||
(:y bbox)))]
|
||||
(gpt/subtract position current)))
|
||||
|
||||
(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 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/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [page-id (or (get options :page-id)
|
||||
(get state :current-page-id))
|
||||
objects (dsh/lookup-page-objects state page-id)
|
||||
shape (get objects id)
|
||||
;; FIXME: performance rect
|
||||
bbox (-> shape :points grc/points->rect)
|
||||
frame (cfh/get-frame objects shape)
|
||||
delta (if (:absolute? options)
|
||||
(get-delta position bbox)
|
||||
(get-relative-delta position bbox frame))
|
||||
modif-tree (dwm/create-modif-tree [id] (ctm/move-modifiers delta))]
|
||||
(rx/of (dwm/apply-modifiers {:modifiers modif-tree
|
||||
(let [page-id (or (get options :page-id)
|
||||
(get state :current-page-id))
|
||||
objects (dsh/lookup-page-objects state page-id)
|
||||
shape (get objects id)
|
||||
|
||||
bbox (-> shape :points grc/points->rect)
|
||||
frame (if (:absolute? options)
|
||||
(cfh/get-frame objects)
|
||||
(cfh/get-parent-frame objects shape))
|
||||
|
||||
delta (calculate-delta position bbox frame)
|
||||
modifiers (dwm/create-modif-tree [id] (ctm/move-modifiers delta))]
|
||||
|
||||
(rx/of (dwm/apply-modifiers {:modifiers modifiers
|
||||
:page-id page-id
|
||||
:ignore-constraints false
|
||||
:ignore-touched (:ignore-touched options)
|
||||
|
|
|
@ -22,7 +22,8 @@
|
|||
;; Change this to :info :debug or :trace to debug this module
|
||||
(log/set-level! :warn)
|
||||
|
||||
(def discard-transaction-time-millis (* 20 1000))
|
||||
(def ^:private
|
||||
discard-transaction-time-millis (* 20 1000))
|
||||
|
||||
(def ^:private
|
||||
schema:undo-entry
|
||||
|
@ -30,7 +31,7 @@
|
|||
[:undo-changes [:vector ::cpc/change]]
|
||||
[:redo-changes [:vector ::cpc/change]]])
|
||||
|
||||
(def check-undo-entry!
|
||||
(def check-undo-entry
|
||||
(sm/check-fn schema:undo-entry))
|
||||
|
||||
(def MAX-UNDO-SIZE 50)
|
||||
|
@ -48,8 +49,7 @@
|
|||
(ptk/reify ::materialize-undo
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(assoc-in [:workspace-undo :index] index)))))
|
||||
(update state :workspace-undo assoc :index index))))
|
||||
|
||||
(defn- add-undo-entry
|
||||
[state entry]
|
||||
|
@ -88,12 +88,9 @@
|
|||
|
||||
(defn append-undo
|
||||
[entry stack?]
|
||||
(dm/assert!
|
||||
"expected valid undo entry"
|
||||
(check-undo-entry! entry))
|
||||
|
||||
(dm/assert!
|
||||
(boolean? stack?))
|
||||
(assert (check-undo-entry entry))
|
||||
(assert (boolean? stack?))
|
||||
|
||||
(ptk/reify ::append-undo
|
||||
ptk/UpdateEvent
|
||||
|
@ -118,17 +115,11 @@
|
|||
|
||||
(defn start-undo-transaction
|
||||
"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/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(->> (rx/of (check-open-transactions))
|
||||
;; Wait the configured time
|
||||
(rx/delay discard-transaction-time-millis)))
|
||||
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(log/info :msg "start-undo-transaction")
|
||||
(log/info :hint "start-undo-transaction")
|
||||
;; We commit the old transaction before starting the new one
|
||||
(let [current-tx (get-in state [:workspace-undo :transaction])
|
||||
pending-tx (get-in state [:workspace-undo :transactions-pending])]
|
||||
|
@ -136,20 +127,28 @@
|
|||
(nil? current-tx) (assoc-in [:workspace-undo :transaction] empty-tx)
|
||||
(nil? pending-tx) (assoc-in [:workspace-undo :transactions-pending] #{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 []
|
||||
(ptk/reify ::discard-undo-transaction
|
||||
ptk/UpdateEvent
|
||||
(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))))
|
||||
|
||||
(defn commit-undo-transaction [id]
|
||||
(ptk/reify ::commit-undo-transaction
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(log/info :msg "commit-undo-transaction")
|
||||
(log/info :hint "commit-undo-transaction")
|
||||
(let [state (-> state
|
||||
(update-in [:workspace-undo :transactions-pending] disj id)
|
||||
(update-in [:workspace-undo :transactions-pending-ts] dissoc id))]
|
||||
|
@ -166,15 +165,15 @@
|
|||
(assoc state :workspace-undo {}))))
|
||||
|
||||
(defn check-open-transactions
|
||||
[]
|
||||
[timeout]
|
||||
(ptk/reify ::check-open-transactions
|
||||
ptk/WatchEvent
|
||||
(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])
|
||||
(update-vals #(.toMillis (dt/diff (dt/now) %))))]
|
||||
(update-vals #(inst-ms (dt/diff (dt/now) %))))]
|
||||
(->> pending-ts
|
||||
(filter (fn [[_ ts]] (>= ts discard-transaction-time-millis)))
|
||||
(filter (fn [[_ ts]] (>= ts timeout)))
|
||||
(rx/from)
|
||||
(rx/tap #(js/console.warn (dm/str "FORCE COMMIT TRANSACTION AFTER " (second %) "MS")))
|
||||
(rx/map first)
|
||||
|
|
|
@ -112,16 +112,16 @@
|
|||
(dom/stop-propagation event)
|
||||
(on-import item event))))]
|
||||
|
||||
[:a {:class (stl/css :card-container)
|
||||
:tab-index (if (or (not is-visible) collapsed) "-1" "0")
|
||||
:id id
|
||||
:data-index index
|
||||
:on-click on-click
|
||||
:on-mouse-down dom/prevent-default
|
||||
:on-mouse-enter #(reset! hover? true)
|
||||
:on-mouse-leave #(reset! hover? false)
|
||||
:on-key-down on-key-down}
|
||||
[:div {:class (stl/css :template-card)}
|
||||
[:div {:class (stl/css :card-container)
|
||||
:tab-index (if (or (not is-visible) collapsed) "-1" "0")
|
||||
:id id
|
||||
:data-index index}
|
||||
[:a {:class (stl/css :template-card)
|
||||
:on-click on-click
|
||||
:on-mouse-down dom/prevent-default
|
||||
:on-mouse-enter #(reset! hover? true)
|
||||
:on-mouse-leave #(reset! hover? false)
|
||||
:on-key-down on-key-down}
|
||||
[:div {:class (stl/css :img-container)}
|
||||
[:img {:src (dm/str thb)
|
||||
:alt (:name item)
|
||||
|
|
|
@ -175,7 +175,7 @@
|
|||
show-in-viewer-ref (mf/use-ref nil)
|
||||
|
||||
;; PRESETS
|
||||
preset-state* (mf/use-state false)
|
||||
preset-state* (mf/use-state false)
|
||||
show-presets-dropdown? (deref preset-state*)
|
||||
|
||||
open-presets
|
||||
|
@ -205,11 +205,11 @@
|
|||
|
||||
;; ORIENTATION
|
||||
|
||||
orientation (when (= type :frame)
|
||||
(cond (> (:width values) (:height values))
|
||||
:horiz
|
||||
:else
|
||||
:vert))
|
||||
orientation
|
||||
(when (= type :frame)
|
||||
(if (> (:width values) (:height values))
|
||||
:horiz
|
||||
:vert))
|
||||
|
||||
on-orientation-change
|
||||
(mf/use-fn
|
||||
|
@ -235,10 +235,8 @@
|
|||
(run! #(st/emit! (udw/set-shape-proportion-lock % new-lock)) ids))))
|
||||
|
||||
;; POSITION
|
||||
|
||||
do-position-change
|
||||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [shape' value attr]
|
||||
(st/emit! (udw/update-position (:id shape') {attr value}))))
|
||||
|
||||
|
@ -248,7 +246,7 @@
|
|||
(fn [value attr]
|
||||
(st/emit! (udw/trigger-bounding-box-cloaking ids))
|
||||
(binding [cts/*wasm-sync* true]
|
||||
(doall (map #(do-position-change %1 value attr) shapes)))))
|
||||
(run! #(do-position-change %1 value attr) shapes))))
|
||||
|
||||
;; ROTATION
|
||||
|
||||
|
|
|
@ -304,13 +304,11 @@
|
|||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(when (number? value)
|
||||
(let [page-id' (or page-id (get state :current-page-id))]
|
||||
(rx/concat
|
||||
(map #(dwt/update-position % (zipmap attributes (repeat value))
|
||||
{:ignore-touched true
|
||||
:page-id page-id'})
|
||||
shape-ids))))))))
|
||||
|
||||
(let [page-id (or page-id (get state :current-page-id))]
|
||||
(->> (rx/from shape-ids)
|
||||
(rx/map #(dwt/update-position % (zipmap attributes (repeat value))
|
||||
{:ignore-touched true
|
||||
:page-id page-id})))))))))
|
||||
|
||||
(defn update-layout-sizing-limits
|
||||
([value shape-ids attributes] (update-layout-sizing-limits value shape-ids attributes nil))
|
||||
|
|
|
@ -140,24 +140,18 @@
|
|||
|
||||
|
||||
[:div {:class (stl/css :theme-row-right)}
|
||||
(if-let [sets-count (some-> theme :sets seq count)]
|
||||
[:> button* {:class (stl/css :sets-count-button)
|
||||
(let [sets-count (some-> theme :sets seq count)]
|
||||
[:> button* {:class (stl/css-case :sets-count-button sets-count
|
||||
:sets-count-empty-button (not sets-count))
|
||||
:variant "secondary"
|
||||
:type "button"
|
||||
:title (tr "workspace.token.sets-hint")
|
||||
:on-click on-edit-theme}
|
||||
[:div {:class (stl/css :label-wrapper)}
|
||||
[:> text* {:as "span" :typography "body-medium"}
|
||||
(tr "workspace.token.num-active-sets" sets-count)]
|
||||
[:> icon* {:icon-id "arrow-right"}]]]
|
||||
|
||||
[:> 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")]
|
||||
(if sets-count
|
||||
(tr "workspace.token.num-active-sets" sets-count)
|
||||
(tr "workspace.token.no-active-sets"))]
|
||||
[:> icon* {:icon-id "arrow-right"}]]])
|
||||
|
||||
[:> icon-button* {:on-click delete-theme
|
||||
|
|
|
@ -368,13 +368,13 @@
|
|||
(mf/use-fn
|
||||
(mf/deps collapsed-paths)
|
||||
(fn [tree-index position data]
|
||||
(let [props {:from-index (:index data)
|
||||
:to-index tree-index
|
||||
:position position
|
||||
:collapsed-paths collapsed-paths}]
|
||||
(let [params {:from-index (:index data)
|
||||
:to-index tree-index
|
||||
:position position
|
||||
:collapsed-paths collapsed-paths}]
|
||||
(if (:is-group data)
|
||||
(st/emit! (dt/drop-token-set-group props))
|
||||
(st/emit! (dt/drop-token-set props))))))
|
||||
(st/emit! (dt/drop-token-set-group params))
|
||||
(st/emit! (dt/drop-token-set params))))))
|
||||
|
||||
on-toggle-collapse
|
||||
(mf/use-fn
|
||||
|
|
|
@ -103,8 +103,9 @@
|
|||
(defn- generate-tooltip
|
||||
"Generates a tooltip for a given token"
|
||||
[is-viewer shape theme-token token half-applied no-valid-value ref-not-in-active-set]
|
||||
(let [{:keys [name value type]} token
|
||||
{:keys [resolved-value]} theme-token
|
||||
(let [{:keys [name value type resolved-value]} 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)
|
||||
applied-tokens (:applied-tokens shape)
|
||||
app-token-vals (set (vals applied-tokens))
|
||||
|
|
|
@ -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
|
||||
(:require
|
||||
[app.common.files.helpers :as cfh]
|
||||
[app.common.logging :as l]
|
||||
[app.common.types.token :as ctt]
|
||||
[app.main.data.helpers :as dsh]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
|
@ -9,6 +16,7 @@
|
|||
[app.main.ui.workspace.tokens.changes :as wtch]
|
||||
[app.main.ui.workspace.tokens.style-dictionary :as wtsd]
|
||||
[app.main.ui.workspace.tokens.token-set :as wtts]
|
||||
[app.util.time :as dt]
|
||||
[beicon.v2.core :as rx]
|
||||
[clojure.data :as data]
|
||||
[clojure.set :as set]
|
||||
|
@ -70,7 +78,7 @@
|
|||
(reduce
|
||||
(fn [acc [attrs v]]
|
||||
(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)
|
||||
;; Exact match in attrs
|
||||
a (assoc a v)))
|
||||
|
@ -127,8 +135,14 @@
|
|||
[state resolved-tokens]
|
||||
(let [file-id (get state :current-file-id)
|
||||
current-page-id (get state :current-page-id)
|
||||
fdata (dsh/lookup-file-data state file-id)]
|
||||
(->> (rx/from (:pages fdata))
|
||||
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/filter (fn [id] (not= id current-page-id)))))
|
||||
(rx/mapcat
|
||||
(fn [page-id]
|
||||
(let [page
|
||||
|
@ -140,6 +154,12 @@
|
|||
actions
|
||||
(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/from actions)
|
||||
(->> (rx/from frame-ids)
|
||||
|
@ -151,7 +171,11 @@
|
|||
(fn [shape]
|
||||
(dissoc shape :position-data))
|
||||
{: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
|
||||
[]
|
||||
|
@ -164,6 +188,6 @@
|
|||
(rx/mapcat (fn [sd-tokens]
|
||||
(let [undo-id (js/Symbol)]
|
||||
(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)
|
||||
(rx/of (dwu/commit-undo-transaction undo-id)))))))))))
|
||||
|
|
|
@ -176,8 +176,9 @@
|
|||
|
||||
(defn format-shadows
|
||||
[shadows]
|
||||
(when (some? shadows)
|
||||
(format-array format-shadow shadows)))
|
||||
(if (some? shadows)
|
||||
(format-array format-shadow shadows)
|
||||
(array)))
|
||||
|
||||
;;export interface Fill {
|
||||
;; fillColor?: string;
|
||||
|
|
|
@ -6738,7 +6738,7 @@ msgstr "%s sets activos"
|
|||
|
||||
#: src/app/main/ui/workspace/tokens/modals/themes.cljs:147
|
||||
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
|
||||
#, fuzzy
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue