Compare commits

..

30 Commits

Author SHA1 Message Date
Andrey Antukh
86bcd1b681 🐛 Fix issue on shortcuts restore operation (#6462)
* 🐛 Fix issue on shortcuts restore operation

Happens when the order of shortcuts pop events is inconsistent with
push events. Using less strictly order policy for pop operations
allows relax this and make it eventually consistent.

* 💄 Add cosmetic changes on shortcuts hooks on colorpicker and    wport

* 📎 Update changelog

* 📎 Add PR feedback changes
2025-05-12 15:08:14 +02:00
Andrey Antukh
70b1989f10 Merge tag '2.7.0-RC2' 2025-05-12 13:11:40 +02:00
ºelhombretecla
e6b5618bd3 🎉 Add 2.7 release slides (#6440) 2025-05-12 11:38:00 +02:00
Alejandro Alonso
5199b306aa Merge pull request #6446 from penpot/niwinz-staging-bugfixes-2
🐛 Properly propagate export errors from worker to main thread
2025-05-12 10:47:46 +02:00
Alejandro Alonso
8febfaa21e Merge pull request #6447 from penpot/niwinz-staging-bugfixes
 Ensure read-only mode on non-workspace access to file
2025-05-12 10:43:02 +02:00
andrés gonzález
5e675dbf0b 📚 Update changelog for 2.7 (#6448) 2025-05-09 12:20:39 +02:00
Andrey Antukh
fc5d9659d6 🐛 Properly propagate export errors from worker to main thread 2025-05-09 11:15:04 +02:00
Andrey Antukh
bc20598b3d Don't persist file on several read operations
after applying migrations
2025-05-08 19:15:28 +02:00
Andrey Antukh
9de8ebb52c Add read-only option for files/get-file 2025-05-08 19:12:50 +02:00
Alonso Torres
9be569c54c 🐛 Fix problem when duplicating grid layout (#6426) 2025-05-08 13:39:47 +02:00
Alonso Torres
7e6a621484 🐛 Restore component fix order inside flex (#6432) 2025-05-08 13:37:59 +02:00
Eva Marco
66b47f9444 🐛 Fix resize bar background (#6435) 2025-05-08 13:37:00 +02:00
Alejandro Alonso
71aa8e5a86 Merge pull request #6434 from penpot/niwinz-staging-webhook-audit
 Add webhook trigger to the audit events
2025-05-08 13:00:04 +02:00
Andrey Antukh
e203536506 Add webhook trigger to audit events 2025-05-08 11:56:06 +02:00
Belén Albeza
3fe16bd8f9 🐛 Fix history panel shortcut (#6420)
*  Remove duplicate ID in file menu

* 🐛 Fix shortcut for Show Version History

*  Add regression test
2025-05-07 16:49:54 +02:00
Alejandro Alonso
a9725a1aac Merge pull request #6422 from penpot/superalex-add-even-to-power-up-link-from-workspace-menu
🐛 Add event to power up link from workspace menu
2025-05-07 15:13:31 +02:00
Alejandro Alonso
c3e76817cd 🐛 Add event to power up link from workspace menu 2025-05-07 13:45:57 +02:00
alonso.torres
36b78e5e21 🐛 Fix restore component restore layout 2025-05-05 11:50:08 +02:00
Alonso Torres
a2c9d307df 🐛 Fix problem in viewer with the back button (#6385) 2025-05-05 08:54:05 +02:00
Andrés Moya
e52fd90963 💄 Add copyright header to all tokens source files (#6389) 2025-05-01 10:06:17 +02:00
alonso.torres
d8b3b216e9 🐛 Fix import modal style problem 2025-04-30 10:48:48 +02:00
Andrey Antukh
c2b13a6d5d 📚 Update changelog 2025-04-29 14:46:15 +02:00
Andrey Antukh
a60b3d4b08 Merge pull request #6375 from penpot/hiru-move-tokens-staging
🔧 Reorganize tokens data functions
2025-04-29 10:43:13 +02:00
Andrés Moya
b14798b405 🔧 Make private function 2025-04-29 10:28:00 +02:00
Andrés Moya
8382a88efe 🔧 Move errors and warnings to workspace.data 2025-04-29 10:27:56 +02:00
Andrés Moya
53057c4bf2 🔧 Move token update to workspace.data and rename to propagation 2025-04-29 10:27:50 +02:00
Andrés Moya
3e0f38e8c3 🔧 Move token helpers to common.files 2025-04-29 10:27:46 +02:00
Andrés Moya
a5bbe765b9 🔧 Move style-dictionary and tinycolor to main.data 2025-04-29 10:27:42 +02:00
Andrés Moya
4455adc813 🔧 Move token lib edit to workspace.data and remove unused code 2025-04-29 10:27:38 +02:00
Andrés Moya
abca763aa5 🔧 Move token application to workspace.data 2025-04-29 10:27:20 +02:00
57 changed files with 885 additions and 564 deletions

View File

@@ -8,8 +8,9 @@
### :heart: Community contributions (Thank you!)
### :sparkles: New features
- Design improvements to the Invitations page with an empty state [GitHub #2608](https://github.com/penpot/penpot/issues/2608) by [@iprithvitharun](https://github.com/iprithvitharun)
### :sparkles: New features
- Update board presets with a newer devices [Taiga #10610](https://tree.taiga.io/project/penpot/us/10610)
- Propagate "sharing a prototype" to editors and viewers [Taiga #8853](https://tree.taiga.io/project/penpot/us/8853)
@@ -20,6 +21,9 @@
### :bug: Bugs fixed
- Fix problem in viewer with the back button [Taiga #10907](https://tree.taiga.io/project/penpot/issue/10907)
- Fix resize bar background on tokens panel [Taiga #10811](https://tree.taiga.io/project/penpot/issue/10811)
- Fix shortcut for history version panel [Taiga #11006](https://tree.taiga.io/project/penpot/issue/11006)
- Fix positioning of comment drafts when near the right / bottom edges of viewport [Taiga #10534](https://tree.taiga.io/project/penpot/issue/10534)
- Fix path having a wrong selrect [Taiga #10257](https://tree.taiga.io/project/penpot/issue/10257)
- Fix SVG `stroke-linecap` property when importing SVGs [Taiga #9489](https://tree.taiga.io/project/penpot/issue/9489)
@@ -41,8 +45,10 @@
- Fix Out of Sync Token Value & Color Picker [Github #102](https://github.com/tokens-studio/penpot/issues/102)
- Fix Color should preserve color space [Github #69](https://github.com/tokens-studio/penpot/issues/69)
- Fix cannot rename Design Token Sets when group of same name exists [Taiga Issue #10773](https://tree.taiga.io/project/penpot/issue/10773)
- Fix problem when duplicating grid layout [Github #6391](https://github.com/penpot/penpot/issues/6391)
- Fix issue that makes workspace shortcuts stop working [Taiga #11062](https://tree.taiga.io/project/penpot/issue/11062)
## 2.6.2 (Unreleased)
## 2.6.2
### :bug: Bugs fixed

View File

@@ -108,6 +108,7 @@
[::ip-addr {:optional true} ::sm/text]
[::props {:optional true} [:map-of :keyword :any]]
[::context {:optional true} [:map-of :keyword :any]]
[::tracked-at {:optional true} ::sm/inst]
[::webhooks/event? {:optional true} ::sm/boolean]
[::webhooks/batch-timeout {:optional true} ::dt/duration]
[::webhooks/batch-key {:optional true}
@@ -118,12 +119,12 @@
(defn prepare-event
[cfg mdata params result]
(let [resultm (meta result)
request (-> params meta ::http/request)
profile-id (or (::profile-id resultm)
(:profile-id result)
(::rpc/profile-id params)
uuid/zero)
(let [resultm (meta result)
request (-> params meta ::http/request)
profile-id (or (::profile-id resultm)
(:profile-id result)
(::rpc/profile-id params)
uuid/zero)
session-id (get params ::rpc/external-session-id)
event-origin (get params ::rpc/external-event-origin)
@@ -135,14 +136,14 @@
(clean-props))
token-id (::actoken/id request)
context (-> (::context resultm)
(assoc :external-session-id session-id)
(assoc :external-event-origin event-origin)
(assoc :access-token-id (some-> token-id str))
(d/without-nils))
token-id (::actoken/id request)
context (-> (::context resultm)
(assoc :external-session-id session-id)
(assoc :external-event-origin event-origin)
(assoc :access-token-id (some-> token-id str))
(d/without-nils))
ip-addr (inet/parse-request request)]
ip-addr (inet/parse-request request)]
{::type (or (::type resultm)
(::rpc/type cfg))

View File

@@ -15,6 +15,7 @@
[app.config :as cf]
[app.db :as db]
[app.http.client :as http]
[app.loggers.audit :as audit]
[app.util.time :as dt]
[app.worker :as wrk]
[clojure.data.json :as json]
@@ -67,18 +68,27 @@
(defmethod ig/init-key ::process-event-handler
[_ cfg]
(fn [{:keys [props] :as task}]
(l/dbg :hint "process webhook event" :name (:name props))
(when-let [items (lookup-webhooks cfg props)]
(l/trc :hint "webhooks found for event" :total (count items))
(db/tx-run! cfg (fn [cfg]
(doseq [item items]
(wrk/submit! (-> cfg
(assoc ::wrk/task :run-webhook)
(assoc ::wrk/queue :webhooks)
(assoc ::wrk/max-retries 3)
(assoc ::wrk/params {:event props
:config item})))))))))
(let [items (lookup-webhooks cfg props)
event {::audit/profile-id (:profile-id props)
::audit/name "webhook"
::audit/type "trigger"
::audit/props {:name (get props :name)
:event-id (get props :id)
:total-affected (count items)}}]
(audit/insert! cfg event)
(when items
(l/trc :hint "webhooks found for event" :total (count items))
(db/tx-run! cfg (fn [cfg]
(doseq [item items]
(wrk/submit! (-> cfg
(assoc ::wrk/task :run-webhook)
(assoc ::wrk/queue :webhooks)
(assoc ::wrk/max-retries 3)
(assoc ::wrk/params {:event props
:config item}))))))))))
;; --- RUN
(declare interpret-exception)

View File

@@ -208,7 +208,7 @@
[:project-id {:optional true} ::sm/uuid]])
(defn- migrate-file
[{:keys [::db/conn] :as cfg} {:keys [id] :as file}]
[{:keys [::db/conn] :as cfg} {:keys [id] :as file} {:keys [read-only?]}]
(binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)
pmap/*tracked* (pmap/create-tracked)]
(let [;; For avoid unnecesary overhead of creating multiple pointers and
@@ -219,43 +219,45 @@
file (-> file
(update :data feat.fdata/process-pointers deref)
(update :data feat.fdata/process-objects (partial into {}))
(fmg/migrate-file))
(fmg/migrate-file))]
;; When file is migrated, we break the rule of no perform
;; mutations on get operations and update the file with all
;; migrations applied
;;
;; WARN: he following code will not work on read-only mode,
;; it is a known issue; we keep is not implemented until we
;; really need this.
file (if (contains? (:features file) "fdata/objects-map")
(feat.fdata/enable-objects-map file)
file)
file (if (contains? (:features file) "fdata/pointer-map")
(feat.fdata/enable-pointer-map file)
file)]
(if (or read-only? (db/read-only? conn))
file
(let [;; When file is migrated, we break the rule of no perform
;; mutations on get operations and update the file with all
;; migrations applied
file (if (contains? (:features file) "fdata/objects-map")
(feat.fdata/enable-objects-map file)
file)
file (if (contains? (:features file) "fdata/pointer-map")
(feat.fdata/enable-pointer-map file)
file)]
(db/update! conn :file
{:data (blob/encode (:data file))
:version (:version file)
:features (db/create-array conn "text" (:features file))}
{:id id})
(db/update! conn :file
{:data (blob/encode (:data file))
:version (:version file)
:features (db/create-array conn "text" (:features file))}
{:id id}
{::db/return-keys false})
(when (contains? (:features file) "fdata/pointer-map")
(feat.fdata/persist-pointers! cfg id))
(when (contains? (:features file) "fdata/pointer-map")
(feat.fdata/persist-pointers! cfg id))
(feat.fmigr/upsert-migrations! conn file)
(feat.fmigr/resolve-applied-migrations cfg file))))
(feat.fmigr/upsert-migrations! conn file)
(feat.fmigr/resolve-applied-migrations cfg file))))))
(defn get-file
[{:keys [::db/conn ::wrk/executor] :as cfg} id
& {:keys [project-id
migrate?
include-deleted?
lock-for-update?]
lock-for-update?
preload-pointers?]
:or {include-deleted? false
lock-for-update? false
migrate? true}}]
migrate? true
preload-pointers? false}
:as options}]
(assert (db/connection? conn) "expected cfg with valid connection")
@@ -273,10 +275,16 @@
;; because it has heavy and synchronous operations for
;; decoding file body that are not very friendly with virtual
;; threads.
file (px/invoke! executor #(decode-row file))]
file (px/invoke! executor #(decode-row file))
file (if (and migrate? (fmg/need-migration? file))
(migrate-file cfg file options)
file)]
(if preload-pointers?
(binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)]
(update file :data feat.fdata/process-pointers deref))
(if (and migrate? (fmg/need-migration? file))
(migrate-file cfg file)
file)))
(defn get-minimal-file
@@ -484,7 +492,7 @@
(let [perms (get-permissions conn profile-id file-id share-id)
file (get-file cfg file-id)
file (get-file cfg file-id :read-only? true)
proj (db/get conn :project {:id (:project-id file)})
@@ -741,7 +749,9 @@
:project-id project-id
:file-id id)
file (get-file cfg id :project-id project-id)]
file (get-file cfg id
:project-id project-id
:read-only? true)]
(-> (cfeat/get-team-enabled-features cf/flags team)
(cfeat/check-client-features! (:features params))

View File

@@ -10,7 +10,6 @@
[app.common.data.macros :as dm]
[app.common.features :as cfeat]
[app.common.files.helpers :as cfh]
[app.common.files.migrations :as fmg]
[app.common.geom.shapes :as gsh]
[app.common.schema :as sm]
[app.common.thumbnails :as thc]
@@ -18,7 +17,6 @@
[app.config :as cf]
[app.db :as db]
[app.db.sql :as-alias sql]
[app.features.fdata :as feat.fdata]
[app.loggers.audit :as-alias audit]
[app.loggers.webhooks :as-alias webhooks]
[app.media :as media]
@@ -200,14 +198,13 @@
(db/run! cfg (fn [{:keys [::db/conn] :as cfg}]
(files/check-read-permissions! conn profile-id file-id)
(let [team (teams/get-team conn
:profile-id profile-id
:file-id file-id)
(let [team (teams/get-team conn
:profile-id profile-id
:file-id file-id)
file (binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg file-id)]
(-> (files/get-file cfg file-id :migrate? false)
(update :data feat.fdata/process-pointers deref)
(fmg/migrate-file)))]
file (files/get-file cfg file-id
:preload-pointers? true
:read-only? true)]
(-> (cfeat/get-team-enabled-features cf/flags team)
(cfeat/check-file-features! (:features file)))

View File

@@ -1049,6 +1049,33 @@
:id id
:delta delta})))
(defn reorder-children
[changes id children]
(assert-page-id! changes)
(assert-objects! changes)
(let [page-id (::page-id (meta changes))
objects (lookup-objects changes)
shape (get objects id)
old-children (:shapes shape)
redo-change
{:type :reorder-children
:parent-id (:id shape)
:page-id page-id
:shapes children}
undo-change
{:type :reorder-children
:parent-id (:id shape)
:page-id page-id
:shapes old-children}]
(-> changes
(update :redo-changes conj redo-change)
(update :undo-changes conj undo-change)
(apply-changes-local))))
(defn reorder-grid-children
[changes ids]
(assert-page-id! changes)

View File

@@ -1,8 +1,13 @@
(ns app.main.ui.workspace.tokens.token
;; 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.files.tokens
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.main.ui.workspace.tokens.tinycolor :as tinycolor]
[clojure.set :as set]
[cuerdas.core :as str]))
@@ -128,18 +133,6 @@
(defn color-token? [token]
(= (:type token) :color))
;; FIXME: this should be precalculated ?
(defn is-reference? [token]
(str/includes? (:value token) "{"))
(defn color-bullet-color [token-color-value]
(when-let [tc (tinycolor/valid-color token-color-value)]
(if (tinycolor/alpha tc)
{:color (tinycolor/->hex-string tc)
:opacity (tinycolor/alpha tc)}
(tinycolor/->hex-string tc))))
(defn resolved-token-bullet-color [{:keys [resolved-value] :as token}]
(when (and resolved-value (color-token? token))
(color-bullet-color resolved-value)))

View File

@@ -216,10 +216,12 @@
"Generate changes to create a new instance from a component."
([changes objects file-id component-id position page libraries]
(generate-instantiate-component changes objects file-id component-id position page libraries nil nil nil {}))
([changes objects file-id component-id position page libraries old-id parent-id frame-id
([changes objects file-id component-id position page libraries old-id parent-id frame-id params]
(generate-instantiate-component changes objects file-id component-id position page libraries old-id parent-id frame-id {} params))
([changes objects file-id component-id position page libraries old-id parent-id frame-id ids-map
{:keys [force-frame?]
:or {force-frame? false}}]
(let [component (ctf/get-component libraries file-id component-id)
library (get libraries file-id)
parent (when parent-id (get objects parent-id))
@@ -239,6 +241,9 @@
(:data library)
position
(cond-> {}
(contains? ids-map old-id)
(assoc :force-id (get ids-map old-id))
force-frame?
(assoc :force-frame-id frame-id)))
@@ -260,8 +265,11 @@
(cond-> (pcb/add-object changes first-shape {:ignore-touched true})
(some? old-id) (pcb/amend-last-change #(assoc % :old-id old-id)))
duplicated-parent?
(->> ids-map vals (some #(= % (:parent-id first-shape))))
changes
(if (ctl/grid-layout? objects (:parent-id first-shape))
(if (and (ctl/grid-layout? objects (:parent-id first-shape)) (not duplicated-parent?))
(let [target-cell (-> position meta :cell)
[row column]
@@ -1976,17 +1984,26 @@
[changes library-data component-id library-id current-page objects]
(let [{:keys [changes shape]} (prepare-restore-component changes library-data component-id current-page)
parent-id (:parent-id shape)
objects (cond-> (assoc objects (:id shape) shape)
(not (nil? parent-id))
(update-in [parent-id :shapes]
#(conj % (:id shape))))
insert-before?
(and (ctl/flex-layout? objects parent-id)
(not (ctl/reverse? objects parent-id)))
objects
(-> objects
(assoc (:id shape) shape)
(cond-> (and (some? parent-id) insert-before?)
(update-in [parent-id :shapes] #(d/concat-vec [(:id shape)] %)))
(cond-> (and (some? parent-id) (not insert-before?))
(update-in [parent-id :shapes] conj (:id shape))))
;; Adds a resize-parents operation so the groups are updated. We add all the new objects
new-objects-ids (->> changes :redo-changes (filter #(= (:type %) :add-obj)) (mapv :id))
changes (-> changes
(pcb/with-objects objects)
(pcb/resize-parents new-objects-ids))]
(pcb/resize-parents new-objects-ids)
;; Fix the order of the children inside the parent
(pcb/reorder-children parent-id (get-in objects [parent-id :shapes])))]
(assoc changes :file-id library-id)))
(defn generate-detach-component
@@ -2225,6 +2242,7 @@
main-id
parent-id
frame-id
ids-map
{})))]
changes))

View File

@@ -4,36 +4,36 @@
;;
;; Copyright (c) KALEIDOS INC
(ns frontend-tests.tokens.token-test
(ns common-tests.files.tokens-test
(:require
[app.main.ui.workspace.tokens.token :as wtt]
[cljs.test :as t :include-macros true]))
[app.common.files.tokens :as cft]
[clojure.test :as t]))
(t/deftest test-parse-token-value
(t/testing "parses double from a token value"
(t/is (= {:value 100.1 :unit nil} (wtt/parse-token-value "100.1")))
(t/is (= {:value -9 :unit nil} (wtt/parse-token-value "-9"))))
(t/is (= {:value 100.1 :unit nil} (cft/parse-token-value "100.1")))
(t/is (= {:value -9.0 :unit nil} (cft/parse-token-value "-9"))))
(t/testing "trims white-space"
(t/is (= {:value -1.3 :unit nil} (wtt/parse-token-value " -1.3 "))))
(t/is (= {:value -1.3 :unit nil} (cft/parse-token-value " -1.3 "))))
(t/testing "parses unit: px"
(t/is (= {:value 70.3 :unit "px"} (wtt/parse-token-value " 70.3px "))))
(t/is (= {:value 70.3 :unit "px"} (cft/parse-token-value " 70.3px "))))
(t/testing "parses unit: %"
(t/is (= {:value -10 :unit "%"} (wtt/parse-token-value "-10%"))))
(t/is (= {:value -10.0 :unit "%"} (cft/parse-token-value "-10%"))))
(t/testing "parses unit: px")
(t/testing "returns nil for any invalid characters"
(t/is (nil? (wtt/parse-token-value " -1.3a "))))
(t/is (nil? (cft/parse-token-value " -1.3a "))))
(t/testing "doesnt accept invalid double"
(t/is (nil? (wtt/parse-token-value ".3")))))
(t/is (nil? (cft/parse-token-value ".3")))))
(t/deftest token-applied-test
(t/testing "matches passed token with `:token-attributes`"
(t/is (true? (wtt/token-applied? {:name "a"} {:applied-tokens {:x "a"}} #{:x}))))
(t/is (true? (cft/token-applied? {:name "a"} {:applied-tokens {:x "a"}} #{:x}))))
(t/testing "doesn't match empty token"
(t/is (nil? (wtt/token-applied? {} {:applied-tokens {:x "a"}} #{:x}))))
(t/is (nil? (cft/token-applied? {} {:applied-tokens {:x "a"}} #{:x}))))
(t/testing "does't match passed token `:id`"
(t/is (nil? (wtt/token-applied? {:name "b"} {:applied-tokens {:x "a"}} #{:x}))))
(t/is (nil? (cft/token-applied? {:name "b"} {:applied-tokens {:x "a"}} #{:x}))))
(t/testing "doesn't match passed `:token-attributes`"
(t/is (nil? (wtt/token-applied? {:name "a"} {:applied-tokens {:x "a"}} #{:y})))))
(t/is (nil? (cft/token-applied? {:name "a"} {:applied-tokens {:x "a"}} #{:y})))))
(t/deftest shapes-ids-by-applied-attributes
(t/testing "Returns set of matched attributes that fit the applied token"
@@ -54,7 +54,7 @@
shape-applied-x-y
shape-applied-all
shape-applied-none]
expected (wtt/shapes-ids-by-applied-attributes {:name "1"} shapes attributes)]
expected (cft/shapes-ids-by-applied-attributes {:name "1"} shapes attributes)]
(t/is (= (:x expected) (shape-ids shape-applied-x
shape-applied-x-y
shape-applied-all)))
@@ -62,34 +62,34 @@
shape-applied-x-y
shape-applied-all)))
(t/is (= (:z expected) (shape-ids shape-applied-all)))
(t/is (true? (wtt/shapes-applied-all? expected (shape-ids shape-applied-all) attributes)))
(t/is (false? (wtt/shapes-applied-all? expected (apply shape-ids shapes) attributes)))
(t/is (true? (cft/shapes-applied-all? expected (shape-ids shape-applied-all) attributes)))
(t/is (false? (cft/shapes-applied-all? expected (apply shape-ids shapes) attributes)))
(shape-ids shape-applied-x
shape-applied-x-y
shape-applied-all))))
(t/deftest tokens-applied-test
(t/testing "is true when single shape matches the token and attributes"
(t/is (true? (wtt/shapes-token-applied? {:name "a"} [{:applied-tokens {:x "a"}}
(t/is (true? (cft/shapes-token-applied? {:name "a"} [{:applied-tokens {:x "a"}}
{:applied-tokens {:x "b"}}]
#{:x}))))
(t/testing "is false when no shape matches the token or attributes"
(t/is (nil? (wtt/shapes-token-applied? {:name "a"} [{:applied-tokens {:x "b"}}
(t/is (nil? (cft/shapes-token-applied? {:name "a"} [{:applied-tokens {:x "b"}}
{:applied-tokens {:x "b"}}]
#{:x})))
(t/is (nil? (wtt/shapes-token-applied? {:name "a"} [{:applied-tokens {:x "a"}}
(t/is (nil? (cft/shapes-token-applied? {:name "a"} [{:applied-tokens {:x "a"}}
{:applied-tokens {:x "a"}}]
#{:y})))))
(t/deftest name->path-test
(t/is (= ["foo" "bar" "baz"] (wtt/token-name->path "foo.bar.baz")))
(t/is (= ["foo" "bar" "baz"] (wtt/token-name->path "foo..bar.baz")))
(t/is (= ["foo" "bar" "baz"] (wtt/token-name->path "foo..bar.baz...."))))
(t/is (= ["foo" "bar" "baz"] (cft/token-name->path "foo.bar.baz")))
(t/is (= ["foo" "bar" "baz"] (cft/token-name->path "foo..bar.baz")))
(t/is (= ["foo" "bar" "baz"] (cft/token-name->path "foo..bar.baz...."))))
(t/deftest token-name-path-exists?-test
(t/is (true? (wtt/token-name-path-exists? "border-radius" {"border-radius" {"sm" {:name "sm"}}})))
(t/is (true? (wtt/token-name-path-exists? "border-radius" {"border-radius" {:name "sm"}})))
(t/is (true? (wtt/token-name-path-exists? "border-radius.sm" {"border-radius" {:name "sm"}})))
(t/is (true? (wtt/token-name-path-exists? "border-radius.sm.x" {"border-radius" {:name "sm"}})))
(t/is (false? (wtt/token-name-path-exists? "other" {"border-radius" {:name "sm"}})))
(t/is (false? (wtt/token-name-path-exists? "dark.border-radius.md" {"dark" {"border-radius" {"sm" {:name "sm"}}}}))))
(t/is (true? (cft/token-name-path-exists? "border-radius" {"border-radius" {"sm" {:name "sm"}}})))
(t/is (true? (cft/token-name-path-exists? "border-radius" {"border-radius" {:name "sm"}})))
(t/is (true? (cft/token-name-path-exists? "border-radius.sm" {"border-radius" {:name "sm"}})))
(t/is (true? (cft/token-name-path-exists? "border-radius.sm.x" {"border-radius" {:name "sm"}})))
(t/is (false? (cft/token-name-path-exists? "other" {"border-radius" {:name "sm"}})))
(t/is (false? (cft/token-name-path-exists? "dark.border-radius.md" {"dark" {"border-radius" {"sm" {:name "sm"}}}}))))

View File

@@ -1,3 +1,9 @@
;; 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 common-tests.logic.token-test
(:require
[app.common.files.changes-builder :as pcb]

View File

@@ -4,11 +4,13 @@ import { presenceFixture } from "../../data/workspace/ws-notifications";
test.beforeEach(async ({ page }) => {
await WorkspacePage.init(page);
const workspacePage = new WorkspacePage(page);
await workspacePage.setupEmptyFile(page);
});
test("Save and restore version", async ({ page }) => {
const workspacePage = new WorkspacePage(page);
await workspacePage.setupEmptyFile(page);
await workspacePage.mockRPC(/get\-file\?/, "workspace/versions-init.json");
await workspacePage.mockRPC(
"get-file-fragment?file-id=*&fragment-id=406b7b01-d3e2-80e4-8005-3138b7cc5f0b",
@@ -87,3 +89,20 @@ test("Save and restore version", async ({ page }) => {
// check that the history panel is closed after restore
await expect(page.getByRole("tab", { name: "design" })).toBeVisible();
});
test("BUG 11006 - Fix history panel shortcut", async ({ page }) => {
const workspacePage = new WorkspacePage(page);
await workspacePage.mockRPC(/get\-file\?/, "workspace/versions-init.json");
await workspacePage.mockRPC(
"get-file-snapshots?file-id=*",
"workspace/versions-snapshot-1.json",
);
await workspacePage.goToWorkspace();
await page.keyboard.press("Control+Alt+h");
await expect(
workspacePage.rightSidebar.getByText("There are no versions yet"),
).toBeVisible();
});

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

View File

@@ -8,7 +8,7 @@
(:refer-clojure :exclude [meta reset!])
(:require
["@penpot/mousetrap$default" :as mousetrap]
[app.common.data.macros :as dm]
[app.common.data :as d]
[app.common.logging :as log]
[app.common.schema :as sm]
[app.config :as cf]
@@ -135,7 +135,7 @@
[:fn {:optional true} fn?]
[:tooltip {:optional true} :string]]])
(def check-shortcuts!
(def ^:private check-shortcuts
(sm/check-fn schema:shortcuts))
(defn- wrap-cb
@@ -167,23 +167,20 @@
(mousetrap/reset)
(bind! shortcuts)))
(def ^:private conj*
(fnil conj (d/ordered-map)))
(defn push-shortcuts
[key shortcuts]
(assert (keyword? key) "expected a keyword for `key`")
(let [shortcuts (check-shortcuts shortcuts)]
(ptk/reify ::push-shortcuts
ptk/UpdateEvent
(update [_ state]
(update state :shortcuts conj* [key shortcuts]))
(dm/assert!
"expected valid parameters"
(and (keyword? key)
(check-shortcuts! shortcuts)))
(ptk/reify ::push-shortcuts
ptk/UpdateEvent
(update [_ state]
(-> state
(update :shortcuts (fnil conj '()) [key shortcuts])))
ptk/EffectEvent
(effect [_ state _]
(let [[_key shortcuts] (peek (:shortcuts state))]
ptk/EffectEvent
(effect [_ _ _]
(reset! shortcuts)))))
(defn pop-shortcuts
@@ -192,12 +189,9 @@
ptk/UpdateEvent
(update [_ state]
(update state :shortcuts (fn [shortcuts]
(let [current-key (first (peek shortcuts))]
(if (= key current-key)
(pop shortcuts)
shortcuts)))))
(dissoc shortcuts key))))
ptk/EffectEvent
(effect [_ state _]
(let [[key* shortcuts] (peek (:shortcuts state))]
(when (not= key key*)
(reset! shortcuts))))))
(let [[_key shortcuts] (last (:shortcuts state))]
(reset! shortcuts)))))

View File

@@ -1,15 +1,21 @@
(ns app.main.ui.workspace.tokens.style-dictionary
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.main.data.style-dictionary
(:require
["@tokens-studio/sd-transforms" :as sd-transforms]
["style-dictionary$default" :as sd]
[app.common.files.tokens :as cft]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.transit :as t]
[app.common.types.tokens-lib :as ctob]
[app.main.ui.workspace.tokens.errors :as wte]
[app.main.ui.workspace.tokens.tinycolor :as tinycolor]
[app.main.ui.workspace.tokens.token :as wtt]
[app.main.ui.workspace.tokens.warnings :as wtw]
[app.main.data.tinycolor :as tinycolor]
[app.main.data.workspace.tokens.errors :as wte]
[app.main.data.workspace.tokens.warnings :as wtw]
[app.util.time :as dt]
[beicon.v2.core :as rx]
[cuerdas.core :as str]
@@ -54,7 +60,7 @@
"Parses `value` of a numeric `sd-token` into a map like `{:value 1 :unit \"px\"}`.
If the `value` is not parseable and/or has missing references returns a map with `:errors`."
[value]
(let [parsed-value (wtt/parse-token-value value)
(let [parsed-value (cft/parse-token-value value)
out-of-bounds (or (>= (:value parsed-value) sm/max-safe-int)
(<= (:value parsed-value) sm/min-safe-int))]
(if (and parsed-value (not out-of-bounds))
@@ -72,7 +78,7 @@
If the `value` is parseable but is out of range returns a map with `warnings`."
[value has-references?]
(let [parsed-value (wtt/parse-token-value value)
(let [parsed-value (cft/parse-token-value value)
out-of-scope (not (<= 0 (:value parsed-value) 1))
references (seq (ctob/find-token-value-references value))]
(cond
@@ -98,7 +104,7 @@
If the `value` is parseable but is out of range returns a map with `warnings`."
[value has-references?]
(let [parsed-value (wtt/parse-token-value value)
(let [parsed-value (cft/parse-token-value value)
out-of-scope (< (:value parsed-value) 0)
references (seq (ctob/find-token-value-references value))]
(cond

View File

@@ -1,4 +1,10 @@
(ns app.main.ui.workspace.tokens.tinycolor
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.main.data.tinycolor
"Bindings for tinycolor2 which supports a wide range of css compatible colors.
This library was chosen as it is already used by StyleDictionary,

View File

@@ -581,11 +581,14 @@
changes (-> (pcb/empty-changes it)
(cll/generate-restore-component ldata component-id library-id page objects))
page-id
(->> changes :redo-changes (keep :page-id) first)
frames
(->> changes :redo-changes (keep :frame-id))]
(rx/of (dch/commit-changes changes)
(ptk/data-event :layout/update {:ids frames}))))))
(ptk/data-event :layout/update {:page-id page-id :ids frames}))))))
(defn restore-components

View File

@@ -97,15 +97,17 @@
;; Never call this directly but through the data-event `:layout/update`
;; Otherwise a lot of cycle dependencies could be generated
(defn- update-layout-positions
[{:keys [ids undo-group]}]
[{:keys [page-id ids undo-group]}]
(ptk/reify ::update-layout-positions
ptk/WatchEvent
(watch [_ state _]
(let [objects (dsh/lookup-page-objects state)
(let [page-id (or page-id (:current-page-id state))
objects (dsh/lookup-page-objects state page-id)
ids (->> ids (filter #(contains? objects %)))]
(if (d/not-empty? ids)
(let [modif-tree (dwm/create-modif-tree ids (ctm/reflow-modifiers))]
(rx/of (dwm/apply-modifiers {:modifiers modif-tree
(rx/of (dwm/apply-modifiers {:page-id page-id
:modifiers modif-tree
:stack-undo? true
:undo-group undo-group})))
(rx/empty))))))
@@ -127,8 +129,9 @@
(rx/filter #(d/not-empty? %))
(rx/map
(fn [data]
(let [ids (reduce #(into %1 (:ids %2)) #{} data)]
(update-layout-positions {:ids ids}))))
(let [page-id (->> data (keep :page-id) first)
ids (reduce #(into %1 (:ids %2)) #{} data)]
(update-layout-positions {:page-id page-id :ids ids}))))
(rx/take-until stopper))))))
(defn finalize-shape-layout

View File

@@ -464,11 +464,11 @@
:subsections [:panels]
:fn #(st/emit! (dcm/go-to-workspace :layout :assets))}
:toggle-history {:tooltip (ds/alt "H")
:command (ds/a-mod "h")
:toggle-history {:tooltip (ds/meta-alt "H")
:command (ds/ca-mod "h")
:subsections [:panels]
:fn #(emit-when-no-readonly
(dcm/go-to-workspace :layout :document-history))}
(dw/toggle-layout-flag :document-history))}
:toggle-colorpalette {:tooltip (ds/alt "P")
:command (ds/a-mod "p")

View File

@@ -4,16 +4,19 @@
;;
;; Copyright (c) KALEIDOS INC
(ns app.main.ui.workspace.tokens.changes
(ns app.main.data.workspace.tokens.application
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.tokens :as cft]
[app.common.types.shape.layout :as ctsl]
[app.common.types.shape.radius :as ctsr]
[app.common.types.token :as ctt]
[app.common.types.tokens-lib :as ctob]
[app.main.data.event :as ev]
[app.main.data.helpers :as dsh]
[app.main.data.style-dictionary :as sd]
[app.main.data.tinycolor :as tinycolor]
[app.main.data.workspace :as udw]
[app.main.data.workspace.colors :as wdc]
[app.main.data.workspace.shape-layout :as dwsl]
@@ -21,16 +24,13 @@
[app.main.data.workspace.transforms :as dwt]
[app.main.data.workspace.undo :as dwu]
[app.main.store :as st]
[app.main.ui.workspace.tokens.style-dictionary :as sd]
[app.main.ui.workspace.tokens.tinycolor :as tinycolor]
[app.main.ui.workspace.tokens.token :as wtt]
[beicon.v2.core :as rx]
[clojure.set :as set]
[potok.v2.core :as ptk]))
(declare token-properties)
;; Token Updates ---------------------------------------------------------------
;; Events to apply / unapply tokens to shapes ------------------------------------------------------------
(defn apply-token
"Apply `attributes` that match `token` for `shape-ids`.
@@ -56,8 +56,8 @@
(keys))
[])
resolved-value (get-in resolved-tokens [(wtt/token-identifier token) :resolved-value])
tokenized-attributes (wtt/attributes-map attributes token)]
resolved-value (get-in resolved-tokens [(cft/token-identifier token) :resolved-value])
tokenized-attributes (cft/attributes-map attributes token)]
(rx/of
(st/emit! (ptk/event ::ev/event {::ev/name "apply-tokens"}))
(dwu/start-undo-transaction undo-id)
@@ -80,7 +80,7 @@
ptk/WatchEvent
(watch [_ _ _]
(rx/of
(let [remove-token #(when % (wtt/remove-attributes-for-token attributes token %))]
(let [remove-token #(when % (cft/remove-attributes-for-token attributes token %))]
(dwsh/update-shapes
shape-ids
(fn [shape]
@@ -95,7 +95,7 @@
(get token-properties (:type token))
unapply-tokens?
(wtt/shapes-token-applied? token shapes (or all-attributes attributes))
(cft/shapes-token-applied? token shapes (or all-attributes attributes))
shape-ids (map :id shapes)]
(if unapply-tokens?
@@ -109,7 +109,9 @@
:shape-ids shape-ids
:on-update-shape on-update-shape})))))))
;; Shape Updates ---------------------------------------------------------------
;; Events to update the value of attributes with applied tokens ---------------------------------------------------------
;; (note that dwsh/update-shapes function returns an event)
(defn update-shape-radius-all
([value shape-ids attributes] (update-shape-radius-all value shape-ids attributes nil))
@@ -326,7 +328,7 @@
(dwsl/update-layout-child shape-ids props {:ignore-touched true
:page-id page-id}))))))))
;; Token Types -----------------------------------------------------------------
;; Map token types to different properties used along the cokde ---------------------------------------------------------
;; FIXME: the values should be lazy evaluated, probably a function,
;; becasue on future we will need to translate that labels and that

View File

@@ -0,0 +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.main.data.workspace.tokens.color
(:require
[app.common.files.tokens :as cft]
[app.main.data.tinycolor :as tinycolor]))
(defn color-bullet-color [token-color-value]
(when-let [tc (tinycolor/valid-color token-color-value)]
(if (tinycolor/alpha tc)
{:color (tinycolor/->hex-string tc)
:opacity (tinycolor/alpha tc)}
(tinycolor/->hex-string tc))))
(defn resolved-token-bullet-color [{:keys [resolved-value] :as token}]
(when (and resolved-value (cft/color-token? token))
(color-bullet-color resolved-value)))

View File

@@ -1,4 +1,10 @@
(ns app.main.ui.workspace.tokens.errors
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.main.data.workspace.tokens.errors
(:require
[app.util.i18n :refer [tr]]
[cuerdas.core :as str]))

View File

@@ -4,7 +4,7 @@
;;
;; Copyright (c) KALEIDOS INC
(ns app.main.data.tokens
(ns app.main.data.workspace.tokens.library-edit
(:require
[app.common.data.macros :as dm]
[app.common.files.changes-builder :as pcb]
@@ -18,7 +18,7 @@
[app.main.data.helpers :as dsh]
[app.main.data.notifications :as ntf]
[app.main.data.workspace.shapes :as dwsh]
[app.main.ui.workspace.tokens.update :as wtu]
[app.main.data.workspace.tokens.propagation :as dwtp]
[app.util.i18n :refer [tr]]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
@@ -123,7 +123,7 @@
(pcb/update-active-token-themes active-token-themes' prev-active-token-themes))]
(rx/of
(dch/commit-changes changes)
(wtu/update-workspace-tokens))))))
(dwtp/propagate-workspace-tokens))))))
(defn delete-token-theme [group theme-name]
(ptk/reify ::delete-token-theme
@@ -135,7 +135,7 @@
(pcb/set-token-theme group theme-name nil))]
(rx/of
(dch/commit-changes changes)
(wtu/update-workspace-tokens))))))
(dwtp/propagate-workspace-tokens))))))
(defn create-token-set
[set-name]
@@ -221,7 +221,7 @@
(clt/generate-toggle-token-set tlib name))]
(rx/of (dch/commit-changes changes)
(wtu/update-workspace-tokens))))))
(dwtp/propagate-workspace-tokens))))))
(defn toggle-token-set-group [group-path]
(ptk/reify ::toggle-token-set-group
@@ -233,7 +233,7 @@
(clt/generate-toggle-token-set-group (get-tokens-lib state) group-path))]
(rx/of
(dch/commit-changes changes)
(wtu/update-workspace-tokens))))))
(dwtp/propagate-workspace-tokens))))))
(defn import-tokens-lib [lib]
(ptk/reify ::import-tokens-lib
@@ -244,7 +244,7 @@
(pcb/with-library-data data)
(pcb/set-tokens-lib lib))]
(rx/of (dch/commit-changes changes)
(wtu/update-workspace-tokens))))))
(dwtp/propagate-workspace-tokens))))))
(defn delete-token-set-path
[group? path]
@@ -256,7 +256,7 @@
(pcb/with-library-data data)
(pcb/set-token-set (ctob/join-set-path path) group? nil))]
(rx/of (dch/commit-changes changes)
(wtu/update-workspace-tokens))))))
(dwtp/propagate-workspace-tokens))))))
(defn drop-error [{:keys [error to-path]}]
(ptk/reify ::drop-error
@@ -283,7 +283,7 @@
(when-let [changes (clt/generate-move-token-set-group (pcb/empty-changes it) (get-tokens-lib state) drop-opts)]
(rx/of
(dch/commit-changes changes)
(wtu/update-workspace-tokens)))
(dwtp/propagate-workspace-tokens)))
(catch :default e
(rx/of
(drop-error (ex-data e))))))))
@@ -300,7 +300,7 @@
changes (-> (pcb/empty-changes it)
(clt/generate-move-token-set tokens-lib params))]
(rx/of (dch/commit-changes changes)
(wtu/update-workspace-tokens)))
(dwtp/propagate-workspace-tokens)))
(catch :default cause
(rx/of (drop-error (ex-data cause))))))))

View File

@@ -4,18 +4,18 @@
;;
;; Copyright (c) KALEIDOS INC
(ns app.main.ui.workspace.tokens.update
(ns app.main.data.workspace.tokens.propagation
(:require
[app.common.files.helpers :as cfh]
[app.common.logging :as l]
[app.common.types.token :as ctt]
[app.common.types.tokens-lib :as ctob]
[app.main.data.helpers :as dsh]
[app.main.data.style-dictionary :as sd]
[app.main.data.workspace.shapes :as dwsh]
[app.main.data.workspace.thumbnails :as dwt]
[app.main.data.workspace.tokens.application :as dwta]
[app.main.data.workspace.undo :as dwu]
[app.main.ui.workspace.tokens.changes :as wtch]
[app.main.ui.workspace.tokens.style-dictionary :as wtsd]
[app.util.time :as dt]
[beicon.v2.core :as rx]
[clojure.data :as data]
@@ -24,21 +24,21 @@
;; Constants -------------------------------------------------------------------
(def filter-existing-values? false)
(def ^:private filter-existing-values? false)
(def attributes->shape-update
{ctt/border-radius-keys wtch/update-shape-radius-for-corners
ctt/color-keys wtch/update-fill-stroke
ctt/stroke-width-keys wtch/update-stroke-width
ctt/sizing-keys wtch/update-shape-dimensions
ctt/opacity-keys wtch/update-opacity
#{:x :y} wtch/update-shape-position
#{:p1 :p2 :p3 :p4} wtch/update-layout-padding
#{:m1 :m2 :m3 :m4} wtch/update-layout-item-margin
#{:column-gap :row-gap} wtch/update-layout-spacing
#{:width :height} wtch/update-shape-dimensions
#{:layout-item-min-w :layout-item-min-h :layout-item-max-w :layout-item-max-h} wtch/update-layout-sizing-limits
ctt/rotation-keys wtch/update-rotation})
(def ^:private attributes->shape-update
{ctt/border-radius-keys dwta/update-shape-radius-for-corners
ctt/color-keys dwta/update-fill-stroke
ctt/stroke-width-keys dwta/update-stroke-width
ctt/sizing-keys dwta/update-shape-dimensions
ctt/opacity-keys dwta/update-opacity
#{:x :y} dwta/update-shape-position
#{:p1 :p2 :p3 :p4} dwta/update-layout-padding
#{:m1 :m2 :m3 :m4} dwta/update-layout-item-margin
#{:column-gap :row-gap} dwta/update-layout-spacing
#{:width :height} dwta/update-shape-dimensions
#{:layout-item-min-w :layout-item-min-h :layout-item-max-w :layout-item-max-h} dwta/update-layout-sizing-limits
ctt/rotation-keys dwta/update-rotation})
(def attribute-actions-map
(reduce
@@ -48,6 +48,7 @@
;; Helpers ---------------------------------------------------------------------
;; TODO: see if this can be replaced by more standard functions
(defn deep-merge
"Like d/deep-merge but unions set values."
([a b]
@@ -60,7 +61,7 @@
;; Data flows ------------------------------------------------------------------
(defn invert-collect-key-vals
(defn- invert-collect-key-vals
[xs resolved-tokens shape]
(-> (reduce
(fn [acc [k v]]
@@ -74,7 +75,7 @@
(update acc resolved-value (fnil conj #{}) k))))
{} xs)))
(defn split-attribute-groups [attrs-values-map]
(defn- split-attribute-groups [attrs-values-map]
(reduce
(fn [acc [attrs v]]
(cond
@@ -91,7 +92,7 @@
attrs (assoc acc attrs v)))
{} attrs-values-map))
(defn shape-ids-by-values
(defn- shape-ids-by-values
[attrs-values-map object-id]
(->> (map (fn [[value attrs]] [attrs {value #{object-id}}]) attrs-values-map)
(into {})))
@@ -121,7 +122,6 @@
[tokens frame-ids text-ids])))
;; FIXME: revisit this
(defn- actionize-shapes-update-info [page-id shapes-update-info]
(mapcat (fn [[attrs update-infos]]
(let [action (some attribute-actions-map attrs)]
@@ -131,14 +131,15 @@
update-infos)))
shapes-update-info))
(defn update-tokens
(defn propagate-tokens
"Propagate tokens values to all shapes where they are applied"
[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)
tpoint (dt/tpoint-ms)]
(l/inf :status "START" :hint "update-tokens")
(l/inf :status "START" :hint "propagate-tokens")
(->> (rx/concat
(rx/of current-page-id)
(->> (rx/from (:pages fdata))
@@ -155,7 +156,7 @@
(actionize-shapes-update-info page-id attrs)]
(l/inf :status "PROGRESS"
:hint "update-tokens"
:hint "propagate-tokens"
:page-id (str page-id)
:elapsed (tpoint)
::l/sync? true)
@@ -175,21 +176,21 @@
(rx/finalize
(fn [_]
(let [elapsed (tpoint)]
(l/inf :status "END" :hint "update-tokens" :elapsed elapsed)))))))
(l/inf :status "END" :hint "propagate-tokens" :elapsed elapsed)))))))
(defn update-workspace-tokens
(defn propagate-workspace-tokens
[]
(ptk/reify ::update-workspace-tokens
(ptk/reify ::propagate-workspace-tokens
ptk/WatchEvent
(watch [_ state _]
(when-let [tokens-lib (-> (dsh/lookup-file-data state)
(get :tokens-lib))]
(let [tokens (-> (ctob/get-active-themes-set-tokens tokens-lib)
(wtsd/resolve-tokens+))]
(sd/resolve-tokens+))]
(->> (rx/from tokens)
(rx/mapcat (fn [sd-tokens]
(let [undo-id (js/Symbol)]
(rx/concat
(rx/of (dwu/start-undo-transaction undo-id :timeout false))
(update-tokens state sd-tokens)
(propagate-tokens state sd-tokens)
(rx/of (dwu/commit-undo-transaction undo-id))))))))))))

View File

@@ -11,7 +11,7 @@
[app.common.types.tokens-lib :as ctob]
[app.main.data.helpers :as dsh]))
(defn get-selected-token-set-name [state]
(defn- get-selected-token-set-name [state]
(or (get-in state [:workspace-tokens :selected-token-set-name])
(some-> (dsh/lookup-file-data state)
(get :tokens-lib)

View File

@@ -1,4 +1,10 @@
(ns app.main.ui.workspace.tokens.warnings
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.main.data.workspace.tokens.warnings
(:require
[app.util.i18n :refer [tr]]
[cuerdas.core :as str]))

View File

@@ -198,7 +198,7 @@
}
.import-error-disclaimer {
color: var(--color-foreground-primary);
color: var(--color-foreground-secondary);
}
.import-error-list {

View File

@@ -33,6 +33,7 @@
[app.main.ui.releases.v2-4]
[app.main.ui.releases.v2-5]
[app.main.ui.releases.v2-6]
[app.main.ui.releases.v2-7]
[app.util.object :as obj]
[app.util.timers :as tm]
[rumext.v2 :as mf]))
@@ -97,4 +98,4 @@
(defmethod rc/render-release-notes "0.0"
[params]
(rc/render-release-notes (assoc params :version "2.6")))
(rc/render-release-notes (assoc params :version "2.7")))

View File

@@ -0,0 +1,142 @@
;; 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.releases.v2-7
(:require-macros [app.main.style :as stl])
(:require
[app.common.data.macros :as dm]
[app.main.ui.releases.common :as c]
[rumext.v2 :as mf]))
(defmethod c/render-release-notes "2.7"
[{:keys [slide klass next finish navigate version]}]
(mf/html
(case slide
:start
[:div {:class (stl/css-case :modal-overlay true)}
[:div.animated {:class klass}
[:div {:class (stl/css :modal-container)}
[:img {:src "images/features/2.7-slide-0.jpg"
:class (stl/css :start-image)
:border "0"
:alt "Design Tokens make their debut in Penpot!"}]
[:div {:class (stl/css :modal-content)}
[:div {:class (stl/css :modal-header)}
[:h1 {:class (stl/css :modal-title)}
"Whats new in Penpot?"]
[:div {:class (stl/css :version-tag)}
(dm/str "Version " version)]]
[:div {:class (stl/css :features-block)}
[:span {:class (stl/css :feature-title)}
"Penpot 2.7 is out!"]
[:p {:class (stl/css :feature-content)}
"After the huge excitement around our last release. The first-ever native Design Tokens support in a design tool (yay!), were keeping the momentum going with a fresh batch of new features and improvements."]
[:p {:class (stl/css :feature-content)}
"This update brings the first set of upgrades to our new Design Tokens system, a few of the many to come. Weve also expanded who can create sharing prototype links and improved the invitations area. Last but not least, we fixed a bunch of bugs and optimizations that will make the experience more enjoyable for all."]
[:p {:class (stl/css :feature-content)}
"Lets dive in!"]]
[:div {:class (stl/css :navigation)}
[:button {:class (stl/css :next-btn)
:on-click next} "Continue"]]]]]]
0
[:div {:class (stl/css-case :modal-overlay true)}
[:div.animated {:class klass}
[:div {:class (stl/css :modal-container)}
[:img {:src "images/features/2.7-duplicate-set.gif"
:class (stl/css :start-image)
:border "0"
:alt "Design Tokens improvements"}]
[:div {:class (stl/css :modal-content)}
[:div {:class (stl/css :modal-header)}
[:h1 {:class (stl/css :modal-title)}
"Design Tokens improvements"]]
[:div {:class (stl/css :feature)}
[:p {:class (stl/css :feature-content)}
"It hasnt been long since we launched Design Tokens in Penpot (the first native Design Tokens support in a design tool!), and were already rolling out the first set of improvements."]
[:p {:class (stl/css :feature-content)}
"The highlight: you can now duplicate token sets directly from a menu item. A huge time-saver, especially when working from existing sets. Weve also made it easier to create themes by letting you select their set right away, and weve polished some info indicators to make everything a bit clearer. Plus, weve fixed a bunch of early-stage bugs to keep things running smoothly."]]
[:div {:class (stl/css :navigation)}
[:& c/navigation-bullets
{:slide slide
:navigate navigate
:total 3}]
[:button {:on-click next
:class (stl/css :next-btn)} "Continue"]]]]]]
1
[:div {:class (stl/css-case :modal-overlay true)}
[:div.animated {:class klass}
[:div {:class (stl/css :modal-container)}
[:img {:src "images/features/2.7-share.gif"
:class (stl/css :start-image)
:border "0"
:alt "Editors and viewers can now create Share prototype links"}]
[:div {:class (stl/css :modal-content)}
[:div {:class (stl/css :modal-header)}
[:h1 {:class (stl/css :modal-title)}
"Editors and viewers can now create Share prototype links"]]
[:div {:class (stl/css :feature)}
[:p {:class (stl/css :feature-content)}
"From now on, both editors and viewers can create Share Prototype links. Sharing prototypes is key for better team collaboration, no matter the role. Its a common need, team members often have to share presentations without risking any accidental changes to the designs, which means they dont necessarily need editing permissions. In the future, Penpot will introduce more fine-grained control over these permissions."]
[:p {:class (stl/css :feature-content)}
"This update gives editors and viewers the same ability to configure, create, copy, and delete sharing links. A capability that, until now, was limited to owners and admins."]]
[:div {:class (stl/css :navigation)}
[:& c/navigation-bullets
{:slide slide
:navigate navigate
:total 3}]
[:button {:on-click next
:class (stl/css :next-btn)} "Continue"]]]]]]
2
[:div {:class (stl/css-case :modal-overlay true)}
[:div.animated {:class klass}
[:div {:class (stl/css :modal-container)}
[:img {:src "images/features/2.7-invitations.gif"
:class (stl/css :start-image)
:border "0"
:alt "A clearer way to invite your first team members"}]
[:div {:class (stl/css :modal-content)}
[:div {:class (stl/css :modal-header)}
[:h1 {:class (stl/css :modal-title)}
"A clearer way to invite your first team members"]]
[:div {:class (stl/css :feature)}
[:p {:class (stl/css :feature-content)}
"Penpot works perfectly for solo projects, but its always more fun with a team. Thats why weve updated the initial state of the invitations area. Instead of starting blank, it now offers clearer guidance to help you invite your first team members."]
[:p {:class (stl/css :feature-content)}
"This improvement in design and UX writing comes from community member Prithvi Tharun (credit where its due!) Not all open source contributions are about code, and this is a fantastic example of how design and writing make a real difference too."]]
[:div {:class (stl/css :navigation)}
[:& c/navigation-bullets
{:slide slide
:navigate navigate
:total 3}]
[:button {:on-click finish
:class (stl/css :next-btn)} "Let's go"]]]]]])))

View File

@@ -0,0 +1,102 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) KALEIDOS INC
@import "refactor/common-refactor.scss";
.modal-overlay {
@extend .modal-overlay-base;
}
.modal-container {
display: grid;
grid-template-columns: $s-324 1fr;
height: $s-500;
width: $s-888;
border-radius: $br-8;
background-color: var(--modal-background-color);
border: $s-2 solid var(--modal-border-color);
}
.start-image {
width: $s-324;
border-radius: $br-8 0 0 $br-8;
}
.modal-content {
padding: $s-40;
display: grid;
grid-template-rows: auto 1fr $s-32;
gap: $s-24;
a {
color: var(--button-primary-background-color-rest);
}
}
.modal-header {
display: grid;
gap: $s-8;
}
.version-tag {
@include flexCenter;
@include headlineSmallTypography;
height: $s-32;
width: $s-96;
background-color: var(--communication-tag-background-color);
color: var(--communication-tag-foreground-color);
border-radius: $br-8;
}
.modal-title {
@include headlineLargeTypography;
color: var(--modal-title-foreground-color);
}
.features-block {
display: flex;
flex-direction: column;
gap: $s-16;
width: $s-440;
}
.feature {
display: flex;
flex-direction: column;
gap: $s-8;
}
.feature-title {
@include bodyLargeTypography;
color: var(--modal-title-foreground-color);
}
.feature-content {
@include bodyMediumTypography;
margin: 0;
color: var(--modal-text-foreground-color);
}
.feature-list {
@include bodyMediumTypography;
color: var(--modal-text-foreground-color);
list-style: disc;
display: grid;
gap: $s-8;
}
.navigation {
width: 100%;
display: grid;
grid-template-areas: "bullets button";
}
.next-btn {
@extend .button-primary;
width: $s-100;
justify-self: flex-end;
grid-area: button;
}

View File

@@ -127,6 +127,7 @@
width: $s-28;
margin-left: $s-8;
background-color: var(--viewer-controls-background-color);
pointer-events: all;
svg {
@extend .button-icon;
stroke: var(--icon-foreground);

View File

@@ -586,10 +586,10 @@
(mf/with-effect []
(st/emit! (st/emit! (dsc/push-shortcuts ::colorpicker sc/shortcuts)))
#(do
(st/emit! (dsc/pop-shortcuts ::colorpicker))
(when (and @dirty? @last-change on-close)
(on-close @last-change))))
(fn []
(st/emit! (dsc/pop-shortcuts ::colorpicker))
(when (and @dirty? @last-change on-close)
(on-close @last-change))))
[:div {:class (stl/css :colorpicker-tooltip)
:data-testid "colorpicker"

View File

@@ -618,7 +618,7 @@
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click on-pin-version
:on-key-down on-pin-version-key-down
:id "file-menu-show-version-history"}
:id "file-menu-create-version"}
[:span {:class (stl/css :item-name)}
(tr "dashboard.create-version-menu")]]
@@ -627,7 +627,10 @@
:on-key-down on-show-version-history-key-down
:id "file-menu-show-version-history"}
[:span {:class (stl/css :item-name)}
(tr "dashboard.show-version-history")]]
(tr "dashboard.show-version-history")]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :toggle-history))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
[:div {:class (stl/css :separator)}]])
@@ -786,6 +789,7 @@
on-power-up-click
(mf/use-fn
(fn []
(st/emit! (ptk/event ::ev/event {::ev/name "explore-pricing-click" ::ev/origin "workspace-menu"}))
(dom/open-new-window "https://penpot.app/pricing")))
toggle-flag
@@ -900,14 +904,16 @@
:id "file-menu-help-info"}
[:span {:class (stl/css :item-name)} (tr "workspace.header.menu.option.help-info")]
[:span {:class (stl/css :open-arrow)} i/arrow]]
[:> dropdown-menu-item* {:class (stl/css-case :menu-item true)
:on-click on-power-up-click
:on-key-down (fn [event]
(when (kbd/enter? event)
(on-power-up-click)))
:on-pointer-enter close-sub-menu
:id "file-menu-power-up"}
[:span {:class (stl/css :item-name)} (tr "workspace.header.menu.option.power-up")]]]
;; TODO remove this block when subscriptions is full implemented
(when (contains? cf/flags :subscriptions-old)
[:> dropdown-menu-item* {:class (stl/css-case :menu-item true)
:on-click on-power-up-click
:on-key-down (fn [event]
(when (kbd/enter? event)
(on-power-up-click)))
:on-pointer-enter close-sub-menu
:id "file-menu-power-up"}
[:span {:class (stl/css :item-name)} (tr "workspace.header.menu.option.power-up")]])]
(case sub-menu
:file

View File

@@ -85,6 +85,7 @@ $width-settings-bar-max: $s-500;
}
.resize-area-horiz {
background-color: var(--panel-background-color);
position: absolute;
left: 0;
width: 100%;

View File

@@ -1,70 +0,0 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.main.ui.workspace.tokens.common
(:require
[app.common.data :as d]
[app.main.data.shortcuts :as dsc]
[app.main.store :as st]
[app.util.dom :as dom]
[app.util.globals :as globals]
[app.util.keyboard :as kbd]
[cuerdas.core :as str]
[goog.events :as events]
[rumext.v2 :as mf])
(:import goog.events.EventType))
;; Helpers ---------------------------------------------------------------------
(defn camel-keys [m]
(->> m
(d/deep-mapm
(fn [[k v]]
(if (or (keyword? k) (string? k))
[(keyword (str/camel (name k))) v]
[k v])))))
(defn direction-select
"Returns next `n` in `direction` while wrapping around at the last item at the count of `coll`.
`direction` accepts `:up` or `:down`."
[direction n coll]
(let [last-n (dec (count coll))
next-n (case direction
:up (dec n)
:down (inc n))
wrap-around-n (cond
(neg? next-n) last-n
(> next-n last-n) 0
:else next-n)]
wrap-around-n))
(defn use-arrow-highlight [{:keys [shortcuts-key options on-select]}]
(let [highlighted* (mf/use-state nil)
highlighted (deref highlighted*)
on-dehighlight #(reset! highlighted* nil)
on-keyup (fn [event]
(cond
(and (kbd/enter? event) highlighted) (on-select (nth options highlighted))
(kbd/up-arrow? event) (do
(dom/prevent-default event)
(->> (direction-select :up (or highlighted 0) options)
(reset! highlighted*)))
(kbd/down-arrow? event) (do
(dom/prevent-default event)
(->> (direction-select :down (or highlighted -1) options)
(reset! highlighted*)))))]
(mf/with-effect [highlighted]
(let [shortcuts-key shortcuts-key
keys [(events/listen globals/document EventType.KEYUP on-keyup)
(events/listen globals/document EventType.KEYDOWN dom/prevent-default)]]
(st/emit! (dsc/push-shortcuts shortcuts-key {}))
(fn []
(doseq [key keys]
(events/unlistenByKey key))
(st/emit! (dsc/pop-shortcuts shortcuts-key)))))
{:highlighted highlighted
:on-dehighlight on-dehighlight}))

View File

@@ -7,8 +7,8 @@
(ns app.main.ui.workspace.tokens.components.controls.input-token-color-bullet
(:require-macros [app.main.style :as stl])
(:require
[app.main.data.workspace.tokens.color :as dwtc]
[app.main.ui.components.color-bullet :refer [color-bullet]]
[app.main.ui.workspace.tokens.token :as wtt]
[rumext.v2 :as mf]))
(def ^:private schema::input-token-color-bullet
@@ -23,6 +23,6 @@
[:div {:data-testid "token-form-color-bullet"
:class (stl/css :input-token-color-bullet)
:on-click on-click}
(if-let [color' (wtt/color-bullet-color color)]
(if-let [color' (dwtc/color-bullet-color color)]
[:> color-bullet {:color color' :mini true}]
[:div {:class (stl/css :input-token-color-bullet-placeholder)}])])

View File

@@ -9,16 +9,16 @@
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.tokens :as cft]
[app.common.types.tokens-lib :as ctob]
[app.main.data.modal :as modal]
[app.main.data.tokens :as dt]
[app.main.data.workspace.shape-layout :as dwsl]
[app.main.data.workspace.tokens.application :as dwta]
[app.main.data.workspace.tokens.library-edit :as dwtl]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.dropdown :refer [dropdown]]
[app.main.ui.ds.foundations.assets.icon :refer [icon*]]
[app.main.ui.workspace.tokens.changes :as wtch]
[app.main.ui.workspace.tokens.token :as wtt]
[app.util.dom :as dom]
[app.util.i18n :refer [tr]]
[app.util.timers :as timers]
@@ -30,15 +30,15 @@
;; Actions ---------------------------------------------------------------------
(defn attribute-actions [token selected-shapes attributes]
(let [ids-by-attributes (wtt/shapes-ids-by-applied-attributes token selected-shapes attributes)
(let [ids-by-attributes (cft/shapes-ids-by-applied-attributes token selected-shapes attributes)
shape-ids (into #{} (map :id selected-shapes))]
{:all-selected? (wtt/shapes-applied-all? ids-by-attributes shape-ids attributes)
{:all-selected? (cft/shapes-applied-all? ids-by-attributes shape-ids attributes)
:shape-ids shape-ids
:selected-pred #(seq (% ids-by-attributes))}))
(defn generic-attribute-actions [attributes title {:keys [token selected-shapes on-update-shape hint]}]
(let [on-update-shape-fn (or on-update-shape
(-> (wtch/get-token-properties token)
(-> (dwta/get-token-properties token)
(:on-update-shape)))
{:keys [selected-pred shape-ids]} (attribute-actions token selected-shapes attributes)]
(map (fn [attribute]
@@ -52,8 +52,8 @@
:selected? selected?
:action (fn []
(if selected?
(st/emit! (wtch/unapply-token props))
(st/emit! (wtch/apply-token (assoc props :on-update-shape on-update-shape-fn)))))}))
(st/emit! (dwta/unapply-token props))
(st/emit! (dwta/apply-token (assoc props :on-update-shape on-update-shape-fn)))))}))
attributes)))
(defn all-or-separate-actions [{:keys [attribute-labels on-update-shape-all on-update-shape hint]}
@@ -67,8 +67,8 @@
:selected? all-selected?
:hint hint
:action #(if all-selected?
(st/emit! (wtch/unapply-token props))
(st/emit! (wtch/apply-token (assoc props :on-update-shape (or on-update-shape-all on-update-shape)))))})
(st/emit! (dwta/unapply-token props))
(st/emit! (dwta/apply-token (assoc props :on-update-shape (or on-update-shape-all on-update-shape)))))})
single-actions (map (fn [[attr title]]
(let [selected? (selected-pred attr)]
{:title title
@@ -78,10 +78,10 @@
:shape-ids shape-ids}
event (cond
all-selected? (-> (assoc props :attributes-to-remove attributes)
(wtch/apply-token))
selected? (wtch/unapply-token props)
(dwta/apply-token))
selected? (dwta/unapply-token props)
:else (-> (assoc props :on-update-shape on-update-shape)
(wtch/apply-token)))]
(dwta/apply-token)))]
(st/emit! event))}))
attribute-labels)]
(concat [all-action] single-actions)))
@@ -105,17 +105,17 @@
:token token
:shape-ids shape-ids}]
(if all-selected?
(st/emit! (wtch/unapply-token props))
(st/emit! (wtch/apply-token (assoc props :on-update-shape on-update-shape))))))}
(st/emit! (dwta/unapply-token props))
(st/emit! (dwta/apply-token (assoc props :on-update-shape on-update-shape))))))}
{:title "Horizontal"
:selected? horizontal-selected?
:action (fn []
(let [props {:token token
:shape-ids shape-ids}
event (cond
all-selected? (wtch/apply-token (assoc props :attributes-to-remove vertical-attrs))
horizontal-selected? (wtch/apply-token (assoc props :attributes-to-remove horizontal-attrs))
:else (wtch/apply-token (assoc props
all-selected? (dwta/apply-token (assoc props :attributes-to-remove vertical-attrs))
horizontal-selected? (dwta/apply-token (assoc props :attributes-to-remove horizontal-attrs))
:else (dwta/apply-token (assoc props
:attributes horizontal-attrs
:on-update-shape on-update-shape)))]
(st/emit! event)))}
@@ -125,9 +125,9 @@
(let [props {:token token
:shape-ids shape-ids}
event (cond
all-selected? (wtch/apply-token (assoc props :attributes-to-remove horizontal-attrs))
vertical-selected? (wtch/apply-token (assoc props :attributes-to-remove vertical-attrs))
:else (wtch/apply-token (assoc props
all-selected? (dwta/apply-token (assoc props :attributes-to-remove horizontal-attrs))
vertical-selected? (dwta/apply-token (assoc props :attributes-to-remove vertical-attrs))
:else (dwta/apply-token (assoc props
:attributes vertical-attrs
:on-update-shape on-update-shape)))]
(st/emit! event)))}]
@@ -147,10 +147,10 @@
:shape-ids shape-ids}
event (cond
all-selected? (-> (assoc props :attributes-to-remove attrs)
(wtch/apply-token))
selected? (wtch/unapply-token props)
(dwta/apply-token))
selected? (dwta/unapply-token props)
:else (-> (assoc props :on-update-shape on-update-shape)
(wtch/apply-token)))]
(dwta/apply-token)))]
(st/emit! event))}))
all-attr-labels)]
(concat multi-items single-items)))
@@ -159,13 +159,13 @@
(st/emit!
(when (= (count attributes) 1)
(dwsl/update-layout shape-ids {:layout-padding-type :multiple}))
(wtch/update-layout-padding value shape-ids attributes)))
(dwta/update-layout-padding value shape-ids attributes)))
(defn update-shape-layout-margin [value shape-ids attributes]
(st/emit!
(when (= (count attributes) 1)
(dwsl/update-layout shape-ids {:layout-item-margin-type :multiple}))
(wtch/update-layout-item-margin value shape-ids attributes)))
(dwta/update-layout-item-margin value shape-ids attributes)))
(defn spacing-attribute-actions [{:keys [token selected-shapes] :as context-data}]
(let [padding-items (layout-spacing-items {:token token
@@ -195,7 +195,7 @@
gap-items (all-or-separate-actions {:attribute-labels {:column-gap "Column Gap"
:row-gap "Row Gap"}
:hint (tr "workspace.token.gaps")
:on-update-shape wtch/update-layout-spacing}
:on-update-shape dwta/update-layout-spacing}
context-data)]
(concat gap-items
[:separator]
@@ -208,25 +208,25 @@
(all-or-separate-actions {:attribute-labels {:width "Width"
:height "Height"}
:hint (tr "workspace.token.size")
:on-update-shape wtch/update-shape-dimensions}
:on-update-shape dwta/update-shape-dimensions}
context-data)
[:separator]
(all-or-separate-actions {:attribute-labels {:layout-item-min-w "Min Width"
:layout-item-min-h "Min Height"}
:hint (tr "workspace.token.min-size")
:on-update-shape wtch/update-layout-sizing-limits}
:on-update-shape dwta/update-layout-sizing-limits}
context-data)
[:separator]
(all-or-separate-actions {:attribute-labels {:layout-item-max-w "Max Width"
:layout-item-max-h "Max Height"}
:hint (tr "workspace.token.max-size")
:on-update-shape wtch/update-layout-sizing-limits}
:on-update-shape dwta/update-layout-sizing-limits}
context-data)))
(defn update-shape-radius-for-corners [value shape-ids attributes]
(st/emit!
(ptk/data-event :expand-border-radius)
(wtch/update-shape-radius-for-corners value shape-ids attributes)))
(dwta/update-shape-radius-for-corners value shape-ids attributes)))
(def shape-attribute-actions-map
(let [stroke-width (partial generic-attribute-actions #{:stroke-width} "Stroke Width")]
@@ -235,11 +235,11 @@
:r4 "Bottom Left"
:r3 "Bottom Right"}
:hint (tr "workspace.token.radius")
:on-update-shape-all wtch/update-shape-radius-all
:on-update-shape-all dwta/update-shape-radius-all
:on-update-shape update-shape-radius-for-corners})
:color (fn [context-data]
[(generic-attribute-actions #{:fill} "Fill" (assoc context-data :on-update-shape wtch/update-fill :hint (tr "workspace.token.color")))
(generic-attribute-actions #{:stroke-color} "Stroke" (assoc context-data :on-update-shape wtch/update-stroke-color))])
[(generic-attribute-actions #{:fill} "Fill" (assoc context-data :on-update-shape dwta/update-fill :hint (tr "workspace.token.color")))
(generic-attribute-actions #{:stroke-color} "Stroke" (assoc context-data :on-update-shape dwta/update-stroke-color))])
:spacing spacing-attribute-actions
:sizing sizing-attribute-actions
:rotation (partial generic-attribute-actions #{:rotation} "Rotation")
@@ -252,19 +252,19 @@
:separator
{:title "Border Radius" :submenu :border-radius}]
[:separator]
(stroke-width (assoc context-data :on-update-shape wtch/update-stroke-width))
(stroke-width (assoc context-data :on-update-shape dwta/update-stroke-width))
[:separator]
(generic-attribute-actions #{:x} "X" (assoc context-data :on-update-shape wtch/update-shape-position :hint (tr "workspace.token.axis")))
(generic-attribute-actions #{:y} "Y" (assoc context-data :on-update-shape wtch/update-shape-position))))}))
(generic-attribute-actions #{:x} "X" (assoc context-data :on-update-shape dwta/update-shape-position :hint (tr "workspace.token.axis")))
(generic-attribute-actions #{:y} "Y" (assoc context-data :on-update-shape dwta/update-shape-position))))}))
(defn default-actions [{:keys [token selected-token-set-name]}]
(let [{:keys [modal]} (wtch/get-token-properties token)]
(let [{:keys [modal]} (dwta/get-token-properties token)]
[{:title (tr "workspace.token.edit")
:no-selectable true
:action (fn [event]
(let [{:keys [key fields]} modal]
(dom/stop-propagation event)
(st/emit! (dt/assign-token-context-menu nil)
(st/emit! (dwtl/assign-token-context-menu nil)
(modal/show key {:x (.-clientX ^js event)
:y (.-clientY ^js event)
:position :right
@@ -274,10 +274,10 @@
:token token}))))}
{:title (tr "workspace.token.duplicate")
:no-selectable true
:action #(st/emit! (dt/duplicate-token (:name token)))}
:action #(st/emit! (dwtl/duplicate-token (:name token)))}
{:title (tr "workspace.token.delete")
:no-selectable true
:action #(st/emit! (dt/delete-token
:action #(st/emit! (dwtl/delete-token
(ctob/prefixed-set-path-string->set-name-string selected-token-set-name)
(:name token)))}]))
@@ -446,7 +446,7 @@
(mf/portal
(mf/html
[:& dropdown {:show is-open?
:on-close #(st/emit! (dt/assign-token-context-menu nil))}
:on-close #(st/emit! (dwtl/assign-token-context-menu nil))}
[:div {:class (stl/css :token-context-menu)
:data-testid "tokens-context-menu-for-token"
:ref dropdown-ref

View File

@@ -1,34 +0,0 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.main.ui.workspace.tokens.core
(:require
[app.common.data :as d]
[app.main.ui.workspace.tokens.token :as wtt]))
;; Helpers ---------------------------------------------------------------------
(defn resolve-token-value [{:keys [value resolved-value] :as _token}]
(or
resolved-value
(d/parse-double value)))
(defn maybe-resolve-token-value [{:keys [value] :as token}]
(when value (resolve-token-value token)))
(defn tokens->select-options [{:keys [shape tokens attributes selected-attributes]}]
(map
(fn [{:keys [name] :as token}]
(cond-> (assoc token :label name)
(wtt/token-applied? token shape (or selected-attributes attributes)) (assoc :selected? true)))
tokens))
(defn tokens-name-map->select-options [{:keys [shape tokens attributes selected-attributes]}]
(map
(fn [[_k {:keys [name] :as token}]]
(cond-> (assoc token :label name)
(wtt/token-applied? token shape (or selected-attributes attributes)) (assoc :selected? true)))
tokens))

View File

@@ -10,9 +10,16 @@
[app.common.colors :as c]
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.tokens :as cft]
[app.common.types.tokens-lib :as ctob]
[app.main.data.modal :as modal]
[app.main.data.tokens :as dt]
[app.main.data.style-dictionary :as sd]
[app.main.data.tinycolor :as tinycolor]
[app.main.data.workspace.tokens.application :as dwta]
[app.main.data.workspace.tokens.errors :as wte]
[app.main.data.workspace.tokens.library-edit :as dwtl]
[app.main.data.workspace.tokens.propagation :as dwtp]
[app.main.data.workspace.tokens.warnings :as wtw]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.ds.buttons.button :refer [button*]]
@@ -22,15 +29,8 @@
[app.main.ui.ds.notifications.context-notification :refer [context-notification*]]
[app.main.ui.workspace.colorpicker :as colorpicker]
[app.main.ui.workspace.colorpicker.ramp :refer [ramp-selector*]]
[app.main.ui.workspace.tokens.changes :as wtch]
[app.main.ui.workspace.tokens.components.controls.input-token-color-bullet :refer [input-token-color-bullet*]]
[app.main.ui.workspace.tokens.components.controls.input-tokens :refer [input-tokens*]]
[app.main.ui.workspace.tokens.errors :as wte]
[app.main.ui.workspace.tokens.style-dictionary :as sd]
[app.main.ui.workspace.tokens.tinycolor :as tinycolor]
[app.main.ui.workspace.tokens.token :as wtt]
[app.main.ui.workspace.tokens.update :as wtu]
[app.main.ui.workspace.tokens.warnings :as wtw]
[app.util.dom :as dom]
[app.util.functions :as uf]
[app.util.i18n :refer [tr]]
@@ -64,7 +64,7 @@
(let [path-exists-schema
(m/-simple-schema
{:type :token/name-exists
:pred #(not (wtt/token-name-path-exists? % tokens-tree))
:pred #(not (cft/token-name-path-exists? % tokens-tree))
:type-properties {:error/fn #(str "A token already exists at the path: " (:value %))}})]
(m/schema
[:and
@@ -239,8 +239,8 @@
[{:keys [token token-type action selected-token-set-name on-display-colorpicker]}]
(let [create? (not (instance? ctob/Token token))
token (or token {:type token-type})
token-properties (wtch/get-token-properties token)
color? (wtt/color-token? token)
token-properties (dwta/get-token-properties token)
color? (cft/color-token? token)
selected-set-tokens (mf/deref refs/workspace-selected-token-set-tokens)
active-theme-tokens (cond-> (mf/deref refs/workspace-active-theme-sets-tokens)
@@ -254,7 +254,7 @@
:interactive? true})
token-path (mf/use-memo
(mf/deps (:name token))
#(wtt/token-name->path (:name token)))
#(cft/token-name->path (:name token)))
selected-set-tokens-tree (mf/use-memo
(mf/deps token-path selected-set-tokens)
@@ -329,7 +329,7 @@
value-input-ref (mf/use-ref nil)
value-ref (mf/use-var (:value token))
token-resolve-result* (mf/use-state (get resolved-tokens (wtt/token-identifier token)))
token-resolve-result* (mf/use-state (get resolved-tokens (cft/token-identifier token)))
token-resolve-result (deref token-resolve-result*)
set-resolve-value
@@ -452,16 +452,16 @@
(when (and (seq result) (not err))
(st/emit!
(if (ctob/token? token)
(dt/update-token (:name token)
{:name final-name
:value final-value
:description final-description})
(dwtl/update-token (:name token)
{:name final-name
:value final-value
:description final-description})
(dt/create-token {:name final-name
:type token-type
:value final-value
:description final-description}))
(wtu/update-workspace-tokens)
(dwtl/create-token {:name final-name
:type token-type
:value final-value
:description final-description}))
(dwtp/propagate-workspace-tokens)
(modal/hide)))))))))
on-delete-token
@@ -470,7 +470,7 @@
(fn [e]
(dom/prevent-default e)
(modal/hide!)
(st/emit! (dt/delete-token (ctob/prefixed-set-path-string->set-name-string selected-token-set-name) (:name token)))))
(st/emit! (dwtl/delete-token (ctob/prefixed-set-path-string->set-name-string selected-token-set-name) (:name token)))))
on-cancel
(mf/use-fn

View File

@@ -12,7 +12,7 @@
[app.common.types.tokens-lib :as ctob]
[app.main.data.event :as ev]
[app.main.data.modal :as modal]
[app.main.data.tokens :as wdt]
[app.main.data.workspace.tokens.library-edit :as dwtl]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]]
@@ -117,7 +117,7 @@
(fn [e]
(dom/prevent-default e)
(dom/stop-propagation e)
(st/emit! (wdt/delete-token-theme group name)))
(st/emit! (dwtl/delete-token-theme group name)))
on-edit-theme
(fn [e]
(dom/prevent-default e)
@@ -131,7 +131,7 @@
[:div {:on-click (fn [e]
(dom/prevent-default e)
(dom/stop-propagation e)
(st/emit! (wdt/toggle-token-theme-active? group name)))}
(st/emit! (dwtl/toggle-token-theme-active? group name)))}
[:& switch {:name (tr "workspace.token.theme-name" name)
:on-change (constantly nil)
:selected? selected?}]]]
@@ -292,7 +292,7 @@
(mf/use-fn
(mf/deps current-theme on-back)
(fn []
(st/emit! (wdt/delete-token-theme (:group current-theme) (:name current-theme)))
(st/emit! (dwtl/delete-token-theme (:group current-theme) (:name current-theme)))
(on-back)))
;; Sets tree handlers
@@ -386,7 +386,7 @@
(mf/use-fn
(mf/deps theme)
(fn [theme']
(st/emit! (wdt/update-token-theme [(:group theme) (:name theme)] theme'))))]
(st/emit! (dwtl/update-token-theme [(:group theme) (:name theme)] theme'))))]
[:> edit-create-theme*
{:change-view change-view
@@ -402,7 +402,7 @@
(mf/use-fn
(fn [theme]
(st/emit! (ptk/event ::ev/event {::ev/name "create-tokens-theme"})
(wdt/create-token-theme theme))))
(dwtl/create-token-theme theme))))
has-prev-view (has-prev-view (:prev-type state))]
[:> edit-create-theme*

View File

@@ -10,7 +10,7 @@
[app.common.data.macros :as dm]
[app.common.types.tokens-lib :as ctob]
[app.main.data.event :as ev]
[app.main.data.tokens :as dt]
[app.main.data.workspace.tokens.library-edit :as dwtl]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.context :as ctx]
@@ -31,26 +31,26 @@
(defn- on-start-creation
[]
(st/emit! (dt/start-token-set-creation [])))
(st/emit! (dwtl/start-token-set-creation [])))
(defn- on-toggle-token-set-click [name]
(st/emit! (dt/toggle-token-set name)))
(st/emit! (dwtl/toggle-token-set name)))
(defn- on-toggle-token-set-group-click [path]
(st/emit! (dt/toggle-token-set-group path)))
(st/emit! (dwtl/toggle-token-set-group path)))
(defn- on-select-token-set-click [name]
(st/emit! (dt/set-selected-token-set-name name)))
(st/emit! (dwtl/set-selected-token-set-name name)))
(defn on-update-token-set
[token-set name]
(st/emit! (dt/clear-token-set-edition)
(dt/update-token-set token-set name)))
(st/emit! (dwtl/clear-token-set-edition)
(dwtl/update-token-set token-set name)))
(defn- on-update-token-set-group
[path name]
(st/emit! (dt/clear-token-set-edition)
(dt/rename-token-set-group path name)))
(st/emit! (dwtl/clear-token-set-edition)
(dwtl/rename-token-set-group path name)))
(defn- on-create-token-set
[parent-set name]
@@ -63,7 +63,7 @@
(ctob/normalize-set-name name))]
(st/emit! (ptk/data-event ::ev/event {::ev/name "create-token-set" :name name})
(dt/create-token-set name))))
(dwtl/create-token-set name))))
(defn group-edition-id
"Prefix editing groups `edition-id` so it can be differentiated from sets with the same id."
@@ -167,7 +167,7 @@
(dom/prevent-default event)
(dom/stop-propagation event)
(when (and can-edit? (not is-editing))
(st/emit! (dt/assign-token-set-context-menu
(st/emit! (dwtl/assign-token-set-context-menu
{:position (dom/get-client-position event)
:is-group true
:id id
@@ -270,7 +270,7 @@
(dom/prevent-default event)
(dom/stop-propagation event)
(when (and can-edit? (not is-editing))
(st/emit! (dt/assign-token-set-context-menu
(st/emit! (dwtl/assign-token-set-context-menu
{:position (dom/get-client-position event)
:is-group false
:id id
@@ -383,8 +383,8 @@
:position position
:collapsed-paths collapsed-paths}]
(if (:is-group data)
(st/emit! (dt/drop-token-set-group params))
(st/emit! (dt/drop-token-set params))))))
(st/emit! (dwtl/drop-token-set-group params))
(st/emit! (dwtl/drop-token-set params))))))
on-toggle-collapse
(mf/use-fn
@@ -560,15 +560,15 @@
(mf/deps can-edit?)
(fn [_]
(when can-edit?
(st/emit! (dt/clear-token-set-edition)
(dt/clear-token-set-creation)))))
(st/emit! (dwtl/clear-token-set-edition)
(dwtl/clear-token-set-creation)))))
on-start-edition
(mf/use-fn
(mf/deps can-edit?)
(fn [id]
(when can-edit?
(st/emit! (dt/start-token-set-edition id)))))]
(st/emit! (dwtl/start-token-set-edition id)))))]
[:> controlled-sets-list*
{:token-sets token-sets

View File

@@ -8,7 +8,7 @@
(:require-macros [app.main.style :as stl])
(:require
[app.common.data.macros :as dm]
[app.main.data.tokens :as dt]
[app.main.data.workspace.tokens.library-edit :as dwtl]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.dropdown :refer [dropdown]]
@@ -36,24 +36,24 @@
{::mf/private true}
[{:keys [is-group id edition-id path]}]
(let [create-set-at-path
(mf/use-fn (mf/deps path) #(st/emit! (dt/start-token-set-creation path)))
(mf/use-fn (mf/deps path) #(st/emit! (dwtl/start-token-set-creation path)))
on-edit
(mf/use-fn
(mf/deps id)
(fn []
(st/emit! (dt/start-token-set-edition edition-id))))
(st/emit! (dwtl/start-token-set-edition edition-id))))
on-duplicate
(mf/use-fn
(mf/deps is-group id)
(fn []
(st/emit! (dt/duplicate-token-set id is-group))))
(st/emit! (dwtl/duplicate-token-set id is-group))))
on-delete
(mf/use-fn
(mf/deps is-group path)
#(st/emit! (dt/delete-token-set-path is-group path)))]
#(st/emit! (dwtl/delete-token-set-path is-group path)))]
[:ul {:class (stl/css :context-list)}
(when is-group
@@ -75,7 +75,7 @@
(+ (dm/get-prop position :x) 5)
on-close
(mf/use-fn #(st/emit! (dt/assign-token-set-context-menu nil)))]
(mf/use-fn #(st/emit! (dwtl/assign-token-set-context-menu nil)))]
[:& dropdown {:show (some? position)
:on-close on-close}

View File

@@ -13,7 +13,10 @@
[app.main.data.event :as ev]
[app.main.data.modal :as modal]
[app.main.data.notifications :as ntf]
[app.main.data.tokens :as dt]
[app.main.data.style-dictionary :as sd]
[app.main.data.workspace.tokens.application :as dwta]
[app.main.data.workspace.tokens.errors :as wte]
[app.main.data.workspace.tokens.library-edit :as dwtl]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.dropdown-menu :refer [dropdown-menu dropdown-menu-item*]]
@@ -26,12 +29,9 @@
[app.main.ui.hooks :as h]
[app.main.ui.hooks.resize :refer [use-resize-hook]]
[app.main.ui.workspace.sidebar.assets.common :as cmm]
[app.main.ui.workspace.tokens.changes :as wtch]
[app.main.ui.workspace.tokens.context-menu :refer [token-context-menu]]
[app.main.ui.workspace.tokens.errors :as wte]
[app.main.ui.workspace.tokens.sets :as tsets]
[app.main.ui.workspace.tokens.sets-context-menu :refer [token-set-context-menu*]]
[app.main.ui.workspace.tokens.style-dictionary :as sd]
[app.main.ui.workspace.tokens.theme-select :refer [theme-select]]
[app.main.ui.workspace.tokens.token-pill :refer [token-pill*]]
[app.util.array :as array]
@@ -70,7 +70,7 @@
{::mf/private true}
[{:keys [type tokens selected-shapes active-theme-tokens is-open]}]
(let [{:keys [modal title]}
(get wtch/token-properties type)
(get dwta/token-properties type)
can-edit?
(mf/use-ctx ctx/can-edit?)
@@ -83,7 +83,7 @@
(mf/use-fn
(fn [event token]
(dom/prevent-default event)
(st/emit! (dt/assign-token-context-menu
(st/emit! (dwtl/assign-token-context-menu
{:type :token
:position (dom/get-client-position event)
:errors (:errors token)
@@ -92,14 +92,14 @@
on-toggle-open-click
(mf/use-fn
(mf/deps is-open type)
#(st/emit! (dt/set-token-type-section-open type (not is-open))))
#(st/emit! (dwtl/set-token-type-section-open type (not is-open))))
on-popover-open-click
(mf/use-fn
(mf/deps type title modal)
(fn [event]
(dom/stop-propagation event)
(st/emit! (dt/set-token-type-section-open type true)
(st/emit! (dwtl/set-token-type-section-open type true)
;; FIXME: use dom/get-client-position
(modal/show (:key modal)
{:x (.-clientX ^js event)
@@ -116,7 +116,7 @@
(fn [event token]
(dom/stop-propagation event)
(when (seq selected-shapes)
(st/emit! (wtch/toggle-token {:token token
(st/emit! (dwta/toggle-token {:token token
:shapes selected-shapes})))))]
[:div {:on-click on-toggle-open-click :class (stl/css :token-section-wrapper)}
@@ -151,7 +151,7 @@
[tokens-by-type]
(loop [empty #js []
filled #js []
types (-> wtch/token-properties keys seq)]
types (-> dwta/token-properties keys seq)]
(if-let [type (first types)]
(if (not-empty (get tokens-by-type type))
(recur empty
@@ -325,7 +325,7 @@
(let [match (->> (ctob/get-sets tokens-lib)
(first)
(:name))]
(st/emit! (dt/set-selected-token-set-name match)))))
(st/emit! (dwtl/set-selected-token-set-name match)))))
[:*
[:& token-context-menu]
@@ -394,7 +394,7 @@
(sd/process-json-stream {:file-name file-name})
(rx/subs! (fn [lib]
(st/emit! (ptk/data-event ::ev/event {::ev/name "import-tokens"})
(dt/import-tokens-lib lib)))
(dwtl/import-tokens-lib lib)))
(fn [err]
(js/console.error err)
(st/emit! (ntf/show {:content (wte/humanize-errors [(ex-data err)])

View File

@@ -193,12 +193,14 @@
}
.resize-area-horiz {
background-color: var(--panel-background-color);
position: absolute;
left: 0;
width: 100%;
padding: $s-3 0 $s-1 0;
height: $s-6;
cursor: ns-resize;
z-index: 1;
}
.resize-handle-horiz {

View File

@@ -11,7 +11,7 @@
[app.common.types.tokens-lib :as ctob]
[app.common.uuid :as uuid]
[app.main.data.modal :as modal]
[app.main.data.tokens :as wdt]
[app.main.data.workspace.tokens.library-edit :as dwtl]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.dropdown :refer [dropdown]]
@@ -31,7 +31,7 @@
selected? (get active-theme-paths theme-id)
select-theme (fn [e]
(dom/stop-propagation e)
(st/emit! (wdt/toggle-token-theme-active? group name))
(st/emit! (dwtl/toggle-token-theme-active? group name))
(on-close))]]
[:li {:key theme-id
:role "option"

View File

@@ -11,12 +11,13 @@
(:require
[app.common.data :as d]
[app.common.files.helpers :as cfh]
[app.common.files.tokens :as cft]
[app.main.data.workspace.tokens.application :as dwta]
[app.main.data.workspace.tokens.color :as dwtc]
[app.main.refs :as refs]
[app.main.ui.components.color-bullet :refer [color-bullet]]
[app.main.ui.ds.foundations.assets.icon :refer [icon*]]
[app.main.ui.ds.foundations.utilities.token.token-status :refer [token-status-icon*]]
[app.main.ui.workspace.tokens.changes :as wtch]
[app.main.ui.workspace.tokens.token :as wtt]
[app.util.dom :as dom]
[app.util.i18n :refer [tr]]
[cuerdas.core :as str]
@@ -80,6 +81,7 @@
:y "Y"})
;; Helper functions
(defn partially-applied-attr
"Translates partially applied attributes based on the dictionary."
[app-token-keys is-applied {:keys [attributes all-attributes]}]
@@ -106,7 +108,7 @@
(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)
{:keys [title] :as token-props} (dwta/get-token-properties theme-token)
applied-tokens (:applied-tokens shape)
app-token-vals (set (vals applied-tokens))
app-token-keys (keys applied-tokens)
@@ -156,9 +158,9 @@
(defn- applied-all-attributes?
[token selected-shapes attributes]
(let [ids-by-attributes (wtt/shapes-ids-by-applied-attributes token selected-shapes attributes)
(let [ids-by-attributes (cft/shapes-ids-by-applied-attributes token selected-shapes attributes)
shape-ids (into #{} xf:map-id selected-shapes)]
(wtt/shapes-applied-all? ids-by-attributes shape-ids attributes)))
(cft/shapes-applied-all? ids-by-attributes shape-ids attributes)))
(mf/defc token-pill*
{::mf/wrap [mf/memo]}
@@ -166,11 +168,11 @@
(let [{:keys [name value errors]} token
has-selected? (pos? (count selected-shapes))
is-reference? (wtt/is-reference? token)
is-reference? (cft/is-reference? token)
contains-path? (str/includes? name ".")
{:keys [attributes all-attributes]}
(get wtch/token-properties (:type token))
(get dwta/token-properties (:type token))
full-applied?
(if has-selected?
@@ -179,7 +181,7 @@
applied?
(if has-selected?
(wtt/shapes-token-applied? token selected-shapes (d/nilv all-attributes attributes))
(cft/shapes-token-applied? token selected-shapes (d/nilv all-attributes attributes))
false)
half-applied?
@@ -201,10 +203,10 @@
no-valid-value)
color
(when (wtt/color-token? token)
(when (cft/color-token? token)
(let [theme-token (get active-theme-tokens (:name token))]
(or (wtt/resolved-token-bullet-color theme-token)
(wtt/resolved-token-bullet-color token))))
(or (dwtc/resolved-token-bullet-color theme-token)
(dwtc/resolved-token-bullet-color token))))
on-click
(mf/use-fn

View File

@@ -472,16 +472,23 @@
(defn setup-shortcuts
[path-editing? drawing-path? text-editing? grid-editing?]
(hooks/use-shortcuts ::workspace wsc/shortcuts)
(mf/use-effect
(mf/deps path-editing? drawing-path? grid-editing?)
(fn []
(cond
grid-editing?
(do (st/emit! (dsc/push-shortcuts ::grid gsc/shortcuts))
#(st/emit! (dsc/pop-shortcuts ::grid)))
(or drawing-path? path-editing?)
(do (st/emit! (dsc/push-shortcuts ::path psc/shortcuts))
#(st/emit! (dsc/pop-shortcuts ::path)))
text-editing?
(do (st/emit! (dsc/push-shortcuts ::text tsc/shortcuts))
#(st/emit! (dsc/pop-shortcuts ::text)))))))
(mf/with-effect [path-editing? drawing-path? grid-editing?]
(cond
grid-editing?
(do
(st/emit! (dsc/push-shortcuts ::grid gsc/shortcuts))
(fn []
(st/emit! (dsc/pop-shortcuts ::grid))))
(or drawing-path? path-editing?)
(do
(st/emit! (dsc/push-shortcuts ::path psc/shortcuts))
(fn []
(st/emit! (dsc/pop-shortcuts ::path))))
text-editing?
(do
(st/emit! (dsc/push-shortcuts ::text tsc/shortcuts))
(fn []
(st/emit! (dsc/pop-shortcuts ::text)))))))

View File

@@ -421,11 +421,9 @@
:uri uri}))
(rx/catch
(fn [cause]
(rx/of (ex/raise :type :internal
:code :export-error
:hint "unexpected error on exporting file"
:file-id (:id file)
:cause cause))))))))
(rx/of {:type :error
:file-id (:id file)
:hint (ex-message cause)})))))))
(= format :legacy-zip)
(->> (rx/from files)

View File

@@ -15,11 +15,11 @@
[app.common.test-helpers.tokens :as ctht]
[app.common.types.tokens-lib :as ctob]
[app.main.data.helpers :as dsh]
[app.main.data.tokens :as dt]
[app.main.data.workspace.libraries :as dwl]
[app.main.data.workspace.selection :as dws]
[app.main.ui.workspace.tokens.changes :as wtch]
[app.main.ui.workspace.tokens.update :as wtu]
[app.main.data.workspace.tokens.application :as dwta]
[app.main.data.workspace.tokens.library-edit :as dwtl]
[app.main.data.workspace.tokens.propagation :as dwtp]
[cljs.test :as t :include-macros true]
[frontend-tests.helpers.pages :as thp]
[frontend-tests.helpers.state :as ths]
@@ -134,10 +134,10 @@
store (ths/setup-store file)
;; ==== Action
events [(wtch/apply-token {:shape-ids [(cthi/id :frame1)]
events [(dwta/apply-token {:shape-ids [(cthi/id :frame1)]
:attributes #{:r1 :r2 :r3 :r4}
:token (toht/get-token file "test-token-2")
:on-update-shape wtch/update-shape-radius-all})]
:on-update-shape dwta/update-shape-radius-all})]
step2 (fn [_]
(let [events2 [(dwl/sync-file (:id file) (:id file))]]
@@ -171,7 +171,7 @@
store (ths/setup-store file)
;; ==== Action
events [(wtch/unapply-token {:shape-ids [(cthi/id :frame1)]
events [(dwta/unapply-token {:shape-ids [(cthi/id :frame1)]
:attributes #{:r1 :r2 :r3 :r4}
:token (toht/get-token file "test-token-1")})]
@@ -203,14 +203,14 @@
store (ths/setup-store file)
;; ==== Action
events [(dt/set-selected-token-set-name "test-token-set")
(dt/update-token "test-token-1"
{:name "test-token-1"
:type :border-radius
:value 66})]
events [(dwtl/set-selected-token-set-name "test-token-set")
(dwtl/update-token "test-token-1"
{:name "test-token-1"
:type :border-radius
:value 66})]
step2 (fn [_]
(let [events2 [(wtu/update-workspace-tokens)
(let [events2 [(dwtp/propagate-workspace-tokens)
(dwl/sync-file (:id file) (:id file))]]
(tohs/run-store-async
store done events2
@@ -242,14 +242,14 @@
store (ths/setup-store file)
;; ==== Action
events [(wtch/apply-token {:shape-ids [(cthi/id :c-frame1)]
events [(dwta/apply-token {:shape-ids [(cthi/id :c-frame1)]
:attributes #{:r1 :r2 :r3 :r4}
:token (toht/get-token file "test-token-2")
:on-update-shape wtch/update-shape-radius-all})
(wtch/apply-token {:shape-ids [(cthi/id :frame1)]
:on-update-shape dwta/update-shape-radius-all})
(dwta/apply-token {:shape-ids [(cthi/id :frame1)]
:attributes #{:r1 :r2 :r3 :r4}
:token (toht/get-token file "test-token-3")
:on-update-shape wtch/update-shape-radius-all})]
:on-update-shape dwta/update-shape-radius-all})]
step2 (fn [_]
(let [events2 [(dwl/sync-file (:id file) (:id file))]]
@@ -283,13 +283,13 @@
store (ths/setup-store file)
;; ==== Action
events [(wtch/unapply-token {:shape-ids [(cthi/id :c-frame1)]
events [(dwta/unapply-token {:shape-ids [(cthi/id :c-frame1)]
:attributes #{:r1 :r2 :r3 :r4}
:token (toht/get-token file "test-token-1")})
(wtch/apply-token {:shape-ids [(cthi/id :frame1)]
(dwta/apply-token {:shape-ids [(cthi/id :frame1)]
:attributes #{:r1 :r2 :r3 :r4}
:token (toht/get-token file "test-token-3")
:on-update-shape wtch/update-shape-radius-all})]
:on-update-shape dwta/update-shape-radius-all})]
step2 (fn [_]
(let [events2 [(dwl/sync-file (:id file) (:id file))]]
@@ -359,28 +359,28 @@
store (ths/setup-store file)
;; ==== Action
events [(dt/set-selected-token-set-name "test-token-set")
(dt/update-token "token-radius"
{:name "token-radius"
:value 30})
(dt/update-token "token-rotation"
{:name "token-rotation"
:value 45})
(dt/update-token "token-opacity"
{:name "token-opacity"
:value 0.9})
(dt/update-token "token-stroke-width"
{:name "token-stroke-width"
:value 8})
(dt/update-token "token-color"
{:name "token-color"
:value "#ff0000"})
(dt/update-token "token-dimensions"
{:name "token-dimensions"
:value 200})]
events [(dwtl/set-selected-token-set-name "test-token-set")
(dwtl/update-token "token-radius"
{:name "token-radius"
:value 30})
(dwtl/update-token "token-rotation"
{:name "token-rotation"
:value 45})
(dwtl/update-token "token-opacity"
{:name "token-opacity"
:value 0.9})
(dwtl/update-token "token-stroke-width"
{:name "token-stroke-width"
:value 8})
(dwtl/update-token "token-color"
{:name "token-color"
:value "#ff0000"})
(dwtl/update-token "token-dimensions"
{:name "token-dimensions"
:value 200})]
step2 (fn [_]
(let [events2 [(wtu/update-workspace-tokens)
(let [events2 [(dwtp/propagate-workspace-tokens)
(dwl/sync-file (:id file) (:id file))]]
(tohs/run-store-async
store done events2

View File

@@ -13,7 +13,6 @@
[frontend-tests.tokens.logic.token-data-test]
[frontend-tests.tokens.style-dictionary-test]
[frontend-tests.tokens.token-form-test]
[frontend-tests.tokens.token-test]
[frontend-tests.util-range-tree-test]
[frontend-tests.util-simple-math-test]
[frontend-tests.util-snap-data-test]))
@@ -42,5 +41,4 @@
'frontend-tests.tokens.logic.token-actions-test
'frontend-tests.tokens.logic.token-data-test
'frontend-tests.tokens.style-dictionary-test
'frontend-tests.tokens.token-test
'frontend-tests.tokens.token-form-test))

View File

@@ -1,8 +1,14 @@
;; 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 frontend-tests.tokens.helpers.state
(:require
[app.common.types.tokens-lib :as ctob]
[app.main.data.helpers :as dsh]
[app.main.ui.workspace.tokens.style-dictionary :as sd]
[app.main.data.style-dictionary :as sd]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))

View File

@@ -1,8 +1,14 @@
;; 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 frontend-tests.tokens.helpers.tokens
(:require
[app.common.files.tokens :as cft]
[app.common.test-helpers.ids-map :as thi]
[app.common.types.tokens-lib :as ctob]
[app.main.ui.workspace.tokens.token :as wtt]))
[app.common.types.tokens-lib :as ctob]))
(defn get-token [file name]
(some-> (get-in file [:data :tokens-lib])
@@ -14,7 +20,7 @@
(let [first-page-id (get-in file [:data :pages 0])
shape-id (thi/id shape-label)
token (get-token file token-label)
applied-attributes (wtt/attributes-map attributes token)]
applied-attributes (cft/attributes-map attributes token)]
(update-in file [:data
:pages-index first-page-id
:objects shape-id

View File

@@ -1,10 +1,16 @@
;; 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 frontend-tests.tokens.logic.token-actions-test
(:require
[app.common.test-helpers.compositions :as ctho]
[app.common.test-helpers.files :as cthf]
[app.common.test-helpers.shapes :as cths]
[app.common.types.tokens-lib :as ctob]
[app.main.ui.workspace.tokens.changes :as wtch]
[app.main.data.workspace.tokens.application :as dwta]
[cljs.test :as t :include-macros true]
[frontend-tests.helpers.pages :as thp]
[frontend-tests.helpers.state :as ths]
@@ -48,10 +54,10 @@
(let [file (setup-file-with-tokens)
store (ths/setup-store file)
rect-1 (cths/get-shape file :rect-1)
events [(wtch/apply-token {:shape-ids [(:id rect-1)]
events [(dwta/apply-token {:shape-ids [(:id rect-1)]
:attributes #{:r1 :r2 :r3 :r4}
:token (toht/get-token file "borderRadius.md")
:on-update-shape wtch/update-shape-radius-all})]]
:on-update-shape dwta/update-shape-radius-all})]]
(tohs/run-store-async
store done events
(fn [new-state]
@@ -73,14 +79,14 @@
(let [file (setup-file-with-tokens)
store (ths/setup-store file)
rect-1 (cths/get-shape file :rect-1)
events [(wtch/apply-token {:shape-ids [(:id rect-1)]
events [(dwta/apply-token {:shape-ids [(:id rect-1)]
:attributes #{:r1 :r2 :r3 :r4}
:token (toht/get-token file "borderRadius.sm")
:on-update-shape wtch/update-shape-radius-all})
(wtch/apply-token {:shape-ids [(:id rect-1)]
:on-update-shape dwta/update-shape-radius-all})
(dwta/apply-token {:shape-ids [(:id rect-1)]
:attributes #{:r1 :r2 :r3 :r4}
:token (toht/get-token file "borderRadius.md")
:on-update-shape wtch/update-shape-radius-all})]]
:on-update-shape dwta/update-shape-radius-all})]]
(tohs/run-store-async
store done events
(fn [new-state]
@@ -101,17 +107,17 @@
store (ths/setup-store file)
rect-1 (cths/get-shape file :rect-1)
events [;; Apply "borderRadius.sm" to all border radius attributes
(wtch/apply-token {:attributes #{:r1 :r2 :r3 :r4}
(dwta/apply-token {:attributes #{:r1 :r2 :r3 :r4}
:token (toht/get-token file "borderRadius.sm")
:shape-ids [(:id rect-1)]
:on-update-shape wtch/update-shape-radius-all})
:on-update-shape dwta/update-shape-radius-all})
;; Apply single `:r1` attribute to same shape
;; while removing other attributes from the border-radius set
;; but keep `:r4` for testing purposes
(wtch/apply-token {:attributes #{:r1 :r2 :r3}
(dwta/apply-token {:attributes #{:r1 :r2 :r3}
:token (toht/get-token file "borderRadius.md")
:shape-ids [(:id rect-1)]
:on-update-shape wtch/update-shape-radius-all})]]
:on-update-shape dwta/update-shape-radius-all})]]
(tohs/run-store-async
store done events
(fn [new-state]
@@ -133,14 +139,14 @@
store (ths/setup-store file)
rect-1 (cths/get-shape file :rect-1)
rect-2 (cths/get-shape file :rect-2)
events [(wtch/apply-token {:shape-ids [(:id rect-1)]
events [(dwta/apply-token {:shape-ids [(:id rect-1)]
:attributes #{:r3 :r4}
:token (toht/get-token file "borderRadius.sm")
:on-update-shape wtch/update-shape-radius-for-corners})
(wtch/apply-token {:shape-ids [(:id rect-2)]
:on-update-shape dwta/update-shape-radius-for-corners})
(dwta/apply-token {:shape-ids [(:id rect-2)]
:attributes #{:r1 :r2 :r3 :r4}
:token (toht/get-token file "borderRadius.sm")
:on-update-shape wtch/update-shape-radius-all})]]
:on-update-shape dwta/update-shape-radius-all})]]
(tohs/run-store-async
store done events
(fn [new-state]
@@ -185,22 +191,22 @@
store (ths/setup-store file)
rect-1 (cths/get-shape file :rect-1)
rect-2 (cths/get-shape file :rect-2)
events [(wtch/apply-token {:shape-ids [(:id rect-1)]
events [(dwta/apply-token {:shape-ids [(:id rect-1)]
:attributes #{:color}
:token (toht/get-token file "color.primary")
:on-update-shape wtch/update-fill})
(wtch/apply-token {:shape-ids [(:id rect-1)]
:on-update-shape dwta/update-fill})
(dwta/apply-token {:shape-ids [(:id rect-1)]
:attributes #{:stroke-color}
:token (toht/get-token file "color.primary")
:on-update-shape wtch/update-stroke-color})
(wtch/apply-token {:shape-ids [(:id rect-2)]
:on-update-shape dwta/update-stroke-color})
(dwta/apply-token {:shape-ids [(:id rect-2)]
:attributes #{:color}
:token (toht/get-token file "color.secondary")
:on-update-shape wtch/update-fill})
(wtch/apply-token {:shape-ids [(:id rect-2)]
:on-update-shape dwta/update-fill})
(dwta/apply-token {:shape-ids [(:id rect-2)]
:attributes #{:stroke-color}
:token (toht/get-token file "color.secondary")
:on-update-shape wtch/update-stroke-color})]]
:on-update-shape dwta/update-stroke-color})]]
(tohs/run-store-async
store done events
(fn [new-state]
@@ -239,10 +245,10 @@
#(ctob/add-token-in-set % "Set A" (ctob/make-token dimensions-token))))
store (ths/setup-store file)
rect-1 (cths/get-shape file :rect-1)
events [(wtch/apply-token {:shape-ids [(:id rect-1)]
events [(dwta/apply-token {:shape-ids [(:id rect-1)]
:attributes #{:width :height}
:token (toht/get-token file "dimensions.sm")
:on-update-shape wtch/update-shape-dimensions})]]
:on-update-shape dwta/update-shape-dimensions})]]
(tohs/run-store-async
store done events
(fn [new-state]
@@ -272,10 +278,10 @@
store (ths/setup-store file)
frame-1 (cths/get-shape file :frame-1)
frame-2 (cths/get-shape file :frame-2)
events [(wtch/apply-token {:shape-ids [(:id frame-1) (:id frame-2)]
events [(dwta/apply-token {:shape-ids [(:id frame-1) (:id frame-2)]
:attributes #{:padding}
:token (toht/get-token file "padding.sm")
:on-update-shape wtch/update-layout-padding})]]
:on-update-shape dwta/update-layout-padding})]]
(tohs/run-store-async
store done events
(fn [new-state]
@@ -303,10 +309,10 @@
#(ctob/add-token-in-set % "Set A" (ctob/make-token sizing-token))))
store (ths/setup-store file)
rect-1 (cths/get-shape file :rect-1)
events [(wtch/apply-token {:shape-ids [(:id rect-1)]
events [(dwta/apply-token {:shape-ids [(:id rect-1)]
:attributes #{:width :height}
:token (toht/get-token file "sizing.sm")
:on-update-shape wtch/update-shape-dimensions})]]
:on-update-shape dwta/update-shape-dimensions})]]
(tohs/run-store-async
store done events
(fn [new-state]
@@ -344,18 +350,18 @@
rect-1 (cths/get-shape file :rect-1)
rect-2 (cths/get-shape file :rect-2)
rect-3 (cths/get-shape file :rect-3)
events [(wtch/apply-token {:shape-ids [(:id rect-1)]
events [(dwta/apply-token {:shape-ids [(:id rect-1)]
:attributes #{:opacity}
:token (toht/get-token file "opacity.float")
:on-update-shape wtch/update-opacity})
(wtch/apply-token {:shape-ids [(:id rect-2)]
:on-update-shape dwta/update-opacity})
(dwta/apply-token {:shape-ids [(:id rect-2)]
:attributes #{:opacity}
:token (toht/get-token file "opacity.percent")
:on-update-shape wtch/update-opacity})
(wtch/apply-token {:shape-ids [(:id rect-3)]
:on-update-shape dwta/update-opacity})
(dwta/apply-token {:shape-ids [(:id rect-3)]
:attributes #{:opacity}
:token (toht/get-token file "opacity.invalid")
:on-update-shape wtch/update-opacity})]]
:on-update-shape dwta/update-opacity})]]
(tohs/run-store-async
store done events
(fn [new-state]
@@ -388,10 +394,10 @@
#(ctob/add-token-in-set % "Set A" (ctob/make-token rotation-token))))
store (ths/setup-store file)
rect-1 (cths/get-shape file :rect-1)
events [(wtch/apply-token {:shape-ids [(:id rect-1)]
events [(dwta/apply-token {:shape-ids [(:id rect-1)]
:attributes #{:rotation}
:token (toht/get-token file "rotation.medium")
:on-update-shape wtch/update-rotation})]]
:on-update-shape dwta/update-rotation})]]
(tohs/run-store-async
store done events
(fn [new-state]
@@ -419,10 +425,10 @@
store (ths/setup-store file)
rect-with-stroke (cths/get-shape file :rect-1)
rect-without-stroke (cths/get-shape file :rect-2)
events [(wtch/apply-token {:shape-ids [(:id rect-with-stroke) (:id rect-without-stroke)]
events [(dwta/apply-token {:shape-ids [(:id rect-with-stroke) (:id rect-without-stroke)]
:attributes #{:stroke-width}
:token (toht/get-token file "stroke-width.sm")
:on-update-shape wtch/update-stroke-width})]]
:on-update-shape dwta/update-stroke-width})]]
(tohs/run-store-async
store done events
(fn [new-state]
@@ -445,9 +451,9 @@
store (ths/setup-store file)
rect-1 (cths/get-shape file :rect-1)
rect-2 (cths/get-shape file :rect-2)
events [(wtch/toggle-token {:shapes [rect-1 rect-2]
events [(dwta/toggle-token {:shapes [rect-1 rect-2]
:token-type-props {:attributes #{:r1 :r2 :r3 :r4}
:on-update-shape wtch/update-shape-radius-all}
:on-update-shape dwta/update-shape-radius-all}
:token (toht/get-token file "borderRadius.md")})]]
(tohs/run-store-async
store done events
@@ -476,7 +482,7 @@
rect-without-token (cths/get-shape file :rect-2)
rect-with-other-token (cths/get-shape file :rect-3)
events [(wtch/toggle-token {:shapes [rect-with-token rect-without-token rect-with-other-token]
events [(dwta/toggle-token {:shapes [rect-with-token rect-without-token rect-with-other-token]
:token (toht/get-token file "borderRadius.sm")
:token-type-props {:attributes #{:r1 :r2 :r3 :r4}}})]]
(tohs/run-store-async
@@ -509,7 +515,7 @@
rect-without-token (cths/get-shape file :rect-2)
rect-with-other-token-2 (cths/get-shape file :rect-3)
events [(wtch/toggle-token {:shapes [rect-with-other-token-1 rect-without-token rect-with-other-token-2]
events [(dwta/toggle-token {:shapes [rect-with-other-token-1 rect-without-token rect-with-other-token-2]
:token (toht/get-token file "borderRadius.sm")
:token-type-props {:attributes #{:r1 :r2 :r3 :r4}}})]]
(tohs/run-store-async

View File

@@ -1,8 +1,14 @@
;; 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 frontend-tests.tokens.logic.token-data-test
(:require
[app.common.test-helpers.files :as cthf]
[app.common.types.tokens-lib :as ctob]
[app.main.data.tokens :as dt]
[app.main.data.workspace.tokens.library-edit :as dwtl]
[cljs.test :as t :include-macros true]
[frontend-tests.helpers.pages :as thp]
[frontend-tests.helpers.state :as ths]
@@ -27,7 +33,7 @@
done
(let [file (setup-file-with-token-lib)
store (ths/setup-store file)
events [(dt/duplicate-token-set "Set A" false)]]
events [(dwtl/duplicate-token-set "Set A" false)]]
(tohs/run-store-async
store done events
@@ -46,7 +52,7 @@
done
(let [file (setup-file-with-token-lib)
store (ths/setup-store file)
events [(dt/duplicate-token-set "Set B" false)]]
events [(dwtl/duplicate-token-set "Set B" false)]]
(tohs/run-store-async
store done events

View File

@@ -1,8 +1,14 @@
;; 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 frontend-tests.tokens.style-dictionary-test
(:require
[app.common.transit :as tr]
[app.common.types.tokens-lib :as ctob]
[app.main.ui.workspace.tokens.style-dictionary :as sd]
[app.main.data.style-dictionary :as sd]
[beicon.v2.core :as rx]
[cljs.test :as t :include-macros true]
[promesa.core :as p]))