mirror of
https://github.com/penpot/penpot.git
synced 2026-01-11 07:49:06 -05:00
Compare commits
74 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30a06249ff | ||
|
|
59ca09c24e | ||
|
|
1aeafdfca7 | ||
|
|
a714085523 | ||
|
|
eccc4226c7 | ||
|
|
4d6d85b3de | ||
|
|
c607b61af6 | ||
|
|
e16ec9c719 | ||
|
|
59e5656bd7 | ||
|
|
723eef9565 | ||
|
|
8448036d67 | ||
|
|
e1c9691567 | ||
|
|
577b731b22 | ||
|
|
ef3588d05f | ||
|
|
d4893523bc | ||
|
|
f10792619d | ||
|
|
7a0702650a | ||
|
|
ee1230c488 | ||
|
|
ede1176606 | ||
|
|
9506606e15 | ||
|
|
7e5f93ca3d | ||
|
|
6655563aba | ||
|
|
8ee9b45243 | ||
|
|
caa6897f81 | ||
|
|
660bc1a4dd | ||
|
|
3ddd45e99b | ||
|
|
9b71e04e1c | ||
|
|
39620fe9c4 | ||
|
|
db7c1fc7dd | ||
|
|
c89abf56ac | ||
|
|
d22f6e37c9 | ||
|
|
19b9b3cbd9 | ||
|
|
c1d3e4cd6e | ||
|
|
2164593757 | ||
|
|
9485ce03b5 | ||
|
|
ba832389d1 | ||
|
|
a8ee9be7b9 | ||
|
|
c8c83c1e1d | ||
|
|
afcfbdedda | ||
|
|
53f55444cd | ||
|
|
fa8665df88 | ||
|
|
5cc678ddc3 | ||
|
|
64c8741233 | ||
|
|
0cae9d6ad5 | ||
|
|
0c586551c4 | ||
|
|
2f4cb19745 | ||
|
|
b80ccbec0f | ||
|
|
246415be2b | ||
|
|
7faa9e970e | ||
|
|
04a0d867b0 | ||
|
|
a18214a1a5 | ||
|
|
68397edd4d | ||
|
|
1e2d9a15a3 | ||
|
|
0f101fad9f | ||
|
|
a91737b4d7 | ||
|
|
284d5ecb77 | ||
|
|
5d95d755ad | ||
|
|
4466abd150 | ||
|
|
27690c3da6 | ||
|
|
f436d72f51 | ||
|
|
20ea188070 | ||
|
|
c4f076910b | ||
|
|
72f2395142 | ||
|
|
47d28758d7 | ||
|
|
b7573c0b72 | ||
|
|
2ed743b6be | ||
|
|
036e335fc4 | ||
|
|
0e99b37c21 | ||
|
|
3cdbd7f381 | ||
|
|
76caff2b61 | ||
|
|
bb370b3e50 | ||
|
|
45d56f40e1 | ||
|
|
4a1ab75d8f | ||
|
|
a58ad2298a |
@@ -33,6 +33,12 @@ jobs:
|
||||
command: |
|
||||
clojure -M:dev:test
|
||||
|
||||
- run:
|
||||
name: "NODE tests"
|
||||
working_directory: "./common"
|
||||
command: |
|
||||
yarn run test
|
||||
|
||||
- save_cache:
|
||||
paths:
|
||||
- ~/.m2
|
||||
|
||||
18
CHANGES.md
18
CHANGES.md
@@ -1,18 +1,5 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 2.5.0
|
||||
|
||||
### :rocket: Epics and highlights
|
||||
|
||||
### :boom: Breaking changes & Deprecations
|
||||
|
||||
### :heart: Community contributions (Thank you!)
|
||||
|
||||
### :sparkles: New features
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
|
||||
## 2.4.0
|
||||
|
||||
### :rocket: Epics and highlights
|
||||
@@ -37,17 +24,20 @@
|
||||
### :sparkles: New features
|
||||
|
||||
- Viewer role for team members [Taiga #1056 & #6590](https://tree.taiga.io/project/penpot/us/1056 & https://tree.taiga.io/project/penpot/us/6590)
|
||||
- File history versions management [Taiga](https://tree.taiga.io/project/penpot/us/187?milestone=411120)
|
||||
- File history versions management [Taiga #187](https://tree.taiga.io/project/penpot/us/187?milestone=411120)
|
||||
- Rename selected layer via keyboard shortcut and context menu option [Taiga #8882](https://tree.taiga.io/project/penpot/us/8882)
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
- Fix problem with some texts desynchronization [Taiga #9379](https://tree.taiga.io/project/penpot/issue/9379)
|
||||
|
||||
## 2.3.3
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
- Fix problem creating manual overlay interactions [Taiga #9146](https://tree.taiga.io/project/penpot/issue/9146)
|
||||
- Fix plugins list default URL
|
||||
- Activate plugins feature by default
|
||||
|
||||
## 2.3.2
|
||||
|
||||
|
||||
@@ -134,6 +134,16 @@
|
||||
(update :data feat.fdata/process-pointers deref)
|
||||
(update :data feat.fdata/process-objects (partial into {}))))))))
|
||||
|
||||
(defn clean-file-features
|
||||
[file]
|
||||
(update file :features (fn [features]
|
||||
(if (set? features)
|
||||
(-> features
|
||||
(cfeat/migrate-legacy-features)
|
||||
(set/difference cfeat/frontend-only-features)
|
||||
(set/difference cfeat/backend-only-features))
|
||||
#{}))))
|
||||
|
||||
(defn get-project
|
||||
[cfg project-id]
|
||||
(db/get cfg :project {:id project-id}))
|
||||
@@ -445,8 +455,11 @@
|
||||
(fn [features]
|
||||
(let [features (cfeat/check-supported-features! features)]
|
||||
(-> (::features cfg #{})
|
||||
(set/difference cfeat/frontend-only-features)
|
||||
(set/union features))))))
|
||||
(set/union features)
|
||||
;; We never want to store
|
||||
;; frontend-only features on file
|
||||
(set/difference cfeat/frontend-only-features))))))
|
||||
|
||||
|
||||
_ (when (contains? cf/flags :file-schema-validation)
|
||||
(fval/validate-file-schema! file))
|
||||
|
||||
@@ -508,15 +508,6 @@
|
||||
(update :object-id #(str/replace-first % #"^(.*?)/" (str file-id "/")))))
|
||||
thumbnails))
|
||||
|
||||
(defn- clean-features
|
||||
[file]
|
||||
(update file :features (fn [features]
|
||||
(if (set? features)
|
||||
(-> features
|
||||
(cfeat/migrate-legacy-features)
|
||||
(set/difference cfeat/backend-only-features))
|
||||
#{}))))
|
||||
|
||||
(defmethod read-section :v1/files
|
||||
[{:keys [::db/conn ::input ::project-id ::bfc/overwrite ::name] :as system}]
|
||||
|
||||
@@ -527,7 +518,7 @@
|
||||
file-id (:id file)
|
||||
file-id' (bfc/lookup-index file-id)
|
||||
|
||||
file (clean-features file)
|
||||
file (bfc/clean-file-features file)
|
||||
thumbnails (:thumbnails file)]
|
||||
|
||||
(when (not= file-id expected-file-id)
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.features :as cfeat]
|
||||
[app.common.json :as json]
|
||||
[app.common.logging :as l]
|
||||
[app.common.schema :as sm]
|
||||
@@ -55,7 +56,8 @@
|
||||
[:map
|
||||
[:id ::sm/uuid]
|
||||
[:name :string]
|
||||
[:project-id ::sm/uuid]]]]
|
||||
[:project-id ::sm/uuid]
|
||||
[:features ::cfeat/features]]]]
|
||||
|
||||
[:relations {:optional true}
|
||||
[:vector
|
||||
@@ -203,7 +205,10 @@
|
||||
(dissoc :libraries))
|
||||
|
||||
embed-assets
|
||||
(update :data #(bfc/embed-assets cfg % file-id)))))
|
||||
(update :data #(bfc/embed-assets cfg % file-id))
|
||||
|
||||
:always
|
||||
(bfc/clean-file-features))))
|
||||
|
||||
(defn- resolve-extension
|
||||
[mtype]
|
||||
@@ -259,7 +264,8 @@
|
||||
(vswap! bfc/*state* update :files assoc file-id
|
||||
{:id file-id
|
||||
:project-id (:project-id file)
|
||||
:name (:name file)})
|
||||
:name (:name file)
|
||||
:features (:features file)})
|
||||
|
||||
(let [file (cond-> (dissoc file :data)
|
||||
(:options data)
|
||||
|
||||
@@ -144,6 +144,8 @@
|
||||
[:quotes-comments-per-file {:optional true} ::sm/int]
|
||||
[:quotes-snapshots-per-file {:optional true} ::sm/int]
|
||||
[:quotes-snapshots-per-team {:optional true} ::sm/int]
|
||||
[:quotes-team-access-requests-per-team {:optional true} ::sm/int]
|
||||
[:quotes-team-access-requests-per-requester {:optional true} ::sm/int]
|
||||
|
||||
[:auth-data-cookie-domain {:optional true} :string]
|
||||
[:auth-token-cookie-name {:optional true} :string]
|
||||
|
||||
@@ -226,8 +226,8 @@
|
||||
[:priority {:optional true} [:enum :high :low]]
|
||||
[:extra-data {:optional true} ::sm/text]])
|
||||
|
||||
(def ^:private valid-context?
|
||||
(sm/validator schema:context))
|
||||
(def ^:private check-context
|
||||
(sm/check-fn schema:context))
|
||||
|
||||
(defn template-factory
|
||||
[& {:keys [id schema]}]
|
||||
@@ -236,10 +236,8 @@
|
||||
(sm/check-fn schema)
|
||||
(constantly nil))]
|
||||
(fn [context]
|
||||
(assert (valid-context? context) "expected a valid context")
|
||||
(check-fn context)
|
||||
|
||||
(let [email (build-email-template id context)]
|
||||
(let [context (-> context check-context check-fn)
|
||||
email (build-email-template id context)]
|
||||
(when-not email
|
||||
(ex/raise :type :internal
|
||||
:code :email-template-does-not-exists
|
||||
@@ -271,7 +269,7 @@
|
||||
"Schedule an already defined email to be sent using asynchronously
|
||||
using worker task."
|
||||
[{:keys [::conn ::factory] :as context}]
|
||||
(assert (db/connection? conn) "expected a valid database connection")
|
||||
(assert (db/connectable? conn) "expected a valid database connection or pool")
|
||||
|
||||
(let [email (if factory
|
||||
(factory context)
|
||||
@@ -348,7 +346,7 @@
|
||||
[:subject ::sm/text]
|
||||
[:content ::sm/text]])
|
||||
|
||||
(def feedback
|
||||
(def user-feedback
|
||||
"A profile feedback email."
|
||||
(template-factory
|
||||
:id ::feedback
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
[app.rpc.doc :as-alias doc]
|
||||
[app.util.services :as sv]))
|
||||
|
||||
(declare ^:private send-feedback!)
|
||||
(declare ^:private send-user-feedback!)
|
||||
|
||||
(def ^:private schema:send-user-feedback
|
||||
[:map {:title "send-user-feedback"}
|
||||
@@ -34,14 +34,16 @@
|
||||
:hint "feedback not enabled"))
|
||||
|
||||
(let [profile (profile/get-profile pool profile-id)]
|
||||
(send-feedback! pool profile params)
|
||||
(send-user-feedback! pool profile params)
|
||||
nil))
|
||||
|
||||
(defn- send-feedback!
|
||||
(defn- send-user-feedback!
|
||||
[pool profile params]
|
||||
(let [dest (cf/get :feedback-destination)]
|
||||
(let [dest (or (cf/get :user-feedback-destination)
|
||||
;; LEGACY
|
||||
(cf/get :feedback-destination))]
|
||||
(eml/send! {::eml/conn pool
|
||||
::eml/factory eml/feedback
|
||||
::eml/factory eml/user-feedback
|
||||
:from dest
|
||||
:to dest
|
||||
:profile profile
|
||||
|
||||
@@ -118,11 +118,12 @@
|
||||
;; feature on frontend and make it permanent on file
|
||||
features (-> (:features params #{})
|
||||
(set/intersection cfeat/no-migration-features)
|
||||
(set/difference cfeat/frontend-only-features)
|
||||
(set/union features))
|
||||
|
||||
params (-> params
|
||||
(assoc :profile-id profile-id)
|
||||
(assoc :features features))]
|
||||
(assoc :features (set/difference features cfeat/frontend-only-features)))]
|
||||
|
||||
(quotes/check! cfg {::quotes/id ::quotes/files-per-project
|
||||
::quotes/team-id team-id
|
||||
|
||||
@@ -50,8 +50,7 @@
|
||||
" where file_id=? and tag=? and deleted_at is null")
|
||||
res (db/exec! conn [sql file-id tag])]
|
||||
(->> res
|
||||
(d/index-by :object-id (fn [row]
|
||||
(files/resolve-public-uri (:media-id row))))
|
||||
(d/index-by :object-id :media-id)
|
||||
(d/without-nils))))
|
||||
|
||||
(defn- get-object-thumbnails
|
||||
@@ -62,8 +61,7 @@
|
||||
" where file_id=? and deleted_at is null")
|
||||
res (db/exec! conn [sql file-id])]
|
||||
(->> res
|
||||
(d/index-by :object-id (fn [row]
|
||||
(files/resolve-public-uri (:media-id row))))
|
||||
(d/index-by :object-id :media-id)
|
||||
(d/without-nils))))
|
||||
|
||||
([conn file-id object-ids]
|
||||
@@ -75,8 +73,7 @@
|
||||
res (db/exec! conn [sql file-id ids])]
|
||||
|
||||
(->> res
|
||||
(d/index-by :object-id (fn [row]
|
||||
(files/resolve-public-uri (:media-id row))))
|
||||
(d/index-by :object-id :media-id)
|
||||
(d/without-nils)))))
|
||||
|
||||
(sv/defmethod ::get-file-object-thumbnails
|
||||
@@ -127,8 +124,11 @@
|
||||
(if-let [frame (-> frames first)]
|
||||
(let [frame-id (:id frame)
|
||||
object-id (thc/fmt-object-id (:id file) page-id frame-id "frame")
|
||||
frame (if-let [thumb (get thumbnails object-id)]
|
||||
(assoc frame :thumbnail thumb :shapes [])
|
||||
|
||||
frame (if-let [media-id (get thumbnails object-id)]
|
||||
(-> frame
|
||||
(assoc :thumbnail-id media-id)
|
||||
(assoc :shapes []))
|
||||
(dissoc frame :thumbnail))
|
||||
|
||||
children-ids
|
||||
|
||||
@@ -147,7 +147,7 @@
|
||||
|
||||
params (-> params
|
||||
(assoc :profile-id profile-id)
|
||||
(assoc :features features)
|
||||
(assoc :features (set/difference features cfeat/frontend-only-features))
|
||||
(assoc :team team)
|
||||
(assoc :file file)
|
||||
(assoc :changes changes))
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
[app.storage :as sto]
|
||||
[app.util.services :as sv]
|
||||
[app.util.time :as dt]
|
||||
[app.worker :as wrk]))
|
||||
[app.worker :as wrk]
|
||||
[clojure.set :as set]))
|
||||
|
||||
;; --- Helpers & Specs
|
||||
|
||||
@@ -416,6 +417,7 @@
|
||||
::quotes/profile-id profile-id})
|
||||
|
||||
(let [features (-> (cfeat/get-enabled-features cf/flags)
|
||||
(set/difference cfeat/frontend-only-features)
|
||||
(cfeat/check-client-features! (:features params)))
|
||||
params (-> params
|
||||
(assoc :profile-id profile-id)
|
||||
|
||||
@@ -532,17 +532,20 @@
|
||||
team-owner (get-team-owner conn team-id)
|
||||
|
||||
file (when (some? file-id)
|
||||
(get-file-for-team-access-request cfg file-id))
|
||||
(get-file-for-team-access-request cfg file-id))]
|
||||
|
||||
request (upsert-team-access-request conn team-id profile-id)]
|
||||
|
||||
;; FIXME missing quotes
|
||||
(-> cfg
|
||||
(assoc ::quotes/profile-id profile-id)
|
||||
(assoc ::quotes/team-id team-id)
|
||||
(quotes/check! {::quotes/id ::quotes/team-access-requests-per-team}
|
||||
{::quotes/id ::quotes/team-access-requests-per-requester}))
|
||||
|
||||
(teams/check-profile-muted conn requester)
|
||||
(teams/check-email-bounce conn (:email team-owner) false)
|
||||
(teams/check-email-spam conn (:email team-owner) true)
|
||||
|
||||
(let [factory (cond
|
||||
(let [request (upsert-team-access-request conn team-id profile-id)
|
||||
factory (cond
|
||||
(and (some? file) (:is-default team) is-viewer)
|
||||
eml/request-file-access-yourpenpot-view
|
||||
|
||||
@@ -565,9 +568,9 @@
|
||||
:team-id team-id
|
||||
:file-name (:name file)
|
||||
:file-id file-id
|
||||
:page-id (:page-id file)}))
|
||||
:page-id (:page-id file)})
|
||||
|
||||
(with-meta {:request request}
|
||||
{::audit/props {:request 1}})))
|
||||
(with-meta {:request request}
|
||||
{::audit/props {:request 1}}))))
|
||||
|
||||
|
||||
|
||||
@@ -442,7 +442,7 @@
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; QUOTE: SNAPSHOTS-PER-FILE
|
||||
;; QUOTE: SNAPSHOTS-PER-TEAM
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def ^:private schema:snapshots-per-team
|
||||
@@ -472,6 +472,57 @@
|
||||
(assoc ::count-sql [sql:get-snapshots-per-team team-id])
|
||||
(generic-check!)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; QUOTE: TEAM-ACCESS-REQUESTS-PER-TEAM
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def ^:private schema:team-access-requests-per-team
|
||||
[:map
|
||||
[::profile-id ::sm/uuid]
|
||||
[::team-id ::sm/uuid]])
|
||||
|
||||
(def ^:private valid-team-access-requests-per-team-quote?
|
||||
(sm/lazy-validator schema:team-access-requests-per-team))
|
||||
|
||||
(def ^:private sql:get-team-access-requests-per-team
|
||||
"SELECT count(*) AS total
|
||||
FROM team_access_request AS tar
|
||||
WHERE tar.team_id = ?")
|
||||
|
||||
(defmethod check-quote ::team-access-requests-per-team
|
||||
[{:keys [::profile-id ::team-id ::target] :as quote}]
|
||||
(assert (valid-team-access-requests-per-team-quote? quote) "invalid quote parameters")
|
||||
(-> quote
|
||||
(assoc ::default (cf/get :quotes-team-access-requests-per-team Integer/MAX_VALUE))
|
||||
(assoc ::quote-sql [sql:get-quotes-2 target team-id profile-id profile-id])
|
||||
(assoc ::count-sql [sql:get-team-access-requests-per-team team-id])
|
||||
(generic-check!)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; QUOTE: TEAM-ACCESS-REQUESTS-PER-REQUESTER
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def ^:private schema:team-access-requests-per-requester
|
||||
[:map
|
||||
[::profile-id ::sm/uuid]])
|
||||
|
||||
(def ^:private valid-team-access-requests-per-requester-quote?
|
||||
(sm/lazy-validator schema:team-access-requests-per-requester))
|
||||
|
||||
(def ^:private sql:get-team-access-requests-per-requester
|
||||
"SELECT count(*) AS total
|
||||
FROM team_access_request AS tar
|
||||
WHERE tar.requester_id = ?")
|
||||
|
||||
(defmethod check-quote ::team-access-requests-per-requester
|
||||
[{:keys [::profile-id ::target] :as quote}]
|
||||
(assert (valid-team-access-requests-per-requester-quote? quote) "invalid quote parameters")
|
||||
(-> quote
|
||||
(assoc ::default (cf/get :quotes-team-access-requests-per-requester Integer/MAX_VALUE))
|
||||
(assoc ::quote-sql [sql:get-quotes-1 target profile-id])
|
||||
(assoc ::count-sql [sql:get-team-access-requests-per-requester profile-id])
|
||||
(generic-check!)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; QUOTE: DEFAULT
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
@@ -1090,8 +1090,7 @@
|
||||
(t/is (contains? result :file-id))
|
||||
|
||||
(t/is (= (:id file) (:file-id result)))
|
||||
(t/is (str/starts-with? (get-in result [:page :objects frame1-id :thumbnail])
|
||||
"http://localhost:3449/assets/by-id/"))
|
||||
(t/is (uuid? (get-in result [:page :objects frame1-id :thumbnail-id])))
|
||||
(t/is (= [] (get-in result [:page :objects frame1-id :shapes]))))
|
||||
|
||||
;; Delete thumbnail data
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "common",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"license": "MPL-2.0",
|
||||
"author": "Kaleidos INC",
|
||||
"private": true,
|
||||
"packageManager": "yarn@4.3.1",
|
||||
"type": "module",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/penpot/penpot"
|
||||
@@ -15,6 +15,8 @@
|
||||
"sax": "^1.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"concurrently": "^9.0.1",
|
||||
"nodemon": "^3.1.7",
|
||||
"shadow-cljs": "2.28.18",
|
||||
"source-map-support": "^0.5.21",
|
||||
"ws": "^8.17.0"
|
||||
@@ -23,9 +25,9 @@
|
||||
"fmt:clj:check": "cljfmt check --parallel=false src/ test/",
|
||||
"fmt:clj": "cljfmt fix --parallel=true src/ test/",
|
||||
"lint:clj": "clj-kondo --parallel=true --lint src/",
|
||||
"test:watch": "clojure -M:dev:shadow-cljs watch test",
|
||||
"test:compile": "clojure -M:dev:shadow-cljs compile test --config-merge '{:autorun false}'",
|
||||
"test:run": "node target/test.js",
|
||||
"test": "yarn run test:compile && yarn run test:run"
|
||||
"lint": "yarn run lint:clj",
|
||||
"watch:test": "concurrently \"clojure -M:dev:shadow-cljs watch test\" \"nodemon -C -d 2 -w target/tests/ --exec 'node target/tests/test.js'\"",
|
||||
"build:test": "clojure -M:dev:shadow-cljs compile test",
|
||||
"test": "yarn run build:test && node target/tests/test.js"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
{:deps {:aliases [:dev]}
|
||||
:builds
|
||||
{:test
|
||||
{:target :node-test
|
||||
:output-to "target/test.js"
|
||||
:output-dir "target/test/"
|
||||
:ns-regexp "^common-tests.*-test$"
|
||||
:autorun true
|
||||
{:target :esm
|
||||
:output-dir "target/tests"
|
||||
:runtime :node
|
||||
:js-options {:js-provider :import}
|
||||
|
||||
:compiler-options
|
||||
{:output-feature-set :es-next
|
||||
:output-wrapper false
|
||||
:source-map true
|
||||
:source-map-include-sources-content true
|
||||
:source-map-detail-level :all
|
||||
:warnings {:fn-deprecated false}}}
|
||||
|
||||
:modules
|
||||
{:test {:init-fn common-tests.runner/-main
|
||||
:prepend-js "globalThis.navigator = {userAgent: \"\"}"}}}
|
||||
|
||||
:bench
|
||||
{:target :node-script
|
||||
|
||||
@@ -59,7 +59,8 @@
|
||||
#{"fdata/shape-data-type"
|
||||
"styles/v2"
|
||||
"layout/grid"
|
||||
"components/v2"})
|
||||
"components/v2"
|
||||
"plugins/runtime"})
|
||||
|
||||
;; A set of features which only affects on frontend and can be enabled
|
||||
;; and disabled freely by the user any time. This features does not
|
||||
@@ -154,6 +155,7 @@
|
||||
team-features (into #{} xf-remove-ephimeral (:features team))]
|
||||
(-> enabled-features
|
||||
(set/intersection no-migration-features)
|
||||
(set/difference frontend-only-features)
|
||||
(set/union team-features))))
|
||||
|
||||
(defn check-client-features!
|
||||
|
||||
@@ -409,12 +409,14 @@
|
||||
;; Resize parent containers that need to
|
||||
(pcb/resize-parents parents))))
|
||||
|
||||
(defn change-show-in-viewer [shape hide?]
|
||||
(defn change-show-in-viewer
|
||||
[shape hide?]
|
||||
(assoc shape :hide-in-viewer hide?))
|
||||
|
||||
(defn add-new-interaction [shape interaction]
|
||||
(-> shape
|
||||
(update :interactions ctsi/add-interaction interaction)))
|
||||
(defn add-new-interaction
|
||||
[shape interaction]
|
||||
(update shape :interactions ctsi/add-interaction interaction))
|
||||
|
||||
(defn show-in-viewer [shape]
|
||||
(defn show-in-viewer
|
||||
[shape]
|
||||
(dissoc shape :hide-in-viewer))
|
||||
|
||||
@@ -27,10 +27,22 @@
|
||||
#?(:clj (Instant/now)
|
||||
:cljs (.local ^js DateTime)))
|
||||
|
||||
#?(:clj
|
||||
(defn is-after?
|
||||
[one other]
|
||||
(.isAfter one other)))
|
||||
(defn is-after?
|
||||
"Analgous to: da > db"
|
||||
[da db]
|
||||
(let [result (compare da db)]
|
||||
(cond
|
||||
(neg? result) false
|
||||
(zero? result) false
|
||||
:else true)))
|
||||
|
||||
(defn is-before?
|
||||
[da db]
|
||||
(let [result (compare da db)]
|
||||
(cond
|
||||
(neg? result) true
|
||||
(zero? result) false
|
||||
:else false)))
|
||||
|
||||
(defn instant?
|
||||
[o]
|
||||
|
||||
@@ -529,13 +529,6 @@
|
||||
(or (d/not-empty? (dm/get-prop modifiers :geometry-child))
|
||||
(d/not-empty? (dm/get-prop modifiers :structure-child))))
|
||||
|
||||
(defn only-move?
|
||||
"Returns true if there are only move operations"
|
||||
[modifiers]
|
||||
(let [move-op? #(= :move (dm/get-prop % :type))]
|
||||
(and (every? move-op? (dm/get-prop modifiers :geometry-child))
|
||||
(every? move-op? (dm/get-prop modifiers :geometry-parent)))))
|
||||
|
||||
(defn has-geometry?
|
||||
[modifiers]
|
||||
(or (d/not-empty? (dm/get-prop modifiers :geometry-parent))
|
||||
@@ -550,6 +543,14 @@
|
||||
[modifiers]
|
||||
(d/not-empty? (dm/get-prop modifiers :structure-child)))
|
||||
|
||||
(defn only-move?
|
||||
"Returns true if there are only move operations"
|
||||
[modifiers]
|
||||
(let [move-op? #(= :move (dm/get-prop % :type))]
|
||||
(and (not (has-structure? modifiers))
|
||||
(every? move-op? (dm/get-prop modifiers :geometry-child))
|
||||
(every? move-op? (dm/get-prop modifiers :geometry-parent)))))
|
||||
|
||||
;; Extract subsets of modifiers
|
||||
|
||||
(defn select-child
|
||||
|
||||
89
common/test/common_tests/runner.cljc
Normal file
89
common/test/common_tests/runner.cljc
Normal file
@@ -0,0 +1,89 @@
|
||||
;; 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.runner
|
||||
(:require
|
||||
[clojure.test :as t]
|
||||
[common-tests.colors-test]
|
||||
[common-tests.data-test]
|
||||
[common-tests.files-builder-test]
|
||||
[common-tests.files-changes-test]
|
||||
[common-tests.files-migrations-test]
|
||||
[common-tests.geom-point-test]
|
||||
[common-tests.geom-shapes-test]
|
||||
[common-tests.geom-test]
|
||||
[common-tests.logic.chained-propagation-test]
|
||||
[common-tests.logic.comp-creation-test]
|
||||
[common-tests.logic.comp-detach-with-nested-test]
|
||||
[common-tests.logic.comp-remove-swap-slots-test]
|
||||
[common-tests.logic.comp-reset-test]
|
||||
[common-tests.logic.comp-sync-test]
|
||||
[common-tests.logic.comp-touched-test]
|
||||
[common-tests.logic.copying-and-duplicating-test]
|
||||
[common-tests.logic.duplicated-pages-test]
|
||||
[common-tests.logic.move-shapes-test]
|
||||
[common-tests.logic.multiple-nesting-levels-test]
|
||||
[common-tests.logic.swap-and-reset-test]
|
||||
[common-tests.logic.swap-as-override-test]
|
||||
[common-tests.pages-helpers-test]
|
||||
[common-tests.record-test]
|
||||
[common-tests.schema-test]
|
||||
[common-tests.svg-path-test]
|
||||
[common-tests.svg-test]
|
||||
[common-tests.text-test]
|
||||
[common-tests.time-test]
|
||||
[common-tests.types-modifiers-test]
|
||||
[common-tests.types-shape-interactions-test]
|
||||
[common-tests.types.shape-decode-encode-test]
|
||||
[common-tests.types.tokens-lib-test]
|
||||
[common-tests.types.types-component-test]
|
||||
[common-tests.types.types-libraries-test]))
|
||||
|
||||
#?(:cljs (enable-console-print!))
|
||||
|
||||
#?(:cljs
|
||||
(defmethod cljs.test/report [:cljs.test/default :end-run-tests] [m]
|
||||
(if (cljs.test/successful? m)
|
||||
(.exit js/process 0)
|
||||
(.exit js/process 1))))
|
||||
|
||||
(defn -main
|
||||
[& args]
|
||||
(t/run-tests
|
||||
'common-tests.colors-test
|
||||
'common-tests.data-test
|
||||
'common-tests.files-builder-test
|
||||
'common-tests.files-changes-test
|
||||
'common-tests.files-migrations-test
|
||||
'common-tests.geom-point-test
|
||||
'common-tests.geom-shapes-test
|
||||
'common-tests.geom-test
|
||||
'common-tests.logic.chained-propagation-test
|
||||
'common-tests.logic.comp-creation-test
|
||||
'common-tests.logic.comp-detach-with-nested-test
|
||||
'common-tests.logic.comp-remove-swap-slots-test
|
||||
'common-tests.logic.comp-reset-test
|
||||
'common-tests.logic.comp-sync-test
|
||||
'common-tests.logic.comp-touched-test
|
||||
'common-tests.logic.copying-and-duplicating-test
|
||||
'common-tests.logic.duplicated-pages-test
|
||||
'common-tests.logic.move-shapes-test
|
||||
'common-tests.logic.multiple-nesting-levels-test
|
||||
'common-tests.logic.swap-and-reset-test
|
||||
'common-tests.logic.swap-as-override-test
|
||||
'common-tests.pages-helpers-test
|
||||
'common-tests.record-test
|
||||
'common-tests.schema-test
|
||||
'common-tests.svg-path-test
|
||||
'common-tests.svg-test
|
||||
'common-tests.text-test
|
||||
'common-tests.types-modifiers-test
|
||||
'common-tests.types-shape-interactions-test
|
||||
'common-tests.types.shape-decode-encode-test
|
||||
'common-tests.types.types-component-test
|
||||
'common-tests.types.types-libraries-test
|
||||
'common-tests.types.tokens-lib-test
|
||||
'common-tests.time-test))
|
||||
16
common/test/common_tests/time_test.cljc
Normal file
16
common/test/common_tests/time_test.cljc
Normal file
@@ -0,0 +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 common-tests.time-test
|
||||
(:require
|
||||
[app.common.time :as dt]
|
||||
[clojure.test :as t]))
|
||||
|
||||
(t/deftest compare-time
|
||||
(let [dta (dt/parse-instant 10000)
|
||||
dtb (dt/parse-instant 20000)]
|
||||
(t/is (false? (dt/is-after? dta dtb)))
|
||||
(t/is (true? (dt/is-before? dta dtb)))))
|
||||
@@ -148,4 +148,4 @@
|
||||
;; (app.common.pprint/pprint shape)
|
||||
;; (app.common.pprint/pprint shape-3)
|
||||
(= shape shape-3)))
|
||||
{:num 1000})))
|
||||
{:num 100})))
|
||||
|
||||
@@ -14,8 +14,16 @@
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[clojure.test :as t]))
|
||||
|
||||
(t/testing "token"
|
||||
(t/deftest make-token
|
||||
(defn setup-virtual-time
|
||||
[next]
|
||||
(let [current (volatile! (inst-ms (dt/now)))]
|
||||
(with-redefs [dt/now #(dt/parse-instant (vswap! current inc))]
|
||||
(next))))
|
||||
|
||||
(t/use-fixtures :once setup-virtual-time)
|
||||
|
||||
(t/deftest tokens
|
||||
(t/testing "make-token"
|
||||
(let [now (dt/now)
|
||||
token1 (ctob/make-token :name "test-token-1"
|
||||
:type :boolean
|
||||
@@ -40,14 +48,14 @@
|
||||
(t/is (= (:modified-at token2) now))
|
||||
(t/is (ctob/valid-token? token2))))
|
||||
|
||||
(t/deftest invalid-tokens
|
||||
(t/testing "invalid-tokens"
|
||||
(let [args {:name 777
|
||||
:type :invalid}]
|
||||
(t/is (thrown-with-msg? Exception #"expected valid token"
|
||||
(t/is (thrown-with-msg? #?(:cljs js/Error :clj Exception) #"expected valid token"
|
||||
(apply ctob/make-token args)))
|
||||
(t/is (false? (ctob/valid-token? {})))))
|
||||
|
||||
(t/deftest find-token-value-references
|
||||
(t/testing "find-token-value-references"
|
||||
(t/testing "finds references inside curly braces in a string"
|
||||
(t/is (= #{"foo" "bar"} (ctob/find-token-value-references "{foo} + {bar}")))
|
||||
(t/testing "ignores extra text"
|
||||
@@ -57,8 +65,8 @@
|
||||
(t/testing "handles edge-case for extra curly braces"
|
||||
(t/is (= #{"foo" "bar"} (ctob/find-token-value-references "{foo}} + {bar}"))))))
|
||||
|
||||
(t/testing "token-set"
|
||||
(t/deftest make-token-set
|
||||
(t/deftest token-set
|
||||
(t/testing "make-token-set"
|
||||
(let [now (dt/now)
|
||||
token-set1 (ctob/make-token-set :name "test-token-set-1")
|
||||
token-set2 (ctob/make-token-set :name "test-token-set-2"
|
||||
@@ -76,13 +84,13 @@
|
||||
(t/is (= (:modified-at token-set2) now))
|
||||
(t/is (empty? (:tokens token-set2)))))
|
||||
|
||||
(t/deftest invalid-token-set
|
||||
(t/testing "invalid-token-set"
|
||||
(let [args {:name 777
|
||||
:description 999}]
|
||||
(t/is (thrown-with-msg? Exception #"expected valid token set"
|
||||
(t/is (thrown-with-msg? #?(:cljs js/Error :clj Exception) #"expected valid token set"
|
||||
(apply ctob/make-token-set args)))))
|
||||
|
||||
(t/deftest move-token-set
|
||||
(t/testing "move-token-set"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-set (ctob/make-token-set :name "A"))
|
||||
(ctob/add-set (ctob/make-token-set :name "B"))
|
||||
@@ -107,7 +115,7 @@
|
||||
(t/is (= original-order (move "A" "foo/bar/baz")))
|
||||
(t/is (= original-order (move "Missing" "Move"))))))
|
||||
|
||||
(t/deftest tokens-tree
|
||||
(t/testing "tokens-tree"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-set (ctob/make-token-set :name "A"
|
||||
:tokens {"foo.bar.baz" (ctob/make-token :name "foo.bar.baz"
|
||||
@@ -125,8 +133,8 @@
|
||||
(t/is (= (get-in expected ["foo" "bar" "bam" :name]) "foo.bar.bam"))
|
||||
(t/is (= (get-in expected ["baz" "boo" :name]) "baz.boo")))))
|
||||
|
||||
(t/testing "token-theme"
|
||||
(t/deftest make-token-theme
|
||||
(t/deftest token-theme
|
||||
(t/testing "make-token-theme"
|
||||
(let [now (dt/now)
|
||||
token-theme1 (ctob/make-token-theme :name "test-token-theme-1")
|
||||
token-theme2 (ctob/make-token-theme :name "test-token-theme-2"
|
||||
@@ -150,24 +158,24 @@
|
||||
(t/is (= (:modified-at token-theme2) now))
|
||||
(t/is (empty? (:sets token-theme2)))))
|
||||
|
||||
(t/deftest invalid-token-theme
|
||||
(t/testing "invalid-token-theme"
|
||||
(let [args {:name 777
|
||||
:group nil
|
||||
:description 999
|
||||
:is-source 42}]
|
||||
(t/is (thrown-with-msg? Exception #"expected valid token theme"
|
||||
(t/is (thrown-with-msg? #?(:cljs js/Error :clj Exception) #"expected valid token theme"
|
||||
(apply ctob/make-token-theme args))))))
|
||||
|
||||
|
||||
(t/testing "tokens-lib"
|
||||
(t/deftest make-tokens-lib
|
||||
(t/deftest tokens-lib
|
||||
(t/testing "make-tokens-lib"
|
||||
(let [tokens-lib (ctob/make-tokens-lib)]
|
||||
(t/is (= (ctob/set-count tokens-lib) 0))))
|
||||
|
||||
(t/deftest invalid-tokens-lib
|
||||
(t/testing "invalid-tokens-lib"
|
||||
(let [args {:sets nil
|
||||
:themes nil}]
|
||||
(t/is (thrown-with-msg? Exception #"expected valid tokens lib"
|
||||
(t/is (thrown-with-msg? #?(:cljs js/Error :clj Exception) #"expected valid tokens lib"
|
||||
(apply ctob/make-tokens-lib args))))))
|
||||
|
||||
|
||||
@@ -263,8 +271,8 @@
|
||||
(t/is (nil? token-set')))))
|
||||
|
||||
|
||||
(t/testing "token in a lib"
|
||||
(t/deftest add-token
|
||||
(t/deftest token-in-a-lib
|
||||
(t/testing "add-token"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-set (ctob/make-token-set :name "test-token-set")))
|
||||
token (ctob/make-token :name "test-token"
|
||||
@@ -283,7 +291,7 @@
|
||||
(t/is (= (:name token') "test-token"))
|
||||
(t/is (dt/is-after? (:modified-at token-set') (:modified-at token-set)))))
|
||||
|
||||
(t/deftest update-token
|
||||
(t/testing "update-token"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-set (ctob/make-token-set :name "test-token-set"))
|
||||
(ctob/add-token-in-set "test-token-set"
|
||||
@@ -324,7 +332,7 @@
|
||||
(t/is (dt/is-after? (:modified-at token-set') (:modified-at token-set)))
|
||||
(t/is (dt/is-after? (:modified-at token') (:modified-at token)))))
|
||||
|
||||
(t/deftest rename-token
|
||||
(t/testing "rename-token"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-set (ctob/make-token-set :name "test-token-set"))
|
||||
(ctob/add-token-in-set "test-token-set"
|
||||
@@ -356,7 +364,7 @@
|
||||
(t/is (dt/is-after? (:modified-at token-set') (:modified-at token-set)))
|
||||
(t/is (dt/is-after? (:modified-at token') (:modified-at token)))))
|
||||
|
||||
(t/deftest delete-token
|
||||
(t/testing "delete-token"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-set (ctob/make-token-set :name "test-token-set"))
|
||||
(ctob/add-token-in-set "test-token-set"
|
||||
@@ -377,7 +385,7 @@
|
||||
(t/is (nil? token'))
|
||||
(t/is (dt/is-after? (:modified-at token-set') (:modified-at token-set)))))
|
||||
|
||||
(t/deftest list-active-themes-tokens-in-order
|
||||
(t/testing "list-active-themes-tokens-in-order"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-theme (ctob/make-token-theme :name "out-of-order-theme"
|
||||
;; Out of order sets in theme
|
||||
@@ -405,8 +413,8 @@
|
||||
(t/is (= ["set-a-token" "set-b-token"] expected-token-names)))))
|
||||
|
||||
|
||||
(t/testing "token-theme in a lib"
|
||||
(t/deftest add-token-theme
|
||||
(t/deftest token-theme-in-a-lib
|
||||
(t/testing "add-token-theme"
|
||||
(let [tokens-lib (ctob/make-tokens-lib)
|
||||
token-theme (ctob/make-token-theme :name "test-token-theme")
|
||||
tokens-lib' (ctob/add-theme tokens-lib token-theme)
|
||||
@@ -418,7 +426,7 @@
|
||||
(t/is (= (first token-themes') token-theme))
|
||||
(t/is (= token-theme' token-theme))))
|
||||
|
||||
(t/deftest update-token-theme
|
||||
(t/testing "update-token-theme"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-theme (ctob/make-token-theme :name "test-token-theme")))
|
||||
|
||||
@@ -440,7 +448,7 @@
|
||||
(t/is (= (:description token-theme') "some description"))
|
||||
(t/is (dt/is-after? (:modified-at token-theme') (:modified-at token-theme)))))
|
||||
|
||||
(t/deftest rename-token-theme
|
||||
(t/testing "rename-token-theme"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-theme (ctob/make-token-theme :name "test-token-theme")))
|
||||
|
||||
@@ -457,7 +465,7 @@
|
||||
(t/is (= (:name token-theme') "updated-name"))
|
||||
(t/is (dt/is-after? (:modified-at token-theme') (:modified-at token-theme)))))
|
||||
|
||||
(t/deftest delete-token-theme
|
||||
(t/testing "delete-token-theme"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-theme (ctob/make-token-theme :name "test-token-theme")))
|
||||
|
||||
@@ -470,7 +478,7 @@
|
||||
(t/is (= (ctob/theme-count tokens-lib') 0))
|
||||
(t/is (nil? token-theme'))))
|
||||
|
||||
(t/deftest toggle-set-in-theme
|
||||
(t/testing "toggle-set-in-theme"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-set (ctob/make-token-set :name "token-set-1"))
|
||||
(ctob/add-set (ctob/make-token-set :name "token-set-2"))
|
||||
@@ -487,8 +495,8 @@
|
||||
(t/is (dt/is-after? (:modified-at token-theme') (:modified-at token-theme))))))
|
||||
|
||||
|
||||
(t/testing "serialization"
|
||||
(t/deftest transit-serialization
|
||||
(t/deftest serialization
|
||||
(t/testing "transit-serialization"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-set (ctob/make-token-set :name "test-token-set"))
|
||||
(ctob/add-token-in-set "test-token-set" (ctob/make-token :name "test-token"
|
||||
@@ -503,23 +511,24 @@
|
||||
(t/is (= (ctob/set-count tokens-lib') 1))
|
||||
(t/is (= (ctob/theme-count tokens-lib') 1))))
|
||||
|
||||
(t/deftest fressian-serialization
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-set (ctob/make-token-set :name "test-token-set"))
|
||||
(ctob/add-token-in-set "test-token-set" (ctob/make-token :name "test-token"
|
||||
:type :boolean
|
||||
:value true))
|
||||
(ctob/add-theme (ctob/make-token-theme :name "test-token-theme"))
|
||||
(ctob/toggle-set-in-theme "" "test-token-theme" "test-token-set"))
|
||||
encoded-blob (fres/encode tokens-lib)
|
||||
tokens-lib' (fres/decode encoded-blob)]
|
||||
#?(:clj
|
||||
(t/testing "fressian-serialization"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-set (ctob/make-token-set :name "test-token-set"))
|
||||
(ctob/add-token-in-set "test-token-set" (ctob/make-token :name "test-token"
|
||||
:type :boolean
|
||||
:value true))
|
||||
(ctob/add-theme (ctob/make-token-theme :name "test-token-theme"))
|
||||
(ctob/toggle-set-in-theme "" "test-token-theme" "test-token-set"))
|
||||
encoded-blob (fres/encode tokens-lib)
|
||||
tokens-lib' (fres/decode encoded-blob)]
|
||||
|
||||
(t/is (ctob/valid-tokens-lib? tokens-lib'))
|
||||
(t/is (= (ctob/set-count tokens-lib') 1))
|
||||
(t/is (= (ctob/theme-count tokens-lib') 1)))))
|
||||
(t/is (ctob/valid-tokens-lib? tokens-lib'))
|
||||
(t/is (= (ctob/set-count tokens-lib') 1))
|
||||
(t/is (= (ctob/theme-count tokens-lib') 1))))))
|
||||
|
||||
(t/testing "grouping"
|
||||
(t/deftest split-and-join
|
||||
(t/deftest grouping
|
||||
(t/testing "split-and-join"
|
||||
(let [name "group/subgroup/name"
|
||||
path (ctob/split-path name "/")
|
||||
name' (ctob/join-path path "/")]
|
||||
@@ -528,14 +537,14 @@
|
||||
(t/is (= (nth path 2) "name"))
|
||||
(t/is (= name' name))))
|
||||
|
||||
(t/deftest remove-spaces
|
||||
(t/testing "remove-spaces"
|
||||
(let [name "group / subgroup / name"
|
||||
path (ctob/split-path name "/")]
|
||||
(t/is (= (first path) "group"))
|
||||
(t/is (= (second path) "subgroup"))
|
||||
(t/is (= (nth path 2) "name"))))
|
||||
|
||||
(t/deftest group-and-ungroup
|
||||
(t/testing "group-and-ungroup"
|
||||
(let [token-set1 (ctob/make-token-set :name "token-set1")
|
||||
token-set2 (ctob/make-token-set :name "some group/token-set2")
|
||||
|
||||
@@ -548,7 +557,7 @@
|
||||
(t/is (= (:name token-set1'') "token-set1"))
|
||||
(t/is (= (:name token-set2'') "some group/token-set2"))))
|
||||
|
||||
(t/deftest get-groups-str
|
||||
(t/testing "get-groups-str"
|
||||
(let [token-set1 (ctob/make-token-set :name "token-set1")
|
||||
token-set2 (ctob/make-token-set :name "some-group/token-set2")
|
||||
token-set3 (ctob/make-token-set :name "some-group/some-subgroup/token-set3")]
|
||||
@@ -556,7 +565,7 @@
|
||||
(t/is (= (ctob/get-groups-str token-set2 "/") "some-group"))
|
||||
(t/is (= (ctob/get-groups-str token-set3 "/") "some-group/some-subgroup"))))
|
||||
|
||||
(t/deftest get-final-name
|
||||
(t/testing "get-final-name"
|
||||
(let [token-set1 (ctob/make-token-set :name "token-set1")
|
||||
token-set2 (ctob/make-token-set :name "some-group/token-set2")
|
||||
token-set3 (ctob/make-token-set :name "some-group/some-subgroup/token-set3")]
|
||||
@@ -565,7 +574,7 @@
|
||||
(t/is (= (ctob/get-final-name token-set3 "/") "token-set3"))))
|
||||
|
||||
(t/testing "grouped tokens"
|
||||
(t/deftest grouped-tokens
|
||||
(t/testing "grouped-tokens"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-set (ctob/make-token-set :name "test-token-set"))
|
||||
(ctob/add-token-in-set "test-token-set"
|
||||
@@ -599,7 +608,7 @@
|
||||
(t/is (= (:name (nth tokens-list 3)) "group1.subgroup11.token4"))
|
||||
(t/is (= (:name (nth tokens-list 4)) "group2.token5"))))
|
||||
|
||||
(t/deftest update-token-in-groups
|
||||
(t/testing "update-token-in-groups"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-set (ctob/make-token-set :name "test-token-set"))
|
||||
(ctob/add-token-in-set "test-token-set"
|
||||
@@ -634,7 +643,7 @@
|
||||
(t/is (dt/is-after? (:modified-at token-set') (:modified-at token-set)))
|
||||
(t/is (dt/is-after? (:modified-at token') (:modified-at token)))))
|
||||
|
||||
(t/deftest rename-token-in-groups
|
||||
(t/testing "rename-token-in-groups"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-set (ctob/make-token-set :name "test-token-set"))
|
||||
(ctob/add-token-in-set "test-token-set"
|
||||
@@ -668,7 +677,7 @@
|
||||
(t/is (dt/is-after? (:modified-at token-set') (:modified-at token-set)))
|
||||
(t/is (dt/is-after? (:modified-at token') (:modified-at token)))))
|
||||
|
||||
(t/deftest move-token-of-group
|
||||
(t/testing "move-token-of-group"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-set (ctob/make-token-set :name "test-token-set"))
|
||||
(ctob/add-token-in-set "test-token-set"
|
||||
@@ -703,7 +712,7 @@
|
||||
(t/is (dt/is-after? (:modified-at token-set') (:modified-at token-set)))
|
||||
(t/is (dt/is-after? (:modified-at token') (:modified-at token)))))
|
||||
|
||||
(t/deftest delete-token-in-group
|
||||
(t/testing "delete-token-in-group"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-set (ctob/make-token-set :name "test-token-set"))
|
||||
(ctob/add-token-in-set "test-token-set"
|
||||
@@ -727,7 +736,7 @@
|
||||
(t/is (dt/is-after? (:modified-at token-set') (:modified-at token-set))))))
|
||||
|
||||
(t/testing "grouped sets"
|
||||
(t/deftest grouped-sets
|
||||
(t/testing "grouped-sets"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-set (ctob/make-token-set :name "token-set-1"))
|
||||
(ctob/add-set (ctob/make-token-set :name "group1/token-set-2"))
|
||||
@@ -786,7 +795,7 @@
|
||||
(t/is (= (ctob/group? (second node-set5)) false))
|
||||
(t/is (= (:name (second node-set5)) "group2/token-set-5"))))
|
||||
|
||||
(t/deftest update-set-in-groups
|
||||
(t/testing "update-set-in-groups"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-set (ctob/make-token-set :name "token-set-1"))
|
||||
(ctob/add-set (ctob/make-token-set :name "group1/token-set-2"))
|
||||
@@ -812,7 +821,7 @@
|
||||
(t/is (= (:description token-set') "some description"))
|
||||
(t/is (dt/is-after? (:modified-at token-set') (:modified-at token-set)))))
|
||||
|
||||
(t/deftest rename-set-in-groups
|
||||
(t/testing "rename-set-in-groups"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-set (ctob/make-token-set :name "token-set-1"))
|
||||
(ctob/add-set (ctob/make-token-set :name "group1/token-set-2"))
|
||||
@@ -839,7 +848,7 @@
|
||||
(t/is (= (:description token-set') nil))
|
||||
(t/is (dt/is-after? (:modified-at token-set') (:modified-at token-set)))))
|
||||
|
||||
(t/deftest move-set-of-group
|
||||
(t/testing "move-set-of-group"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-set (ctob/make-token-set :name "token-set-1"))
|
||||
(ctob/add-set (ctob/make-token-set :name "group1/token-set-2"))
|
||||
@@ -868,7 +877,7 @@
|
||||
(t/is (= (:description token-set') nil))
|
||||
(t/is (dt/is-after? (:modified-at token-set') (:modified-at token-set)))))
|
||||
|
||||
(t/deftest delete-set-in-group
|
||||
(t/testing "delete-set-in-group"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-set (ctob/make-token-set :name "token-set-1"))
|
||||
(ctob/add-set (ctob/make-token-set :name "group1/token-set-2")))
|
||||
@@ -884,7 +893,7 @@
|
||||
(t/is (nil? token-set')))))
|
||||
|
||||
(t/testing "grouped themes"
|
||||
(t/deftest grouped-themes
|
||||
(t/testing "grouped-themes"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-theme (ctob/make-token-theme :group "" :name "token-theme-1"))
|
||||
(ctob/add-theme (ctob/make-token-theme :group "group1" :name "token-theme-2"))
|
||||
@@ -941,7 +950,7 @@
|
||||
(t/is (= (ctob/group? (second node-theme4)) false))
|
||||
(t/is (= (:name (second node-theme4)) "token-theme-4"))))
|
||||
|
||||
(t/deftest update-theme-in-groups
|
||||
(t/testing "update-theme-in-groups"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-theme (ctob/make-token-theme :group "" :name "token-theme-1"))
|
||||
(ctob/add-theme (ctob/make-token-theme :group "group1" :name "token-theme-2"))
|
||||
@@ -967,7 +976,7 @@
|
||||
(t/is (= (:description token-theme') "some description"))
|
||||
(t/is (dt/is-after? (:modified-at token-theme') (:modified-at token-theme)))))
|
||||
|
||||
(t/deftest get-theme-groups
|
||||
(t/testing "get-theme-groups"
|
||||
(let [token-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-theme (ctob/make-token-theme :group "" :name "token-theme-1"))
|
||||
(ctob/add-theme (ctob/make-token-theme :group "group1" :name "token-theme-2"))
|
||||
@@ -976,13 +985,14 @@
|
||||
token-groups (ctob/get-theme-groups token-lib)]
|
||||
(t/is (= token-groups ["group1" "group2"]))))
|
||||
|
||||
(t/deftest rename-theme-in-groups
|
||||
(t/testing "rename-theme-in-groups"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-theme (ctob/make-token-theme :group "" :name "token-theme-1"))
|
||||
(ctob/add-theme (ctob/make-token-theme :group "group1" :name "token-theme-2"))
|
||||
(ctob/add-theme (ctob/make-token-theme :group "group1" :name "token-theme-3"))
|
||||
(ctob/add-theme (ctob/make-token-theme :group "group2" :name "token-theme-4")))
|
||||
|
||||
|
||||
tokens-lib' (-> tokens-lib
|
||||
(ctob/update-theme "group1" "token-theme-2"
|
||||
(fn [token-theme]
|
||||
@@ -1003,7 +1013,7 @@
|
||||
(t/is (= (:description token-theme') nil))
|
||||
(t/is (dt/is-after? (:modified-at token-theme') (:modified-at token-theme)))))
|
||||
|
||||
(t/deftest move-theme-of-group
|
||||
(t/testing "move-theme-of-group"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-theme (ctob/make-token-theme :group "" :name "token-theme-1"))
|
||||
(ctob/add-theme (ctob/make-token-theme :group "group1" :name "token-theme-2"))
|
||||
@@ -1033,7 +1043,7 @@
|
||||
(t/is (= (:description token-theme') nil))
|
||||
(t/is (dt/is-after? (:modified-at token-theme') (:modified-at token-theme)))))
|
||||
|
||||
(t/deftest delete-theme-in-group
|
||||
(t/testing "delete-theme-in-group"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-theme (ctob/make-token-theme :group "" :name "token-theme-1"))
|
||||
(ctob/add-theme (ctob/make-token-theme :group "group1" :name "token-theme-2")))
|
||||
@@ -1049,8 +1059,8 @@
|
||||
(t/is (nil? token-theme'))))))
|
||||
|
||||
#?(:clj
|
||||
(t/testing "dtcg encoding/decoding"
|
||||
(t/deftest decode-dtcg-json
|
||||
(t/deftest dtcg-encoding-decoding
|
||||
(t/testing "decode-dtcg-json"
|
||||
(let [json (-> (slurp "test/common_tests/types/data/tokens-multi-set-example.json")
|
||||
(tr/decode-str))
|
||||
lib (ctob/decode-dtcg-json (ctob/ensure-tokens-lib nil) json)
|
||||
@@ -1078,7 +1088,7 @@
|
||||
(t/testing "invalid tokens got discarded"
|
||||
(t/is (nil? (get-set-token "typography" "H1.Bold"))))))
|
||||
|
||||
(t/deftest encode-dtcg-json
|
||||
(t/testing "encode-dtcg-json"
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-set (ctob/make-token-set :name "core"
|
||||
:tokens {"colors.red.600"
|
||||
@@ -1111,7 +1121,7 @@
|
||||
"$type" "color"}}}}}
|
||||
expected))))
|
||||
|
||||
(t/deftest encode-decode-dtcg-json
|
||||
(t/testing "encode-decode-dtcg-json"
|
||||
(with-redefs [dt/now (constantly #inst "2024-10-16T12:01:20.257840055-00:00")]
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-set (ctob/make-token-set :name "core"
|
||||
|
||||
446
common/yarn.lock
446
common/yarn.lock
@@ -88,7 +88,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ansi-styles@npm:^4.0.0":
|
||||
"ansi-styles@npm:^4.0.0, ansi-styles@npm:^4.1.0":
|
||||
version: 4.3.0
|
||||
resolution: "ansi-styles@npm:4.3.0"
|
||||
dependencies:
|
||||
@@ -104,6 +104,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"anymatch@npm:~3.1.2":
|
||||
version: 3.1.3
|
||||
resolution: "anymatch@npm:3.1.3"
|
||||
dependencies:
|
||||
normalize-path: "npm:^3.0.0"
|
||||
picomatch: "npm:^2.0.4"
|
||||
checksum: 10c0/57b06ae984bc32a0d22592c87384cd88fe4511b1dd7581497831c56d41939c8a001b28e7b853e1450f2bf61992dfcaa8ae2d0d161a0a90c4fb631ef07098fbac
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"asn1.js@npm:^4.10.1":
|
||||
version: 4.10.1
|
||||
resolution: "asn1.js@npm:4.10.1"
|
||||
@@ -139,6 +149,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"binary-extensions@npm:^2.0.0":
|
||||
version: 2.3.0
|
||||
resolution: "binary-extensions@npm:2.3.0"
|
||||
checksum: 10c0/75a59cafc10fb12a11d510e77110c6c7ae3f4ca22463d52487709ca7f18f69d886aa387557cc9864fbdb10153d0bdb4caacabf11541f55e89ed6e18d12ece2b5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"bn.js@npm:^4.0.0, bn.js@npm:^4.1.0, bn.js@npm:^4.11.9":
|
||||
version: 4.12.0
|
||||
resolution: "bn.js@npm:4.12.0"
|
||||
@@ -153,6 +170,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"brace-expansion@npm:^1.1.7":
|
||||
version: 1.1.11
|
||||
resolution: "brace-expansion@npm:1.1.11"
|
||||
dependencies:
|
||||
balanced-match: "npm:^1.0.0"
|
||||
concat-map: "npm:0.0.1"
|
||||
checksum: 10c0/695a56cd058096a7cb71fb09d9d6a7070113c7be516699ed361317aca2ec169f618e28b8af352e02ab4233fb54eb0168460a40dc320bab0034b36ab59aaad668
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"brace-expansion@npm:^2.0.1":
|
||||
version: 2.0.1
|
||||
resolution: "brace-expansion@npm:2.0.1"
|
||||
@@ -162,6 +189,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"braces@npm:~3.0.2":
|
||||
version: 3.0.3
|
||||
resolution: "braces@npm:3.0.3"
|
||||
dependencies:
|
||||
fill-range: "npm:^7.1.1"
|
||||
checksum: 10c0/7c6dfd30c338d2997ba77500539227b9d1f85e388a5f43220865201e407e076783d0881f2d297b9f80951b4c957fcf0b51c1d2d24227631643c3f7c284b0aa04
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"brorand@npm:^1.0.1, brorand@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "brorand@npm:1.1.0"
|
||||
@@ -308,6 +344,35 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"chalk@npm:^4.1.2":
|
||||
version: 4.1.2
|
||||
resolution: "chalk@npm:4.1.2"
|
||||
dependencies:
|
||||
ansi-styles: "npm:^4.1.0"
|
||||
supports-color: "npm:^7.1.0"
|
||||
checksum: 10c0/4a3fef5cc34975c898ffe77141450f679721df9dde00f6c304353fa9c8b571929123b26a0e4617bde5018977eb655b31970c297b91b63ee83bb82aeb04666880
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"chokidar@npm:^3.5.2":
|
||||
version: 3.6.0
|
||||
resolution: "chokidar@npm:3.6.0"
|
||||
dependencies:
|
||||
anymatch: "npm:~3.1.2"
|
||||
braces: "npm:~3.0.2"
|
||||
fsevents: "npm:~2.3.2"
|
||||
glob-parent: "npm:~5.1.2"
|
||||
is-binary-path: "npm:~2.1.0"
|
||||
is-glob: "npm:~4.0.1"
|
||||
normalize-path: "npm:~3.0.0"
|
||||
readdirp: "npm:~3.6.0"
|
||||
dependenciesMeta:
|
||||
fsevents:
|
||||
optional: true
|
||||
checksum: 10c0/8361dcd013f2ddbe260eacb1f3cb2f2c6f2b0ad118708a343a5ed8158941a39cb8fb1d272e0f389712e74ee90ce8ba864eece9e0e62b9705cb468a2f6d917462
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"chownr@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "chownr@npm:2.0.0"
|
||||
@@ -332,6 +397,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cliui@npm:^8.0.1":
|
||||
version: 8.0.1
|
||||
resolution: "cliui@npm:8.0.1"
|
||||
dependencies:
|
||||
string-width: "npm:^4.2.0"
|
||||
strip-ansi: "npm:^6.0.1"
|
||||
wrap-ansi: "npm:^7.0.0"
|
||||
checksum: 10c0/4bda0f09c340cbb6dfdc1ed508b3ca080f12992c18d68c6be4d9cf51756033d5266e61ec57529e610dacbf4da1c634423b0c1b11037709cc6b09045cbd815df5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"color-convert@npm:^2.0.1":
|
||||
version: 2.0.1
|
||||
resolution: "color-convert@npm:2.0.1"
|
||||
@@ -352,14 +428,41 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "common@workspace:."
|
||||
dependencies:
|
||||
concurrently: "npm:^9.0.1"
|
||||
luxon: "npm:^3.4.4"
|
||||
nodemon: "npm:^3.1.7"
|
||||
sax: "npm:^1.4.1"
|
||||
shadow-cljs: "npm:2.28.11"
|
||||
shadow-cljs: "npm:2.28.18"
|
||||
source-map-support: "npm:^0.5.21"
|
||||
ws: "npm:^8.17.0"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"concat-map@npm:0.0.1":
|
||||
version: 0.0.1
|
||||
resolution: "concat-map@npm:0.0.1"
|
||||
checksum: 10c0/c996b1cfdf95b6c90fee4dae37e332c8b6eb7d106430c17d538034c0ad9a1630cb194d2ab37293b1bdd4d779494beee7786d586a50bd9376fd6f7bcc2bd4c98f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"concurrently@npm:^9.0.1":
|
||||
version: 9.1.0
|
||||
resolution: "concurrently@npm:9.1.0"
|
||||
dependencies:
|
||||
chalk: "npm:^4.1.2"
|
||||
lodash: "npm:^4.17.21"
|
||||
rxjs: "npm:^7.8.1"
|
||||
shell-quote: "npm:^1.8.1"
|
||||
supports-color: "npm:^8.1.1"
|
||||
tree-kill: "npm:^1.2.2"
|
||||
yargs: "npm:^17.7.2"
|
||||
bin:
|
||||
conc: dist/bin/concurrently.js
|
||||
concurrently: dist/bin/concurrently.js
|
||||
checksum: 10c0/f2f42f94dde508bfbaf47b5ac654db9e8a4bf07d3d7b6267dd058ae6f362eec677ae7c8ede398d081e5fd0d1de5811dc9a53e57d3f1f68e72ac6459db9e0896b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"console-browserify@npm:^1.1.0":
|
||||
version: 1.2.0
|
||||
resolution: "console-browserify@npm:1.2.0"
|
||||
@@ -460,6 +563,18 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"debug@npm:^4":
|
||||
version: 4.3.7
|
||||
resolution: "debug@npm:4.3.7"
|
||||
dependencies:
|
||||
ms: "npm:^2.1.3"
|
||||
peerDependenciesMeta:
|
||||
supports-color:
|
||||
optional: true
|
||||
checksum: 10c0/1471db19c3b06d485a622d62f65947a19a23fbd0dd73f7fd3eafb697eec5360cde447fb075919987899b1a2096e85d35d4eb5a4de09a57600ac9cf7e6c8e768b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"define-data-property@npm:^1.0.1, define-data-property@npm:^1.1.4":
|
||||
version: 1.1.4
|
||||
resolution: "define-data-property@npm:1.1.4"
|
||||
@@ -585,6 +700,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"escalade@npm:^3.1.1":
|
||||
version: 3.2.0
|
||||
resolution: "escalade@npm:3.2.0"
|
||||
checksum: 10c0/ced4dd3a78e15897ed3be74e635110bbf3b08877b0a41be50dcb325ee0e0b5f65fc2d50e9845194d7c4633f327e2e1c6cce00a71b617c5673df0374201d67f65
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"events@npm:^3.0.0":
|
||||
version: 3.3.0
|
||||
resolution: "events@npm:3.3.0"
|
||||
@@ -610,6 +732,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fill-range@npm:^7.1.1":
|
||||
version: 7.1.1
|
||||
resolution: "fill-range@npm:7.1.1"
|
||||
dependencies:
|
||||
to-regex-range: "npm:^5.0.1"
|
||||
checksum: 10c0/b75b691bbe065472f38824f694c2f7449d7f5004aa950426a2c28f0306c60db9b880c0b0e4ed819997ffb882d1da02cfcfc819bddc94d71627f5269682edf018
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"foreground-child@npm:^3.1.0":
|
||||
version: 3.1.1
|
||||
resolution: "foreground-child@npm:3.1.1"
|
||||
@@ -638,6 +769,25 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fsevents@npm:~2.3.2":
|
||||
version: 2.3.3
|
||||
resolution: "fsevents@npm:2.3.3"
|
||||
dependencies:
|
||||
node-gyp: "npm:latest"
|
||||
checksum: 10c0/a1f0c44595123ed717febbc478aa952e47adfc28e2092be66b8ab1635147254ca6cfe1df792a8997f22716d4cbafc73309899ff7bfac2ac3ad8cf2e4ecc3ec60
|
||||
conditions: os=darwin
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin<compat/fsevents>":
|
||||
version: 2.3.3
|
||||
resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin<compat/fsevents>::version=2.3.3&hash=df0bf1"
|
||||
dependencies:
|
||||
node-gyp: "npm:latest"
|
||||
conditions: os=darwin
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"function-bind@npm:^1.1.2":
|
||||
version: 1.1.2
|
||||
resolution: "function-bind@npm:1.1.2"
|
||||
@@ -645,6 +795,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"get-caller-file@npm:^2.0.5":
|
||||
version: 2.0.5
|
||||
resolution: "get-caller-file@npm:2.0.5"
|
||||
checksum: 10c0/c6c7b60271931fa752aeb92f2b47e355eac1af3a2673f47c9589e8f8a41adc74d45551c1bc57b5e66a80609f10ffb72b6f575e4370d61cc3f7f3aaff01757cde
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.4":
|
||||
version: 1.2.4
|
||||
resolution: "get-intrinsic@npm:1.2.4"
|
||||
@@ -658,6 +815,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"glob-parent@npm:~5.1.2":
|
||||
version: 5.1.2
|
||||
resolution: "glob-parent@npm:5.1.2"
|
||||
dependencies:
|
||||
is-glob: "npm:^4.0.1"
|
||||
checksum: 10c0/cab87638e2112bee3f839ef5f6e0765057163d39c66be8ec1602f3823da4692297ad4e972de876ea17c44d652978638d2fd583c6713d0eb6591706825020c9ee
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"glob@npm:^10.2.2, glob@npm:^10.3.10":
|
||||
version: 10.3.16
|
||||
resolution: "glob@npm:10.3.16"
|
||||
@@ -689,6 +855,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"has-flag@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "has-flag@npm:3.0.0"
|
||||
checksum: 10c0/1c6c83b14b8b1b3c25b0727b8ba3e3b647f99e9e6e13eb7322107261de07a4c1be56fc0d45678fc376e09772a3a1642ccdaf8fc69bdf123b6c086598397ce473
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"has-flag@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "has-flag@npm:4.0.0"
|
||||
checksum: 10c0/2e789c61b7888d66993e14e8331449e525ef42aac53c627cc53d1c3334e768bcb6abdc4f5f0de1478a25beec6f0bd62c7549058b7ac53e924040d4f301f02fd1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"has-property-descriptors@npm:^1.0.0, has-property-descriptors@npm:^1.0.2":
|
||||
version: 1.0.2
|
||||
resolution: "has-property-descriptors@npm:1.0.2"
|
||||
@@ -813,6 +993,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ignore-by-default@npm:^1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "ignore-by-default@npm:1.0.1"
|
||||
checksum: 10c0/9ab6e70e80f7cc12735def7ecb5527cfa56ab4e1152cd64d294522827f2dcf1f6d85531241537dc3713544e88dd888f65cb3c49c7b2cddb9009087c75274e533
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"imurmurhash@npm:^0.1.4":
|
||||
version: 0.1.4
|
||||
resolution: "imurmurhash@npm:0.1.4"
|
||||
@@ -851,6 +1038,22 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-binary-path@npm:~2.1.0":
|
||||
version: 2.1.0
|
||||
resolution: "is-binary-path@npm:2.1.0"
|
||||
dependencies:
|
||||
binary-extensions: "npm:^2.0.0"
|
||||
checksum: 10c0/a16eaee59ae2b315ba36fad5c5dcaf8e49c3e27318f8ab8fa3cdb8772bf559c8d1ba750a589c2ccb096113bb64497084361a25960899cb6172a6925ab6123d38
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-extglob@npm:^2.1.1":
|
||||
version: 2.1.1
|
||||
resolution: "is-extglob@npm:2.1.1"
|
||||
checksum: 10c0/5487da35691fbc339700bbb2730430b07777a3c21b9ebaecb3072512dfd7b4ba78ac2381a87e8d78d20ea08affb3f1971b4af629173a6bf435ff8a4c47747912
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-fullwidth-code-point@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "is-fullwidth-code-point@npm:3.0.0"
|
||||
@@ -858,6 +1061,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-glob@npm:^4.0.1, is-glob@npm:~4.0.1":
|
||||
version: 4.0.3
|
||||
resolution: "is-glob@npm:4.0.3"
|
||||
dependencies:
|
||||
is-extglob: "npm:^2.1.1"
|
||||
checksum: 10c0/17fb4014e22be3bbecea9b2e3a76e9e34ff645466be702f1693e8f1ee1adac84710d0be0bd9f967d6354036fd51ab7c2741d954d6e91dae6bb69714de92c197a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-lambda@npm:^1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "is-lambda@npm:1.0.1"
|
||||
@@ -865,6 +1077,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-number@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "is-number@npm:7.0.0"
|
||||
checksum: 10c0/b4686d0d3053146095ccd45346461bc8e53b80aeb7671cc52a4de02dbbf7dc0d1d2a986e2fe4ae206984b4d34ef37e8b795ebc4f4295c978373e6575e295d811
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"isarray@npm:^1.0.0, isarray@npm:~1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "isarray@npm:1.0.0"
|
||||
@@ -906,6 +1125,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lodash@npm:^4.17.21":
|
||||
version: 4.17.21
|
||||
resolution: "lodash@npm:4.17.21"
|
||||
checksum: 10c0/d8cbea072bb08655bb4c989da418994b073a608dffa608b09ac04b43a791b12aeae7cd7ad919aa4c925f33b48490b5cfe6c1f71d827956071dae2e7bb3a6b74c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0":
|
||||
version: 10.2.2
|
||||
resolution: "lru-cache@npm:10.2.2"
|
||||
@@ -977,6 +1203,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minimatch@npm:^3.1.2":
|
||||
version: 3.1.2
|
||||
resolution: "minimatch@npm:3.1.2"
|
||||
dependencies:
|
||||
brace-expansion: "npm:^1.1.7"
|
||||
checksum: 10c0/0262810a8fc2e72cca45d6fd86bd349eee435eb95ac6aa45c9ea2180e7ee875ef44c32b55b5973ceabe95ea12682f6e3725cbb63d7a2d1da3ae1163c8b210311
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minimatch@npm:^9.0.1":
|
||||
version: 9.0.4
|
||||
resolution: "minimatch@npm:9.0.4"
|
||||
@@ -1086,6 +1321,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ms@npm:^2.1.3":
|
||||
version: 2.1.3
|
||||
resolution: "ms@npm:2.1.3"
|
||||
checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"negotiator@npm:^0.6.3":
|
||||
version: 0.6.3
|
||||
resolution: "negotiator@npm:0.6.3"
|
||||
@@ -1144,6 +1386,26 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"nodemon@npm:^3.1.7":
|
||||
version: 3.1.7
|
||||
resolution: "nodemon@npm:3.1.7"
|
||||
dependencies:
|
||||
chokidar: "npm:^3.5.2"
|
||||
debug: "npm:^4"
|
||||
ignore-by-default: "npm:^1.0.1"
|
||||
minimatch: "npm:^3.1.2"
|
||||
pstree.remy: "npm:^1.1.8"
|
||||
semver: "npm:^7.5.3"
|
||||
simple-update-notifier: "npm:^2.0.0"
|
||||
supports-color: "npm:^5.5.0"
|
||||
touch: "npm:^3.1.0"
|
||||
undefsafe: "npm:^2.0.5"
|
||||
bin:
|
||||
nodemon: bin/nodemon.js
|
||||
checksum: 10c0/e0b46939abdbce251b1d6281005a5763cee57db295bb00bc4a753b0f5320dac00fe53547fb6764c70a086cf6d1238875cccb800fbc71544b3ecbd3ef71183c87
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"nopt@npm:^7.0.0":
|
||||
version: 7.2.1
|
||||
resolution: "nopt@npm:7.2.1"
|
||||
@@ -1155,6 +1417,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"normalize-path@npm:^3.0.0, normalize-path@npm:~3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "normalize-path@npm:3.0.0"
|
||||
checksum: 10c0/e008c8142bcc335b5e38cf0d63cfd39d6cf2d97480af9abdbe9a439221fd4d749763bab492a8ee708ce7a194bb00c9da6d0a115018672310850489137b3da046
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"object-inspect@npm:^1.13.1":
|
||||
version: 1.13.1
|
||||
resolution: "object-inspect@npm:1.13.1"
|
||||
@@ -1255,6 +1524,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1":
|
||||
version: 2.3.1
|
||||
resolution: "picomatch@npm:2.3.1"
|
||||
checksum: 10c0/26c02b8d06f03206fc2ab8d16f19960f2ff9e81a658f831ecb656d8f17d9edc799e8364b1f4a7873e89d9702dff96204be0fa26fe4181f6843f040f819dac4be
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"proc-log@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "proc-log@npm:3.0.0"
|
||||
@@ -1293,6 +1569,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"pstree.remy@npm:^1.1.8":
|
||||
version: 1.1.8
|
||||
resolution: "pstree.remy@npm:1.1.8"
|
||||
checksum: 10c0/30f78c88ce6393cb3f7834216cb6e282eb83c92ccb227430d4590298ab2811bc4a4745f850a27c5178e79a8f3e316591de0fec87abc19da648c2b3c6eb766d14
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"public-encrypt@npm:^4.0.0":
|
||||
version: 4.0.3
|
||||
resolution: "public-encrypt@npm:4.0.3"
|
||||
@@ -1375,6 +1658,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"readdirp@npm:~3.6.0":
|
||||
version: 3.6.0
|
||||
resolution: "readdirp@npm:3.6.0"
|
||||
dependencies:
|
||||
picomatch: "npm:^2.2.1"
|
||||
checksum: 10c0/6fa848cf63d1b82ab4e985f4cf72bd55b7dcfd8e0a376905804e48c3634b7e749170940ba77b32804d5fe93b3cc521aa95a8d7e7d725f830da6d93f3669ce66b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"readline-sync@npm:^1.4.7":
|
||||
version: 1.4.10
|
||||
resolution: "readline-sync@npm:1.4.10"
|
||||
@@ -1382,6 +1674,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"require-directory@npm:^2.1.1":
|
||||
version: 2.1.1
|
||||
resolution: "require-directory@npm:2.1.1"
|
||||
checksum: 10c0/83aa76a7bc1531f68d92c75a2ca2f54f1b01463cb566cf3fbc787d0de8be30c9dbc211d1d46be3497dac5785fe296f2dd11d531945ac29730643357978966e99
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"retry@npm:^0.12.0":
|
||||
version: 0.12.0
|
||||
resolution: "retry@npm:0.12.0"
|
||||
@@ -1399,6 +1698,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"rxjs@npm:^7.8.1":
|
||||
version: 7.8.1
|
||||
resolution: "rxjs@npm:7.8.1"
|
||||
dependencies:
|
||||
tslib: "npm:^2.1.0"
|
||||
checksum: 10c0/3c49c1ecd66170b175c9cacf5cef67f8914dcbc7cd0162855538d365c83fea631167cacb644b3ce533b2ea0e9a4d0b12175186985f89d75abe73dbd8f7f06f68
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.1, safe-buffer@npm:^5.1.2, safe-buffer@npm:^5.2.0, safe-buffer@npm:^5.2.1, safe-buffer@npm:~5.2.0":
|
||||
version: 5.2.1
|
||||
resolution: "safe-buffer@npm:5.2.1"
|
||||
@@ -1436,6 +1744,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"semver@npm:^7.5.3":
|
||||
version: 7.6.3
|
||||
resolution: "semver@npm:7.6.3"
|
||||
bin:
|
||||
semver: bin/semver.js
|
||||
checksum: 10c0/88f33e148b210c153873cb08cfe1e281d518aaa9a666d4d148add6560db5cd3c582f3a08ccb91f38d5f379ead256da9931234ed122057f40bb5766e65e58adaf
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"set-function-length@npm:^1.2.1":
|
||||
version: 1.2.2
|
||||
resolution: "set-function-length@npm:1.2.2"
|
||||
@@ -1476,9 +1793,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"shadow-cljs@npm:2.28.11":
|
||||
version: 2.28.11
|
||||
resolution: "shadow-cljs@npm:2.28.11"
|
||||
"shadow-cljs@npm:2.28.18":
|
||||
version: 2.28.18
|
||||
resolution: "shadow-cljs@npm:2.28.18"
|
||||
dependencies:
|
||||
node-libs-browser: "npm:^2.2.1"
|
||||
readline-sync: "npm:^1.4.7"
|
||||
@@ -1488,7 +1805,7 @@ __metadata:
|
||||
ws: "npm:^7.4.6"
|
||||
bin:
|
||||
shadow-cljs: cli/runner.js
|
||||
checksum: 10c0/c5c77d524ee8f44e4ae2ddc196af170d02405cc8731ea71f852c7b220fc1ba8aaf5cf33753fd8a7566c8749bb75d360f903dfb0d131bcdc6c2c33f44404bd6a3
|
||||
checksum: 10c0/4732cd11a5722644a0a91ae5560a55f62432ae5317bd2d1fd5bf12af8354c81776f4fcfce5c477b43af1ac2ecd4a216887337e1b92cca37a1b8cb9c157a393c1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1508,6 +1825,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"shell-quote@npm:^1.8.1":
|
||||
version: 1.8.1
|
||||
resolution: "shell-quote@npm:1.8.1"
|
||||
checksum: 10c0/8cec6fd827bad74d0a49347057d40dfea1e01f12a6123bf82c4649f3ef152fc2bc6d6176e6376bffcd205d9d0ccb4f1f9acae889384d20baff92186f01ea455a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"side-channel@npm:^1.0.6":
|
||||
version: 1.0.6
|
||||
resolution: "side-channel@npm:1.0.6"
|
||||
@@ -1527,6 +1851,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"simple-update-notifier@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "simple-update-notifier@npm:2.0.0"
|
||||
dependencies:
|
||||
semver: "npm:^7.5.3"
|
||||
checksum: 10c0/2a00bd03bfbcbf8a737c47ab230d7920f8bfb92d1159d421bdd194479f6d01ebc995d13fbe13d45dace23066a78a3dc6642999b4e3b38b847e6664191575b20c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"smart-buffer@npm:^4.2.0":
|
||||
version: 4.2.0
|
||||
resolution: "smart-buffer@npm:4.2.0"
|
||||
@@ -1627,7 +1960,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0":
|
||||
"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3":
|
||||
version: 4.2.3
|
||||
resolution: "string-width@npm:4.2.3"
|
||||
dependencies:
|
||||
@@ -1685,6 +2018,33 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"supports-color@npm:^5.5.0":
|
||||
version: 5.5.0
|
||||
resolution: "supports-color@npm:5.5.0"
|
||||
dependencies:
|
||||
has-flag: "npm:^3.0.0"
|
||||
checksum: 10c0/6ae5ff319bfbb021f8a86da8ea1f8db52fac8bd4d499492e30ec17095b58af11f0c55f8577390a749b1c4dde691b6a0315dab78f5f54c9b3d83f8fb5905c1c05
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"supports-color@npm:^7.1.0":
|
||||
version: 7.2.0
|
||||
resolution: "supports-color@npm:7.2.0"
|
||||
dependencies:
|
||||
has-flag: "npm:^4.0.0"
|
||||
checksum: 10c0/afb4c88521b8b136b5f5f95160c98dee7243dc79d5432db7efc27efb219385bbc7d9427398e43dd6cc730a0f87d5085ce1652af7efbe391327bc0a7d0f7fc124
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"supports-color@npm:^8.1.1":
|
||||
version: 8.1.1
|
||||
resolution: "supports-color@npm:8.1.1"
|
||||
dependencies:
|
||||
has-flag: "npm:^4.0.0"
|
||||
checksum: 10c0/ea1d3c275dd604c974670f63943ed9bd83623edc102430c05adb8efc56ba492746b6e95386e7831b872ec3807fd89dd8eb43f735195f37b5ec343e4234cc7e89
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tar@npm:^6.1.11, tar@npm:^6.1.2":
|
||||
version: 6.2.1
|
||||
resolution: "tar@npm:6.2.1"
|
||||
@@ -1715,6 +2075,40 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"to-regex-range@npm:^5.0.1":
|
||||
version: 5.0.1
|
||||
resolution: "to-regex-range@npm:5.0.1"
|
||||
dependencies:
|
||||
is-number: "npm:^7.0.0"
|
||||
checksum: 10c0/487988b0a19c654ff3e1961b87f471702e708fa8a8dd02a298ef16da7206692e8552a0250e8b3e8759270f62e9d8314616f6da274734d3b558b1fc7b7724e892
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"touch@npm:^3.1.0":
|
||||
version: 3.1.1
|
||||
resolution: "touch@npm:3.1.1"
|
||||
bin:
|
||||
nodetouch: bin/nodetouch.js
|
||||
checksum: 10c0/d2e4d269a42c846a22a29065b9af0b263de58effc85a1764bb7a2e8fc4b47700e9e2fcbd7eb1f5bffbb7c73d860f93600cef282b93ddac8f0b62321cb498b36e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tree-kill@npm:^1.2.2":
|
||||
version: 1.2.2
|
||||
resolution: "tree-kill@npm:1.2.2"
|
||||
bin:
|
||||
tree-kill: cli.js
|
||||
checksum: 10c0/7b1b7c7f17608a8f8d20a162e7957ac1ef6cd1636db1aba92f4e072dc31818c2ff0efac1e3d91064ede67ed5dc57c565420531a8134090a12ac10cf792ab14d2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tslib@npm:^2.1.0":
|
||||
version: 2.8.1
|
||||
resolution: "tslib@npm:2.8.1"
|
||||
checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tty-browserify@npm:0.0.0":
|
||||
version: 0.0.0
|
||||
resolution: "tty-browserify@npm:0.0.0"
|
||||
@@ -1722,6 +2116,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"undefsafe@npm:^2.0.5":
|
||||
version: 2.0.5
|
||||
resolution: "undefsafe@npm:2.0.5"
|
||||
checksum: 10c0/96c0466a5fbf395917974a921d5d4eee67bca4b30d3a31ce7e621e0228c479cf893e783a109af6e14329b52fe2f0cb4108665fad2b87b0018c0df6ac771261d5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"unique-filename@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "unique-filename@npm:3.0.0"
|
||||
@@ -1815,7 +2216,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "wrap-ansi@npm:7.0.0"
|
||||
dependencies:
|
||||
@@ -1874,9 +2275,38 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"y18n@npm:^5.0.5":
|
||||
version: 5.0.8
|
||||
resolution: "y18n@npm:5.0.8"
|
||||
checksum: 10c0/4df2842c36e468590c3691c894bc9cdbac41f520566e76e24f59401ba7d8b4811eb1e34524d57e54bc6d864bcb66baab7ffd9ca42bf1eda596618f9162b91249
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"yallist@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "yallist@npm:4.0.0"
|
||||
checksum: 10c0/2286b5e8dbfe22204ab66e2ef5cc9bbb1e55dfc873bbe0d568aa943eb255d131890dfd5bf243637273d31119b870f49c18fcde2c6ffbb7a7a092b870dc90625a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"yargs-parser@npm:^21.1.1":
|
||||
version: 21.1.1
|
||||
resolution: "yargs-parser@npm:21.1.1"
|
||||
checksum: 10c0/f84b5e48169479d2f402239c59f084cfd1c3acc197a05c59b98bab067452e6b3ea46d4dd8ba2985ba7b3d32a343d77df0debd6b343e5dae3da2aab2cdf5886b2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"yargs@npm:^17.7.2":
|
||||
version: 17.7.2
|
||||
resolution: "yargs@npm:17.7.2"
|
||||
dependencies:
|
||||
cliui: "npm:^8.0.1"
|
||||
escalade: "npm:^3.1.1"
|
||||
get-caller-file: "npm:^2.0.5"
|
||||
require-directory: "npm:^2.1.1"
|
||||
string-width: "npm:^4.2.3"
|
||||
y18n: "npm:^5.0.5"
|
||||
yargs-parser: "npm:^21.1.1"
|
||||
checksum: 10c0/ccd7e723e61ad5965fffbb791366db689572b80cca80e0f96aad968dfff4156cd7cd1ad18607afe1046d8241e6fb2d6c08bf7fa7bfb5eaec818735d8feac8f05
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -65,7 +65,7 @@ You will be able to share your plugin with the <a target="_blank" href="https://
|
||||
|
||||
### My plugin works on my local machine, but I couldn’t install it on Penpot. What could be the problem?
|
||||
|
||||
The url you that you need to provide in the plugin manager should look <a target="_blank" href="/plugins/create-a-plugin/#2.6.-step-6.-configure-the-manifest-file">like this</a>: <code class="language-bash">https:\/\/yourdomain.com/assents/manifest.json</code>
|
||||
The url you that you need to provide in the plugin manager should look <a target="_blank" href="/plugins/create-a-plugin/#2.6.-step-6.-configure-the-manifest-file">like this</a>: <code class="language-bash">https:\/\/yourdomain.com/assets/manifest.json</code>
|
||||
|
||||
### Where can I get support if I find a bug or an unexpected behavior?
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@ title: 1. Self-hosting Guide
|
||||
|
||||
# Self-hosting Guide
|
||||
|
||||
This guide explains how to get your own Penpot instance, running on a machine you control, to test it, use it by you or your team, or even customize and extend it any way you like.
|
||||
This guide explains how to get your own Penpot instance, running on a machine you control,
|
||||
to test it, use it by you or your team, or even customize and extend it any way you like.
|
||||
|
||||
If you need more context you can look at the <a
|
||||
href="https://community.penpot.app/t/self-hosting-penpot-i/2336" target="_blank">post
|
||||
@@ -14,13 +15,16 @@ about self-hosting</a> in Penpot community.
|
||||
href="https://design.penpot.app">our SaaS offer</a> for Penpot and your
|
||||
self-hosted Penpot platform!**
|
||||
|
||||
There are two main options for creating a Penpot instance:
|
||||
There are three main options for creating a Penpot instance:
|
||||
|
||||
1. Using the platform of our partner <a href="https://elest.io/open-source/penpot" target="_blank">Elestio</a>.
|
||||
2. Using <a href="https://docker.com" target="_blank">Docker</a> tool.
|
||||
3. Using <a href="https://kubernetes.io/" target="_blank">Kubernetes</a>.
|
||||
|
||||
<p class="advice">
|
||||
The recommended way is to use Elestio, since it's simpler, fully automatic and still greatly flexible. Use Docker if you already know the tool, if need full control of the process or have extra requirements and do not want to depend on any external provider, or need to do any special customization.
|
||||
The recommended way is to use Elestio, since it's simpler, fully automatic and still greatly flexible.
|
||||
Use Docker if you already know the tool, if need full control of the process or have extra requirements
|
||||
and do not want to depend on any external provider, or need to do any special customization.
|
||||
</p>
|
||||
|
||||
Or you can try <a href="#unofficial-self-host-options">other options</a>,
|
||||
@@ -261,7 +265,7 @@ itself.
|
||||
|
||||
This section details everything you need to know to get Penpot up and running in
|
||||
production environments using a Kubernetes cluster of your choice. To do this, we have
|
||||
created a <a href="https://helm.sh/" target="_blank">Helm<a> repository with everything
|
||||
created a <a href="https://helm.sh/" target="_blank">Helm</a> repository with everything
|
||||
you need.
|
||||
|
||||
Therefore, your prerequisite will be to have a Kubernetes cluster on which we can install
|
||||
@@ -287,7 +291,7 @@ in turn have its own release name.
|
||||
With these concepts in mind, we can now explain Helm like this:
|
||||
|
||||
> Helm installs charts into Kubernetes clusters, creating a new release for each
|
||||
> installation. And to find new charts, you can search Helm chart repositories.
|
||||
> installation. To find new charts, you can search Helm chart repositories.
|
||||
|
||||
|
||||
### Install Helm
|
||||
|
||||
@@ -20,6 +20,8 @@ machine.
|
||||
|
||||
* In the [Install with Docker][2] section, you can find the official Docker installation guide.
|
||||
|
||||
* In the [Install with Kubernetes][7] section, you can find the official Kubernetes installation guide.
|
||||
|
||||
* In the [Configuration][3] section, you can find all the customization options you can set up after installing.
|
||||
|
||||
* Or you can try other, not supported by Penpot, [Unofficial options][4].
|
||||
@@ -28,9 +30,11 @@ machine.
|
||||
|
||||
The [Integration Guide][5] explains how to connect Penpot with external apps, so they get notified
|
||||
when certain events occur and may create your own interconnections and collaboration features.
|
||||
|
||||
## Developing Penpot
|
||||
|
||||
Also, if you are a developer, you can get into the code, to explore it, learn how it is made, or extend it and contribute with new functionality. For this, we have a different Docker installation.
|
||||
Also, if you are a developer, you can get into the code, to explore it, learn how it is made,
|
||||
or extend it and contribute with new functionality. For this, we have a different Docker installation.
|
||||
In the [Developer Guide][6] you can find how to setup a development environment and many other dev-oriented documentation.
|
||||
|
||||
[1]: /technical-guide/getting-started/#install-with-elestio
|
||||
@@ -39,3 +43,4 @@ In the [Developer Guide][6] you can find how to setup a development environment
|
||||
[4]: /technical-guide/getting-started/#unofficial-self-host-options
|
||||
[5]: /technical-guide/integration/
|
||||
[6]: /technical-guide/developer/
|
||||
[7]: /technical-guide/getting-started/#install-with-kubernetes
|
||||
|
||||
@@ -43,7 +43,9 @@
|
||||
{:extra-paths ["dev"]
|
||||
:extra-deps
|
||||
{thheller/shadow-cljs {:mvn/version "2.28.18"}
|
||||
com.bhauman/rebel-readline {:mvn/version "RELEASE"}
|
||||
org.clojure/tools.namespace {:mvn/version "RELEASE"}
|
||||
criterium/criterium {:mvn/version "RELEASE"}
|
||||
cider/cider-nrepl {:mvn/version "0.48.0"}}}
|
||||
|
||||
:shadow-cljs
|
||||
|
||||
34
frontend/dev/user.clj
Normal file
34
frontend/dev/user.clj
Normal file
@@ -0,0 +1,34 @@
|
||||
;; 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 user
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.pprint :as pp]
|
||||
[clojure.java.io :as io]
|
||||
[clojure.tools.namespace.repl :as repl]
|
||||
[clojure.pprint :refer [pprint print-table]]
|
||||
[clojure.repl :refer :all]
|
||||
[clojure.walk :refer [macroexpand-all]]
|
||||
[criterium.core :as crit]))
|
||||
|
||||
;; --- Benchmarking Tools
|
||||
|
||||
(defmacro run-quick-bench
|
||||
[& exprs]
|
||||
`(crit/with-progress-reporting (crit/quick-bench (do ~@exprs) :verbose)))
|
||||
|
||||
(defmacro run-quick-bench'
|
||||
[& exprs]
|
||||
`(crit/quick-bench (do ~@exprs)))
|
||||
|
||||
(defmacro run-bench
|
||||
[& exprs]
|
||||
`(crit/with-progress-reporting (crit/bench (do ~@exprs) :verbose)))
|
||||
|
||||
(defmacro run-bench'
|
||||
[& exprs]
|
||||
`(crit/bench (do ~@exprs)))
|
||||
@@ -35,7 +35,7 @@
|
||||
"lint:scss:fix": "yarn run prettier -c resources/styles -c src/**/*.scss -w",
|
||||
"build:test": "clojure -M:dev:shadow-cljs compile test",
|
||||
"test": "yarn run build:test && node target/tests/test.js",
|
||||
"watch:test": "concurrently \"clojure -M:dev:shadow-cljs watch test\" \"nodemon -w target/tests/test.js --exec 'sleep 2 && node target/tests/test.js'\"",
|
||||
"watch:test": "mkdir -p target/tests && concurrently \"clojure -M:dev:shadow-cljs watch test\" \"nodemon -C -d 2 -w target/tests --exec 'node target/tests/test.js'\"",
|
||||
"test:e2e": "playwright test --project default",
|
||||
"translations": "node ./scripts/translations.js",
|
||||
"watch:app:assets": "node ./scripts/watch.js",
|
||||
@@ -100,7 +100,7 @@
|
||||
"@penpot/hljs": "portal:./vendor/hljs",
|
||||
"@penpot/mousetrap": "portal:./vendor/mousetrap",
|
||||
"@penpot/svgo": "penpot/svgo#c6fba7a4dcfbc27b643e7fc0c94fc98cf680b77b",
|
||||
"@penpot/text-editor": "penpot/penpot-text-editor#a100aad8d0efcbb070bed9144dbd2782547e78ba",
|
||||
"@penpot/text-editor": "portal:./text-editor",
|
||||
"@tokens-studio/sd-transforms": "^0.16.1",
|
||||
"compression": "^1.7.4",
|
||||
"date-fns": "^4.1.0",
|
||||
|
||||
@@ -58,10 +58,11 @@ test("Save and restore version", async ({ page }) => {
|
||||
await page.getByRole("textbox").press("Enter");
|
||||
|
||||
await page
|
||||
.locator("li")
|
||||
.filter({ hasText: "INIT" })
|
||||
.getByRole("button")
|
||||
.click();
|
||||
.getByLabel("History", { exact: true })
|
||||
.locator("div")
|
||||
.nth(3)
|
||||
.hover();
|
||||
await page.getByRole("button", { name: "Open version menu" }).click();
|
||||
await page.getByRole("button", { name: "Restore" }).click();
|
||||
|
||||
await workspacePage.mockRPC(
|
||||
|
||||
3
frontend/resources/images/icons/board-2.svg
Normal file
3
frontend/resources/images/icons/board-2.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M3.5 3.5h-2m2 0v-2m0 2h9m-9 0v9m9-9v-2m0 2h2m-2 0v9m0 0h2m-2 0v2m0-2h-9m0 0v2m0-2h-2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 214 B |
@@ -237,13 +237,18 @@ async function renderTemplate(path, context = {}, partials = {}) {
|
||||
return mustache.render(content, context, partials);
|
||||
}
|
||||
|
||||
const renderer = {
|
||||
link(href, title, text) {
|
||||
return `<a href="${href}" target="_blank">${text}</a>`;
|
||||
const extension = {
|
||||
useNewRenderer: true,
|
||||
renderer: {
|
||||
link(token) {
|
||||
const href = token.href;
|
||||
const text = token.text;
|
||||
return `<a href="${href}" target="_blank">${text}</a>`;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
marked.use({ renderer });
|
||||
marked.use(extension);
|
||||
|
||||
async function readTranslations() {
|
||||
const langs = [
|
||||
@@ -503,6 +508,7 @@ export async function compileStyles() {
|
||||
const start = process.hrtime();
|
||||
|
||||
log.info("init: compile styles");
|
||||
|
||||
let result = await compileSassAll(worker);
|
||||
result = concatSass(result);
|
||||
|
||||
|
||||
6
frontend/scripts/repl
Executable file
6
frontend/scripts/repl
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
export OPTIONS="-A:dev -J-XX:-OmitStackTraceInFastThrow";
|
||||
|
||||
set -ex
|
||||
exec clojure $OPTIONS -M -m rebel-readline.main
|
||||
@@ -24,15 +24,22 @@ async function compileSassAll() {
|
||||
async function compileSass(path) {
|
||||
const start = process.hrtime();
|
||||
log.info("changed:", path);
|
||||
const result = await h.compileSass(worker, path, { modules: true });
|
||||
sass.index[result.outputPath] = result.css;
|
||||
|
||||
const output = h.concatSass(sass);
|
||||
try {
|
||||
const result = await h.compileSass(worker, path, { modules: true });
|
||||
sass.index[result.outputPath] = result.css;
|
||||
|
||||
await fs.writeFile("./resources/public/css/main.css", output);
|
||||
const output = h.concatSass(sass);
|
||||
|
||||
const end = process.hrtime(start);
|
||||
log.info("done:", `(${ppt(end)})`);
|
||||
await fs.writeFile("./resources/public/css/main.css", output);
|
||||
|
||||
const end = process.hrtime(start);
|
||||
log.info("done:", `(${ppt(end)})`);
|
||||
} catch (cause) {
|
||||
console.error(cause);
|
||||
const end = process.hrtime(start);
|
||||
log.error("error:", `(${ppt(end)})`);
|
||||
}
|
||||
}
|
||||
|
||||
await fs.mkdir("./resources/public/css/", { recursive: true });
|
||||
|
||||
@@ -90,7 +90,6 @@
|
||||
"unknown"
|
||||
date)))
|
||||
|
||||
|
||||
;; --- Global Config Vars
|
||||
|
||||
(def default-theme "default")
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
[app.util.i18n :as i18n]
|
||||
[app.util.theme :as theme]
|
||||
[beicon.v2.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
[debug]
|
||||
[features]
|
||||
[potok.v2.core :as ptk]
|
||||
@@ -38,11 +39,11 @@
|
||||
(log/setup! {:app :info})
|
||||
|
||||
(when (= :browser cf/target)
|
||||
(log/info :message "Welcome to penpot"
|
||||
:version (:full cf/version)
|
||||
(log/info :version (:full cf/version)
|
||||
:asserts *assert*
|
||||
:build-date cf/build-date
|
||||
:public-uri (dm/str cf/public-uri)))
|
||||
:public-uri (dm/str cf/public-uri))
|
||||
(log/info :flags (str/join "," (map name cf/flags))))
|
||||
|
||||
(declare reinit)
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
[app.common.schema :as sm]
|
||||
[app.common.types.components-list :as ctkl]
|
||||
[app.common.types.team :as ctt]
|
||||
[app.config :as cf]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.notifications :as ntf]
|
||||
[app.main.features :as features]
|
||||
@@ -75,15 +74,13 @@
|
||||
(watch [_ _ _]
|
||||
(case code
|
||||
:upgrade-version
|
||||
(when (or (not= (:version params) (:full cf/version))
|
||||
(true? (:force params)))
|
||||
(rx/of (ntf/dialog
|
||||
:content (tr "notifications.by-code.upgrade-version")
|
||||
:controls :inline-actions
|
||||
:type :inline
|
||||
:level level
|
||||
:actions [{:label "Refresh" :callback force-reload!}]
|
||||
:tag :notification)))
|
||||
(rx/of (ntf/dialog
|
||||
:content (tr "notifications.by-code.upgrade-version")
|
||||
:controls :inline-actions
|
||||
:type :inline
|
||||
:level level
|
||||
:actions [{:label "Refresh" :callback force-reload!}]
|
||||
:tag :notification))
|
||||
|
||||
:maintenance
|
||||
(rx/of (ntf/dialog
|
||||
|
||||
@@ -450,7 +450,9 @@
|
||||
(ptk/reify ::update-team
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:teams id :name] name))
|
||||
(-> state
|
||||
(assoc-in [:teams id :name] name)
|
||||
(assoc-in [:team :name] name)))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
@@ -1118,6 +1120,9 @@
|
||||
(rx/of (rt/nav :dashboard-projects {:team-id team-id}))))))
|
||||
([team-id]
|
||||
(ptk/reify ::go-to-projects-1
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc state :current-team-id team-id))
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of (rt/nav :dashboard-projects {:team-id team-id}))))))
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
(ns app.main.data.exports.assets
|
||||
(:require
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.events :as ev]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.persistence :as dwp]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
@@ -247,6 +248,12 @@
|
||||
(rx/map #(clear-export-state @resource-id))
|
||||
(rx/take-until (rx/delay 6000 stopper))))))))
|
||||
|
||||
(defn request-export
|
||||
[{:keys [exports] :as params}]
|
||||
(if (= 1 (count exports))
|
||||
(request-simple-export (assoc params :export (first exports)))
|
||||
(request-multiple-export params)))
|
||||
|
||||
(defn retry-last-export
|
||||
[]
|
||||
(ptk/reify ::retry-last-export
|
||||
@@ -256,3 +263,16 @@
|
||||
(when (seq params)
|
||||
(rx/of (request-multiple-export params)))))))
|
||||
|
||||
(defn export-shapes-event
|
||||
[exports origin]
|
||||
(let [types (reduce (fn [counts {:keys [type]}]
|
||||
(if (#{:png :pdf :svg :jpeg} type)
|
||||
(update counts type inc)
|
||||
counts))
|
||||
{:png 0, :pdf 0, :svg 0, :jpeg 0}
|
||||
exports)]
|
||||
(ptk/event
|
||||
::ev/event (merge types
|
||||
{::ev/name "export-shapes"
|
||||
::ev/origin origin
|
||||
:num-shapes (count exports)}))))
|
||||
|
||||
@@ -199,18 +199,21 @@
|
||||
|
||||
;; Load libraries
|
||||
(->> (rp/cmd! :get-file-libraries {:file-id file-id})
|
||||
(rx/mapcat identity)
|
||||
(rx/merge-map
|
||||
(fn [{:keys [id synced-at]}]
|
||||
(->> (rp/cmd! :get-file {:id id :features features})
|
||||
(rx/map #(assoc % :synced-at synced-at)))))
|
||||
(rx/merge-map fpmap/resolve-file)
|
||||
(rx/merge-map
|
||||
(fn [{:keys [id] :as file}]
|
||||
(->> (rp/cmd! :get-file-object-thumbnails {:file-id id :tag "component"})
|
||||
(rx/map #(assoc file :thumbnails %)))))
|
||||
(rx/reduce conj [])
|
||||
(rx/map libraries-fetched)))
|
||||
(rx/mapcat (fn [libraries]
|
||||
(rx/merge
|
||||
(->> (rx/from libraries)
|
||||
(rx/merge-map
|
||||
(fn [{:keys [id synced-at]}]
|
||||
(->> (rp/cmd! :get-file {:id id :features features})
|
||||
(rx/map #(assoc % :synced-at synced-at)))))
|
||||
(rx/merge-map fpmap/resolve-file)
|
||||
(rx/reduce conj [])
|
||||
(rx/map libraries-fetched))
|
||||
(->> (rx/from libraries)
|
||||
(rx/map :id)
|
||||
(rx/mapcat (fn [file-id]
|
||||
(rp/cmd! :get-file-object-thumbnails {:file-id file-id :tag "component"})))
|
||||
(rx/map dwl/library-thumbnails-fetched)))))))
|
||||
|
||||
(rx/of (with-meta (workspace-initialized)
|
||||
{:file-id file-id})))
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.comments :as dcm]
|
||||
[app.main.data.events :as ev]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.common :as dwco]
|
||||
[app.main.data.workspace.drawing :as dwd]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
@@ -60,7 +61,11 @@
|
||||
(let [local (:comments-local state)]
|
||||
(cond
|
||||
(:draft local) (rx/of (dcm/close-thread))
|
||||
(:open local) (rx/of (dcm/close-thread)))))))
|
||||
(:open local) (rx/of (dcm/close-thread))
|
||||
|
||||
:else
|
||||
(rx/of (dw/clear-edition-mode)
|
||||
(dw/deselect-all true)))))))
|
||||
|
||||
;; Event responsible of the what should be executed when user clicked
|
||||
;; on the comments layer. An option can be create a new draft thread,
|
||||
|
||||
@@ -168,7 +168,7 @@
|
||||
objects (get page :objects)
|
||||
frame (cfh/get-root-frame objects (:id shape))
|
||||
|
||||
flows (get page :objects)
|
||||
flows (get page :flows)
|
||||
flow (ctp/get-frame-flow flows (:id frame))]
|
||||
(rx/concat
|
||||
(rx/of (dwsh/update-shapes
|
||||
@@ -177,14 +177,14 @@
|
||||
(let [new-interaction (-> ctsi/default-interaction
|
||||
(ctsi/set-destination destination)
|
||||
(assoc :position-relative-to (:id shape)))]
|
||||
(cls/add-new-interaction shape new-interaction))))
|
||||
(cls/add-new-interaction shape new-interaction)))))
|
||||
|
||||
(when destination
|
||||
(dwsh/update-shapes [destination] cls/show-in-viewer))
|
||||
(when destination
|
||||
(rx/of (dwsh/update-shapes [destination] cls/show-in-viewer)))
|
||||
|
||||
(when (and (not (connected-frame? objects (:id frame)))
|
||||
(nil? flow))
|
||||
(add-flow (:id frame))))))))))
|
||||
(when (and (not (connected-frame? objects (:id frame)))
|
||||
(nil? flow))
|
||||
(rx/of (add-flow (:id frame))))))))))
|
||||
|
||||
(defn remove-interaction
|
||||
([shape index]
|
||||
|
||||
@@ -881,11 +881,9 @@
|
||||
(rx/of
|
||||
(dwu/start-undo-transaction undo-id)
|
||||
(update-component shape-id undo-group)
|
||||
(sync-file current-file-id file-id :components (:component-id shape) undo-group)
|
||||
(update-component-thumbnail-sync state component-id file-id "frame")
|
||||
(update-component-thumbnail-sync state component-id file-id "component")
|
||||
(sync-file current-file-id file-id :components component-id undo-group)
|
||||
(when (not current-file?)
|
||||
(sync-file file-id file-id :components (:component-id shape) undo-group))
|
||||
(sync-file file-id file-id :components component-id undo-group))
|
||||
(dwu/commit-undo-transaction undo-id)))))))
|
||||
|
||||
(defn launch-component-sync
|
||||
@@ -937,9 +935,9 @@
|
||||
;; in the grid creating new rows/columns to make space
|
||||
(let [file (wsh/get-file state file-id)
|
||||
libraries (wsh/get-libraries state)
|
||||
page (wsh/lookup-page state)
|
||||
objects (wsh/lookup-page-objects state)
|
||||
parent (get objects (:parent-id shape))
|
||||
page (wsh/lookup-page state)
|
||||
objects (wsh/lookup-page-objects state)
|
||||
parent (get objects (:parent-id shape))
|
||||
|
||||
;; If the target parent is a grid layout we need to pass the target cell
|
||||
target-cell (when (ctl/grid-layout? parent)
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
[app.main.data.shortcuts :as ds]
|
||||
[app.main.data.workspace.texts :as dwt]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.features :as features]
|
||||
[app.main.fonts :as fonts]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
@@ -113,18 +114,24 @@
|
||||
|
||||
(defn calculate-text-values
|
||||
[shape]
|
||||
(let [state-map (deref refs/workspace-editor-state)
|
||||
editor-state (get state-map (:id shape))]
|
||||
(let [state-map (if (features/active-feature? @st/state "text-editor/v2")
|
||||
(deref refs/workspace-v2-editor-state)
|
||||
(deref refs/workspace-editor-state))
|
||||
editor-state (get state-map (:id shape))
|
||||
editor-instance (when (features/active-feature? @st/state "text-editor/v2")
|
||||
(deref refs/workspace-editor))]
|
||||
(d/merge
|
||||
(dwt/current-root-values
|
||||
{:shape shape
|
||||
:attrs txt/root-attrs})
|
||||
(dwt/current-paragraph-values
|
||||
{:editor-state editor-state
|
||||
:editor-instance editor-instance
|
||||
:shape shape
|
||||
:attrs txt/paragraph-attrs})
|
||||
(dwt/current-text-values
|
||||
{:editor-state editor-state
|
||||
:editor-instance editor-instance
|
||||
:shape shape
|
||||
:attrs txt/text-node-attrs}))))
|
||||
|
||||
|
||||
@@ -168,7 +168,7 @@
|
||||
(.error js/console cause)
|
||||
(rx/empty)))
|
||||
|
||||
(rx/tap #(l/trc :hint "thumbnail updated" :elapsed (dm/str (tp) "ms")))
|
||||
(rx/tap #(l/dbg :hint "thumbnail updated" :elapsed (dm/str (tp) "ms")))
|
||||
|
||||
;; We cancel all the stream if user starts editing while
|
||||
;; thumbnail is generating
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
(rx/of (ptk/event ::ev/event {::ev/name "rename-version"}))))))
|
||||
|
||||
(defn restore-version
|
||||
[project-id file-id id]
|
||||
[project-id file-id id origin]
|
||||
(dm/assert! (uuid? project-id))
|
||||
(dm/assert! (uuid? file-id))
|
||||
(dm/assert! (uuid? id))
|
||||
@@ -132,7 +132,17 @@
|
||||
(rx/take 1)
|
||||
(rx/mapcat #(rp/cmd! :restore-file-snapshot {:file-id file-id :id id}))
|
||||
(rx/map #(dw/initialize-file project-id file-id)))
|
||||
(rx/of (ptk/event ::ev/event {::ev/name "restore-version"}))))))
|
||||
(case origin
|
||||
:version
|
||||
(rx/of (ptk/event ::ev/event {::ev/name "restore-pin-version"}))
|
||||
|
||||
:snapshot
|
||||
(rx/of (ptk/event ::ev/event {::ev/name "restore-autosave"}))
|
||||
|
||||
:plugin
|
||||
(rx/of (ptk/event ::ev/event {::ev/name "restore-version-plugin"}))
|
||||
|
||||
(rx/empty))))))
|
||||
|
||||
(defn delete-version
|
||||
[file-id id]
|
||||
|
||||
@@ -33,10 +33,13 @@
|
||||
|
||||
(defn get-team-enabled-features
|
||||
[state]
|
||||
(-> global-enabled-features
|
||||
(set/union (:features-runtime state #{}))
|
||||
(set/intersection cfeat/no-migration-features)
|
||||
(set/union (:features-team state #{}))))
|
||||
(let [runtime-features (:features-runtime state #{})
|
||||
team-features (->> (:features-team state #{})
|
||||
(into #{} cfeat/xf-remove-ephimeral))]
|
||||
(-> global-enabled-features
|
||||
(set/union runtime-features)
|
||||
(set/intersection cfeat/no-migration-features)
|
||||
(set/union team-features))))
|
||||
|
||||
(def features-ref
|
||||
(l/derived get-team-enabled-features st/state =))
|
||||
@@ -124,9 +127,9 @@
|
||||
(let [features (get-team-enabled-features state)]
|
||||
(if (contains? features "render-wasm/v1")
|
||||
(render.wasm/initialize true)
|
||||
(render.wasm/initialize false)))
|
||||
(render.wasm/initialize false))
|
||||
|
||||
(log/trc :hint "initialized features"
|
||||
:team (str/join "," (:features-team state))
|
||||
:runtime (str/join "," (:features-runtime state)))))))
|
||||
(log/inf :hint "initialized"
|
||||
:enabled (str/join "," features)
|
||||
:runtime (str/join "," (:features-runtime state))))))))
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
[app.common.types.shape-tree :as ctt]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.config :as cf]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.workspace.tokens.token-set :as wtts]
|
||||
@@ -582,7 +583,8 @@
|
||||
[object-id]
|
||||
(l/derived
|
||||
(fn [state]
|
||||
(dm/get-in state [:workspace-thumbnails object-id]))
|
||||
(some-> (dm/get-in state [:workspace-thumbnails object-id])
|
||||
(cf/resolve-media)))
|
||||
st/state))
|
||||
|
||||
(def workspace-text-modifier
|
||||
|
||||
@@ -338,7 +338,7 @@
|
||||
;; used to render thumbnails on assets panel.
|
||||
(mf/defc component-svg
|
||||
{::mf/wrap [mf/memo #(mf/deferred % ts/idle-then-raf)]}
|
||||
[{:keys [objects root-shape show-grids? zoom class] :or {zoom 1} :as props}]
|
||||
[{:keys [objects root-shape show-grids? is-hidden zoom class] :or {zoom 1} :as props}]
|
||||
(when root-shape
|
||||
(let [root-shape-id (:id root-shape)
|
||||
include-metadata (mf/use-ctx export/include-metadata-ctx)
|
||||
@@ -381,13 +381,14 @@
|
||||
:xmlns:penpot (when include-metadata "https://penpot.app/xmlns")
|
||||
:fill "none"}
|
||||
|
||||
[:*
|
||||
[:> shape-container {:shape root-shape'}
|
||||
[:& (mf/provider muc/is-component?) {:value true}
|
||||
[:& root-shape-wrapper {:shape root-shape' :view-box vbox}]]]
|
||||
(when-not is-hidden
|
||||
[:*
|
||||
[:> shape-container {:shape root-shape'}
|
||||
[:& (mf/provider muc/is-component?) {:value true}
|
||||
[:& root-shape-wrapper {:shape root-shape' :view-box vbox}]]]
|
||||
|
||||
(when show-grids?
|
||||
[:& empty-grids {:root-shape-id root-shape-id :objects objects}])]])))
|
||||
(when show-grids?
|
||||
[:& empty-grids {:root-shape-id root-shape-id :objects objects}])])])))
|
||||
|
||||
(mf/defc component-svg-thumbnail
|
||||
{::mf/wrap [mf/memo #(mf/deferred % ts/idle-then-raf)]}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
[app.common.types.component :as ctk]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.config :as cf]
|
||||
[app.main.ui.icons :as i]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
@@ -31,7 +32,7 @@
|
||||
i/flex-grid
|
||||
|
||||
:else
|
||||
i/board)
|
||||
(if (cf/external-feature-flag "boards-01" "test") i/board-2 i/board))
|
||||
;; TODO -> THUMBNAIL ICON
|
||||
:image i/img
|
||||
:line (if (cts/has-images? shape) i/img i/path)
|
||||
@@ -56,7 +57,7 @@
|
||||
(if main-instance?
|
||||
i/component
|
||||
(case type
|
||||
:frame i/board
|
||||
:frame (if (cf/external-feature-flag "boards-01" "test") i/board-2 i/board)
|
||||
:image i/img
|
||||
:shape i/path
|
||||
:text i/text
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
}
|
||||
|
||||
.file-entry {
|
||||
display: flex;
|
||||
.file-name {
|
||||
@include flexRow;
|
||||
.file-icon {
|
||||
@@ -114,6 +115,8 @@
|
||||
}
|
||||
.error-message,
|
||||
.progress-message {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: $s-32;
|
||||
color: var(--modal-text-foreground-color);
|
||||
}
|
||||
|
||||
@@ -73,7 +73,9 @@
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :team-form}
|
||||
[{:keys [team] :as props}]
|
||||
(let [initial (mf/use-memo (fn [] (or team {})))
|
||||
(let [initial (mf/use-memo (fn []
|
||||
(or (some-> team (select-keys [:name :id]))
|
||||
{})))
|
||||
form (fm/use-form :schema schema:team-form
|
||||
:initial initial)
|
||||
handle-keydown
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
[app.common.colors :as clr]
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.main.data.events :as ev]
|
||||
[app.main.data.exports.assets :as de]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.refs :as refs]
|
||||
@@ -23,7 +22,6 @@
|
||||
[app.util.i18n :as i18n :refer [tr c]]
|
||||
[app.util.strings :as ust]
|
||||
[cuerdas.core :as str]
|
||||
[potok.v2.core :as ptk]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(def ^:private neutral-icon
|
||||
@@ -59,13 +57,8 @@
|
||||
(fn [event]
|
||||
(dom/prevent-default event)
|
||||
(st/emit! (modal/hide)
|
||||
(de/request-multiple-export
|
||||
{:exports enabled-exports
|
||||
:cmd cmd})
|
||||
(ptk/event
|
||||
::ev/event {::ev/name "export-shapes"
|
||||
::ev/origin origin
|
||||
:num-shapes (count enabled-exports)})))
|
||||
(de/request-multiple-export {:exports enabled-exports :cmd cmd})
|
||||
(de/export-shapes-event enabled-exports origin)))
|
||||
|
||||
on-toggle-enabled
|
||||
(mf/use-fn
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
(def ^:icon arrow (icon-xref :arrow))
|
||||
(def ^:icon asc-sort (icon-xref :asc-sort))
|
||||
(def ^:icon board (icon-xref :board))
|
||||
(def ^:icon board-2 (icon-xref :board-2))
|
||||
(def ^:icon boards-thumbnail (icon-xref :boards-thumbnail))
|
||||
(def ^:icon boolean-difference (icon-xref :boolean-difference))
|
||||
(def ^:icon boolean-exclude (icon-xref :boolean-exclude))
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
"The introduction of our brand new Plugin system allows you to access even richer ecosystem of capabilities."]
|
||||
|
||||
[:p {:class (stl/css :feature-content)}
|
||||
"We are beyond excitement about how this will further involve the Penpot community in building the best design and prototyping platform."]
|
||||
"We are beyond excited about how this will further involve the Penpot community in building the best design and prototyping platform."]
|
||||
|
||||
[:p {:class (stl/css :feature-content)}
|
||||
"Let’s dive in!"]]
|
||||
@@ -69,7 +69,7 @@
|
||||
"Penpot Plugins encourage developers to easily customize and expand the platform using standard web technologies like JavaScript, CSS, and HTML."]
|
||||
|
||||
[:p {:class (stl/css :feature-content)}
|
||||
"Find everything you need in ouor full comprehensive documentation to start building your plugins now!"]]
|
||||
"Find everything you need in our full comprehensive documentation to start building your plugins now!"]]
|
||||
|
||||
[:div {:class (stl/css :navigation)}
|
||||
[:& c/navigation-bullets
|
||||
@@ -101,7 +101,7 @@
|
||||
"Be sure to keep an eye on our evolving " [:a {:href "https://penpot.app/penpothub" :target "_blank"} "Penpot Hub"] " to pick the ones that are best suited to enhance your workflow."]
|
||||
|
||||
[:p {:class (stl/css :feature-content)}
|
||||
"This is just the beginning of a myriad of possibilities. Let’s build this community together <3."]]
|
||||
"This is just the beginning of a myriad of possibilities. Let’s build this community together ❤️."]]
|
||||
|
||||
[:div {:class (stl/css :navigation)}
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
bounds (mf/with-memo [bounds points]
|
||||
(or bounds (gsb/get-frame-bounds shape)))
|
||||
|
||||
thumb (:thumbnail shape)
|
||||
thumb (cf/resolve-media (:thumbnail-id shape))
|
||||
|
||||
debug? (dbg/enabled? :thumbnails)
|
||||
safari? (cf/check-browser? :safari)
|
||||
@@ -171,7 +171,7 @@
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [shape (unchecked-get props "shape")]
|
||||
(when ^boolean (:thumbnail shape)
|
||||
(when ^boolean (:thumbnail-id shape)
|
||||
[:> frame-container props
|
||||
[:> frame-thumbnail-image props]])))
|
||||
|
||||
|
||||
@@ -49,11 +49,16 @@
|
||||
|
||||
(defn generate-paragraph-styles
|
||||
[_shape data]
|
||||
(let [line-height (:line-height data 1.2)
|
||||
(let [line-height (:line-height data)
|
||||
line-height
|
||||
(if (and (some? line-height) (not= "" line-height))
|
||||
line-height
|
||||
(:line-height txt/default-text-attrs))
|
||||
|
||||
text-align (:text-align data "start")
|
||||
base #js {;; Fix a problem when exporting HTML
|
||||
:fontSize 0 ;;(str (:font-size data (:font-size txt/default-text-attrs)) "px")
|
||||
:lineHeight (:line-height data (:line-height txt/default-text-attrs))
|
||||
:lineHeight line-height
|
||||
:margin 0}]
|
||||
|
||||
(cond-> base
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.main.data.events :as ev]
|
||||
[app.main.data.exports.assets :as de]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
@@ -18,7 +17,6 @@
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [tr c]]
|
||||
[app.util.keyboard :as kbd]
|
||||
[potok.v2.core :as ptk]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc exports
|
||||
@@ -63,15 +61,9 @@
|
||||
:object-id (-> shapes first :id)}
|
||||
(cond-> share-id (assoc :share-id share-id)))
|
||||
exports (mapv #(merge % defaults) @exports)]
|
||||
(if (= 1 (count exports))
|
||||
(st/emit!
|
||||
(de/request-simple-export {:export (first exports)})
|
||||
(ptk/event
|
||||
::ev/event {::ev/name "export-shapes" ::ev/origin "viewer" :num-shapes 1}))
|
||||
(st/emit!
|
||||
(de/request-multiple-export {:exports exports})
|
||||
(ptk/event
|
||||
::ev/event {::ev/name "export-shapes" ::ev/origin "viewer" :num-shapes (count exports)}))))))
|
||||
(st/emit!
|
||||
(de/request-export {:exports exports})
|
||||
(de/export-shapes-event exports "viewer")))))
|
||||
|
||||
add-export
|
||||
(mf/use-callback
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.shortcuts :as sc]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.data.workspace.versions :as dwv]
|
||||
[app.main.features :as features]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
@@ -524,6 +525,32 @@
|
||||
(when (kbd/enter? event)
|
||||
(on-add-shared event))))
|
||||
|
||||
on-show-version-history
|
||||
(mf/use-fn
|
||||
(mf/deps file-id)
|
||||
(fn [_]
|
||||
(st/emit! (dw/toggle-layout-flag :document-history))))
|
||||
|
||||
on-show-version-history-key-down
|
||||
(mf/use-fn
|
||||
(mf/deps on-show-version-history)
|
||||
(fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(on-show-version-history event))))
|
||||
|
||||
on-pin-version
|
||||
(mf/use-fn
|
||||
(mf/deps file-id)
|
||||
(fn [_]
|
||||
(st/emit! (dwv/create-version file-id))))
|
||||
|
||||
on-pin-version-key-down
|
||||
(mf/use-fn
|
||||
(mf/deps on-pin-version)
|
||||
(fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(on-pin-version event))))
|
||||
|
||||
on-export-shapes
|
||||
(mf/use-fn #(st/emit! (de/show-workspace-export-dialog {:origin "workspace:menu"})))
|
||||
|
||||
@@ -575,14 +602,34 @@
|
||||
:on-click on-remove-shared
|
||||
:on-key-down on-remove-shared-key-down
|
||||
:id "file-menu-remove-shared"}
|
||||
[:span {:class (stl/css :item-name)} (tr "dashboard.unpublish-shared")]])
|
||||
[:span {:class (stl/css :item-name)}
|
||||
(tr "dashboard.unpublish-shared")]])
|
||||
|
||||
(when can-edit
|
||||
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
|
||||
:on-click on-add-shared
|
||||
:on-key-down on-add-shared-key-down
|
||||
:id "file-menu-add-shared"}
|
||||
[:span {:class (stl/css :item-name)} (tr "dashboard.add-shared")]]))
|
||||
[:span {:class (stl/css :item-name)}
|
||||
(tr "dashboard.add-shared")]]))
|
||||
|
||||
[:div {:class (stl/css :separator)}]
|
||||
|
||||
[:> 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"}
|
||||
[:span {:class (stl/css :item-name)}
|
||||
(tr "dashboard.create-version-menu")]]
|
||||
|
||||
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
|
||||
:on-click on-show-version-history
|
||||
: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")]]
|
||||
|
||||
[:div {:class (stl/css :separator)}]
|
||||
|
||||
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
|
||||
:on-click on-export-shapes
|
||||
|
||||
@@ -43,9 +43,12 @@
|
||||
}
|
||||
|
||||
.separator {
|
||||
margin-top: $s-8;
|
||||
border-top: $s-1 solid var(--color-background-quaternary);
|
||||
height: $s-4;
|
||||
border-top: $s-1 solid var(--color-background-secondary);
|
||||
left: calc(-1 * $s-4);
|
||||
margin-top: $s-8;
|
||||
position: relative;
|
||||
width: calc(100% + $s-8);
|
||||
}
|
||||
|
||||
.shortcut {
|
||||
|
||||
@@ -27,6 +27,13 @@
|
||||
[app.util.text.content.styles :as styles]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(defn- gen-name
|
||||
[editor]
|
||||
(when (some? editor)
|
||||
(let [editor-root (.-root editor)
|
||||
result (.-textContent editor-root)]
|
||||
(when (not= result "") result))))
|
||||
|
||||
(defn- initialize-event-handlers
|
||||
"Internal editor events handler initializer/destructor"
|
||||
[shape-id content selection-ref editor-ref container-ref]
|
||||
@@ -51,6 +58,8 @@
|
||||
instance
|
||||
(dwt/create-editor editor-node options)
|
||||
|
||||
update-name? (nil? content)
|
||||
|
||||
on-key-up
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
@@ -60,7 +69,7 @@
|
||||
on-blur
|
||||
(fn []
|
||||
(when-let [content (content/dom->cljs (dwt/get-editor-root instance))]
|
||||
(st/emit! (dwt/v2-update-text-shape-content shape-id content true)))
|
||||
(st/emit! (dwt/v2-update-text-shape-content shape-id content update-name? (gen-name instance))))
|
||||
|
||||
(let [container-node (mf/ref-val container-ref)]
|
||||
(dom/set-style! container-node "opacity" 0)))
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
[app.common.math :as mth]
|
||||
[app.common.text :as txt]
|
||||
[app.common.types.modifiers :as ctm]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.modifiers :as mdwm]
|
||||
[app.main.data.workspace.texts :as dwt]
|
||||
[app.main.fonts :as fonts]
|
||||
@@ -184,7 +185,7 @@
|
||||
(mf/use-fn
|
||||
(fn [shape node]
|
||||
;; Unique to indentify the pending state
|
||||
(let [uid (js/Symbol)]
|
||||
(let [uid (uuid/next)]
|
||||
(swap! pending-update* assoc uid (:id shape))
|
||||
(p/then
|
||||
(update-text-shape shape node)
|
||||
|
||||
@@ -237,15 +237,11 @@
|
||||
[:& comments-sidebar]
|
||||
|
||||
(true? is-history?)
|
||||
[:> tab-switcher* {:tabs #js [#js {:label "History" :id "history" :content versions-tab}
|
||||
#js {:label "Actions" :id "actions" :content history-tab}]
|
||||
:default-selected "history"
|
||||
;;:selected (name section)
|
||||
;;:on-change-tab on-tab-change
|
||||
:class (stl/css :left-sidebar-tabs)
|
||||
;;:action-button-position "start"
|
||||
;;:action-button (mf/html [:& collapse-button {:on-click handle-collapse}])
|
||||
}]
|
||||
[:> tab-switcher*
|
||||
{:tabs #js [#js {:label (tr "workspace.versions.tab.history") :id "history" :content versions-tab}
|
||||
#js {:label (tr "workspace.versions.tab.actions") :id "actions" :content history-tab}]
|
||||
:default-selected "history"
|
||||
:class (stl/css :left-sidebar-tabs)}]
|
||||
|
||||
:else
|
||||
[:> options-toolbox props])]]]))
|
||||
|
||||
@@ -113,4 +113,5 @@ $width-settings-bar-max: $s-500;
|
||||
.versions-tab {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
height: calc(100vh - $s-88);
|
||||
}
|
||||
|
||||
@@ -133,23 +133,23 @@
|
||||
|
||||
options
|
||||
[{:name (tr "workspace.assets.box-filter-all")
|
||||
:id "section-all"
|
||||
:id "all"
|
||||
:handler on-section-filter-change}
|
||||
{:name (tr "workspace.assets.components")
|
||||
:id "section-components"
|
||||
:id "components"
|
||||
:handler on-section-filter-change}
|
||||
|
||||
(when (not components-v2)
|
||||
{:name (tr "workspace.assets.graphics")
|
||||
:id "section-graphics"
|
||||
:id "graphics"
|
||||
:handler on-section-filter-change})
|
||||
|
||||
{:name (tr "workspace.assets.colors")
|
||||
:id "section-colors"
|
||||
:id "colors"
|
||||
:handler on-section-filter-change}
|
||||
|
||||
{:name (tr "workspace.assets.typography")
|
||||
:id "section-typographies"
|
||||
:id "typographies"
|
||||
:handler on-section-filter-change}]]
|
||||
|
||||
[:article {:class (stl/css :assets-bar)}
|
||||
|
||||
@@ -269,17 +269,19 @@
|
||||
|
||||
(mf/defc component-item-thumbnail
|
||||
"Component that renders the thumbnail image or the original SVG."
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [file-id root-shape component container class]}]
|
||||
(let [page-id (:main-instance-page component)
|
||||
root-id (:main-instance-id component)
|
||||
{::mf/props :obj}
|
||||
[{:keys [file-id root-shape component container class is-hidden]}]
|
||||
(let [page-id (:main-instance-page component)
|
||||
root-id (:main-instance-id component)
|
||||
retry (mf/use-state 0)
|
||||
|
||||
retry (mf/use-state 0)
|
||||
thumbnail-uri*
|
||||
(mf/with-memo [file-id page-id root-id]
|
||||
(let [object-id (thc/fmt-object-id file-id page-id root-id "component")]
|
||||
(refs/workspace-thumbnail-by-id object-id)))
|
||||
|
||||
thumbnail-uri* (mf/with-memo [file-id page-id root-id]
|
||||
(let [object-id (thc/fmt-object-id file-id page-id root-id "component")]
|
||||
(refs/workspace-thumbnail-by-id object-id)))
|
||||
thumbnail-uri (mf/deref thumbnail-uri*)
|
||||
thumbnail-uri
|
||||
(mf/deref thumbnail-uri*)
|
||||
|
||||
on-error
|
||||
(mf/use-fn
|
||||
@@ -288,7 +290,8 @@
|
||||
(when (< @retry 3)
|
||||
(inc retry))))]
|
||||
|
||||
(if (and (some? thumbnail-uri) (contains? cf/flags :component-thumbnails))
|
||||
(if (and (some? thumbnail-uri)
|
||||
(contains? cf/flags :component-thumbnails))
|
||||
[:& component-svg-thumbnail
|
||||
{:thumbnail-uri thumbnail-uri
|
||||
:class class
|
||||
@@ -301,7 +304,8 @@
|
||||
{:root-shape root-shape
|
||||
:class class
|
||||
:objects (:objects container)
|
||||
:show-grids? true}])))
|
||||
:show-grids? true
|
||||
:is-hidden is-hidden}])))
|
||||
|
||||
(defn generate-components-menu-entries
|
||||
[shapes components-v2]
|
||||
|
||||
@@ -57,15 +57,6 @@
|
||||
component)]
|
||||
[root-shape container])))
|
||||
|
||||
|
||||
;; NOTE: We don't schedule the thumbnail generation on idle right now
|
||||
;; until we can queue and handle thumbnail batching properly.
|
||||
#_(mf/with-effect []
|
||||
(when-not (some? thumbnail-uri)
|
||||
(tm/schedule-on-idle
|
||||
#(st/emit! (dwl/update-component-thumbnail (:id component) file-id)))))
|
||||
|
||||
|
||||
(mf/defc components-item
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [component renaming listing-thumbs? selected
|
||||
@@ -180,13 +171,13 @@
|
||||
(when ^boolean dragging?
|
||||
[:div {:class (stl/css :dragging)}])]
|
||||
|
||||
(when visible?
|
||||
[:& cmm/component-item-thumbnail {:file-id file-id
|
||||
:class (stl/css-case :thumbnail true
|
||||
:asset-list-thumbnail (not listing-thumbs?))
|
||||
:root-shape root-shape
|
||||
:component component
|
||||
:container container}])])]))
|
||||
[:& cmm/component-item-thumbnail {:file-id file-id
|
||||
:class (stl/css-case :thumbnail true
|
||||
:asset-list-thumbnail (not listing-thumbs?))
|
||||
:root-shape root-shape
|
||||
:component component
|
||||
:container container
|
||||
:is-hidden (not visible?)}]])]))
|
||||
|
||||
(mf/defc components-group
|
||||
{::mf/wrap-props false}
|
||||
|
||||
@@ -74,14 +74,13 @@
|
||||
|
||||
(mf/defc file-library-content
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [file local? open-status-ref on-clear-selection]}]
|
||||
[{:keys [file local? open-status-ref on-clear-selection filters]}]
|
||||
(let [components-v2 (mf/use-ctx ctx/components-v2)
|
||||
open-status (mf/deref open-status-ref)
|
||||
|
||||
file-id (:id file)
|
||||
project-id (:project-id file)
|
||||
|
||||
filters (mf/use-ctx cmm/assets-filters)
|
||||
filters-section (:section filters)
|
||||
|
||||
filters-term (:term filters)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.config :as cf]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
@@ -154,7 +155,7 @@
|
||||
:circle i/elipse
|
||||
:text i/text
|
||||
:path i/path
|
||||
:frame i/board
|
||||
:frame (if (cf/external-feature-flag "boards-01" "test") i/board-2 i/board)
|
||||
:group i/group
|
||||
:color i/drop-icon
|
||||
:typography i/text-palette
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
[app.common.files.helpers :as cfh]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
@@ -335,7 +336,7 @@
|
||||
:on-click add-filter}
|
||||
[:div {:class (stl/css :filter-menu-item-name-wrapper)}
|
||||
[:span {:class (stl/css :filter-menu-item-icon)}
|
||||
i/board]
|
||||
(if (cf/external-feature-flag "boards-01" "test") i/board-2 i/board)]
|
||||
[:span {:class (stl/css :filter-menu-item-name)}
|
||||
(tr "workspace.sidebar.layers.frames")]]
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.main.data.events :as ev]
|
||||
[app.main.data.exports.assets :as de]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
@@ -21,7 +20,6 @@
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [tr c]]
|
||||
[app.util.keyboard :as kbd]
|
||||
[potok.v2.core :as ptk]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(def exports-attrs
|
||||
@@ -66,21 +64,20 @@
|
||||
;; I can select multiple shapes all of them with no export settings and one of them with only one
|
||||
;; In that situation we must export it directly
|
||||
(if (and (= 1 (count shapes-with-exports)) (= 1 (-> shapes-with-exports first :exports count)))
|
||||
(let [shape (-> shapes-with-exports first)
|
||||
export (-> shape :exports first)
|
||||
sname (:name shape)
|
||||
suffix (:suffix export)
|
||||
defaults {:page-id page-id
|
||||
:file-id file-id
|
||||
:name sname
|
||||
:object-id (:id (first shapes-with-exports))}]
|
||||
(cond-> sname
|
||||
(some? suffix)
|
||||
(str suffix))
|
||||
(let [shape (-> shapes-with-exports first)
|
||||
export (-> shape :exports first)
|
||||
suffix (:suffix export)
|
||||
sname (cond-> (:name shape)
|
||||
(some? suffix)
|
||||
(str suffix))
|
||||
defaults {:page-id page-id
|
||||
:file-id file-id
|
||||
:name sname
|
||||
:object-id (:id (first shapes-with-exports))}
|
||||
full-export (merge export defaults)]
|
||||
(st/emit!
|
||||
(de/request-simple-export {:export (merge export defaults)})
|
||||
(ptk/event
|
||||
::ev/event {::ev/name "export-shapes" ::ev/origin "workspace:sidebar" :num-shapes 1})))
|
||||
(de/request-simple-export {:export full-export})
|
||||
(de/export-shapes-event [full-export] "workspace:sidebar")))
|
||||
(st/emit!
|
||||
(de/show-workspace-export-dialog {:selected (reverse ids) :origin "workspace:sidebar"})))
|
||||
|
||||
@@ -92,16 +89,11 @@
|
||||
:name sname
|
||||
:object-id (first ids)}
|
||||
exports (mapv #(merge % defaults) exports)]
|
||||
(if (= 1 (count exports))
|
||||
(let [export (first exports)]
|
||||
(st/emit!
|
||||
(de/request-simple-export {:export export})
|
||||
(ptk/event
|
||||
::ev/event {::ev/name "export-shapes" ::ev/origin "workspace:sidebar" :num-shapes 1})))
|
||||
(st/emit!
|
||||
(de/request-multiple-export {:exports exports})
|
||||
(ptk/event
|
||||
::ev/event {::ev/name "export-shapes" ::ev/origin "workspace:sidebar" :num-shapes (count exports)})))))))
|
||||
|
||||
(st/emit!
|
||||
(de/request-export {:exports exports})
|
||||
(de/export-shapes-event exports "workspace:sidebar"))))))
|
||||
|
||||
|
||||
;; TODO: maybe move to specific events for avoid to have this logic here?
|
||||
add-export
|
||||
|
||||
@@ -134,7 +134,8 @@
|
||||
time (dt/timeago (:created-at entry) {:locale locale})]
|
||||
[:span {:class (stl/css :date)} time])]]
|
||||
|
||||
[:> icon-button* {:variant "ghost"
|
||||
[:> icon-button* {:class (stl/css :version-entry-options)
|
||||
:variant "ghost"
|
||||
:aria-label (tr "workspace.versions.version-menu")
|
||||
:on-click handle-open-menu
|
||||
:icon "menu"}]]
|
||||
@@ -274,10 +275,11 @@
|
||||
(fn [id label]
|
||||
(st/emit! (dwv/rename-version file-id id label))))
|
||||
|
||||
|
||||
handle-restore-version
|
||||
(mf/use-fn
|
||||
(mf/deps project-id file-id)
|
||||
(fn [id]
|
||||
(fn [origin id]
|
||||
(st/emit!
|
||||
(ntf/dialog
|
||||
:content (tr "workspace.versions.restore-warning")
|
||||
@@ -287,9 +289,21 @@
|
||||
:callback #(st/emit! (ntf/hide))}
|
||||
{:label (tr "labels.restore")
|
||||
:type :primary
|
||||
:callback #(st/emit! (dwv/restore-version project-id file-id id))}]
|
||||
:callback #(st/emit! (dwv/restore-version project-id file-id id origin))}]
|
||||
:tag :restore-dialog))))
|
||||
|
||||
handle-restore-version-pinned
|
||||
(mf/use-fn
|
||||
(mf/deps handle-restore-version)
|
||||
(fn [id]
|
||||
(handle-restore-version :version id)))
|
||||
|
||||
handle-restore-version-snapshot
|
||||
(mf/use-fn
|
||||
(mf/deps handle-restore-version)
|
||||
(fn [id]
|
||||
(handle-restore-version :snapshot id)))
|
||||
|
||||
handle-delete-version
|
||||
(mf/use-fn
|
||||
(mf/deps file-id)
|
||||
@@ -362,7 +376,7 @@
|
||||
:editing? (= (:id entry) editing)
|
||||
:profile (get users (:profile-id entry))
|
||||
:on-rename-version handle-rename-version
|
||||
:on-restore-version handle-restore-version
|
||||
:on-restore-version handle-restore-version-pinned
|
||||
:on-delete-version handle-delete-version}]
|
||||
|
||||
:snapshot
|
||||
@@ -371,7 +385,7 @@
|
||||
:entry entry
|
||||
:is-expanded (contains? @expanded idx-entry)
|
||||
:on-toggle-expand handle-toggle-expand
|
||||
:on-restore-snapshot handle-restore-version
|
||||
:on-restore-snapshot handle-restore-version-snapshot
|
||||
:on-pin-snapshot handle-pin-version}]
|
||||
|
||||
nil))])])]))
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
|
||||
.version-toolbox {
|
||||
padding: $s-8;
|
||||
height: 100%;
|
||||
display: grid;
|
||||
overflow: hidden;
|
||||
grid-template-rows: auto auto 1fr;
|
||||
}
|
||||
|
||||
.versions-entry-empty {
|
||||
@@ -49,6 +53,8 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $s-6;
|
||||
overflow: auto;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.version-entry {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.media :as cm]
|
||||
[app.config :as cf]
|
||||
[app.main.data.events :as ev]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.workspace :as dw]
|
||||
@@ -146,7 +147,7 @@
|
||||
:on-click select-drawtool
|
||||
:data-tool "frame"
|
||||
:data-testid "artboard-btn"}
|
||||
i/board]]
|
||||
(if (cf/external-feature-flag "boards-01" "test") i/board-2 i/board)]]
|
||||
[:li
|
||||
[:button
|
||||
{:title (tr "workspace.toolbar.rect" (sc/get-tooltip :draw-rect))
|
||||
|
||||
@@ -171,6 +171,7 @@
|
||||
(and (some? drawing-obj) (= :path (:type drawing-obj))))
|
||||
node-editing? (and edition (= :path (get-in base-objects [edition :type])))
|
||||
text-editing? (and edition (= :text (get-in base-objects [edition :type])))
|
||||
|
||||
grid-editing? (and edition (ctl/grid-layout? base-objects edition))
|
||||
|
||||
mode-inspect? (= options-mode :inspect)
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
[app.common.files.changes-builder :as cb]
|
||||
[app.common.files.helpers :as cfh]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.record :as cr]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.text :as txt]
|
||||
[app.common.types.color :as ctc]
|
||||
@@ -59,406 +58,432 @@
|
||||
(st/emit! (ch/commit-changes changes))
|
||||
(shape/shape-proxy plugin-id (:id shape))))
|
||||
|
||||
(deftype PenpotContext [$plugin]
|
||||
Object
|
||||
(addListener
|
||||
[_ type callback props]
|
||||
(events/add-listener type $plugin callback props))
|
||||
(defn create-context
|
||||
[plugin-id]
|
||||
(obj/reify {:name "PenpotContext"}
|
||||
;; Private properties
|
||||
:$plugin {:enumerable false :get (fn [] plugin-id)}
|
||||
|
||||
(removeListener
|
||||
[_ listener-id]
|
||||
(events/remove-listener listener-id))
|
||||
;; Public properties
|
||||
:root
|
||||
{:this true
|
||||
:get #(.getRoot ^js %)}
|
||||
|
||||
(getViewport
|
||||
[_]
|
||||
(viewport/viewport-proxy $plugin))
|
||||
:currentFile
|
||||
{:this true
|
||||
:get #(.getFile ^js %)}
|
||||
|
||||
(getFile
|
||||
[_]
|
||||
(when (some? (:current-file-id @st/state))
|
||||
(file/file-proxy $plugin (:current-file-id @st/state))))
|
||||
:currentPage
|
||||
{:this true
|
||||
:get #(.getPage ^js %)}
|
||||
|
||||
(getPage
|
||||
[_]
|
||||
(let [file-id (:current-file-id @st/state)
|
||||
page-id (:current-page-id @st/state)]
|
||||
(when (and (some? file-id) (some? page-id))
|
||||
(page/page-proxy $plugin file-id page-id))))
|
||||
:theme
|
||||
{:this true
|
||||
:get #(.getTheme ^js %)}
|
||||
|
||||
(getSelectedShapes
|
||||
[_]
|
||||
(let [selection (get-in @st/state [:workspace-local :selected])]
|
||||
(apply array (sequence (map (partial shape/shape-proxy $plugin)) selection))))
|
||||
:selection
|
||||
{:this true
|
||||
:get #(.getSelectedShapes ^js %)
|
||||
:set
|
||||
(fn [_ shapes]
|
||||
(cond
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :selection shapes)
|
||||
|
||||
(shapesColors
|
||||
[_ shapes]
|
||||
(cond
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :shapesColors-shapes shapes)
|
||||
:else
|
||||
(let [ids (into (d/ordered-set) (map #(obj/get % "$id")) shapes)]
|
||||
(st/emit! (dws/select-shapes ids)))))}
|
||||
|
||||
:else
|
||||
(let [objects (u/locate-objects)
|
||||
shapes (->> shapes
|
||||
(map #(obj/get % "$id"))
|
||||
(mapcat #(cfh/get-children-with-self objects %)))
|
||||
file-id (:current-file-id @st/state)
|
||||
shared-libs (:workspace-libraries @st/state)]
|
||||
:viewport
|
||||
{:this true
|
||||
:get #(.getViewport ^js %)}
|
||||
|
||||
(->> (ctc/extract-all-colors shapes file-id shared-libs)
|
||||
(group-by :attrs)
|
||||
(format/format-array format/format-color-result)))))
|
||||
:currentUser
|
||||
{:this true
|
||||
:get #(.getCurrentUser ^js %)}
|
||||
|
||||
(replaceColor
|
||||
[_ shapes old-color new-color]
|
||||
:activeUsers
|
||||
{:this true
|
||||
:get #(.getActiveUsers ^js %)}
|
||||
|
||||
(let [old-color (parser/parse-color old-color)
|
||||
new-color (parser/parse-color new-color)]
|
||||
:fonts
|
||||
{:get (fn [] (fonts/fonts-subcontext plugin-id))}
|
||||
|
||||
:library
|
||||
{:get (fn [] (library/library-subcontext plugin-id))}
|
||||
|
||||
:history
|
||||
{:get (fn [] (history/history-subcontext plugin-id))}
|
||||
|
||||
;; Methods
|
||||
|
||||
:addListener
|
||||
(fn [type callback props]
|
||||
(events/add-listener type plugin-id callback props))
|
||||
|
||||
:removeListener
|
||||
(fn [listener-id]
|
||||
(events/remove-listener listener-id))
|
||||
|
||||
:getViewport
|
||||
(fn []
|
||||
(viewport/viewport-proxy plugin-id))
|
||||
|
||||
:getFile
|
||||
(fn []
|
||||
(when (some? (:current-file-id @st/state))
|
||||
(file/file-proxy plugin-id (:current-file-id @st/state))))
|
||||
|
||||
:getPage
|
||||
(fn []
|
||||
(let [file-id (:current-file-id @st/state)
|
||||
page-id (:current-page-id @st/state)]
|
||||
(when (and (some? file-id) (some? page-id))
|
||||
(page/page-proxy plugin-id file-id page-id))))
|
||||
|
||||
:getSelectedShapes
|
||||
(fn []
|
||||
(let [selection (get-in @st/state [:workspace-local :selected])]
|
||||
(apply array (sequence (map (partial shape/shape-proxy plugin-id)) selection))))
|
||||
|
||||
:shapesColors
|
||||
(fn [shapes]
|
||||
(cond
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :replaceColor-shapes shapes)
|
||||
|
||||
(not (sm/validate ::ctc/color old-color))
|
||||
(u/display-not-valid :replaceColor-oldColor old-color)
|
||||
|
||||
(not (sm/validate ::ctc/color new-color))
|
||||
(u/display-not-valid :replaceColor-newColor new-color)
|
||||
(u/display-not-valid :shapesColors-shapes shapes)
|
||||
|
||||
:else
|
||||
(let [file-id (:current-file-id @st/state)
|
||||
shared-libs (:workspace-libraries @st/state)
|
||||
objects (u/locate-objects)
|
||||
shapes
|
||||
(->> shapes
|
||||
(map #(obj/get % "$id"))
|
||||
(mapcat #(cfh/get-children-with-self objects %)))
|
||||
(let [objects (u/locate-objects)
|
||||
shapes (->> shapes
|
||||
(map #(obj/get % "$id"))
|
||||
(mapcat #(cfh/get-children-with-self objects %)))
|
||||
file-id (:current-file-id @st/state)
|
||||
shared-libs (:workspace-libraries @st/state)]
|
||||
|
||||
shapes-by-color
|
||||
(->> (ctc/extract-all-colors shapes file-id shared-libs)
|
||||
(group-by :attrs))]
|
||||
(st/emit! (dwc/change-color-in-selected new-color (get shapes-by-color old-color) old-color))))))
|
||||
(->> (ctc/extract-all-colors shapes file-id shared-libs)
|
||||
(group-by :attrs)
|
||||
(format/format-array format/format-color-result)))))
|
||||
|
||||
(getRoot
|
||||
[_]
|
||||
(when (and (some? (:current-file-id @st/state))
|
||||
(some? (:current-page-id @st/state)))
|
||||
(shape/shape-proxy $plugin uuid/zero)))
|
||||
:replaceColor
|
||||
(fn [shapes old-color new-color]
|
||||
(let [old-color (parser/parse-color old-color)
|
||||
new-color (parser/parse-color new-color)]
|
||||
(cond
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :replaceColor-shapes shapes)
|
||||
|
||||
(getTheme
|
||||
[_]
|
||||
(let [theme (get-in @st/state [:profile :theme])]
|
||||
(if (or (not theme) (= theme "default"))
|
||||
"dark"
|
||||
(get-in @st/state [:profile :theme]))))
|
||||
(not (sm/validate ::ctc/color old-color))
|
||||
(u/display-not-valid :replaceColor-oldColor old-color)
|
||||
|
||||
(getCurrentUser
|
||||
[_]
|
||||
(user/current-user-proxy $plugin (:session-id @st/state)))
|
||||
(not (sm/validate ::ctc/color new-color))
|
||||
(u/display-not-valid :replaceColor-newColor new-color)
|
||||
|
||||
(getActiveUsers
|
||||
[_]
|
||||
(apply array
|
||||
(->> (:workspace-presence @st/state)
|
||||
(vals)
|
||||
(remove #(= (:id %) (:session-id @st/state)))
|
||||
(map #(user/active-user-proxy $plugin (:id %))))))
|
||||
:else
|
||||
(let [file-id (:current-file-id @st/state)
|
||||
shared-libs (:workspace-libraries @st/state)
|
||||
objects (u/locate-objects)
|
||||
shapes
|
||||
(->> shapes
|
||||
(map #(obj/get % "$id"))
|
||||
(mapcat #(cfh/get-children-with-self objects %)))
|
||||
|
||||
(uploadMediaUrl
|
||||
[_ name url]
|
||||
(cond
|
||||
(not (string? name))
|
||||
(u/display-not-valid :uploadMedia-name name)
|
||||
shapes-by-color
|
||||
(->> (ctc/extract-all-colors shapes file-id shared-libs)
|
||||
(group-by :attrs))]
|
||||
(st/emit! (dwc/change-color-in-selected new-color (get shapes-by-color old-color) old-color))))))
|
||||
|
||||
(not (string? url))
|
||||
(u/display-not-valid :uploadMedia-url url)
|
||||
:getRoot
|
||||
(fn []
|
||||
(when (and (some? (:current-file-id @st/state))
|
||||
(some? (:current-page-id @st/state)))
|
||||
(shape/shape-proxy plugin-id uuid/zero)))
|
||||
|
||||
:else
|
||||
:getTheme
|
||||
(fn []
|
||||
(let [theme (get-in @st/state [:profile :theme])]
|
||||
(if (or (not theme) (= theme "default"))
|
||||
"dark"
|
||||
(get-in @st/state [:profile :theme]))))
|
||||
|
||||
:getCurrentUser
|
||||
(fn []
|
||||
(user/current-user-proxy plugin-id (:session-id @st/state)))
|
||||
|
||||
:getActiveUsers
|
||||
(fn []
|
||||
(apply array
|
||||
(->> (:workspace-presence @st/state)
|
||||
(vals)
|
||||
(remove #(= (:id %) (:session-id @st/state)))
|
||||
(map #(user/active-user-proxy plugin-id (:id %))))))
|
||||
|
||||
:uploadMediaUrl
|
||||
(fn [name url]
|
||||
(cond
|
||||
(not (string? name))
|
||||
(u/display-not-valid :uploadMedia-name name)
|
||||
|
||||
(not (string? url))
|
||||
(u/display-not-valid :uploadMedia-url url)
|
||||
|
||||
:else
|
||||
(let [file-id (:current-file-id @st/state)]
|
||||
(js/Promise.
|
||||
(fn [resolve reject]
|
||||
(->> (dwm/upload-media-url name file-id url)
|
||||
(rx/take 1)
|
||||
(rx/map format/format-image)
|
||||
(rx/subs! resolve reject)))))))
|
||||
|
||||
:uploadMediaData
|
||||
(fn [name data mime-type]
|
||||
(let [file-id (:current-file-id @st/state)]
|
||||
(js/Promise.
|
||||
(fn [resolve reject]
|
||||
(->> (dwm/upload-media-url name file-id url)
|
||||
(->> (dwm/process-blobs
|
||||
{:file-id file-id
|
||||
:local? false
|
||||
:name name
|
||||
:blobs [(js/Blob. #js [data] #js {:type mime-type})]
|
||||
:on-image identity
|
||||
:on-svg identity})
|
||||
(rx/take 1)
|
||||
(rx/map format/format-image)
|
||||
(rx/subs! resolve reject)))))))
|
||||
(rx/subs! resolve reject))))))
|
||||
|
||||
(uploadMediaData
|
||||
[_ name data mime-type]
|
||||
(let [file-id (:current-file-id @st/state)]
|
||||
(js/Promise.
|
||||
(fn [resolve reject]
|
||||
(->> (dwm/process-blobs
|
||||
{:file-id file-id
|
||||
:local? false
|
||||
:name name
|
||||
:blobs [(js/Blob. #js [data] #js {:type mime-type})]
|
||||
:on-image identity
|
||||
:on-svg identity})
|
||||
(rx/take 1)
|
||||
(rx/map format/format-image)
|
||||
(rx/subs! resolve reject))))))
|
||||
:group
|
||||
(fn [shapes]
|
||||
(cond
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :group-shapes shapes)
|
||||
|
||||
(group
|
||||
[_ shapes]
|
||||
(cond
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :group-shapes shapes)
|
||||
:else
|
||||
(let [file-id (:current-file-id @st/state)
|
||||
page-id (:current-page-id @st/state)
|
||||
id (uuid/next)
|
||||
ids (into #{} (map #(obj/get % "$id")) shapes)]
|
||||
(st/emit! (dwg/group-shapes id ids))
|
||||
(shape/shape-proxy plugin-id file-id page-id id))))
|
||||
|
||||
:else
|
||||
(let [file-id (:current-file-id @st/state)
|
||||
page-id (:current-page-id @st/state)
|
||||
id (uuid/next)
|
||||
ids (into #{} (map #(obj/get % "$id")) shapes)]
|
||||
(st/emit! (dwg/group-shapes id ids))
|
||||
(shape/shape-proxy $plugin file-id page-id id))))
|
||||
:ungroup
|
||||
(fn [group & rest]
|
||||
(cond
|
||||
(not (shape/shape-proxy? group))
|
||||
(u/display-not-valid :ungroup group)
|
||||
|
||||
(ungroup
|
||||
[_ group & rest]
|
||||
(and (some? rest) (not (every? shape/shape-proxy? rest)))
|
||||
(u/display-not-valid :ungroup rest)
|
||||
|
||||
(cond
|
||||
(not (shape/shape-proxy? group))
|
||||
(u/display-not-valid :ungroup group)
|
||||
:else
|
||||
(let [shapes (concat [group] rest)
|
||||
ids (into #{} (map #(obj/get % "$id")) shapes)]
|
||||
(st/emit! (dwg/ungroup-shapes ids)))))
|
||||
|
||||
(and (some? rest) (not (every? shape/shape-proxy? rest)))
|
||||
(u/display-not-valid :ungroup rest)
|
||||
:createBoard
|
||||
(fn []
|
||||
(create-shape plugin-id :frame))
|
||||
|
||||
:else
|
||||
(let [shapes (concat [group] rest)
|
||||
ids (into #{} (map #(obj/get % "$id")) shapes)]
|
||||
(st/emit! (dwg/ungroup-shapes ids)))))
|
||||
:createRectangle
|
||||
(fn []
|
||||
(create-shape plugin-id :rect))
|
||||
|
||||
(createBoard
|
||||
[_]
|
||||
(create-shape $plugin :frame))
|
||||
:createEllipse
|
||||
(fn []
|
||||
(create-shape plugin-id :circle))
|
||||
|
||||
(createRectangle
|
||||
[_]
|
||||
(create-shape $plugin :rect))
|
||||
|
||||
(createEllipse
|
||||
[_]
|
||||
(create-shape $plugin :circle))
|
||||
|
||||
(createPath
|
||||
[_]
|
||||
(let [page-id (:current-page-id @st/state)
|
||||
page (dm/get-in @st/state [:workspace-data :pages-index page-id])
|
||||
shape (cts/setup-shape
|
||||
{:type :path
|
||||
:content [{:command :move-to :params {:x 0 :y 0}}
|
||||
{:command :line-to :params {:x 100 :y 100}}]})
|
||||
changes
|
||||
(-> (cb/empty-changes)
|
||||
(cb/with-page page)
|
||||
(cb/with-objects (:objects page))
|
||||
(cb/add-object shape))]
|
||||
(st/emit! (ch/commit-changes changes))
|
||||
(shape/shape-proxy $plugin (:id shape))))
|
||||
|
||||
(createText
|
||||
[_ text]
|
||||
(cond
|
||||
(or (not (string? text)) (empty? text))
|
||||
(u/display-not-valid :createText text)
|
||||
|
||||
:else
|
||||
(let [file-id (:current-file-id @st/state)
|
||||
page-id (:current-page-id @st/state)
|
||||
:createPath
|
||||
(fn []
|
||||
(let [page-id (:current-page-id @st/state)
|
||||
page (dm/get-in @st/state [:workspace-data :pages-index page-id])
|
||||
shape (-> (cts/setup-shape {:type :text :x 0 :y 0 :grow-type :auto-width})
|
||||
(txt/change-text text)
|
||||
(assoc :position-data nil))
|
||||
shape (cts/setup-shape
|
||||
{:type :path
|
||||
:content [{:command :move-to :params {:x 0 :y 0}}
|
||||
{:command :line-to :params {:x 100 :y 100}}]})
|
||||
changes
|
||||
(-> (cb/empty-changes)
|
||||
(cb/with-page page)
|
||||
(cb/with-objects (:objects page))
|
||||
(cb/add-object shape))]
|
||||
(st/emit! (ch/commit-changes changes))
|
||||
(shape/shape-proxy $plugin file-id page-id (:id shape)))))
|
||||
(shape/shape-proxy plugin-id (:id shape))))
|
||||
|
||||
(createShapeFromSvg
|
||||
[_ svg-string]
|
||||
(cond
|
||||
(or (not (string? svg-string)) (empty? svg-string))
|
||||
(u/display-not-valid :createShapeFromSvg svg-string)
|
||||
|
||||
:else
|
||||
(let [id (uuid/next)
|
||||
file-id (:current-file-id @st/state)
|
||||
page-id (:current-page-id @st/state)]
|
||||
(st/emit! (dwm/create-svg-shape id "svg" svg-string (gpt/point 0 0)))
|
||||
(shape/shape-proxy $plugin file-id page-id id))))
|
||||
|
||||
(createBoolean [_ bool-type shapes]
|
||||
(let [bool-type (keyword bool-type)]
|
||||
:createText
|
||||
(fn [text]
|
||||
(cond
|
||||
(not (contains? cts/bool-types bool-type))
|
||||
(u/display-not-valid :createBoolean-boolType bool-type)
|
||||
|
||||
(or (not (array? shapes)) (empty? shapes) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :createBoolean-shapes shapes)
|
||||
(or (not (string? text)) (empty? text))
|
||||
(u/display-not-valid :createText text)
|
||||
|
||||
:else
|
||||
(let [ids (into #{} (map #(obj/get % "$id")) shapes)
|
||||
id-ret (atom nil)]
|
||||
(st/emit! (dwb/create-bool bool-type ids {:id-ret id-ret}))
|
||||
(shape/shape-proxy $plugin @id-ret)))))
|
||||
(let [file-id (:current-file-id @st/state)
|
||||
page-id (:current-page-id @st/state)
|
||||
page (dm/get-in @st/state [:workspace-data :pages-index page-id])
|
||||
shape (-> (cts/setup-shape {:type :text :x 0 :y 0 :grow-type :auto-width})
|
||||
(txt/change-text text)
|
||||
(assoc :position-data nil))
|
||||
changes
|
||||
(-> (cb/empty-changes)
|
||||
(cb/with-page page)
|
||||
(cb/with-objects (:objects page))
|
||||
(cb/add-object shape))]
|
||||
(st/emit! (ch/commit-changes changes))
|
||||
(shape/shape-proxy plugin-id file-id page-id (:id shape)))))
|
||||
|
||||
(generateMarkup
|
||||
[_ shapes options]
|
||||
(let [type (d/nilv (obj/get options "type") "html")]
|
||||
:createShapeFromSvg
|
||||
(fn [svg-string]
|
||||
(cond
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :generateMarkup-shapes shapes)
|
||||
|
||||
(and (some? type) (not (contains? #{"html" "svg"} type)))
|
||||
(u/display-not-valid :generateMarkup-type type)
|
||||
(or (not (string? svg-string)) (empty? svg-string))
|
||||
(u/display-not-valid :createShapeFromSvg svg-string)
|
||||
|
||||
:else
|
||||
(let [objects (u/locate-objects)
|
||||
shapes (into [] (map u/proxy->shape) shapes)]
|
||||
(cg/generate-markup-code objects type shapes)))))
|
||||
(let [id (uuid/next)
|
||||
file-id (:current-file-id @st/state)
|
||||
page-id (:current-page-id @st/state)]
|
||||
(st/emit! (dwm/create-svg-shape id "svg" svg-string (gpt/point 0 0)))
|
||||
(shape/shape-proxy plugin-id file-id page-id id))))
|
||||
|
||||
(generateStyle
|
||||
[_ shapes options]
|
||||
(let [type (d/nilv (obj/get options "type") "css")
|
||||
prelude? (d/nilv (obj/get options "withPrelude") false)
|
||||
children? (d/nilv (obj/get options "includeChildren") true)]
|
||||
:createBoolean
|
||||
(fn [bool-type shapes]
|
||||
(let [bool-type (keyword bool-type)]
|
||||
(cond
|
||||
(not (contains? cts/bool-types bool-type))
|
||||
(u/display-not-valid :createBoolean-boolType bool-type)
|
||||
|
||||
(or (not (array? shapes)) (empty? shapes) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :createBoolean-shapes shapes)
|
||||
|
||||
:else
|
||||
(let [ids (into #{} (map #(obj/get % "$id")) shapes)
|
||||
id-ret (atom nil)]
|
||||
(st/emit! (dwb/create-bool bool-type ids {:id-ret id-ret}))
|
||||
(shape/shape-proxy plugin-id @id-ret)))))
|
||||
|
||||
:generateMarkup
|
||||
(fn [shapes options]
|
||||
(let [type (d/nilv (obj/get options "type") "html")]
|
||||
(cond
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :generateMarkup-shapes shapes)
|
||||
|
||||
(and (some? type) (not (contains? #{"html" "svg"} type)))
|
||||
(u/display-not-valid :generateMarkup-type type)
|
||||
|
||||
:else
|
||||
(let [objects (u/locate-objects)
|
||||
shapes (into [] (map u/proxy->shape) shapes)]
|
||||
(cg/generate-markup-code objects type shapes)))))
|
||||
|
||||
:generateStyle
|
||||
(fn [shapes options]
|
||||
(let [type (d/nilv (obj/get options "type") "css")
|
||||
prelude? (d/nilv (obj/get options "withPrelude") false)
|
||||
children? (d/nilv (obj/get options "includeChildren") true)]
|
||||
(cond
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :generateStyle-shapes shapes)
|
||||
|
||||
(and (some? type) (not (contains? #{"css"} type)))
|
||||
(u/display-not-valid :generateStyle-type type)
|
||||
|
||||
(and (some? prelude?) (not (boolean? prelude?)))
|
||||
(u/display-not-valid :generateStyle-withPrelude prelude?)
|
||||
|
||||
(and (some? children?) (not (boolean? children?)))
|
||||
(u/display-not-valid :generateStyle-includeChildren children?)
|
||||
|
||||
:else
|
||||
(let [objects (u/locate-objects)
|
||||
shapes
|
||||
(->> (into #{} (map u/proxy->shape) shapes)
|
||||
(cfh/clean-loops objects))
|
||||
|
||||
shapes-with-children
|
||||
(if children?
|
||||
(->> shapes
|
||||
(mapcat #(cfh/get-children-with-self objects (:id %))))
|
||||
shapes)]
|
||||
(cg/generate-style-code
|
||||
objects type shapes shapes-with-children {:with-prelude? prelude?})))))
|
||||
|
||||
:openViewer
|
||||
(fn []
|
||||
(let [params {:page-id (:current-page-id @st/state)
|
||||
:file-id (:current-file-id @st/state)
|
||||
:section "interactions"}]
|
||||
(st/emit! (dw/go-to-viewer params))))
|
||||
|
||||
:createPage
|
||||
(fn []
|
||||
(let [file-id (:current-file-id @st/state)
|
||||
id (uuid/next)]
|
||||
(st/emit! (dw/create-page {:page-id id :file-id file-id}))
|
||||
(page/page-proxy plugin-id file-id id)))
|
||||
:openPage
|
||||
(fn [page]
|
||||
(let [id (obj/get page "$id")]
|
||||
(st/emit! (dw/go-to-page id))))
|
||||
|
||||
:alignHorizontal
|
||||
(fn [shapes direction]
|
||||
(let [dir (case direction
|
||||
"left" :hleft
|
||||
"center" :hcenter
|
||||
"right" :hright
|
||||
nil)]
|
||||
(cond
|
||||
(nil? dir)
|
||||
(u/display-not-valid :alignHorizontal-direction "Direction not valid")
|
||||
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :alignHorizontal-shapes "Not valid shapes")
|
||||
|
||||
:else
|
||||
(let [ids (into #{} (map #(obj/get % "$id")) shapes)]
|
||||
(st/emit! (dw/align-objects dir ids))))))
|
||||
|
||||
:alignVertical
|
||||
(fn [shapes direction]
|
||||
(let [dir (case direction
|
||||
"top" :vtop
|
||||
"center" :vcenter
|
||||
"bottom" :vbottom
|
||||
nil)]
|
||||
(cond
|
||||
(nil? dir)
|
||||
(u/display-not-valid :alignVertical-direction "Direction not valid")
|
||||
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :alignVertical-shapes "Not valid shapes")
|
||||
|
||||
:else
|
||||
(let [ids (into #{} (map #(obj/get % "$id")) shapes)]
|
||||
(st/emit! (dw/align-objects dir ids))))))
|
||||
|
||||
:distributeHorizontal
|
||||
(fn [shapes]
|
||||
(cond
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :generateStyle-shapes shapes)
|
||||
|
||||
(and (some? type) (not (contains? #{"css"} type)))
|
||||
(u/display-not-valid :generateStyle-type type)
|
||||
|
||||
(and (some? prelude?) (not (boolean? prelude?)))
|
||||
(u/display-not-valid :generateStyle-withPrelude prelude?)
|
||||
|
||||
(and (some? children?) (not (boolean? children?)))
|
||||
(u/display-not-valid :generateStyle-includeChildren children?)
|
||||
|
||||
:else
|
||||
(let [objects (u/locate-objects)
|
||||
shapes
|
||||
(->> (into #{} (map u/proxy->shape) shapes)
|
||||
(cfh/clean-loops objects))
|
||||
|
||||
shapes-with-children
|
||||
(if children?
|
||||
(->> shapes
|
||||
(mapcat #(cfh/get-children-with-self objects (:id %))))
|
||||
shapes)]
|
||||
(cg/generate-style-code
|
||||
objects type shapes shapes-with-children {:with-prelude? prelude?})))))
|
||||
|
||||
(openViewer
|
||||
[_]
|
||||
(let [params {:page-id (:current-page-id @st/state)
|
||||
:file-id (:current-file-id @st/state)
|
||||
:section "interactions"}]
|
||||
(st/emit! (dw/go-to-viewer params))))
|
||||
|
||||
(createPage
|
||||
[_]
|
||||
(let [file-id (:current-file-id @st/state)
|
||||
id (uuid/next)]
|
||||
(st/emit! (dw/create-page {:page-id id :file-id file-id}))
|
||||
(page/page-proxy $plugin file-id id)))
|
||||
|
||||
(openPage
|
||||
[_ page]
|
||||
(let [id (obj/get page "$id")]
|
||||
(st/emit! (dw/go-to-page id))))
|
||||
|
||||
(alignHorizontal
|
||||
[_ shapes direction]
|
||||
(let [dir (case direction
|
||||
"left" :hleft
|
||||
"center" :hcenter
|
||||
"right" :hright
|
||||
nil)]
|
||||
(cond
|
||||
(nil? dir)
|
||||
(u/display-not-valid :alignHorizontal-direction "Direction not valid")
|
||||
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :alignHorizontal-shapes "Not valid shapes")
|
||||
(u/display-not-valid :distributeHorizontal-shapes "Not valid shapes")
|
||||
|
||||
:else
|
||||
(let [ids (into #{} (map #(obj/get % "$id")) shapes)]
|
||||
(st/emit! (dw/align-objects dir ids))))))
|
||||
(st/emit! (dw/distribute-objects :horizontal ids)))))
|
||||
|
||||
(alignVertical
|
||||
[_ shapes direction]
|
||||
(let [dir (case direction
|
||||
"top" :vtop
|
||||
"center" :vcenter
|
||||
"bottom" :vbottom
|
||||
nil)]
|
||||
:distributeVertical
|
||||
(fn [shapes]
|
||||
(cond
|
||||
(nil? dir)
|
||||
(u/display-not-valid :alignVertical-direction "Direction not valid")
|
||||
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :alignVertical-shapes "Not valid shapes")
|
||||
(u/display-not-valid :distributeVertical-shapes "Not valid shapes")
|
||||
|
||||
:else
|
||||
(let [ids (into #{} (map #(obj/get % "$id")) shapes)]
|
||||
(st/emit! (dw/align-objects dir ids))))))
|
||||
(st/emit! (dw/distribute-objects :vertical ids)))))
|
||||
|
||||
(distributeHorizontal
|
||||
[_ shapes]
|
||||
(cond
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :distributeHorizontal-shapes "Not valid shapes")
|
||||
|
||||
:else
|
||||
(let [ids (into #{} (map #(obj/get % "$id")) shapes)]
|
||||
(st/emit! (dw/distribute-objects :horizontal ids)))))
|
||||
|
||||
(distributeVertical
|
||||
[_ shapes]
|
||||
(cond
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :distributeVertical-shapes "Not valid shapes")
|
||||
|
||||
:else
|
||||
(let [ids (into #{} (map #(obj/get % "$id")) shapes)]
|
||||
(st/emit! (dw/distribute-objects :vertical ids)))))
|
||||
|
||||
(flatten
|
||||
[_ shapes]
|
||||
(cond
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :flatten-shapes "Not valid shapes")
|
||||
|
||||
:else
|
||||
(let [ids (into #{} (map #(obj/get % "$id")) shapes)]
|
||||
(st/emit! (dw/convert-selected-to-path ids))))))
|
||||
|
||||
(defn create-context
|
||||
[plugin-id]
|
||||
(cr/add-properties!
|
||||
(PenpotContext. plugin-id)
|
||||
{:name "$plugin" :enumerable false :get (constantly plugin-id)}
|
||||
{:name "root" :get #(.getRoot ^js %)}
|
||||
{:name "currentFile" :get #(.getFile ^js %)}
|
||||
{:name "currentPage" :get #(.getPage ^js %)}
|
||||
{:name "theme" :get #(.getTheme ^js %)}
|
||||
|
||||
{:name "selection"
|
||||
:get #(.getSelectedShapes ^js %)
|
||||
:set
|
||||
(fn [_ shapes]
|
||||
:flatten
|
||||
(fn [shapes]
|
||||
(cond
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :selection shapes)
|
||||
(u/display-not-valid :flatten-shapes "Not valid shapes")
|
||||
|
||||
:else
|
||||
(let [ids (into (d/ordered-set) (map #(obj/get % "$id")) shapes)]
|
||||
(st/emit! (dws/select-shapes ids)))))}
|
||||
|
||||
{:name "viewport" :get #(.getViewport ^js %)}
|
||||
{:name "currentUser" :get #(.getCurrentUser ^js %)}
|
||||
{:name "activeUsers" :get #(.getActiveUsers ^js %)}
|
||||
{:name "fonts" :get (fn [_] (fonts/fonts-subcontext plugin-id))}
|
||||
{:name "library" :get (fn [_] (library/library-subcontext plugin-id))}
|
||||
{:name "history" :get (fn [_] (history/history-subcontext plugin-id))}))
|
||||
(let [ids (into #{} (map #(obj/get % "$id")) shapes)]
|
||||
(st/emit! (dw/convert-selected-to-path ids)))))))
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
(ns app.plugins.comments
|
||||
(:require
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.record :as crc]
|
||||
[app.common.spec :as us]
|
||||
[app.main.data.comments :as dc]
|
||||
[app.main.data.workspace.comments :as dwc]
|
||||
@@ -19,159 +18,170 @@
|
||||
[app.plugins.shape :as shape]
|
||||
[app.plugins.user :as user]
|
||||
[app.plugins.utils :as u]
|
||||
[app.util.object :as obj]
|
||||
[beicon.v2.core :as rx]))
|
||||
|
||||
(deftype CommentProxy [$plugin $file $page $thread $id]
|
||||
Object
|
||||
(remove [_]
|
||||
(js/Promise.
|
||||
(fn [resolve reject]
|
||||
(cond
|
||||
(not (r/check-permission $plugin "comment:write"))
|
||||
(do
|
||||
(u/display-not-valid :remove "Plugin doesn't have 'comment:write' permission")
|
||||
(reject "Plugin doesn't have 'comment:write' permission"))
|
||||
|
||||
:else
|
||||
(->> (rp/cmd! :delete-comment {:id $id})
|
||||
(rx/tap #(st/emit! (dc/retrieve-comment-threads $file)))
|
||||
(rx/subs! #(resolve) reject)))))))
|
||||
|
||||
(defn comment-proxy? [p]
|
||||
(instance? CommentProxy p))
|
||||
(obj/type-of? p "CommentProxy"))
|
||||
|
||||
(defn comment-proxy
|
||||
[plugin-id file-id page-id thread-id users data]
|
||||
(let [data* (atom data)]
|
||||
(crc/add-properties!
|
||||
(CommentProxy. plugin-id file-id page-id thread-id (:id data))
|
||||
{:name "$plugin" :enumerable false :get (constantly plugin-id)}
|
||||
{:name "$file" :enumerable false :get (constantly file-id)}
|
||||
{:name "$page" :enumerable false :get (constantly page-id)}
|
||||
{:name "$thread" :enumerable false :get (constantly thread-id)}
|
||||
{:name "$id" :enumerable false :get (constantly (:id data))}
|
||||
(obj/reify {:name "CommentProxy"}
|
||||
;; Private properties
|
||||
:$plugin {:enumerable false :get (fn [] plugin-id)}
|
||||
:$file {:enumerable false :get (fn [] file-id)}
|
||||
:$page {:enumerable false :get (fn [] page-id)}
|
||||
:$thread {:enumerable false :get (fn [] thread-id)}
|
||||
:$id {:enumerable false :get (fn [] (:id data))}
|
||||
|
||||
{:name "user" :get (fn [_] (user/user-proxy plugin-id (get users (:owner-id data))))}
|
||||
{:name "date" :get (fn [_] (:created-at data))}
|
||||
;; Public properties
|
||||
:user
|
||||
{:get
|
||||
(fn [] (user/user-proxy plugin-id (get users (:owner-id data))))}
|
||||
|
||||
{:name "content"
|
||||
:get (fn [_] (:content @data*))
|
||||
:set
|
||||
(fn [_ content]
|
||||
(let [profile (:profile @st/state)]
|
||||
(cond
|
||||
(or (not (string? content)) (empty? content))
|
||||
(u/display-not-valid :content "Not valid")
|
||||
:date
|
||||
{:get
|
||||
(fn [] (:created-at data))}
|
||||
|
||||
(not= (:id profile) (:owner-id data))
|
||||
(u/display-not-valid :content "Cannot change content from another user's comments")
|
||||
:content
|
||||
{:get
|
||||
(fn [] (:content @data*))
|
||||
|
||||
(not (r/check-permission plugin-id "comment:write"))
|
||||
(u/display-not-valid :content "Plugin doesn't have 'comment:write' permission")
|
||||
:set
|
||||
(fn [content]
|
||||
(let [profile (:profile @st/state)]
|
||||
(cond
|
||||
(or (not (string? content)) (empty? content))
|
||||
(u/display-not-valid :content "Not valid")
|
||||
|
||||
:else
|
||||
(->> (rp/cmd! :update-comment {:id (:id data) :content content})
|
||||
(rx/tap #(st/emit! (dc/retrieve-comment-threads file-id)))
|
||||
(rx/subs! #(swap! data* assoc :content content))))))})))
|
||||
(not= (:id profile) (:owner-id data))
|
||||
(u/display-not-valid :content "Cannot change content from another user's comments")
|
||||
|
||||
(deftype CommentThreadProxy [$plugin $file $page $users $id owner]
|
||||
Object
|
||||
(findComments
|
||||
[_]
|
||||
(js/Promise.
|
||||
(fn [resolve reject]
|
||||
(cond
|
||||
(not (r/check-permission $plugin "comment:read"))
|
||||
(do
|
||||
(u/display-not-valid :findComments "Plugin doesn't have 'comment:read' permission")
|
||||
(reject "Plugin doesn't have 'comment:read' permission"))
|
||||
(not (r/check-permission plugin-id "comment:write"))
|
||||
(u/display-not-valid :content "Plugin doesn't have 'comment:write' permission")
|
||||
|
||||
:else
|
||||
(->> (rp/cmd! :get-comments {:thread-id $id})
|
||||
(rx/subs!
|
||||
(fn [comments]
|
||||
(resolve
|
||||
(format/format-array
|
||||
#(comment-proxy $plugin $file $page $id $users %) comments)))
|
||||
reject))))))
|
||||
:else
|
||||
(->> (rp/cmd! :update-comment {:id (:id data) :content content})
|
||||
(rx/tap #(st/emit! (dc/retrieve-comment-threads file-id)))
|
||||
(rx/subs! #(swap! data* assoc :content content))))))}
|
||||
|
||||
(reply
|
||||
[_ content]
|
||||
(cond
|
||||
(not (r/check-permission $plugin "comment:write"))
|
||||
(u/display-not-valid :reply "Plugin doesn't have 'comment:write' permission")
|
||||
|
||||
(or (not (string? content)) (empty? content))
|
||||
(u/display-not-valid :reply "Not valid")
|
||||
|
||||
:else
|
||||
(js/Promise.
|
||||
(fn [resolve reject]
|
||||
(->> (rp/cmd! :create-comment {:thread-id $id :content content})
|
||||
(rx/subs! #(resolve (comment-proxy $plugin $file $page $id $users %)) reject))))))
|
||||
|
||||
(remove [_]
|
||||
(let [profile (:profile @st/state)]
|
||||
(cond
|
||||
(not (r/check-permission $plugin "comment:write"))
|
||||
(u/display-not-valid :remove "Plugin doesn't have 'comment:write' permission")
|
||||
|
||||
(not= (:id profile) owner)
|
||||
(u/display-not-valid :remove "Cannot change content from another user's comments")
|
||||
|
||||
:else
|
||||
;; Public methods
|
||||
:remove
|
||||
(fn []
|
||||
(js/Promise.
|
||||
(fn [resolve]
|
||||
(js/Promise.
|
||||
(st/emit! (dc/delete-comment-thread-on-workspace {:id $id} #(resolve))))))))))
|
||||
(fn [resolve reject]
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "comment:write"))
|
||||
(do
|
||||
(u/display-not-valid :remove "Plugin doesn't have 'comment:write' permission")
|
||||
(reject "Plugin doesn't have 'comment:write' permission"))
|
||||
|
||||
:else
|
||||
(->> (rp/cmd! :delete-comment {:id (:id data)})
|
||||
(rx/tap #(st/emit! (dc/retrieve-comment-threads file-id)))
|
||||
(rx/subs! #(resolve) reject)))))))))
|
||||
|
||||
(defn comment-thread-proxy? [p]
|
||||
(instance? CommentThreadProxy p))
|
||||
(obj/type-of? p "CommentThreadProxy"))
|
||||
|
||||
(defn comment-thread-proxy
|
||||
[plugin-id file-id page-id users data]
|
||||
(let [data* (atom data)]
|
||||
(crc/add-properties!
|
||||
(CommentThreadProxy. plugin-id file-id page-id users (:id data) (:owner-id data))
|
||||
{:name "$plugin" :enumerable false :get (constantly plugin-id)}
|
||||
{:name "$file" :enumerable false :get (constantly file-id)}
|
||||
{:name "$page" :enumerable false :get (constantly page-id)}
|
||||
{:name "$id" :enumerable false :get (constantly (:id data))}
|
||||
{:name "$users" :enumerable false :get (constantly users)}
|
||||
{:name "page" :enumerable false :get (fn [_] (u/locate-page file-id page-id))}
|
||||
(obj/reify {:name "CommentThreadProxy"}
|
||||
:$plugin {:enumerable false :get (fn [] plugin-id)}
|
||||
:$file {:enumerable false :get (fn [] file-id)}
|
||||
:$page {:enumerable false :get (fn [] page-id)}
|
||||
:$id {:enumerable false :get (fn [] (:id data))}
|
||||
:$users {:enumerable false :get (fn [] users)}
|
||||
|
||||
{:name "seqNumber" :get (fn [_] (:seqn data))}
|
||||
{:name "owner" :get (fn [_] (user/user-proxy plugin-id (get users (:owner-id data))))}
|
||||
{:name "board" :get (fn [_] (shape/shape-proxy plugin-id file-id page-id (:frame-id data)))}
|
||||
:page {:enumerable false :get #(u/locate-page file-id page-id)}
|
||||
:seqNumber {:get #(:seqn data)}
|
||||
:owner {:get #(user/user-proxy plugin-id (get users (:owner-id data)))}
|
||||
:board {:get #(shape/shape-proxy plugin-id file-id page-id (:frame-id data))}
|
||||
|
||||
{:name "position"
|
||||
:get (fn [_] (format/format-point (:position @data*)))
|
||||
:set
|
||||
(fn [_ position]
|
||||
(let [position (parser/parse-point position)]
|
||||
(cond
|
||||
(or (not (us/safe-number? (:x position))) (not (us/safe-number? (:y position))))
|
||||
(u/display-not-valid :position "Not valid point")
|
||||
:position
|
||||
{:get
|
||||
(fn []
|
||||
(format/format-point (:position @data*)))
|
||||
|
||||
(not (r/check-permission plugin-id "comment:write"))
|
||||
(u/display-not-valid :position "Plugin doesn't have 'comment:write' permission")
|
||||
:set
|
||||
(fn [position]
|
||||
(let [position (parser/parse-point position)]
|
||||
(cond
|
||||
(or (not (us/safe-number? (:x position))) (not (us/safe-number? (:y position))))
|
||||
(u/display-not-valid :position "Not valid point")
|
||||
|
||||
:else
|
||||
(do (st/emit! (dwc/update-comment-thread-position @data* [(:x position) (:y position)]))
|
||||
(swap! data* assoc :position (gpt/point position))))))}
|
||||
(not (r/check-permission plugin-id "comment:write"))
|
||||
(u/display-not-valid :position "Plugin doesn't have 'comment:write' permission")
|
||||
|
||||
{:name "resolved"
|
||||
:get (fn [_] (:is-resolved @data*))
|
||||
:set
|
||||
(fn [_ is-resolved]
|
||||
:else
|
||||
(do (st/emit! (dwc/update-comment-thread-position @data* [(:x position) (:y position)]))
|
||||
(swap! data* assoc :position (gpt/point position))))))}
|
||||
|
||||
:resolved
|
||||
{:get
|
||||
(fn [] (:is-resolved @data*))
|
||||
|
||||
:set
|
||||
(fn [is-resolved]
|
||||
(cond
|
||||
(not (boolean? is-resolved))
|
||||
(u/display-not-valid :resolved "Not a boolean type")
|
||||
|
||||
(not (r/check-permission plugin-id "comment:write"))
|
||||
(u/display-not-valid :resolved "Plugin doesn't have 'comment:write' permission")
|
||||
|
||||
:else
|
||||
(do (st/emit! (dc/update-comment-thread (assoc @data* :is-resolved is-resolved)))
|
||||
(swap! data* assoc :is-resolved is-resolved))))}
|
||||
|
||||
:findComments
|
||||
(fn []
|
||||
(js/Promise.
|
||||
(fn [resolve reject]
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "comment:read"))
|
||||
(do
|
||||
(u/display-not-valid :findComments "Plugin doesn't have 'comment:read' permission")
|
||||
(reject "Plugin doesn't have 'comment:read' permission"))
|
||||
|
||||
:else
|
||||
(->> (rp/cmd! :get-comments {:thread-id (:id data)})
|
||||
(rx/subs!
|
||||
(fn [comments]
|
||||
(resolve
|
||||
(format/format-array
|
||||
#(comment-proxy plugin-id file-id page-id (:id data) users %) comments)))
|
||||
reject))))))
|
||||
|
||||
:reply
|
||||
(fn [content]
|
||||
(cond
|
||||
(not (boolean? is-resolved))
|
||||
(u/display-not-valid :resolved "Not a boolean type")
|
||||
|
||||
(not (r/check-permission plugin-id "comment:write"))
|
||||
(u/display-not-valid :resolved "Plugin doesn't have 'comment:write' permission")
|
||||
(u/display-not-valid :reply "Plugin doesn't have 'comment:write' permission")
|
||||
|
||||
(or (not (string? content)) (empty? content))
|
||||
(u/display-not-valid :reply "Not valid")
|
||||
|
||||
:else
|
||||
(do (st/emit! (dc/update-comment-thread (assoc @data* :is-resolved is-resolved)))
|
||||
(swap! data* assoc :is-resolved is-resolved))))})))
|
||||
(js/Promise.
|
||||
(fn [resolve reject]
|
||||
(->> (rp/cmd! :create-comment {:thread-id (:id data) :content content})
|
||||
(rx/subs! #(resolve (comment-proxy plugin-id file-id page-id (:id data) users %)) reject))))))
|
||||
|
||||
:remove
|
||||
(fn []
|
||||
(let [profile (:profile @st/state)
|
||||
owner (get users (:owner-id data))]
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "comment:write"))
|
||||
(u/display-not-valid :remove "Plugin doesn't have 'comment:write' permission")
|
||||
|
||||
(not= (:id profile) owner)
|
||||
(u/display-not-valid :remove "Cannot change content from another user's comments")
|
||||
|
||||
:else
|
||||
(js/Promise.
|
||||
(fn [resolve]
|
||||
(st/emit! (dc/delete-comment-thread-on-workspace {:id (:id data)} #(resolve)))))))))))
|
||||
|
||||
@@ -62,15 +62,16 @@
|
||||
|
||||
(defmethod handle-state-change "shapechange"
|
||||
[_ plugin-id old-val new-val props]
|
||||
(let [shape-id (-> (obj/get props "shapeId") parser/parse-id)
|
||||
old-shape (wsh/lookup-shape old-val shape-id)
|
||||
new-shape (wsh/lookup-shape new-val shape-id)
|
||||
(if-let [shape-id (-> (obj/get props "shapeId") parser/parse-id)]
|
||||
(let [old-shape (wsh/lookup-shape old-val shape-id)
|
||||
new-shape (wsh/lookup-shape new-val shape-id)
|
||||
|
||||
file-id (:current-file-id new-val)
|
||||
page-id (:current-page-id new-val)]
|
||||
(if (and (identical? old-shape new-shape) (some? plugin-id) (some? file-id) (some? page-id) (some? shape-id))
|
||||
::not-changed
|
||||
(shape/shape-proxy plugin-id file-id page-id shape-id))))
|
||||
file-id (:current-file-id new-val)
|
||||
page-id (:current-page-id new-val)]
|
||||
(if (and (identical? old-shape new-shape) (some? plugin-id) (some? file-id) (some? page-id) (some? shape-id))
|
||||
::not-changed
|
||||
(shape/shape-proxy plugin-id file-id page-id shape-id)))
|
||||
::not-changed))
|
||||
|
||||
(defmethod handle-state-change "contentsave"
|
||||
[_ _ old-val new-val _]
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.record :as crc]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.main.data.exports.files :as exports.files]
|
||||
@@ -18,6 +17,7 @@
|
||||
[app.main.repo :as rp]
|
||||
[app.main.store :as st]
|
||||
[app.main.worker :as uw]
|
||||
[app.plugins.format :as format]
|
||||
[app.plugins.page :as page]
|
||||
[app.plugins.parser :as parser]
|
||||
[app.plugins.register :as r]
|
||||
@@ -28,300 +28,294 @@
|
||||
[app.util.time :as dt]
|
||||
[beicon.v2.core :as rx]))
|
||||
|
||||
(declare file-version-proxy)
|
||||
|
||||
(deftype FileVersionProxy [$plugin $file $version $data]
|
||||
Object
|
||||
(restore
|
||||
[_]
|
||||
(cond
|
||||
(not (r/check-permission $plugin "content:write"))
|
||||
(u/display-not-valid :restore "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [project-id (:current-project-id @st/state)]
|
||||
(st/emit! (dwv/restore-version project-id $file $version)))))
|
||||
|
||||
(remove
|
||||
[_]
|
||||
(js/Promise.
|
||||
(fn [resolve reject]
|
||||
(cond
|
||||
(not (r/check-permission $plugin "content:write"))
|
||||
(u/reject-not-valid reject :remove "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(->> (rp/cmd! :delete-file-snapshot {:id $version})
|
||||
(rx/subs! #(resolve) reject))))))
|
||||
|
||||
(pin
|
||||
[_]
|
||||
(js/Promise.
|
||||
(fn [resolve reject]
|
||||
(cond
|
||||
(not (r/check-permission $plugin "content:write"))
|
||||
(u/reject-not-valid reject :pin "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
(not= "system" (:created-by $data))
|
||||
(u/reject-not-valid reject :pin "Only auto-saved versions can be pinned")
|
||||
|
||||
:else
|
||||
(let [params {:id $version
|
||||
:label (dt/format (:created-at $data) :date-full)}]
|
||||
(->> (rx/zip (rp/cmd! :get-team-users {:file-id $file})
|
||||
(rp/cmd! :update-file-snapshot params))
|
||||
(rx/subs! (fn [[users data]]
|
||||
(let [users (d/index-by :id users)]
|
||||
(resolve (file-version-proxy $plugin $file users data))))
|
||||
reject))))))))
|
||||
(defn file-version-proxy?
|
||||
[proxy]
|
||||
(obj/type-of? proxy "FileVersionProxy"))
|
||||
|
||||
(defn file-version-proxy
|
||||
[plugin-id file-id users data]
|
||||
(let [data (atom data)]
|
||||
(crc/add-properties!
|
||||
(FileVersionProxy. plugin-id file-id (:id @data) data)
|
||||
{:name "$plugin" :enumerable false :get (constantly plugin-id)}
|
||||
{:name "$file" :enumerable false :get (constantly file-id)}
|
||||
{:name "$version" :enumerable false :get (constantly (:id @data))}
|
||||
{:name "$data" :enumerable false :get (constantly @data)}
|
||||
(obj/reify {:name "FileVersionProxy"}
|
||||
:$plugin {:enumerable false :get (fn [] plugin-id)}
|
||||
:$file {:enumerable false :get (fn [] file-id)}
|
||||
|
||||
{:name "label"
|
||||
:get (fn [_] (:label @data))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :label "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
(or (not (string? value)) (empty? value))
|
||||
(u/display-not-valid :label value)
|
||||
|
||||
:else
|
||||
(do (swap! data assoc :label value :created-by "user")
|
||||
(->> (rp/cmd! :update-file-snapshot {:id (:id @data) :label value})
|
||||
(rx/take 1)
|
||||
(rx/subs! identity)))))}
|
||||
|
||||
{:name "createdBy"
|
||||
:get (fn [_]
|
||||
(when-let [user-data (get users (:profile-id @data))]
|
||||
(user/user-proxy plugin-id user-data)))}
|
||||
|
||||
{:name "createdAt"
|
||||
:get (fn [_]
|
||||
(.toJSDate ^js (:created-at @data)))}
|
||||
|
||||
{:name "isAutosave"
|
||||
:get (fn [_]
|
||||
(= "system" (:created-by @data)))})))
|
||||
|
||||
(deftype FileProxy [$plugin $id]
|
||||
Object
|
||||
(getPages [_]
|
||||
(let [file (u/locate-file $id)]
|
||||
(apply array (sequence (map #(page/page-proxy $plugin $id %)) (dm/get-in file [:data :pages])))))
|
||||
|
||||
;; Plugin data
|
||||
(getPluginData
|
||||
[self key]
|
||||
(cond
|
||||
(not (string? key))
|
||||
(u/display-not-valid :getPluginData-key key)
|
||||
|
||||
:else
|
||||
(let [file (u/proxy->file self)]
|
||||
(dm/get-in file [:data :plugin-data (keyword "plugin" (str $plugin)) key]))))
|
||||
|
||||
(setPluginData
|
||||
[_ key value]
|
||||
(cond
|
||||
(or (not (string? key)) (empty? key))
|
||||
(u/display-not-valid :setPluginData-key key)
|
||||
|
||||
(and (some? value) (not (string? value)))
|
||||
(u/display-not-valid :setPluginData-value value)
|
||||
|
||||
(not (r/check-permission $plugin "content:write"))
|
||||
(u/display-not-valid :setPluginData "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dw/set-plugin-data $id :file (keyword "plugin" (str $plugin)) key value))))
|
||||
|
||||
(getPluginDataKeys
|
||||
[self]
|
||||
(let [file (u/proxy->file self)]
|
||||
(apply array (keys (dm/get-in file [:data :plugin-data (keyword "plugin" (str $plugin))])))))
|
||||
|
||||
(getSharedPluginData
|
||||
[self namespace key]
|
||||
(cond
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :getSharedPluginData-namespace namespace)
|
||||
|
||||
(not (string? key))
|
||||
(u/display-not-valid :getSharedPluginData-key key)
|
||||
|
||||
:else
|
||||
(let [file (u/proxy->file self)]
|
||||
(dm/get-in file [:data :plugin-data (keyword "shared" namespace) key]))))
|
||||
|
||||
(setSharedPluginData
|
||||
[_ namespace key value]
|
||||
|
||||
(cond
|
||||
(or (not (string? namespace)) (empty? namespace))
|
||||
(u/display-not-valid :setSharedPluginData-namespace namespace)
|
||||
|
||||
(or (not (string? key)) (empty? key))
|
||||
(u/display-not-valid :setSharedPluginData-key key)
|
||||
|
||||
(and (some? value) (not (string? value)))
|
||||
(u/display-not-valid :setSharedPluginData-value value)
|
||||
|
||||
(not (r/check-permission $plugin "content:write"))
|
||||
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dw/set-plugin-data $id :file (keyword "shared" namespace) key value))))
|
||||
|
||||
(getSharedPluginDataKeys
|
||||
[self namespace]
|
||||
(cond
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :getSharedPluginDataKeys namespace)
|
||||
|
||||
:else
|
||||
(let [file (u/proxy->file self)]
|
||||
(apply array (keys (dm/get-in file [:data :plugin-data (keyword "shared" namespace)]))))))
|
||||
|
||||
(createPage
|
||||
[_]
|
||||
(cond
|
||||
(not (r/check-permission $plugin "content:write"))
|
||||
(u/display-not-valid :createPage "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [page-id (uuid/next)]
|
||||
(st/emit! (dw/create-page {:page-id page-id :file-id $id}))
|
||||
(page/page-proxy $plugin $id page-id))))
|
||||
|
||||
(export
|
||||
[self format type]
|
||||
(let [type (or (parser/parse-keyword type) :all)]
|
||||
(cond
|
||||
(not (contains? #{"penpot" "zip"} format))
|
||||
(u/display-not-valid :format type)
|
||||
|
||||
(not (contains? (set exports.files/valid-types) type))
|
||||
(u/display-not-valid :type type)
|
||||
|
||||
:else
|
||||
(let [file (u/proxy->file self)
|
||||
features (features/get-team-enabled-features @st/state)
|
||||
team-id (:current-team-id @st/state)
|
||||
format (case format
|
||||
"penpot" (if (contains? cf/flags :export-file-v3)
|
||||
:binfile-v3
|
||||
:binfile-v1)
|
||||
"zip" :legacy-zip)]
|
||||
(js/Promise.
|
||||
(fn [resolve reject]
|
||||
(->> (uw/ask-many!
|
||||
{:cmd :export-files
|
||||
:format format
|
||||
:type type
|
||||
:team-id team-id
|
||||
:features features
|
||||
:files [file]})
|
||||
(rx/mapcat
|
||||
(fn [msg]
|
||||
(case (:type msg)
|
||||
:error
|
||||
(rx/throw (ex-info "cannot export file" {:type :export-file}))
|
||||
|
||||
:progress
|
||||
(rx/empty)
|
||||
|
||||
:finish
|
||||
(http/send! {:method :get
|
||||
:uri (:uri msg)
|
||||
:mode :no-cors
|
||||
:response-type :buffer}))))
|
||||
(rx/take 1)
|
||||
(rx/map (fn [data] (js/Uint8Array. data)))
|
||||
(rx/subs! resolve reject))))))))
|
||||
|
||||
|
||||
(findVersions
|
||||
[_ criteria]
|
||||
(let [user (obj/get criteria "createdBy" nil)]
|
||||
(js/Promise.
|
||||
(fn [resolve reject]
|
||||
:label
|
||||
{:get #(:label @data)
|
||||
:set
|
||||
(fn [value]
|
||||
(cond
|
||||
(not (r/check-permission $plugin "content:read"))
|
||||
(u/reject-not-valid reject :findVersions "Plugin doesn't have 'content:read' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :label "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
(and (not user) (not (user/user-proxy? user)))
|
||||
(u/reject-not-valid reject :findVersions-user "Created by user is not a valid user object")
|
||||
(or (not (string? value)) (empty? value))
|
||||
(u/display-not-valid :label value)
|
||||
|
||||
:else
|
||||
(->> (rx/zip (rp/cmd! :get-team-users {:file-id $id})
|
||||
(rp/cmd! :get-file-snapshots {:file-id $id}))
|
||||
(rx/take 1)
|
||||
(rx/subs!
|
||||
(fn [[users snapshots]]
|
||||
(let [users (d/index-by :id users)]
|
||||
(->> snapshots
|
||||
(filter #(= (dm/str (:profile-id %)) (obj/get user "id")))
|
||||
(map #(file-version-proxy $plugin $id users %))
|
||||
(sequence)
|
||||
(apply array)
|
||||
(resolve))))
|
||||
reject)))))))
|
||||
(do (swap! data assoc :label value :created-by "user")
|
||||
(->> (rp/cmd! :update-file-snapshot {:id (:id @data) :label value})
|
||||
(rx/take 1)
|
||||
(rx/subs! identity)))))}
|
||||
|
||||
(saveVersion
|
||||
[_ label]
|
||||
(let [users-promise
|
||||
(js/Promise.
|
||||
(fn [resolve reject]
|
||||
(->> (rp/cmd! :get-team-users {:file-id $id})
|
||||
(rx/subs! resolve reject))))
|
||||
:createdBy
|
||||
{:get
|
||||
(fn []
|
||||
(when-let [user-data (get users (:profile-id @data))]
|
||||
(user/user-proxy plugin-id user-data)))}
|
||||
|
||||
create-version-promise
|
||||
(js/Promise.
|
||||
(fn [resolve reject]
|
||||
(cond
|
||||
(not (r/check-permission $plugin "content:write"))
|
||||
(u/reject-not-valid reject :findVersions "Plugin doesn't have 'content:write' permission")
|
||||
:createdAt
|
||||
{:get #(.toJSDate ^js (:created-at @data))}
|
||||
|
||||
:else
|
||||
(st/emit! (dwv/create-version-from-plugins $id label resolve reject)))))]
|
||||
(-> (js/Promise.all #js [users-promise create-version-promise])
|
||||
(.then
|
||||
(fn [[users data]]
|
||||
(let [users (d/index-by :id users)]
|
||||
(file-version-proxy $plugin $id users data))))))))
|
||||
:isAutosave
|
||||
{:get #(= "system" (:created-by @data))}
|
||||
|
||||
(crc/define-properties!
|
||||
FileProxy
|
||||
{:name js/Symbol.toStringTag
|
||||
:get (fn [] (str "FileProxy"))})
|
||||
:restore
|
||||
(fn []
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :restore "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [project-id (:current-project-id @st/state)
|
||||
version-id (get @data :id)]
|
||||
(st/emit! (dwv/restore-version project-id file-id version-id :plugin)))))
|
||||
|
||||
:remove
|
||||
(fn []
|
||||
(js/Promise.
|
||||
(fn [resolve reject]
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/reject-not-valid reject :remove "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [version-id (:id @data)]
|
||||
(->> (rp/cmd! :delete-file-snapshot {:id version-id})
|
||||
(rx/subs! #(resolve) reject)))))))
|
||||
|
||||
:pin
|
||||
(fn []
|
||||
(js/Promise.
|
||||
(fn [resolve reject]
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/reject-not-valid reject :pin "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
(not= "system" (:created-by @data))
|
||||
(u/reject-not-valid reject :pin "Only auto-saved versions can be pinned")
|
||||
|
||||
:else
|
||||
(let [params {:id (:id @data)
|
||||
:label (dt/format (:created-at @data) :date-full)}]
|
||||
(->> (rx/zip (rp/cmd! :get-team-users {:file-id file-id})
|
||||
(rp/cmd! :update-file-snapshot params))
|
||||
(rx/subs! (fn [[users data]]
|
||||
(let [users (d/index-by :id users)]
|
||||
(resolve (file-version-proxy plugin-id file-id users @data))))
|
||||
reject))))))))))
|
||||
|
||||
(defn file-proxy? [p]
|
||||
(instance? FileProxy p))
|
||||
(obj/type-of? p "FileProxy"))
|
||||
|
||||
(defn file-proxy
|
||||
[plugin-id id]
|
||||
(crc/add-properties!
|
||||
(FileProxy. plugin-id id)
|
||||
{:name "$plugin" :enumerable false :get (constantly plugin-id)}
|
||||
{:name "$id" :enumerable false :get (constantly id)}
|
||||
|
||||
{:name "id"
|
||||
:get #(dm/str (obj/get % "$id"))}
|
||||
(obj/reify {:name "FileProxy"}
|
||||
:$plugin {:enumerable false :get (fn [] plugin-id)}
|
||||
:$id {:enumerable false :get (fn [] id)}
|
||||
|
||||
{:name "name"
|
||||
:get #(-> % u/proxy->file :name)}
|
||||
:id
|
||||
{:get #(format/format-id id)}
|
||||
|
||||
{:name "pages"
|
||||
:get #(.getPages ^js %)}))
|
||||
:name
|
||||
{:get #(-> (u/locate-file id) :name)}
|
||||
|
||||
:pages
|
||||
{:this true
|
||||
:get #(.getPages ^js %)}
|
||||
|
||||
:getPages
|
||||
(fn []
|
||||
(let [file (u/locate-file id)]
|
||||
(apply array (sequence (map #(page/page-proxy plugin-id id %)) (dm/get-in file [:data :pages])))))
|
||||
|
||||
;; Plugin data
|
||||
:getPluginData
|
||||
(fn [key]
|
||||
(cond
|
||||
(not (string? key))
|
||||
(u/display-not-valid :getPluginData-key key)
|
||||
|
||||
:else
|
||||
(let [file (u/locate-file id)]
|
||||
(dm/get-in file [:data :plugin-data (keyword "plugin" (str plugin-id)) key]))))
|
||||
|
||||
:setPluginData
|
||||
(fn [key value]
|
||||
(cond
|
||||
(or (not (string? key)) (empty? key))
|
||||
(u/display-not-valid :setPluginData-key key)
|
||||
|
||||
(not (string? value))
|
||||
(u/display-not-valid :setPluginData-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :setPluginData "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dw/set-plugin-data id :file (keyword "plugin" (str plugin-id)) key value))))
|
||||
|
||||
:getPluginDataKeys
|
||||
(fn []
|
||||
(let [file (u/locate-file id)]
|
||||
(apply array (keys (dm/get-in file [:data :plugin-data (keyword "plugin" (dm/str plugin-id))])))))
|
||||
|
||||
:getSharedPluginData
|
||||
(fn [namespace key]
|
||||
(cond
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :getSharedPluginData-namespace namespace)
|
||||
|
||||
(not (string? key))
|
||||
(u/display-not-valid :getSharedPluginData-key key)
|
||||
|
||||
:else
|
||||
(let [file (u/locate-file id)]
|
||||
(dm/get-in file [:data :plugin-data (keyword "shared" namespace) key]))))
|
||||
|
||||
:setSharedPluginData
|
||||
(fn [namespace key value]
|
||||
(cond
|
||||
(or (not (string? namespace)) (empty? namespace))
|
||||
(u/display-not-valid :setSharedPluginData-namespace namespace)
|
||||
|
||||
(or (not (string? key)) (empty? key))
|
||||
(u/display-not-valid :setSharedPluginData-key key)
|
||||
|
||||
(not (string? value))
|
||||
(u/display-not-valid :setSharedPluginData-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dw/set-plugin-data id :file (keyword "shared" namespace) key value))))
|
||||
|
||||
:getSharedPluginDataKeys
|
||||
(fn [namespace]
|
||||
(cond
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :getSharedPluginDataKeys namespace)
|
||||
|
||||
:else
|
||||
(let [file (u/locate-file id)]
|
||||
(apply array (keys (dm/get-in file [:data :plugin-data (keyword "shared" namespace)]))))))
|
||||
|
||||
:createPage
|
||||
(fn []
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :createPage "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [page-id (uuid/next)]
|
||||
(st/emit! (dw/create-page {:page-id page-id :file-id id}))
|
||||
(page/page-proxy plugin-id id page-id))))
|
||||
|
||||
:export
|
||||
(fn [format type]
|
||||
(js/Promise.
|
||||
(fn [resolve reject]
|
||||
(let [type (or (parser/parse-keyword type) :all)]
|
||||
(cond
|
||||
(and (some? format) (not (contains? #{"penpot" "zip"} format)))
|
||||
(u/reject-not-valid reject :format (dm/str "Invalid format: " format))
|
||||
|
||||
(not (contains? (set exports.files/valid-types) type))
|
||||
(u/reject-not-valid reject :format (dm/str "Invalid type: " type))
|
||||
|
||||
:else
|
||||
(let [file (u/locate-file id)
|
||||
features (features/get-team-enabled-features @st/state)
|
||||
team-id (:current-team-id @st/state)
|
||||
format (case format
|
||||
"zip" :legacy-zip
|
||||
|
||||
(if (contains? cf/flags :export-file-v3)
|
||||
:binfile-v3
|
||||
:binfile-v1))]
|
||||
(->> (uw/ask-many!
|
||||
{:cmd :export-files
|
||||
:format format
|
||||
:type type
|
||||
:team-id team-id
|
||||
:features features
|
||||
:files [file]})
|
||||
(rx/mapcat
|
||||
(fn [msg]
|
||||
(.log js/console msg)
|
||||
(case (:type msg)
|
||||
:error
|
||||
(rx/throw (ex-info "cannot export file" {:type :export-file}))
|
||||
|
||||
:progress
|
||||
(rx/empty)
|
||||
|
||||
:finish
|
||||
(http/send! {:method :get
|
||||
:uri (:uri msg)
|
||||
:mode :no-cors
|
||||
:response-type :buffer}))))
|
||||
(rx/take 1)
|
||||
(rx/map #(js/Uint8Array. (:body %)))
|
||||
(rx/subs! resolve reject))))))))
|
||||
:findVersions
|
||||
(fn [criteria]
|
||||
(let [user (obj/get criteria "createdBy" nil)]
|
||||
(js/Promise.
|
||||
(fn [resolve reject]
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "content:read"))
|
||||
(u/reject-not-valid reject :findVersions "Plugin doesn't have 'content:read' permission")
|
||||
|
||||
(and (some? user) (not (user/user-proxy? user)))
|
||||
(u/reject-not-valid reject :findVersions-user "Created by user is not a valid user object")
|
||||
|
||||
:else
|
||||
(->> (rx/zip (rp/cmd! :get-team-users {:file-id id})
|
||||
(rp/cmd! :get-file-snapshots {:file-id id}))
|
||||
(rx/take 1)
|
||||
(rx/subs!
|
||||
(fn [[users snapshots]]
|
||||
(let [users (d/index-by :id users)]
|
||||
(->> snapshots
|
||||
(filter #(or (not (obj/get user "id"))
|
||||
(= (dm/str (:profile-id %))
|
||||
(obj/get user "id"))))
|
||||
(map #(file-version-proxy plugin-id id users %))
|
||||
(sequence)
|
||||
(apply array)
|
||||
(resolve))))
|
||||
reject)))))))
|
||||
|
||||
:saveVersion
|
||||
(fn [label]
|
||||
(let [users-promise
|
||||
(js/Promise.
|
||||
(fn [resolve reject]
|
||||
(->> (rp/cmd! :get-team-users {:file-id id})
|
||||
(rx/subs! resolve reject))))
|
||||
|
||||
create-version-promise
|
||||
(js/Promise.
|
||||
(fn [resolve reject]
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/reject-not-valid reject :findVersions "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwv/create-version-from-plugins id label resolve reject)))))]
|
||||
(-> (js/Promise.all #js [users-promise create-version-promise])
|
||||
(.then
|
||||
(fn [[users data]]
|
||||
(let [users (d/index-by :id users)]
|
||||
(file-version-proxy plugin-id id users data)))))))))
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
(ns app.plugins.flex
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.record :as crc]
|
||||
[app.common.spec :as us]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.main.data.workspace.shape-layout :as dwsl]
|
||||
@@ -21,491 +20,485 @@
|
||||
;; Define in `app.plugins.shape` we do this way to prevent circular dependency
|
||||
(def shape-proxy? nil)
|
||||
|
||||
(deftype FlexLayout [$plugin $file $page $id]
|
||||
Object
|
||||
(remove
|
||||
[_]
|
||||
(st/emit! (dwsl/remove-layout #{$id})))
|
||||
|
||||
(appendChild
|
||||
[_ child]
|
||||
(cond
|
||||
(not (shape-proxy? child))
|
||||
(u/display-not-valid :appendChild child)
|
||||
|
||||
:else
|
||||
(let [child-id (obj/get child "$id")]
|
||||
(st/emit! (dwt/move-shapes-to-frame #{child-id} $id nil nil)
|
||||
(ptk/data-event :layout/update {:ids [$id]}))))))
|
||||
|
||||
(defn flex-layout-proxy? [p]
|
||||
(instance? FlexLayout p))
|
||||
(obj/type-of? p "FlexLayoutProxy"))
|
||||
|
||||
(defn flex-layout-proxy
|
||||
[plugin-id file-id page-id id]
|
||||
(-> (FlexLayout. plugin-id file-id page-id id)
|
||||
(crc/add-properties!
|
||||
{:name "$plugin" :enumerable false :get (constantly plugin-id)}
|
||||
{:name "$id" :enumerable false :get (constantly id)}
|
||||
{:name "$file" :enumerable false :get (constantly file-id)}
|
||||
{:name "$page" :enumerable false :get (constantly page-id)}
|
||||
(obj/reify {:name "FlexLayoutProxy"}
|
||||
:$plugin {:enumerable false :get (fn [] plugin-id)}
|
||||
:$id {:enumerable false :get (fn [] id)}
|
||||
:$file {:enumerable false :get (fn [] file-id)}
|
||||
:$page {:enumerable false :get (fn [] page-id)}
|
||||
|
||||
{:name "dir"
|
||||
:get #(-> % u/proxy->shape :layout-flex-dir d/name)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/flex-direction-types value))
|
||||
(u/display-not-valid :dir value)
|
||||
:dir
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-flex-dir d/name)
|
||||
:set
|
||||
(fn [_ value]
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/flex-direction-types value))
|
||||
(u/display-not-valid :dir value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :dir "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :dir "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-flex-dir value}))))))}
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-flex-dir value})))))}
|
||||
|
||||
{:name "wrap"
|
||||
:get #(-> % u/proxy->shape :layout-wrap-type d/name)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/wrap-types value))
|
||||
(u/display-not-valid :wrap value)
|
||||
:wrap
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-wrap-type d/name)
|
||||
:set
|
||||
(fn [_ value]
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/wrap-types value))
|
||||
(u/display-not-valid :wrap value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :wrap "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :wrap "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-wrap-type value}))))))}
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-wrap-type value})))))}
|
||||
|
||||
{:name "alignItems"
|
||||
:get #(-> % u/proxy->shape :layout-align-items d/name)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/align-items-types value))
|
||||
(u/display-not-valid :alignItems value)
|
||||
:alignItems
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-align-items d/name)
|
||||
:set
|
||||
(fn [_ value]
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/align-items-types value))
|
||||
(u/display-not-valid :alignItems value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :alignItems "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :alignItems "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-align-items value}))))))}
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-align-items value})))))}
|
||||
|
||||
{:name "alignContent"
|
||||
:get #(-> % u/proxy->shape :layout-align-content d/name)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/align-content-types value))
|
||||
(u/display-not-valid :alignContent value)
|
||||
:alignContent
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-align-content d/name)
|
||||
:set
|
||||
(fn [_ value]
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/align-content-types value))
|
||||
(u/display-not-valid :alignContent value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :alignContent "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :alignContent "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-align-content value}))))))}
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-align-content value})))))}
|
||||
|
||||
{:name "justifyItems"
|
||||
:get #(-> % u/proxy->shape :layout-justify-items d/name)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/justify-items-types value))
|
||||
(u/display-not-valid :justifyItems value)
|
||||
:justifyItems
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-justify-items d/name)
|
||||
:set
|
||||
(fn [_ value]
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/justify-items-types value))
|
||||
(u/display-not-valid :justifyItems value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :justifyItems "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :justifyItems "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-justify-items value}))))))}
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-justify-items value})))))}
|
||||
|
||||
{:name "justifyContent"
|
||||
:get #(-> % u/proxy->shape :layout-justify-content d/name)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/justify-content-types value))
|
||||
(u/display-not-valid :justifyContent value)
|
||||
:justifyContent
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-justify-content d/name)
|
||||
:set
|
||||
(fn [_ value]
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/justify-content-types value))
|
||||
(u/display-not-valid :justifyContent value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :justifyContent "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :justifyContent "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-justify-content value}))))))}
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-justify-content value})))))}
|
||||
|
||||
{:name "rowGap"
|
||||
:get #(-> % u/proxy->shape :layout-gap :row-gap (d/nilv 0))
|
||||
:set
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(u/display-not-valid :rowGap value)
|
||||
:rowGap
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-gap :row-gap (d/nilv 0))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(u/display-not-valid :rowGap value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :rowGap "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :rowGap "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-gap {:row-gap value}})))))}
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-gap {:row-gap value}}))))}
|
||||
|
||||
{:name "columnGap"
|
||||
:get #(-> % u/proxy->shape :layout-gap :column-gap (d/nilv 0))
|
||||
:set
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(u/display-not-valid :columnGap value)
|
||||
:columnGap
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-gap :column-gap (d/nilv 0))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(u/display-not-valid :columnGap value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :columnGap "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :columnGap "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-gap {:column-gap value}})))))}
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-gap {:column-gap value}}))))}
|
||||
|
||||
{:name "verticalPadding"
|
||||
:get #(-> % u/proxy->shape :layout-padding :p1 (d/nilv 0))
|
||||
:set
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(u/display-not-valid :verticalPadding value)
|
||||
:verticalPadding
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-padding :p1 (d/nilv 0))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(u/display-not-valid :verticalPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :verticalPadding "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :verticalPadding "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value :p3 value}})))))}
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value :p3 value}}))))}
|
||||
|
||||
{:name "horizontalPadding"
|
||||
:get #(-> % u/proxy->shape :layout-padding :p2 (d/nilv 0))
|
||||
:set
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(u/display-not-valid :horizontalPadding value)
|
||||
:horizontalPadding
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-padding :p2 (d/nilv 0))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(u/display-not-valid :horizontalPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :horizontalPadding "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :horizontalPadding "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value :p4 value}})))))}
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value :p4 value}}))))}
|
||||
|
||||
|
||||
{:name "topPadding"
|
||||
:get #(-> % u/proxy->shape :layout-padding :p1 (d/nilv 0))
|
||||
:set
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(u/display-not-valid :topPadding value)
|
||||
:topPadding
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-padding :p1 (d/nilv 0))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(u/display-not-valid :topPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :topPadding "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :topPadding "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value}})))))}
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value}}))))}
|
||||
|
||||
{:name "rightPadding"
|
||||
:get #(-> % u/proxy->shape :layout-padding :p2 (d/nilv 0))
|
||||
:set
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(u/display-not-valid :rightPadding value)
|
||||
:rightPadding
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-padding :p2 (d/nilv 0))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(u/display-not-valid :rightPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :rightPadding "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :rightPadding "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value}})))))}
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value}}))))}
|
||||
|
||||
{:name "bottomPadding"
|
||||
:get #(-> % u/proxy->shape :layout-padding :p3 (d/nilv 0))
|
||||
:set
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(u/display-not-valid :bottomPadding value)
|
||||
:bottomPadding
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-padding :p3 (d/nilv 0))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(u/display-not-valid :bottomPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :bottomPadding "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :bottomPadding "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p3 value}})))))}
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p3 value}}))))}
|
||||
|
||||
{:name "leftPadding"
|
||||
:get #(-> % u/proxy->shape :layout-padding :p4 (d/nilv 0))
|
||||
:set
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(u/display-not-valid :leftPadding value)
|
||||
:leftPadding
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-padding :p4 (d/nilv 0))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(u/display-not-valid :leftPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :leftPadding "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :leftPadding "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p4 value}})))))})))
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p4 value}}))))}
|
||||
|
||||
:remove
|
||||
(fn []
|
||||
(st/emit! (dwsl/remove-layout #{id})))
|
||||
|
||||
:appendChild
|
||||
(fn [child]
|
||||
(cond
|
||||
(not (shape-proxy? child))
|
||||
(u/display-not-valid :appendChild child)
|
||||
|
||||
:else
|
||||
(let [child-id (obj/get child "$id")]
|
||||
(st/emit! (dwt/move-shapes-to-frame #{child-id} id nil nil)
|
||||
(ptk/data-event :layout/update {:ids [id]})))))))
|
||||
|
||||
(deftype LayoutChildProxy [$plugin $file $page $id])
|
||||
|
||||
(defn layout-child-proxy? [p]
|
||||
(instance? LayoutChildProxy p))
|
||||
(obj/type-of? p "LayoutChildProxy"))
|
||||
|
||||
(defn layout-child-proxy
|
||||
[plugin-id file-id page-id id]
|
||||
(-> (LayoutChildProxy. plugin-id file-id page-id id)
|
||||
(crc/add-properties!
|
||||
{:name "$plugin" :enumerable false :get (constantly plugin-id)}
|
||||
{:name "$id" :enumerable false :get (constantly id)}
|
||||
{:name "$file" :enumerable false :get (constantly file-id)}
|
||||
{:name "$page" :enumerable false :get (constantly page-id)}
|
||||
(obj/reify {:name "LayoutChildProxy"}
|
||||
:$plugin {:enumerable false :get (fn [] plugin-id)}
|
||||
:$id {:enumerable false :get (fn [] id)}
|
||||
:$file {:enumerable false :get (fn [] file-id)}
|
||||
:$page {:enumerable false :get (fn [] page-id)}
|
||||
|
||||
{:name "absolute"
|
||||
:get #(-> % u/proxy->shape :layout-item-absolute boolean)
|
||||
:set
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (boolean? value))
|
||||
(u/display-not-valid :absolute value)
|
||||
:absolute
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-item-absolute boolean)
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (boolean? value))
|
||||
(u/display-not-valid :absolute value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :absolute "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :absolute "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-item-absolute value})))))}
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-item-absolute value}))))}
|
||||
|
||||
{:name "zIndex"
|
||||
:get #(-> % u/proxy->shape :layout-item-z-index (d/nilv 0))
|
||||
:set
|
||||
(fn [self value]
|
||||
(cond
|
||||
(us/safe-int? value)
|
||||
(u/display-not-valid :zIndex value)
|
||||
:zIndex
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-item-z-index (d/nilv 0))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(us/safe-int? value)
|
||||
(u/display-not-valid :zIndex value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :zIndex "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :zIndex "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-z-index value})))))}
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-z-index value}))))}
|
||||
|
||||
{:name "horizontalSizing"
|
||||
:get #(-> % u/proxy->shape :layout-item-h-sizing (d/nilv :fix) d/name)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/item-h-sizing-types value))
|
||||
(u/display-not-valid :horizontalPadding value)
|
||||
:horizontalSizing
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-item-h-sizing (d/nilv :fix) d/name)
|
||||
:set
|
||||
(fn [_ value]
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/item-h-sizing-types value))
|
||||
(u/display-not-valid :horizontalPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :horizontalPadding "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :horizontalPadding "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-h-sizing value}))))))}
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-h-sizing value})))))}
|
||||
|
||||
{:name "verticalSizing"
|
||||
:get #(-> % u/proxy->shape :layout-item-v-sizing (d/nilv :fix) d/name)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/item-v-sizing-types value))
|
||||
(u/display-not-valid :verticalSizing value)
|
||||
:verticalSizing
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-item-v-sizing (d/nilv :fix) d/name)
|
||||
:set
|
||||
(fn [_ value]
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/item-v-sizing-types value))
|
||||
(u/display-not-valid :verticalSizing value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :verticalSizing "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :verticalSizing "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-v-sizing value}))))))}
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-v-sizing value})))))}
|
||||
|
||||
{:name "alignSelf"
|
||||
:get #(-> % u/proxy->shape :layout-item-align-self (d/nilv :auto) d/name)
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/item-align-self-types value))
|
||||
(u/display-not-valid :alignSelf value)
|
||||
:alignSelf
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-item-align-self (d/nilv :auto) d/name)
|
||||
:set
|
||||
(fn [_ value]
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/item-align-self-types value))
|
||||
(u/display-not-valid :alignSelf value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :alignSelf "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :alignSelf "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-align-self value}))))))}
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-align-self value})))))}
|
||||
|
||||
{:name "verticalMargin"
|
||||
:get #(-> % u/proxy->shape :layout-item-margin :m1 (d/nilv 0))
|
||||
:set
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(u/display-not-valid :verticalMargin value)
|
||||
:verticalMargin
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-item-margin :m1 (d/nilv 0))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(u/display-not-valid :verticalMargin value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :verticalMargin "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :verticalMargin "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m1 value :m3 value}})))))}
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m1 value :m3 value}}))))}
|
||||
|
||||
{:name "horizontalMargin"
|
||||
:get #(-> % u/proxy->shape :layout-item-margin :m2 (d/nilv 0))
|
||||
:set
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(u/display-not-valid :horizontalMargin value)
|
||||
:horizontalMargin
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-item-margin :m2 (d/nilv 0))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(u/display-not-valid :horizontalMargin value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :horizontalMargin "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :horizontalMargin "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m2 value :m4 value}})))))}
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m2 value :m4 value}}))))}
|
||||
|
||||
{:name "topMargin"
|
||||
:get #(-> % u/proxy->shape :layout-item-margin :m1 (d/nilv 0))
|
||||
:set
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(u/display-not-valid :topMargin value)
|
||||
:topMargin
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-item-margin :m1 (d/nilv 0))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(u/display-not-valid :topMargin value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :topMargin "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :topMargin "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m1 value}})))))}
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m1 value}}))))}
|
||||
|
||||
{:name "rightMargin"
|
||||
:get #(-> % u/proxy->shape :layout-item-margin :m2 (d/nilv 0))
|
||||
:set
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(u/display-not-valid :rightMargin value)
|
||||
:rightMargin
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-item-margin :m2 (d/nilv 0))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(u/display-not-valid :rightMargin value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :rightMargin "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :rightMargin "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m2 value}})))))}
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m2 value}}))))}
|
||||
|
||||
{:name "bottomMargin"
|
||||
:get #(-> % u/proxy->shape :layout-item-margin :m3 (d/nilv 0))
|
||||
:set
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(u/display-not-valid :bottomMargin value)
|
||||
:bottomMargin
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-item-margin :m3 (d/nilv 0))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(u/display-not-valid :bottomMargin value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :bottomMargin "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :bottomMargin "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m3 value}})))))}
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m3 value}}))))}
|
||||
|
||||
{:name "leftMargin"
|
||||
:get #(-> % u/proxy->shape :layout-item-margin :m4 (d/nilv 0))
|
||||
:set
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(u/display-not-valid :leftMargin value)
|
||||
:leftMargin
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-item-margin :m4 (d/nilv 0))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(u/display-not-valid :leftMargin value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :leftMargin "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :leftMargin "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m4 value}})))))}
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m4 value}}))))}
|
||||
|
||||
{:name "maxWidth"
|
||||
:get #(-> % u/proxy->shape :layout-item-max-w)
|
||||
:set
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(u/display-not-valid :maxWidth value)
|
||||
:maxWidth
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-item-max-w)
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(u/display-not-valid :maxWidth value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :maxWidth "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :maxWidth "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-max-w value})))))}
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-max-w value}))))}
|
||||
|
||||
{:name "minWidth"
|
||||
:get #(-> % u/proxy->shape :layout-item-min-w)
|
||||
:set
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(u/display-not-valid :minWidth value)
|
||||
:minWidth
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-item-min-w)
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(u/display-not-valid :minWidth value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :minWidth "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :minWidth "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-min-w value})))))}
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-min-w value}))))}
|
||||
|
||||
{:name "maxHeight"
|
||||
:get #(-> % u/proxy->shape :layout-item-max-h)
|
||||
:set
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(u/display-not-valid :maxHeight value)
|
||||
:maxHeight
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-item-max-h)
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(u/display-not-valid :maxHeight value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :maxHeight "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :maxHeight "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-max-h value})))))}
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-max-h value}))))}
|
||||
|
||||
{:name "minHeight"
|
||||
:get #(-> % u/proxy->shape :layout-item-min-h)
|
||||
:set
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(u/display-not-valid :minHeight value)
|
||||
:minHeight
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape :layout-item-min-h)
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(u/display-not-valid :minHeight value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :minHeight "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :minHeight "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-min-h value})))))})))
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-min-h value}))))}))
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
(ns app.plugins.fonts
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.record :as cr]
|
||||
[app.main.data.workspace.texts :as dwt]
|
||||
[app.main.fonts :as fonts]
|
||||
[app.main.store :as st]
|
||||
[app.plugins.format :as format]
|
||||
[app.plugins.register :as r]
|
||||
[app.plugins.shape :as shape]
|
||||
[app.plugins.text :as text]
|
||||
@@ -18,117 +18,133 @@
|
||||
[app.util.object :as obj]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
(deftype PenpotFontVariant [name fontVariantId fontWeight fontStyle])
|
||||
(defn font-variant-proxy? [p]
|
||||
(obj/type-of? p "FontVariantProxy"))
|
||||
|
||||
(defn variant-proxy? [p]
|
||||
(instance? PenpotFontVariant p))
|
||||
|
||||
(deftype PenpotFont [name fontId fontFamily fontStyle fontVariantId fontWeight variants]
|
||||
Object
|
||||
|
||||
(applyToText [_ text variant]
|
||||
(cond
|
||||
(not (shape/shape-proxy? text))
|
||||
(u/display-not-valid :applyToText text)
|
||||
|
||||
(not (r/check-permission (obj/get text "$plugin") "content:write"))
|
||||
(u/display-not-valid :applyToText "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get text "$id")
|
||||
values {:font-id fontId
|
||||
:font-family fontFamily
|
||||
:font-style (d/nilv (obj/get variant "fontStyle") fontStyle)
|
||||
:font-variant-id (d/nilv (obj/get variant "fontVariantId") fontVariantId)
|
||||
:font-weight (d/nilv (obj/get variant "fontWeight") fontWeight)}]
|
||||
(st/emit! (dwt/update-attrs id values)))))
|
||||
|
||||
(applyToRange [_ range variant]
|
||||
(cond
|
||||
(not (text/text-range? range))
|
||||
(u/display-not-valid :applyToRange range)
|
||||
|
||||
(not (r/check-permission (obj/get range "$plugin") "content:write"))
|
||||
(u/display-not-valid :applyToRange "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get range "$id")
|
||||
start (obj/get range "start")
|
||||
end (obj/get range "end")
|
||||
values {:font-id fontId
|
||||
:font-family fontFamily
|
||||
:font-style (d/nilv (obj/get variant "fontStyle") fontStyle)
|
||||
:font-variant-id (d/nilv (obj/get variant "fontVariantId") fontVariantId)
|
||||
:font-weight (d/nilv (obj/get variant "fontWeight") fontWeight)}]
|
||||
(st/emit! (dwt/update-text-range id start end values))))))
|
||||
(defn font-variant-proxy [name id weight style]
|
||||
(obj/reify {:name "FontVariantProxy"}
|
||||
:name {:get (fn [] name)}
|
||||
:fontVariantId {:get (fn [] id)}
|
||||
:fontWeight {:get (fn [] weight)}
|
||||
:fontStyle {:get (fn [] style)}))
|
||||
|
||||
(defn font-proxy? [p]
|
||||
(instance? PenpotFont p))
|
||||
(obj/type-of? p "FontProxy"))
|
||||
|
||||
(defn font-proxy
|
||||
[{:keys [id family name variants] :as font}]
|
||||
(when (some? font)
|
||||
(let [default-variant (fonts/get-default-variant font)]
|
||||
(PenpotFont.
|
||||
name
|
||||
id
|
||||
family
|
||||
(:style default-variant)
|
||||
(:id default-variant)
|
||||
(:weight default-variant)
|
||||
(apply
|
||||
array
|
||||
(->> variants
|
||||
(map (fn [{:keys [id name style weight]}]
|
||||
(PenpotFontVariant. name id weight style)))))))))
|
||||
(obj/reify {:name "FontProxy"}
|
||||
:name {:get (fn [] name)}
|
||||
:fontId {:get (fn [] id)}
|
||||
:fontFamily {:get (fn [] family)}
|
||||
:fontStyle {:get (fn [] (:style default-variant))}
|
||||
:fontVariantId {:get (fn [] (:id default-variant))}
|
||||
:fontWeight {:get (fn [] (:weight default-variant))}
|
||||
|
||||
(deftype PenpotFontsSubcontext [$plugin]
|
||||
Object
|
||||
(findById
|
||||
[_ id]
|
||||
(cond
|
||||
(not (string? id))
|
||||
(u/display-not-valid :findbyId id)
|
||||
:variants
|
||||
{:get
|
||||
(fn []
|
||||
(format/format-array
|
||||
(fn [{:keys [id name style weight]}]
|
||||
(font-variant-proxy name id weight style))
|
||||
variants))}
|
||||
|
||||
:else
|
||||
(font-proxy (d/seek #(str/includes? (str/lower (:id %)) (str/lower id)) (vals @fonts/fontsdb)))))
|
||||
:applyToText
|
||||
(fn [text variant]
|
||||
(cond
|
||||
(not (shape/shape-proxy? text))
|
||||
(u/display-not-valid :applyToText text)
|
||||
|
||||
(findByName
|
||||
[_ name]
|
||||
(cond
|
||||
(not (string? name))
|
||||
(u/display-not-valid :findByName name)
|
||||
(not (r/check-permission (obj/get text "$plugin") "content:write"))
|
||||
(u/display-not-valid :applyToText "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(font-proxy (d/seek #(str/includes? (str/lower (:name %)) (str/lower name)) (vals @fonts/fontsdb)))))
|
||||
:else
|
||||
(let [id (obj/get text "$id")
|
||||
values {:font-id id
|
||||
:font-family family
|
||||
:font-style (d/nilv (obj/get variant "fontStyle") (:style default-variant))
|
||||
:font-variant-id (d/nilv (obj/get variant "fontVariantId") (:id default-variant))
|
||||
:font-weight (d/nilv (obj/get variant "fontWeight") (:wegith default-variant))}]
|
||||
(st/emit! (dwt/update-attrs id values)))))
|
||||
|
||||
(findAllById
|
||||
[_ id]
|
||||
(cond
|
||||
(not (string? id))
|
||||
(u/display-not-valid :findAllById name)
|
||||
:applyToRange
|
||||
(fn [range variant]
|
||||
(cond
|
||||
(not (text/text-range-proxy? range))
|
||||
(u/display-not-valid :applyToRange range)
|
||||
|
||||
:else
|
||||
(apply array (->> (vals @fonts/fontsdb)
|
||||
(filter #(str/includes? (str/lower (:id %)) (str/lower id)))
|
||||
(map font-proxy)))))
|
||||
(not (r/check-permission (obj/get range "$plugin") "content:write"))
|
||||
(u/display-not-valid :applyToRange "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
(findAllByName
|
||||
[_ name]
|
||||
(cond
|
||||
(not (string? name))
|
||||
(u/display-not-valid :findAllByName name)
|
||||
|
||||
:else
|
||||
(apply array (->> (vals @fonts/fontsdb)
|
||||
(filter #(str/includes? (str/lower (:name %)) (str/lower name)))
|
||||
(map font-proxy))))))
|
||||
:else
|
||||
(let [id (obj/get range "$id")
|
||||
start (obj/get range "start")
|
||||
end (obj/get range "end")
|
||||
values {:font-id id
|
||||
:font-family family
|
||||
:font-style (d/nilv (obj/get variant "fontStyle") (:style default-variant))
|
||||
:font-variant-id (d/nilv (obj/get variant "fontVariantId") (:id default-variant))
|
||||
:font-weight (d/nilv (obj/get variant "fontWeight") (:weight default-variant))}]
|
||||
(st/emit! (dwt/update-text-range id start end values)))))))))
|
||||
|
||||
(defn fonts-subcontext
|
||||
[plugin-id]
|
||||
(cr/add-properties!
|
||||
(PenpotFontsSubcontext. plugin-id)
|
||||
{:name "$plugin" :enumerable false :get (constantly plugin-id)}
|
||||
{:name "all" :get
|
||||
(fn [_]
|
||||
(apply array (->> @fonts/fontsdb vals (map font-proxy))))}))
|
||||
(obj/reify {:name "PenpotFontsSubcontext"}
|
||||
:$plugin {:name "" :enumerable false :get (constantly plugin-id)}
|
||||
|
||||
:all
|
||||
{:get
|
||||
(fn []
|
||||
(format/format-array
|
||||
font-proxy
|
||||
(vals @fonts/fontsdb)))}
|
||||
|
||||
:findById
|
||||
(fn [id]
|
||||
(cond
|
||||
(not (string? id))
|
||||
(u/display-not-valid :findbyId id)
|
||||
|
||||
:else
|
||||
(->> (vals @fonts/fontsdb)
|
||||
(d/seek #(str/includes? (str/lower (:id %)) (str/lower id)))
|
||||
(font-proxy))))
|
||||
|
||||
:findByName
|
||||
(fn [name]
|
||||
(cond
|
||||
(not (string? name))
|
||||
(u/display-not-valid :findByName name)
|
||||
|
||||
:else
|
||||
(->> (vals @fonts/fontsdb)
|
||||
(d/seek #(str/includes? (str/lower (:name %)) (str/lower name)))
|
||||
(font-proxy))))
|
||||
|
||||
:findAllById
|
||||
(fn [id]
|
||||
(cond
|
||||
(not (string? id))
|
||||
(u/display-not-valid :findAllById name)
|
||||
|
||||
:else
|
||||
(format/format-array
|
||||
(fn [font]
|
||||
(when (str/includes? (str/lower (:id font)) (str/lower id))
|
||||
(font-proxy font)))
|
||||
(vals @fonts/fontsdb))))
|
||||
|
||||
:findAllByName
|
||||
(fn [name]
|
||||
(cond
|
||||
(not (string? name))
|
||||
(u/display-not-valid :findAllByName name)
|
||||
|
||||
:else
|
||||
(format/format-array
|
||||
(fn [font]
|
||||
(when (str/includes? (str/lower (:name font)) (str/lower name))
|
||||
(font-proxy font)))
|
||||
(vals @fonts/fontsdb))))))
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
;; height: number;
|
||||
;; mtype?: string;
|
||||
;; id: string;
|
||||
;; keepApectRatio?: boolean;
|
||||
;; keepAspectRatio?: boolean;
|
||||
;; };
|
||||
(defn format-image
|
||||
[{:keys [name width height mtype id keep-aspect-ratio] :as image}]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,47 +6,41 @@
|
||||
|
||||
(ns app.plugins.history
|
||||
(:require
|
||||
[app.common.record :as crc]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.store :as st]
|
||||
[app.plugins.register :as r]
|
||||
[app.plugins.utils :as u]))
|
||||
|
||||
(deftype HistorySubcontext [$plugin]
|
||||
Object
|
||||
(undoBlockBegin
|
||||
[_]
|
||||
(cond
|
||||
(not (r/check-permission $plugin "content:write"))
|
||||
(u/display-not-valid :resize "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (js/Symbol)]
|
||||
(st/emit! (dwu/start-undo-transaction id))
|
||||
id)))
|
||||
|
||||
(undoBlockFinish
|
||||
[_ block-id]
|
||||
(cond
|
||||
(not (r/check-permission $plugin "content:write"))
|
||||
(u/display-not-valid :resize "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
(not block-id)
|
||||
(u/display-not-valid :undoBlockFinish block-id)
|
||||
|
||||
:else
|
||||
(st/emit! (dwu/commit-undo-transaction block-id)))))
|
||||
|
||||
(crc/define-properties!
|
||||
HistorySubcontext
|
||||
{:name js/Symbol.toStringTag
|
||||
:get (fn [] (str "HistorySubcontext"))})
|
||||
[app.plugins.utils :as u]
|
||||
[app.util.object :as obj]))
|
||||
|
||||
(defn history-subcontext? [p]
|
||||
(instance? HistorySubcontext p))
|
||||
(obj/type-of? p "HistorySubcontext"))
|
||||
|
||||
(defn history-subcontext
|
||||
[plugin-id]
|
||||
(HistorySubcontext. plugin-id))
|
||||
(obj/reify {:name "HistorySubcontext"}
|
||||
:$plugin {:enumerable false :get (fn [] plugin-id)}
|
||||
|
||||
:undoBlockBegin
|
||||
(fn []
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :resize "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (js/Symbol)]
|
||||
(st/emit! (dwu/start-undo-transaction id))
|
||||
id)))
|
||||
|
||||
:undoBlockFinish
|
||||
(fn [block-id]
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :resize "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
(not block-id)
|
||||
(u/display-not-valid :undoBlockFinish block-id)
|
||||
|
||||
:else
|
||||
(st/emit! (dwu/commit-undo-transaction block-id))))))
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,6 @@
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.files.helpers :as cfh]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.record :as crc]
|
||||
[app.common.spec :as us]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.comments :as dc]
|
||||
@@ -31,390 +30,394 @@
|
||||
[beicon.v2.core :as rx]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
(deftype FlowProxy [$plugin $file $page $id]
|
||||
Object
|
||||
(remove [_]
|
||||
(st/emit! (dwi/remove-flow $page $id))))
|
||||
(declare page-proxy)
|
||||
|
||||
(defn flow-proxy? [p]
|
||||
(instance? FlowProxy p))
|
||||
(obj/type-of? p "FlowProxy"))
|
||||
|
||||
(defn flow-proxy
|
||||
[plugin-id file-id page-id id]
|
||||
(crc/add-properties!
|
||||
(FlowProxy. plugin-id file-id page-id id)
|
||||
{:name "$plugin" :enumerable false :get (constantly plugin-id)}
|
||||
{:name "$file" :enumerable false :get (constantly file-id)}
|
||||
{:name "$page" :enumerable false :get (constantly page-id)}
|
||||
{:name "$id" :enumerable false :get (constantly id)}
|
||||
{:name "page" :enumerable false :get (fn [_] (u/locate-page file-id page-id))}
|
||||
(obj/reify {:name "FlowProxy"}
|
||||
:$plugin {:enumerable false :get (fn [] plugin-id)}
|
||||
:$file {:enumerable false :get (fn [] file-id)}
|
||||
:$page {:enumerable false :get (fn [] page-id)}
|
||||
:$id {:enumerable false :get (fn [] id)}
|
||||
|
||||
{:name "name"
|
||||
:get #(-> % u/proxy->flow :name)
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(or (not (string? value)) (empty? value))
|
||||
(u/display-not-valid :name value)
|
||||
:page
|
||||
{:enumerable false
|
||||
:get
|
||||
(fn []
|
||||
(page-proxy plugin-id file-id page-id))}
|
||||
|
||||
:else
|
||||
(st/emit! (dwi/update-flow page-id id #(assoc % :name value)))))}
|
||||
:name
|
||||
{:this true
|
||||
:get #(-> % u/proxy->flow :name)
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(or (not (string? value)) (empty? value))
|
||||
(u/display-not-valid :name value)
|
||||
|
||||
{:name "startingBoard"
|
||||
:get
|
||||
(fn [self]
|
||||
(let [frame (-> self u/proxy->flow :starting-frame)]
|
||||
(u/locate-shape file-id page-id frame)))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (shape/shape-proxy? value))
|
||||
(u/display-not-valid :startingBoard value)
|
||||
:else
|
||||
(st/emit! (dwi/update-flow page-id id #(assoc % :name value)))))}
|
||||
|
||||
:else
|
||||
(st/emit! (dwi/update-flow page-id id #(assoc % :starting-frame (obj/get value "$id"))))))}))
|
||||
:startingBoard
|
||||
{:this true
|
||||
:get
|
||||
(fn [self]
|
||||
(when-let [frame (-> self u/proxy->flow :starting-frame)]
|
||||
(shape/shape-proxy file-id page-id frame)))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (shape/shape-proxy? value))
|
||||
(u/display-not-valid :startingBoard value)
|
||||
|
||||
(deftype PageProxy [$plugin $file $id]
|
||||
Object
|
||||
(getShapeById
|
||||
[_ shape-id]
|
||||
(cond
|
||||
(not (string? shape-id))
|
||||
(u/display-not-valid :getShapeById shape-id)
|
||||
:else
|
||||
(st/emit! (dwi/update-flow page-id id #(assoc % :starting-frame (obj/get value "$id"))))))}
|
||||
|
||||
:else
|
||||
(let [shape-id (uuid/uuid shape-id)
|
||||
shape (u/locate-shape $file $id shape-id)]
|
||||
(when (some? shape)
|
||||
(shape/shape-proxy $plugin $file $id shape-id)))))
|
||||
:remove
|
||||
(fn []
|
||||
(st/emit! (dwi/remove-flow page-id id)))))
|
||||
|
||||
(getRoot
|
||||
[_]
|
||||
(shape/shape-proxy $plugin $file $id uuid/zero))
|
||||
|
||||
(findShapes
|
||||
[_ criteria]
|
||||
;; Returns a lazy (iterable) of all available shapes
|
||||
(let [criteria (parser/parse-criteria criteria)
|
||||
match-criteria?
|
||||
(if (some? criteria)
|
||||
(fn [[_ shape]]
|
||||
(and
|
||||
(or (not (:name criteria))
|
||||
(= (str/lower (:name criteria)) (str/lower (:name shape))))
|
||||
|
||||
(or (not (:name-like criteria))
|
||||
(str/includes? (str/lower (:name shape)) (str/lower (:name-like criteria))))
|
||||
|
||||
(or (not (:type criteria))
|
||||
(= (:type criteria) (:type shape)))))
|
||||
identity)]
|
||||
(when (and (some? $file) (some? $id))
|
||||
(let [page (u/locate-page $file $id)
|
||||
xf (comp
|
||||
(filter match-criteria?)
|
||||
(map #(shape/shape-proxy $plugin $file $id (first %))))]
|
||||
(apply array (sequence xf (:objects page)))))))
|
||||
|
||||
;; Plugin data
|
||||
(getPluginData
|
||||
[self key]
|
||||
(cond
|
||||
(not (string? key))
|
||||
(u/display-not-valid :page-plugin-data-key key)
|
||||
|
||||
:else
|
||||
(let [page (u/proxy->page self)]
|
||||
(dm/get-in page [:plugin-data (keyword "plugin" (str $plugin)) key]))))
|
||||
|
||||
(setPluginData
|
||||
[_ key value]
|
||||
(cond
|
||||
(not (string? key))
|
||||
(u/display-not-valid :setPluginData-key key)
|
||||
|
||||
(and (some? value) (not (string? value)))
|
||||
(u/display-not-valid :setPluginData-value value)
|
||||
|
||||
(not (r/check-permission $plugin "content:write"))
|
||||
(u/display-not-valid :setPluginData "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dw/set-plugin-data $file :page $id (keyword "plugin" (str $plugin)) key value))))
|
||||
|
||||
(getPluginDataKeys
|
||||
[self]
|
||||
(let [page (u/proxy->page self)]
|
||||
(apply array (keys (dm/get-in page [:plugin-data (keyword "plugin" (str $plugin))])))))
|
||||
|
||||
(getSharedPluginData
|
||||
[self namespace key]
|
||||
(cond
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :page-plugin-data-namespace namespace)
|
||||
|
||||
(not (string? key))
|
||||
(u/display-not-valid :page-plugin-data-key key)
|
||||
|
||||
:else
|
||||
(let [page (u/proxy->page self)]
|
||||
(dm/get-in page [:plugin-data (keyword "shared" namespace) key]))))
|
||||
|
||||
(setSharedPluginData
|
||||
[_ namespace key value]
|
||||
|
||||
(cond
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :setSharedPluginData-namespace namespace)
|
||||
|
||||
(not (string? key))
|
||||
(u/display-not-valid :setSharedPluginData-key key)
|
||||
|
||||
(and (some? value) (not (string? value)))
|
||||
(u/display-not-valid :setSharedPluginData-value value)
|
||||
|
||||
(not (r/check-permission $plugin "content:write"))
|
||||
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dw/set-plugin-data $file :page $id (keyword "shared" namespace) key value))))
|
||||
|
||||
(getSharedPluginDataKeys
|
||||
[self namespace]
|
||||
(cond
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :page-plugin-data-namespace namespace)
|
||||
|
||||
:else
|
||||
(let [page (u/proxy->page self)]
|
||||
(apply array (keys (dm/get-in page [:plugin-data (keyword "shared" namespace)]))))))
|
||||
|
||||
(openPage
|
||||
[_]
|
||||
(cond
|
||||
(not (r/check-permission $plugin "content:read"))
|
||||
(u/display-not-valid :openPage "Plugin doesn't have 'content:read' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dw/go-to-page $id))))
|
||||
|
||||
(createFlow
|
||||
[_ name frame]
|
||||
(cond
|
||||
(or (not (string? name)) (empty? name))
|
||||
(u/display-not-valid :createFlow-name name)
|
||||
|
||||
(not (shape/shape-proxy? frame))
|
||||
(u/display-not-valid :createFlow-frame frame)
|
||||
|
||||
:else
|
||||
(let [flow-id (uuid/next)]
|
||||
(st/emit! (dwi/add-flow flow-id $id name (obj/get frame "$id")))
|
||||
(flow-proxy $plugin $file $id flow-id))))
|
||||
|
||||
(removeFlow
|
||||
[_ flow]
|
||||
(cond
|
||||
(not (flow-proxy? flow))
|
||||
(u/display-not-valid :removeFlow-flow flow)
|
||||
|
||||
:else
|
||||
(st/emit! (dwi/remove-flow $id (obj/get flow "$id")))))
|
||||
|
||||
(addRulerGuide
|
||||
[_ orientation value board]
|
||||
(let [shape (u/proxy->shape board)]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(u/display-not-valid :addRulerGuide "Value not a safe number")
|
||||
|
||||
(not (contains? #{"vertical" "horizontal"} orientation))
|
||||
(u/display-not-valid :addRulerGuide "Orientation should be either 'vertical' or 'horizontal'")
|
||||
|
||||
(and (some? shape)
|
||||
(or (not (shape/shape-proxy? board))
|
||||
(not (cfh/frame-shape? shape))))
|
||||
(u/display-not-valid :addRulerGuide "The shape is not a board")
|
||||
|
||||
(not (r/check-permission $plugin "content:write"))
|
||||
(u/display-not-valid :addRulerGuide "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (uuid/next)]
|
||||
(st/emit!
|
||||
(dwgu/update-guides
|
||||
(d/without-nils
|
||||
{:id id
|
||||
:axis (parser/orientation->axis orientation)
|
||||
:position value
|
||||
:frame-id (when board (obj/get board "$id"))})))
|
||||
(rg/ruler-guide-proxy $plugin $file $id id)))))
|
||||
|
||||
(removeRulerGuide
|
||||
[_ value]
|
||||
(cond
|
||||
(not (rg/ruler-guide-proxy? value))
|
||||
(u/display-not-valid :removeRulerGuide "Guide not provided")
|
||||
|
||||
(not (r/check-permission $plugin "content:write"))
|
||||
(u/display-not-valid :removeRulerGuide "Plugin doesn't have 'comment:write' permission")
|
||||
|
||||
:else
|
||||
(let [guide (u/proxy->ruler-guide value)]
|
||||
(st/emit! (dwgu/remove-guide guide)))))
|
||||
|
||||
(addCommentThread
|
||||
[_ content position board]
|
||||
(let [shape (when board (u/proxy->shape board))
|
||||
position (parser/parse-point position)]
|
||||
(cond
|
||||
(or (not (string? content)) (empty? content))
|
||||
(u/display-not-valid :addCommentThread "Content not valid")
|
||||
|
||||
(or (not (us/safe-number? (:x position)))
|
||||
(not (us/safe-number? (:y position))))
|
||||
(u/display-not-valid :addCommentThread "Position not valid")
|
||||
|
||||
(and (some? board) (or (not (shape/shape-proxy? board)) (not (cfh/frame-shape? shape))))
|
||||
(u/display-not-valid :addCommentThread "Board not valid")
|
||||
|
||||
(not (r/check-permission $plugin "comment:write"))
|
||||
(u/display-not-valid :addCommentThread "Plugin doesn't have 'comment:write' permission")
|
||||
|
||||
:else
|
||||
(let [position
|
||||
(cond-> position
|
||||
(some? board)
|
||||
(-> (update :x - (:x board))
|
||||
(update :y - (:y board))))]
|
||||
(js/Promise.
|
||||
(fn [resolve]
|
||||
(st/emit!
|
||||
(dc/create-thread-on-workspace
|
||||
{:file-id $file
|
||||
:page-id $id
|
||||
:position (gpt/point position)
|
||||
:content content}
|
||||
|
||||
(fn [data]
|
||||
(->> (rp/cmd! :get-team-users {:file-id $file})
|
||||
(rx/subs!
|
||||
(fn [users]
|
||||
(let [users (d/index-by :id users)]
|
||||
(resolve (pc/comment-thread-proxy $plugin $file $id users data)))))))
|
||||
false))))))))
|
||||
|
||||
(removeCommentThread
|
||||
[_ thread]
|
||||
(cond
|
||||
(not (pc/comment-thread-proxy? thread))
|
||||
(u/display-not-valid :removeCommentThread "Comment thread not valid")
|
||||
|
||||
(not (r/check-permission $plugin "comment:write"))
|
||||
(u/display-not-valid :removeCommentThread "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(js/Promise.
|
||||
(fn [resolve]
|
||||
(let [thread-id (obj/get thread "$id")]
|
||||
(js/Promise.
|
||||
(st/emit! (dc/delete-comment-thread-on-workspace {:id thread-id} #(resolve)))))))))
|
||||
|
||||
(findCommentThreads
|
||||
[_ criteria]
|
||||
(let [only-yours (boolean (obj/get criteria "onlyYours" false))
|
||||
show-resolved (boolean (obj/get criteria "showResolved" true))
|
||||
user-id (-> @st/state :profile :id)]
|
||||
(js/Promise.
|
||||
(fn [resolve reject]
|
||||
(cond
|
||||
(not (r/check-permission $plugin "comment:read"))
|
||||
(do
|
||||
(u/display-not-valid :findCommentThreads "Plugin doesn't have 'comment:read' permission")
|
||||
(reject "Plugin doesn't have 'comment:read' permission"))
|
||||
|
||||
:else
|
||||
(->> (rx/zip (rp/cmd! :get-team-users {:file-id $file})
|
||||
(rp/cmd! :get-comment-threads {:file-id $file}))
|
||||
(rx/take 1)
|
||||
(rx/subs!
|
||||
(fn [[users comments]]
|
||||
(let [users (d/index-by :id users)
|
||||
comments
|
||||
(cond->> comments
|
||||
(not show-resolved)
|
||||
(filter (comp not :is-resolved))
|
||||
|
||||
only-yours
|
||||
(filter #(contains? (:participants %) user-id)))]
|
||||
(resolve
|
||||
(format/format-array
|
||||
#(pc/comment-thread-proxy $plugin $file $id users %) comments))))
|
||||
reject))))))))
|
||||
|
||||
(crc/define-properties!
|
||||
PageProxy
|
||||
{:name js/Symbol.toStringTag
|
||||
:get (fn [] (str "PageProxy"))})
|
||||
|
||||
(defn page-proxy? [p]
|
||||
(instance? PageProxy p))
|
||||
(defn page-proxy? [proxy]
|
||||
(obj/type-of? proxy "PageProxy"))
|
||||
|
||||
(defn page-proxy
|
||||
[plugin-id file-id id]
|
||||
(crc/add-properties!
|
||||
(PageProxy. plugin-id file-id id)
|
||||
{:name "$plugin" :enumerable false :get (constantly plugin-id)}
|
||||
{:name "$id" :enumerable false :get (constantly id)}
|
||||
{:name "$file" :enumerable false :get (constantly file-id)}
|
||||
(obj/reify {:name "PageProxy"}
|
||||
:$plugin {:enumerable false :get (fn [] plugin-id)}
|
||||
:$file {:enumerable false :get (fn [] file-id)}
|
||||
:$id {:enumerable false :get (fn [] id)}
|
||||
|
||||
{:name "id"
|
||||
:get #(dm/str (obj/get % "$id"))}
|
||||
:id
|
||||
{:get #(dm/str id)}
|
||||
|
||||
{:name "name"
|
||||
:get #(-> % u/proxy->page :name)
|
||||
:set
|
||||
(fn [_ value]
|
||||
:name
|
||||
{:this true
|
||||
:get #(-> % u/proxy->page :name)
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :name value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :name "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dw/rename-page id value))))}
|
||||
|
||||
:getRoot
|
||||
(fn []
|
||||
(shape/shape-proxy plugin-id file-id id uuid/zero))
|
||||
|
||||
:root
|
||||
{:this true
|
||||
:enumerable false
|
||||
:get #(.getRoot ^js %)}
|
||||
|
||||
:background
|
||||
{:this true
|
||||
:get #(or (-> % u/proxy->page :background) cc/canvas)
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(or (not (string? value)) (not (cc/valid-hex-color? value)))
|
||||
(u/display-not-valid :background value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :background "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dw/change-canvas-color id {:color value}))))}
|
||||
|
||||
:flows
|
||||
{:this true
|
||||
:get
|
||||
(fn [self]
|
||||
(let [flows (d/nilv (-> (u/proxy->page self) :flows) [])]
|
||||
(->> (vals flows)
|
||||
(format/format-array #(flow-proxy plugin-id file-id id (:id %))))))}
|
||||
|
||||
:rulerGuides
|
||||
{:this true
|
||||
:get
|
||||
(fn [self]
|
||||
(let [guides (-> (u/proxy->page self) :guides)]
|
||||
(->> guides
|
||||
(vals)
|
||||
(filter #(nil? (:frame-id %)))
|
||||
(format/format-array #(rg/ruler-guide-proxy plugin-id file-id id (:id %))))))}
|
||||
|
||||
|
||||
:getShapeById
|
||||
(fn [shape-id]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :name value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :name "Plugin doesn't have 'content:write' permission")
|
||||
(not (string? shape-id))
|
||||
(u/display-not-valid :getShapeById shape-id)
|
||||
|
||||
:else
|
||||
(st/emit! (dw/rename-page id value))))}
|
||||
(let [shape-id (uuid/uuid shape-id)
|
||||
shape (u/locate-shape file-id id shape-id)]
|
||||
(when (some? shape)
|
||||
(shape/shape-proxy plugin-id file-id id shape-id)))))
|
||||
|
||||
{:name "root"
|
||||
:enumerable false
|
||||
:get #(.getRoot ^js %)}
|
||||
:findShapes
|
||||
(fn [criteria]
|
||||
;; Returns a lazy (iterable) of all available shapes
|
||||
(let [criteria (parser/parse-criteria criteria)
|
||||
match-criteria?
|
||||
(if (some? criteria)
|
||||
(fn [[_ shape]]
|
||||
(and
|
||||
(or (not (:name criteria))
|
||||
(= (str/lower (:name criteria)) (str/lower (:name shape))))
|
||||
|
||||
{:name "background"
|
||||
:enumerable false
|
||||
:get #(or (-> % u/proxy->page :background) cc/canvas)
|
||||
:set
|
||||
(fn [_ value]
|
||||
(or (not (:name-like criteria))
|
||||
(str/includes? (str/lower (:name shape)) (str/lower (:name-like criteria))))
|
||||
|
||||
(or (not (:type criteria))
|
||||
(= (:type criteria) (:type shape)))))
|
||||
identity)]
|
||||
(when (and (some? file-id) (some? id))
|
||||
(let [page (u/locate-page file-id id)
|
||||
xf (comp
|
||||
(filter match-criteria?)
|
||||
(map #(shape/shape-proxy plugin-id file-id id (first %))))]
|
||||
(apply array (sequence xf (:objects page)))))))
|
||||
|
||||
;; Plugin data
|
||||
:getPluginData
|
||||
(fn [key]
|
||||
(cond
|
||||
(or (not (string? value)) (not (cc/valid-hex-color? value)))
|
||||
(u/display-not-valid :background value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :background "Plugin doesn't have 'content:write' permission")
|
||||
(not (string? key))
|
||||
(u/display-not-valid :page-plugin-data-key key)
|
||||
|
||||
:else
|
||||
(st/emit! (dw/change-canvas-color id {:color value}))))}
|
||||
(let [page (u/locate-page file-id id)]
|
||||
(dm/get-in page [:plugin-data (keyword "plugin" (str plugin-id)) key]))))
|
||||
|
||||
{:name "flows"
|
||||
:get
|
||||
(fn [self]
|
||||
(let [flows (d/nilv (-> (u/proxy->page self) :flows) [])]
|
||||
(format/format-array #(flow-proxy plugin-id file-id id (:id %)) flows)))}
|
||||
:setPluginData
|
||||
(fn [key value]
|
||||
(cond
|
||||
(not (string? key))
|
||||
(u/display-not-valid :setPluginData-key key)
|
||||
|
||||
{:name "rulerGuides"
|
||||
:get
|
||||
(fn [self]
|
||||
(let [guides (-> (u/proxy->page self) :guides)]
|
||||
(->> guides
|
||||
(vals)
|
||||
(filter #(nil? (:frame-id %)))
|
||||
(format/format-array #(rg/ruler-guide-proxy plugin-id file-id id (:id %))))))}))
|
||||
(and (some? value) (not (string? value)))
|
||||
(u/display-not-valid :setPluginData-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :setPluginData "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dw/set-plugin-data file-id :page id (keyword "plugin" (str plugin-id)) key value))))
|
||||
|
||||
:getPluginDataKeys
|
||||
(fn []
|
||||
(let [page (u/locate-page file-id id)]
|
||||
(apply array (keys (dm/get-in page [:plugin-data (keyword "plugin" (str plugin-id))])))))
|
||||
|
||||
:getSharedPluginData
|
||||
(fn [namespace key]
|
||||
(cond
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :page-plugin-data-namespace namespace)
|
||||
|
||||
(not (string? key))
|
||||
(u/display-not-valid :page-plugin-data-key key)
|
||||
|
||||
:else
|
||||
(let [page (u/locate-page file-id id)]
|
||||
(dm/get-in page [:plugin-data (keyword "shared" namespace) key]))))
|
||||
|
||||
:setSharedPluginData
|
||||
(fn [namespace key value]
|
||||
(cond
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :setSharedPluginData-namespace namespace)
|
||||
|
||||
(not (string? key))
|
||||
(u/display-not-valid :setSharedPluginData-key key)
|
||||
|
||||
(and (some? value) (not (string? value)))
|
||||
(u/display-not-valid :setSharedPluginData-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dw/set-plugin-data file-id :page id (keyword "shared" namespace) key value))))
|
||||
|
||||
:getSharedPluginDataKeys
|
||||
(fn [self namespace]
|
||||
(cond
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :page-plugin-data-namespace namespace)
|
||||
|
||||
:else
|
||||
(let [page (u/proxy->page self)]
|
||||
(apply array (keys (dm/get-in page [:plugin-data (keyword "shared" namespace)]))))))
|
||||
|
||||
:openPage
|
||||
(fn []
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "content:read"))
|
||||
(u/display-not-valid :openPage "Plugin doesn't have 'content:read' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dw/go-to-page id))))
|
||||
|
||||
:createFlow
|
||||
(fn [name frame]
|
||||
(cond
|
||||
(or (not (string? name)) (empty? name))
|
||||
(u/display-not-valid :createFlow-name name)
|
||||
|
||||
(not (shape/shape-proxy? frame))
|
||||
(u/display-not-valid :createFlow-frame frame)
|
||||
|
||||
:else
|
||||
(let [flow-id (uuid/next)]
|
||||
(st/emit! (dwi/add-flow flow-id id name (obj/get frame "$id")))
|
||||
(flow-proxy plugin-id file-id id flow-id))))
|
||||
|
||||
:removeFlow
|
||||
(fn [flow]
|
||||
(cond
|
||||
(not (flow-proxy? flow))
|
||||
(u/display-not-valid :removeFlow-flow flow)
|
||||
|
||||
:else
|
||||
(st/emit! (dwi/remove-flow id (obj/get flow "$id")))))
|
||||
|
||||
:addRulerGuide
|
||||
(fn [orientation value board]
|
||||
(let [shape (u/proxy->shape board)]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(u/display-not-valid :addRulerGuide "Value not a safe number")
|
||||
|
||||
(not (contains? #{"vertical" "horizontal"} orientation))
|
||||
(u/display-not-valid :addRulerGuide "Orientation should be either 'vertical' or 'horizontal'")
|
||||
|
||||
(and (some? shape)
|
||||
(or (not (shape/shape-proxy? board))
|
||||
(not (cfh/frame-shape? shape))))
|
||||
(u/display-not-valid :addRulerGuide "The shape is not a board")
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :addRulerGuide "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [ruler-id (uuid/next)]
|
||||
(st/emit!
|
||||
(dwgu/update-guides
|
||||
(d/without-nils
|
||||
{:id ruler-id
|
||||
:axis (parser/orientation->axis orientation)
|
||||
:position value
|
||||
:frame-id (when board (obj/get board "$id"))})))
|
||||
(rg/ruler-guide-proxy plugin-id file-id id ruler-id)))))
|
||||
|
||||
:removeRulerGuide
|
||||
(fn [value]
|
||||
(cond
|
||||
(not (rg/ruler-guide-proxy? value))
|
||||
(u/display-not-valid :removeRulerGuide "Guide not provided")
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :removeRulerGuide "Plugin doesn't have 'comment:write' permission")
|
||||
|
||||
:else
|
||||
(let [guide (u/proxy->ruler-guide value)]
|
||||
(st/emit! (dwgu/remove-guide guide)))))
|
||||
|
||||
:addCommentThread
|
||||
(fn [content position board]
|
||||
(let [shape (when board (u/proxy->shape board))
|
||||
position (parser/parse-point position)]
|
||||
(cond
|
||||
(or (not (string? content)) (empty? content))
|
||||
(u/display-not-valid :addCommentThread "Content not valid")
|
||||
|
||||
(or (not (us/safe-number? (:x position)))
|
||||
(not (us/safe-number? (:y position))))
|
||||
(u/display-not-valid :addCommentThread "Position not valid")
|
||||
|
||||
(and (some? board) (or (not (shape/shape-proxy? board)) (not (cfh/frame-shape? shape))))
|
||||
(u/display-not-valid :addCommentThread "Board not valid")
|
||||
|
||||
(not (r/check-permission plugin-id "comment:write"))
|
||||
(u/display-not-valid :addCommentThread "Plugin doesn't have 'comment:write' permission")
|
||||
|
||||
:else
|
||||
(let [position
|
||||
(cond-> position
|
||||
(some? board)
|
||||
(-> (update :x - (:x board))
|
||||
(update :y - (:y board))))]
|
||||
(js/Promise.
|
||||
(fn [resolve]
|
||||
(st/emit!
|
||||
(dc/create-thread-on-workspace
|
||||
{:file-id file-id
|
||||
:page-id id
|
||||
:position (gpt/point position)
|
||||
:content content}
|
||||
|
||||
(fn [data]
|
||||
(->> (rp/cmd! :get-team-users {:file-id file-id})
|
||||
(rx/subs!
|
||||
(fn [users]
|
||||
(let [users (d/index-by :id users)]
|
||||
(resolve (pc/comment-thread-proxy plugin-id file-id id users data)))))))
|
||||
false))))))))
|
||||
|
||||
:removeCommentThread
|
||||
(fn [thread]
|
||||
(cond
|
||||
(not (pc/comment-thread-proxy? thread))
|
||||
(u/display-not-valid :removeCommentThread "Comment thread not valid")
|
||||
|
||||
(not (r/check-permission plugin-id "comment:write"))
|
||||
(u/display-not-valid :removeCommentThread "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(js/Promise.
|
||||
(fn [resolve]
|
||||
(let [thread-id (obj/get thread "$id")]
|
||||
(js/Promise.
|
||||
(st/emit! (dc/delete-comment-thread-on-workspace {:id thread-id} #(resolve)))))))))
|
||||
|
||||
:findCommentThreads
|
||||
(fn [criteria]
|
||||
(let [only-yours (boolean (obj/get criteria "onlyYours" false))
|
||||
show-resolved (boolean (obj/get criteria "showResolved" true))
|
||||
user-id (-> @st/state :profile :id)]
|
||||
(js/Promise.
|
||||
(fn [resolve reject]
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "comment:read"))
|
||||
(do
|
||||
(u/display-not-valid :findCommentThreads "Plugin doesn't have 'comment:read' permission")
|
||||
(reject "Plugin doesn't have 'comment:read' permission"))
|
||||
|
||||
:else
|
||||
(->> (rx/zip (rp/cmd! :get-team-users {:file-id file-id})
|
||||
(rp/cmd! :get-comment-threads {:file-id file-id}))
|
||||
(rx/take 1)
|
||||
(rx/subs!
|
||||
(fn [[users comments]]
|
||||
(let [users (d/index-by :id users)
|
||||
comments
|
||||
(cond->> comments
|
||||
(not show-resolved)
|
||||
(filter (comp not :is-resolved))
|
||||
|
||||
only-yours
|
||||
(filter #(contains? (:participants %) user-id)))]
|
||||
(resolve
|
||||
(format/format-array
|
||||
#(pc/comment-thread-proxy plugin-id file-id id users %) comments))))
|
||||
reject)))))))))
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
;; height: number;
|
||||
;; mtype?: string;
|
||||
;; id: string;
|
||||
;; keepApectRatio?: boolean;
|
||||
;; keepAspectRatio?: boolean;
|
||||
;;}
|
||||
(defn parse-image-data
|
||||
[^js image-data]
|
||||
@@ -77,7 +77,7 @@
|
||||
:width (obj/get image-data "width")
|
||||
:height (obj/get image-data "height")
|
||||
:mtype (obj/get image-data "mtype")
|
||||
:keep-aspect-ratio (obj/get image-data "keepApectRatio")})))
|
||||
:keep-aspect-ratio (obj/get image-data "keepAspectRatio")})))
|
||||
|
||||
;; export type Gradient = {
|
||||
;; type: 'linear' | 'radial';
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
(conj "content:read")
|
||||
|
||||
(contains? permissions "library:write")
|
||||
(conj "content:write")
|
||||
(conj "library:read")
|
||||
|
||||
(contains? permissions "comment:write")
|
||||
(conj "comment:read"))
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.files.helpers :as cfh]
|
||||
[app.common.record :as crc]
|
||||
[app.common.spec :as us]
|
||||
[app.main.data.workspace.guides :as dwgu]
|
||||
[app.main.store :as st]
|
||||
@@ -20,80 +19,82 @@
|
||||
(def shape-proxy identity)
|
||||
(def shape-proxy? identity)
|
||||
|
||||
(deftype RulerGuideProxy [$plugin $file $page $id]
|
||||
Object
|
||||
(remove [self]
|
||||
(let [guide (u/proxy->ruler-guide self)]
|
||||
(st/emit! (dwgu/remove-guide guide)))))
|
||||
|
||||
(defn ruler-guide-proxy? [p]
|
||||
(instance? RulerGuideProxy p))
|
||||
(obj/type-of? p "RulerGuideProxy"))
|
||||
|
||||
(defn ruler-guide-proxy
|
||||
[plugin-id file-id page-id id]
|
||||
(crc/add-properties!
|
||||
(RulerGuideProxy. plugin-id file-id page-id id)
|
||||
{:name "$plugin" :enumerable false :get (constantly plugin-id)}
|
||||
{:name "$file" :enumerable false :get (constantly file-id)}
|
||||
{:name "$page" :enumerable false :get (constantly page-id)}
|
||||
{:name "$id" :enumerable false :get (constantly id)}
|
||||
(obj/reify {:name "RuleGuideProxy"}
|
||||
:$plugin {:enumerable false :get (constantly plugin-id)}
|
||||
:$file {:enumerable false :get (constantly file-id)}
|
||||
:$page {:enumerable false :get (constantly page-id)}
|
||||
:$id {:enumerable false :get (constantly id)}
|
||||
|
||||
{:name "board" :enumerable false
|
||||
:get
|
||||
(fn [self]
|
||||
(let [board-id (-> self u/proxy->ruler-guide :frame-id)]
|
||||
(when board-id
|
||||
(shape-proxy plugin-id file-id page-id board-id))))
|
||||
:board
|
||||
{:this true
|
||||
:enumerable false
|
||||
:get
|
||||
(fn [self]
|
||||
(let [board-id (-> self u/proxy->ruler-guide :frame-id)]
|
||||
(when board-id
|
||||
(shape-proxy plugin-id file-id page-id board-id))))
|
||||
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [shape (u/locate-shape file-id page-id (obj/get value "$id"))]
|
||||
(cond
|
||||
(not (shape-proxy? value))
|
||||
(u/display-not-valid :board "The board is not a shape proxy")
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [shape (u/locate-shape file-id page-id (obj/get value "$id"))]
|
||||
(cond
|
||||
(not (shape-proxy? value))
|
||||
(u/display-not-valid :board "The board is not a shape proxy")
|
||||
|
||||
(not (cfh/frame-shape? shape))
|
||||
(u/display-not-valid :board "The shape is not a board")
|
||||
(not (cfh/frame-shape? shape))
|
||||
(u/display-not-valid :board "The shape is not a board")
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :board "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :board "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [board-id (when value (obj/get value "$id"))
|
||||
guide (-> self u/proxy->ruler-guide)]
|
||||
(st/emit! (dwgu/update-guides (assoc guide :frame-id board-id)))))))}
|
||||
:else
|
||||
(let [board-id (when value (obj/get value "$id"))
|
||||
guide (-> self u/proxy->ruler-guide)]
|
||||
(st/emit! (dwgu/update-guides (assoc guide :frame-id board-id)))))))}
|
||||
|
||||
{:name "orientation"
|
||||
:get #(-> % u/proxy->ruler-guide :axis format/axis->orientation)}
|
||||
:orientation
|
||||
{:this true
|
||||
:get #(-> % u/proxy->ruler-guide :axis format/axis->orientation)}
|
||||
|
||||
{:name "position"
|
||||
:get
|
||||
(fn [self]
|
||||
(let [guide (u/proxy->ruler-guide self)]
|
||||
(if (:frame-id guide)
|
||||
(let [objects (u/locate-objects file-id page-id)
|
||||
board-pos (dm/get-in objects [(:frame-id guide) (:axis guide)])
|
||||
position (:position guide)]
|
||||
(- position board-pos))
|
||||
:position
|
||||
{:this true
|
||||
:get
|
||||
(fn [self]
|
||||
(let [guide (u/proxy->ruler-guide self)]
|
||||
(if (:frame-id guide)
|
||||
(let [objects (u/locate-objects file-id page-id)
|
||||
board-pos (dm/get-in objects [(:frame-id guide) (:axis guide)])
|
||||
position (:position guide)]
|
||||
(- position board-pos))
|
||||
|
||||
;; No frame
|
||||
(:position guide))))
|
||||
:set
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(u/display-not-valid :position "Not valid position")
|
||||
;; No frame
|
||||
(:position guide))))
|
||||
:set
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(u/display-not-valid :position "Not valid position")
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :position "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :position "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [guide (u/proxy->ruler-guide self)
|
||||
position
|
||||
(if (:frame-id guide)
|
||||
(let [objects (u/locate-objects file-id page-id)
|
||||
board-pos (dm/get-in objects [(:frame-id guide) (:axis guide)])]
|
||||
(+ board-pos value))
|
||||
:else
|
||||
(let [guide (u/proxy->ruler-guide self)
|
||||
position
|
||||
(if (:frame-id guide)
|
||||
(let [objects (u/locate-objects file-id page-id)
|
||||
board-pos (dm/get-in objects [(:frame-id guide) (:axis guide)])]
|
||||
(+ board-pos value))
|
||||
|
||||
value)]
|
||||
(st/emit! (dwgu/update-guides (assoc guide :position position))))))}))
|
||||
value)]
|
||||
(st/emit! (dwgu/update-guides (assoc guide :position position))))))}
|
||||
|
||||
:remove
|
||||
(fn []
|
||||
(let [guide (u/locate-ruler-guide file-id page-id id)]
|
||||
(st/emit! (dwgu/remove-guide guide))))))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -27,21 +27,16 @@
|
||||
;; This regex seems duplicated but probably in the future when we support diferent units
|
||||
;; this will need to reflect changes for each property
|
||||
|
||||
(def font-size-re #"^\d*\.?\d*$")
|
||||
(def line-height-re #"^\d*\.?\d*$")
|
||||
(def letter-spacing-re #"^\d*\.?\d*$")
|
||||
(def text-transform-re #"uppercase|capitalize|lowercase|none")
|
||||
(def text-decoration-re #"underline|line-through|none")
|
||||
(def text-direction-re #"ltr|rtl")
|
||||
(def text-align-re #"left|center|right|justify")
|
||||
(def vertical-align-re #"top|center|bottom")
|
||||
(def ^:private font-size-re #"^\d*\.?\d*$")
|
||||
(def ^:private line-height-re #"^\d*\.?\d*$")
|
||||
(def ^:private letter-spacing-re #"^\d*\.?\d*$")
|
||||
(def ^:private text-transform-re #"uppercase|capitalize|lowercase|none")
|
||||
(def ^:private text-decoration-re #"underline|line-through|none")
|
||||
(def ^:private text-direction-re #"ltr|rtl")
|
||||
(def ^:private text-align-re #"left|center|right|justify")
|
||||
(def ^:private vertical-align-re #"top|center|bottom")
|
||||
|
||||
(defn mixed-value
|
||||
[values]
|
||||
(let [s (set values)]
|
||||
(if (= (count s) 1) (first s) "mixed")))
|
||||
|
||||
(defn font-data
|
||||
(defn- font-data
|
||||
[font variant]
|
||||
(d/without-nils
|
||||
{:font-id (:id font)
|
||||
@@ -50,284 +45,326 @@
|
||||
:font-style (:style variant)
|
||||
:font-weight (:weight variant)}))
|
||||
|
||||
(defn variant-data
|
||||
(defn- variant-data
|
||||
[variant]
|
||||
(d/without-nils
|
||||
{:font-variant-id (:id variant)
|
||||
:font-style (:style variant)
|
||||
:font-weight (:weight variant)}))
|
||||
|
||||
(deftype TextRange [$plugin $file $page $id start end]
|
||||
Object
|
||||
(applyTypography [_ typography]
|
||||
(let [typography (u/proxy->library-typography typography)
|
||||
attrs (-> typography
|
||||
(assoc :typography-ref-file $file)
|
||||
(assoc :typography-ref-id (:id typography))
|
||||
(dissoc :id :name))]
|
||||
(st/emit! (dwt/update-text-range $id start end attrs)))))
|
||||
|
||||
(defn text-range?
|
||||
[range]
|
||||
(instance? TextRange range))
|
||||
|
||||
(defn text-props
|
||||
(defn- text-props
|
||||
[shape]
|
||||
(d/merge
|
||||
(dwt/current-root-values {:shape shape :attrs txt/root-attrs})
|
||||
(dwt/current-paragraph-values {:shape shape :attrs txt/paragraph-attrs})
|
||||
(dwt/current-text-values {:shape shape :attrs txt/text-node-attrs})))
|
||||
|
||||
(defn text-range
|
||||
(defn text-range-proxy?
|
||||
[range]
|
||||
(obj/type-of? range "TextRange"))
|
||||
|
||||
(defn text-range-proxy
|
||||
[plugin-id file-id page-id id start end]
|
||||
(-> (TextRange. plugin-id file-id page-id id start end)
|
||||
(crc/add-properties!
|
||||
{:name "$plugin" :enumerable false :get (constantly plugin-id)}
|
||||
{:name "$id" :enumerable false :get (constantly id)}
|
||||
{:name "$file" :enumerable false :get (constantly file-id)}
|
||||
{:name "$page" :enumerable false :get (constantly page-id)}
|
||||
(obj/reify {:name "TextRange"}
|
||||
:$plugin {:enumerable false :get (constantly plugin-id)}
|
||||
:$id {:enumerable false :get (constantly id)}
|
||||
:$file {:enumerable false :get (constantly file-id)}
|
||||
:$page {:enumerable false :get (constantly page-id)}
|
||||
|
||||
{:name "shape"
|
||||
:get #(-> % u/proxy->shape)}
|
||||
:shape
|
||||
{:this true
|
||||
:get #(-> % u/proxy->shape)}
|
||||
|
||||
{:name "characters"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :text) (str/join "")))}
|
||||
:characters
|
||||
{:this true
|
||||
:get
|
||||
(fn [self]
|
||||
(let [range-data
|
||||
(-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :text) (str/join ""))))}
|
||||
|
||||
{:name "fontId"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :font-id) mixed-value))
|
||||
:fontId
|
||||
{:this true
|
||||
:get
|
||||
(fn [self]
|
||||
(let [range-data
|
||||
(-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :font-id) u/mixed-value)))
|
||||
|
||||
:set
|
||||
(fn [_ value]
|
||||
(let [font (when (string? value) (fonts/get-font-data value))
|
||||
variant (fonts/get-default-variant font)]
|
||||
(cond
|
||||
(not font)
|
||||
(u/display-not-valid :fontId value)
|
||||
:set
|
||||
(fn [_ value]
|
||||
(let [font (when (string? value) (fonts/get-font-data value))
|
||||
variant (fonts/get-default-variant font)]
|
||||
(cond
|
||||
(not font)
|
||||
(u/display-not-valid :fontId value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fontId "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fontId "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end (font-data font variant))))))}
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end (font-data font variant))))))}
|
||||
|
||||
{:name "fontFamily"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :font-family) mixed-value))
|
||||
:fontFamily
|
||||
{:this true
|
||||
:get
|
||||
(fn [self]
|
||||
(let [range-data
|
||||
(-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :font-family) u/mixed-value)))
|
||||
|
||||
:set
|
||||
(fn [_ value]
|
||||
(let [font (fonts/find-font-data {:family value})
|
||||
variant (fonts/get-default-variant font)]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontFamily value)
|
||||
:set
|
||||
(fn [_ value]
|
||||
(let [font (fonts/find-font-data {:family value})
|
||||
variant (fonts/get-default-variant font)]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontFamily value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fontFamily "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fontFamily "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end (font-data font variant))))))}
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end (font-data font variant))))))}
|
||||
|
||||
{:name "fontVariantId"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :font-variant-id) mixed-value))
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [font (fonts/get-font-data (obj/get self "fontId"))
|
||||
variant (fonts/get-variant font value)]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontVariantId value)
|
||||
:fontVariantId
|
||||
{:this true
|
||||
:get
|
||||
(fn [self]
|
||||
(let [range-data
|
||||
(-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :font-variant-id) u/mixed-value)))
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [font (fonts/get-font-data (obj/get self "fontId"))
|
||||
variant (fonts/get-variant font value)]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontVariantId value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fontVariantId "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fontVariantId "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end (variant-data variant))))))}
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end (variant-data variant))))))}
|
||||
|
||||
{:name "fontSize"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :font-size) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(let [value (str/trim (dm/str value))]
|
||||
(cond
|
||||
(or (empty? value) (not (re-matches font-size-re value)))
|
||||
(u/display-not-valid :fontSize value)
|
||||
:fontSize
|
||||
{:this true
|
||||
:get
|
||||
(fn [self]
|
||||
(let [range-data
|
||||
(-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :font-size) u/mixed-value)))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(let [value (str/trim (dm/str value))]
|
||||
(cond
|
||||
(or (empty? value) (not (re-matches font-size-re value)))
|
||||
(u/display-not-valid :fontSize value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fontSize "Plugin doesn't have 'content:write' permission")
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fontSize "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:font-size value})))))}
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:font-size value})))))}
|
||||
|
||||
{:name "fontWeight"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :font-weight) mixed-value))
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [font (fonts/get-font-data (obj/get self "fontId"))
|
||||
weight (dm/str value)
|
||||
style (obj/get self "fontStyle")
|
||||
variant
|
||||
(or
|
||||
(fonts/find-variant font {:style style :weight weight})
|
||||
(fonts/find-variant font {:weight weight}))]
|
||||
(cond
|
||||
(nil? variant)
|
||||
(u/display-not-valid :fontWeight (dm/str "Font weight '" value "' not supported for the current font"))
|
||||
:fontWeight
|
||||
{:this true
|
||||
:get
|
||||
(fn [self]
|
||||
(let [range-data
|
||||
(-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :font-weight) u/mixed-value)))
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fontWeight "Plugin doesn't have 'content:write' permission")
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [font (fonts/get-font-data (obj/get self "fontId"))
|
||||
weight (dm/str value)
|
||||
style (obj/get self "fontStyle")
|
||||
variant
|
||||
(or
|
||||
(fonts/find-variant font {:style style :weight weight})
|
||||
(fonts/find-variant font {:weight weight}))]
|
||||
(cond
|
||||
(nil? variant)
|
||||
(u/display-not-valid :fontWeight (dm/str "Font weight '" value "' not supported for the current font"))
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end (variant-data variant))))))}
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fontWeight "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
{:name "fontStyle"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :font-style) mixed-value))
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [font (fonts/get-font-data (obj/get self "fontId"))
|
||||
style (dm/str value)
|
||||
weight (obj/get self "fontWeight")
|
||||
variant
|
||||
(or
|
||||
(fonts/find-variant font {:weight weight :style style})
|
||||
(fonts/find-variant font {:style style}))]
|
||||
(cond
|
||||
(nil? variant)
|
||||
(u/display-not-valid :fontStyle (dm/str "Font style '" value "' not supported for the current font"))
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end (variant-data variant))))))}
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fontStyle "Plugin doesn't have 'content:write' permission")
|
||||
:fontStyle
|
||||
{:this true
|
||||
:get
|
||||
(fn [self]
|
||||
(let [range-data
|
||||
(-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :font-style) u/mixed-value)))
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [font (fonts/get-font-data (obj/get self "fontId"))
|
||||
style (dm/str value)
|
||||
weight (obj/get self "fontWeight")
|
||||
variant
|
||||
(or
|
||||
(fonts/find-variant font {:weight weight :style style})
|
||||
(fonts/find-variant font {:style style}))]
|
||||
(cond
|
||||
(nil? variant)
|
||||
(u/display-not-valid :fontStyle (dm/str "Font style '" value "' not supported for the current font"))
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end (variant-data variant))))))}
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fontStyle "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
{:name "lineHeight"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :line-height) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(let [value (str/trim (dm/str value))]
|
||||
(cond
|
||||
(or (empty? value) (not (re-matches line-height-re value)))
|
||||
(u/display-not-valid :lineHeight value)
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end (variant-data variant))))))}
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :lineHeight "Plugin doesn't have 'content:write' permission")
|
||||
:lineHeight
|
||||
{:this true
|
||||
:get
|
||||
(fn [self]
|
||||
(let [range-data
|
||||
(-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :line-height) u/mixed-value)))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(let [value (str/trim (dm/str value))]
|
||||
(cond
|
||||
(or (empty? value) (not (re-matches line-height-re value)))
|
||||
(u/display-not-valid :lineHeight value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:line-height value})))))}
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :lineHeight "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
{:name "letterSpacing"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :letter-spacing) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(let [value (str/trim (dm/str value))]
|
||||
(cond
|
||||
(or (empty? value) (re-matches letter-spacing-re value))
|
||||
(u/display-not-valid :letterSpacing value)
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:line-height value})))))}
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :letterSpacing "Plugin doesn't have 'content:write' permission")
|
||||
:letterSpacing
|
||||
{:this true
|
||||
:get
|
||||
(fn [self]
|
||||
(let [range-data
|
||||
(-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :letter-spacing) u/mixed-value)))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(let [value (str/trim (dm/str value))]
|
||||
(cond
|
||||
(or (empty? value) (re-matches letter-spacing-re value))
|
||||
(u/display-not-valid :letterSpacing value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:letter-spacing value})))))}
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :letterSpacing "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
{:name "textTransform"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :text-transform) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(and (string? value) (re-matches text-transform-re value))
|
||||
(u/display-not-valid :textTransform value)
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:letter-spacing value})))))}
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :textTransform "Plugin doesn't have 'content:write' permission")
|
||||
:textTransform
|
||||
{:this true
|
||||
:get
|
||||
(fn [self]
|
||||
(let [range-data
|
||||
(-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :text-transform) u/mixed-value)))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(and (string? value) (not (re-matches text-transform-re value)))
|
||||
(u/display-not-valid :textTransform value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:text-transform value}))))}
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :textTransform "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
{:name "textDecoration"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :text-decoration) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(and (string? value) (re-matches text-decoration-re value))
|
||||
(u/display-not-valid :textDecoration value)
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:text-transform value}))))}
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :textDecoration "Plugin doesn't have 'content:write' permission")
|
||||
:textDecoration
|
||||
{:this true
|
||||
:get
|
||||
(fn [self]
|
||||
(let [range-data
|
||||
(-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :text-decoration) u/mixed-value)))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(and (string? value) (re-matches text-decoration-re value))
|
||||
(u/display-not-valid :textDecoration value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:text-decoration value}))))}
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :textDecoration "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
{:name "direction"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :direction) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(and (string? value) (re-matches text-direction-re value))
|
||||
(u/display-not-valid :direction value)
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:text-decoration value}))))}
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :direction "Plugin doesn't have 'content:write' permission")
|
||||
:direction
|
||||
{:this true
|
||||
:get
|
||||
(fn [self]
|
||||
(let [range-data
|
||||
(-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :direction) u/mixed-value)))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(and (string? value) (re-matches text-direction-re value))
|
||||
(u/display-not-valid :direction value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:direction value}))))}
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :direction "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
{:name "align"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :text-align) mixed-value))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(and (string? value) (re-matches text-align-re value))
|
||||
(u/display-not-valid :align value)
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:direction value}))))}
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :align "Plugin doesn't have 'content:write' permission")
|
||||
:align
|
||||
{:this true
|
||||
:get
|
||||
(fn [self]
|
||||
(let [range-data
|
||||
(-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :text-align) u/mixed-value)))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(and (string? value) (re-matches text-align-re value))
|
||||
(u/display-not-valid :align value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:text-align value}))))}
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :align "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
{:name "fills"
|
||||
:get #(let [range-data
|
||||
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :fills) mixed-value format/format-fills))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(let [value (parser/parse-fills value)]
|
||||
(cond
|
||||
(not (sm/validate [:vector ::cts/fill] value))
|
||||
(u/display-not-valid :fills value)
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:text-align value}))))}
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fills "Plugin doesn't have 'content:write' permission")
|
||||
:fills
|
||||
{:this true
|
||||
:get
|
||||
(fn [self]
|
||||
(let [range-data
|
||||
(-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
|
||||
(->> range-data (map :fills) u/mixed-value format/format-fills)))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(let [value (parser/parse-fills value)]
|
||||
(cond
|
||||
(not (sm/validate [:vector ::cts/fill] value))
|
||||
(u/display-not-valid :fills value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:fills value})))))})))
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fills "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:fills value})))))}
|
||||
|
||||
:applyTypography
|
||||
(fn [typography]
|
||||
(let [typography (u/proxy->library-typography typography)
|
||||
attrs (-> typography
|
||||
(assoc :typography-ref-file file-id)
|
||||
(assoc :typography-ref-id (:id typography))
|
||||
(dissoc :id :name))]
|
||||
(st/emit! (dwt/update-text-range id start end attrs))))))
|
||||
|
||||
(defn add-text-props
|
||||
[shape-proxy plugin-id]
|
||||
|
||||
@@ -41,23 +41,28 @@
|
||||
|
||||
|
||||
(defn current-user-proxy? [p]
|
||||
(instance? CurrentUserProxy p))
|
||||
(obj/type-of? p "CurrentUserProxy"))
|
||||
|
||||
(defn current-user-proxy
|
||||
[plugin-id session-id]
|
||||
(-> (CurrentUserProxy. plugin-id)
|
||||
(-> (obj/reify {:name "CurrentUserProxy"}
|
||||
:$plugin {:enumerable false :get (fn [] plugin-id)})
|
||||
(add-session-properties session-id)))
|
||||
|
||||
(defn active-user-proxy? [p]
|
||||
(instance? ActiveUserProxy p))
|
||||
(obj/type-of? p "ActiveUserProxy"))
|
||||
|
||||
(defn active-user-proxy
|
||||
[plugin-id session-id]
|
||||
(-> (ActiveUserProxy. plugin-id)
|
||||
(add-session-properties session-id)
|
||||
(crc/add-properties!
|
||||
{:name "position" :get (fn [_] (-> (u/locate-presence session-id) :point format/format-point))}
|
||||
{:name "zoom" :get (fn [_] (-> (u/locate-presence session-id) :zoom))})))
|
||||
(-> (obj/reify {:name "ActiveUserProxy"}
|
||||
:$plugin {:enumerable false :get (fn [] plugin-id)}
|
||||
|
||||
:position
|
||||
{:get (fn [] (-> (u/locate-presence session-id) :point format/format-point))}
|
||||
|
||||
:zoom
|
||||
{:get (fn [] (-> (u/locate-presence session-id) :zoom))})
|
||||
(add-session-properties session-id)))
|
||||
|
||||
(defn- add-user-properties
|
||||
[user-proxy data]
|
||||
@@ -75,13 +80,14 @@
|
||||
{:name "avatarUrl"
|
||||
:get (fn [_] (cfg/resolve-profile-photo-url data))})))
|
||||
|
||||
(defn user-proxy?
|
||||
[p]
|
||||
(or (instance? UserProxy p)
|
||||
(current-user-proxy? p)
|
||||
(active-user-proxy? p)))
|
||||
|
||||
(defn user-proxy
|
||||
[plugin-id data]
|
||||
(-> (UserProxy. plugin-id)
|
||||
(-> (obj/reify {:name "UserProxy"}
|
||||
:$plugin {:enumerable false :get (fn [] plugin-id)})
|
||||
(add-user-properties data)))
|
||||
|
||||
(defn user-proxy?
|
||||
[p]
|
||||
(or (obj/type-of? p "UserProxy")
|
||||
(current-user-proxy? p)
|
||||
(active-user-proxy? p)))
|
||||
|
||||
@@ -119,26 +119,33 @@
|
||||
flow-id (obj/get proxy "$id")
|
||||
page (locate-page file-id page-id)]
|
||||
(when (some? page)
|
||||
(d/seek #(= (:id %) flow-id) (:flows page)))))
|
||||
(get (:flows page) flow-id))))
|
||||
|
||||
(defn locate-ruler-guide
|
||||
[file-id page-id ruler-id]
|
||||
(let [page (locate-page file-id page-id)]
|
||||
(when (some? page)
|
||||
(d/seek #(= (:id %) ruler-id) (-> page :guides vals)))))
|
||||
|
||||
(defn proxy->ruler-guide
|
||||
[proxy]
|
||||
(let [file-id (obj/get proxy "$file")
|
||||
page-id (obj/get proxy "$page")
|
||||
ruler-id (obj/get proxy "$id")
|
||||
page (locate-page file-id page-id)]
|
||||
(when (some? page)
|
||||
(d/seek #(= (:id %) ruler-id) (-> page :guides vals)))))
|
||||
ruler-id (obj/get proxy "$id")]
|
||||
(locate-ruler-guide file-id page-id ruler-id)))
|
||||
|
||||
(defn locate-interaction
|
||||
[file-id page-id shape-id index]
|
||||
(when-let [shape (locate-shape file-id page-id shape-id)]
|
||||
(get-in shape [:interactions index])))
|
||||
|
||||
(defn proxy->interaction
|
||||
[proxy]
|
||||
(let [file-id (obj/get proxy "$file")
|
||||
page-id (obj/get proxy "$page")
|
||||
shape-id (obj/get proxy "$shape")
|
||||
index (obj/get proxy "$index")
|
||||
shape (locate-shape file-id page-id shape-id)]
|
||||
(when (some? shape)
|
||||
(get-in shape [:interactions index]))))
|
||||
index (obj/get proxy "$index")]
|
||||
(locate-interaction file-id page-id shape-id index)))
|
||||
|
||||
(defn get-data
|
||||
([self attr]
|
||||
@@ -193,3 +200,8 @@
|
||||
(let [msg (dm/str "[PENPOT PLUGIN] Value not valid: " value ". Code: " code)]
|
||||
(.error js/console msg)
|
||||
(reject msg)))
|
||||
|
||||
(defn mixed-value
|
||||
[values]
|
||||
(let [s (set values)]
|
||||
(if (= (count s) 1) (first s) "mixed")))
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
(ns app.plugins.viewport
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.record :as crc]
|
||||
[app.common.spec :as us]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.viewport :as dwv]
|
||||
@@ -17,83 +16,81 @@
|
||||
[app.plugins.utils :as u]
|
||||
[app.util.object :as obj]))
|
||||
|
||||
(deftype ViewportProxy [$plugin]
|
||||
Object
|
||||
(zoomReset [_]
|
||||
(st/emit! dwz/reset-zoom))
|
||||
|
||||
(zoomToFitAll [_]
|
||||
(st/emit! dwz/zoom-to-fit-all))
|
||||
|
||||
(zoomIntoView [_ shapes]
|
||||
(let [ids
|
||||
(->> shapes
|
||||
(map (fn [v]
|
||||
(if (string? v)
|
||||
(uuid/uuid v)
|
||||
(uuid/uuid (obj/get v "x"))))))]
|
||||
(st/emit! (dwz/fit-to-shapes ids)))))
|
||||
|
||||
(crc/define-properties!
|
||||
ViewportProxy
|
||||
{:name js/Symbol.toStringTag
|
||||
:get (fn [] (str "ViewportProxy"))})
|
||||
|
||||
(defn viewport-proxy? [p]
|
||||
(instance? ViewportProxy p))
|
||||
(obj/type-of? p "ViewportProxy"))
|
||||
|
||||
(defn viewport-proxy
|
||||
[plugin-id]
|
||||
(crc/add-properties!
|
||||
(ViewportProxy. plugin-id)
|
||||
{:name "center"
|
||||
:get
|
||||
(fn [_]
|
||||
(let [vp (dm/get-in @st/state [:workspace-local :vbox])
|
||||
x (+ (:x vp) (/ (:width vp) 2))
|
||||
y (+ (:y vp) (/ (:height vp) 2))]
|
||||
(.freeze js/Object #js {:x x :y y})))
|
||||
(obj/reify {:name "ViewportProxy"}
|
||||
:$plugin {:enumerable false :get (fn [] plugin-id)}
|
||||
|
||||
:set
|
||||
(fn [_ value]
|
||||
(let [new-x (obj/get value "x")
|
||||
new-y (obj/get value "y")]
|
||||
(cond
|
||||
(not (us/safe-number? new-x))
|
||||
(u/display-not-valid :center-x new-x)
|
||||
:center
|
||||
{:get
|
||||
(fn []
|
||||
(let [vp (dm/get-in @st/state [:workspace-local :vbox])
|
||||
x (+ (:x vp) (/ (:width vp) 2))
|
||||
y (+ (:y vp) (/ (:height vp) 2))]
|
||||
(.freeze js/Object #js {:x x :y y})))
|
||||
|
||||
(not (us/safe-number? new-y))
|
||||
(u/display-not-valid :center-y new-y)
|
||||
:set
|
||||
(fn [value]
|
||||
(let [new-x (obj/get value "x")
|
||||
new-y (obj/get value "y")]
|
||||
(cond
|
||||
(not (us/safe-number? new-x))
|
||||
(u/display-not-valid :center-x new-x)
|
||||
|
||||
:else
|
||||
(let [vb (dm/get-in @st/state [:workspace-local :vbox])
|
||||
old-x (+ (:x vb) (/ (:width vb) 2))
|
||||
old-y (+ (:y vb) (/ (:height vb) 2))
|
||||
delta-x (- new-x old-x)
|
||||
delta-y (- new-y old-y)
|
||||
to-position
|
||||
{:x #(+ % delta-x)
|
||||
:y #(+ % delta-y)}]
|
||||
(st/emit! (dwv/update-viewport-position to-position))))))}
|
||||
(not (us/safe-number? new-y))
|
||||
(u/display-not-valid :center-y new-y)
|
||||
|
||||
{:name "zoom"
|
||||
:get
|
||||
(fn [_]
|
||||
(dm/get-in @st/state [:workspace-local :zoom]))
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(u/display-not-valid :zoom value)
|
||||
:else
|
||||
(let [vb (dm/get-in @st/state [:workspace-local :vbox])
|
||||
old-x (+ (:x vb) (/ (:width vb) 2))
|
||||
old-y (+ (:y vb) (/ (:height vb) 2))
|
||||
delta-x (- new-x old-x)
|
||||
delta-y (- new-y old-y)
|
||||
to-position
|
||||
{:x #(+ % delta-x)
|
||||
:y #(+ % delta-y)}]
|
||||
(st/emit! (dwv/update-viewport-position to-position))))))}
|
||||
|
||||
:else
|
||||
(let [z (dm/get-in @st/state [:workspace-local :zoom])]
|
||||
(st/emit! (dwz/set-zoom (/ value z))))))}
|
||||
:zoom
|
||||
{:get
|
||||
(fn []
|
||||
(dm/get-in @st/state [:workspace-local :zoom]))
|
||||
|
||||
{:name "bounds"
|
||||
:get
|
||||
(fn [_]
|
||||
(let [vbox (dm/get-in @st/state [:workspace-local :vbox])]
|
||||
(.freeze js/Object (format/format-bounds vbox))))}))
|
||||
:set
|
||||
(fn [value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(u/display-not-valid :zoom value)
|
||||
|
||||
:else
|
||||
(let [z (dm/get-in @st/state [:workspace-local :zoom])]
|
||||
(st/emit! (dwz/set-zoom (/ value z))))))}
|
||||
|
||||
:bounds
|
||||
{:get
|
||||
(fn []
|
||||
(let [vbox (dm/get-in @st/state [:workspace-local :vbox])]
|
||||
(.freeze js/Object (format/format-bounds vbox))))}
|
||||
|
||||
:zoomReset
|
||||
(fn []
|
||||
(st/emit! dwz/reset-zoom))
|
||||
|
||||
:zoomToFitAll
|
||||
(fn []
|
||||
(st/emit! dwz/zoom-to-fit-all))
|
||||
|
||||
:zoomIntoView
|
||||
(fn [shapes]
|
||||
(let [ids
|
||||
(->> shapes
|
||||
(map (fn [v]
|
||||
(if (string? v)
|
||||
(uuid/uuid v)
|
||||
(uuid/uuid (obj/get v "x"))))))]
|
||||
(st/emit! (dwz/fit-to-shapes ids))))))
|
||||
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
[app.common.types.shape.impl :as ctsi]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.util.object :as obj]
|
||||
[promesa.core :as p]))
|
||||
|
||||
(defn initialize
|
||||
@@ -28,21 +27,24 @@
|
||||
|
||||
(defn create-shape
|
||||
[id]
|
||||
(let [buffer (uuid/uuid->u32 id)]
|
||||
(._create_shape ^js internal-module (aget buffer 0) (aget buffer 1) (aget buffer 2) (aget buffer 3))))
|
||||
(let [buffer (uuid/uuid->u32 id)
|
||||
create-shape (unchecked-get internal-module "_create_shape")]
|
||||
(^function create-shape (aget buffer 0) (aget buffer 1) (aget buffer 2) (aget buffer 3))))
|
||||
|
||||
(defn use-shape
|
||||
[id]
|
||||
(let [buffer (uuid/uuid->u32 id)]
|
||||
(._use_shape ^js internal-module (aget buffer 0) (aget buffer 1) (aget buffer 2) (aget buffer 3))))
|
||||
(let [buffer (uuid/uuid->u32 id)
|
||||
use-shape (unchecked-get internal-module "_use_shape")]
|
||||
(^function use-shape (aget buffer 0) (aget buffer 1) (aget buffer 2) (aget buffer 3))))
|
||||
|
||||
(defn set-shape-selrect
|
||||
[selrect]
|
||||
(let [x1 (:x1 selrect)
|
||||
y1 (:y1 selrect)
|
||||
x2 (:x2 selrect)
|
||||
y2 (:y2 selrect)]
|
||||
(._set_shape_selrect ^js internal-module x1 y1 x2 y2)))
|
||||
y2 (:y2 selrect)
|
||||
set-shape-selrect (unchecked-get internal-module "_set_shape_selrect")]
|
||||
(^function set-shape-selrect x1 y1 x2 y2)))
|
||||
|
||||
(defn set-shape-transform
|
||||
[transform]
|
||||
@@ -51,27 +53,33 @@
|
||||
c (:c transform)
|
||||
d (:d transform)
|
||||
e (:e transform)
|
||||
f (:f transform)]
|
||||
(._set_shape_transform ^js internal-module a b c d e f)))
|
||||
f (:f transform)
|
||||
set-shape-transform (unchecked-get internal-module "_set_shape_transform")]
|
||||
(^function set-shape-transform a b c d e f)))
|
||||
|
||||
(defn set-shape-rotation
|
||||
[rotation]
|
||||
(._set_shape_rotation ^js internal-module rotation))
|
||||
(let [set-shape-rotation (unchecked-get internal-module "_set_shape_rotation")]
|
||||
(^function set-shape-rotation rotation)))
|
||||
|
||||
(defn set-shape-children
|
||||
[shape_ids]
|
||||
(._clear_shape_children ^js internal-module)
|
||||
(doseq [id shape_ids]
|
||||
(let [buffer (uuid/uuid->u32 id)]
|
||||
(._add_shape_child ^js internal-module (aget buffer 0) (aget buffer 1) (aget buffer 2) (aget buffer 3)))))
|
||||
(let [clear-shape-children (unchecked-get internal-module "_clear_shape_children")
|
||||
add-shape-child (unchecked-get internal-module "_add_shape_child")]
|
||||
(^function clear-shape-children)
|
||||
(doseq [id shape_ids]
|
||||
(let [buffer (uuid/uuid->u32 id)]
|
||||
(^function add-shape-child (aget buffer 0) (aget buffer 1) (aget buffer 2) (aget buffer 3))))))
|
||||
|
||||
(defn set-shape-fills
|
||||
[fills]
|
||||
(._clear_shape_fills ^js internal-module)
|
||||
(doseq [fill (filter #(contains? % :fill-color) fills)]
|
||||
(let [a (:fill-opacity fill)
|
||||
[r g b] (cc/hex->rgb (:fill-color fill))]
|
||||
(._add_shape_solid_fill ^js internal-module r g b a))))
|
||||
(let [clear-shape-fills (unchecked-get internal-module "_clear_shape_fills")
|
||||
add-shape-fill (unchecked-get internal-module "_add_shape_solid_fill")]
|
||||
(^function clear-shape-fills)
|
||||
(doseq [fill (filter #(contains? % :fill-color) fills)]
|
||||
(let [a (:fill-opacity fill)
|
||||
[r g b] (cc/hex->rgb (:fill-color fill))]
|
||||
(^function add-shape-fill r g b a)))))
|
||||
|
||||
(defn set-objects
|
||||
[objects]
|
||||
@@ -98,9 +106,10 @@
|
||||
[zoom vbox]
|
||||
(js/requestAnimationFrame
|
||||
(fn []
|
||||
(let [pan-x (- (dm/get-prop vbox :x))
|
||||
pan-y (- (dm/get-prop vbox :y))]
|
||||
(._draw_all_shapes ^js internal-module zoom pan-x pan-y)))))
|
||||
(let [pan-x (- (dm/get-prop vbox :x))
|
||||
pan-y (- (dm/get-prop vbox :y))
|
||||
draw-all-shapes (unchecked-get internal-module "_draw_all_shapes")]
|
||||
(^function draw-all-shapes zoom pan-x pan-y)))))
|
||||
|
||||
(defn cancel-draw
|
||||
[frame-id]
|
||||
@@ -129,12 +138,10 @@
|
||||
handle (.registerContext ^js gl context #js {"majorVersion" 2})]
|
||||
(.makeContextCurrent ^js gl handle)
|
||||
;; Initialize Skia
|
||||
(init-fn (.-width ^js canvas)
|
||||
(.-height ^js canvas))
|
||||
(^function init-fn (.-width ^js canvas)
|
||||
(.-height ^js canvas))
|
||||
(set! (.-width canvas) (.-clientWidth ^js canvas))
|
||||
(set! (.-height canvas) (.-clientHeight ^js canvas))
|
||||
|
||||
(obj/set! js/window "shape_list" (fn [] ((unchecked-get internal-module "_shape_list"))))))
|
||||
(set! (.-height canvas) (.-clientHeight ^js canvas))))
|
||||
|
||||
(defonce module
|
||||
(if (exists? js/dynamicImport)
|
||||
|
||||
@@ -148,9 +148,7 @@
|
||||
(mf/set-ref-val! internal-state initial))
|
||||
|
||||
(mf/with-effect [initial]
|
||||
(if (fn? initial)
|
||||
(swap! form-mutator update :data merge (initial))
|
||||
(swap! form-mutator update :data merge initial)))
|
||||
(swap! form-mutator d/deep-merge initial))
|
||||
|
||||
form-mutator))
|
||||
|
||||
|
||||
247
frontend/src/app/util/object.cljc
Normal file
247
frontend/src/app/util/object.cljc
Normal file
@@ -0,0 +1,247 @@
|
||||
;; 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.util.object
|
||||
"A collection of helpers for work with javascript objects."
|
||||
(:refer-clojure :exclude [set! new get merge clone contains? array? into-array reify])
|
||||
#?(:cljs (:require-macros [app.util.object]))
|
||||
(:require
|
||||
[clojure.core :as c]))
|
||||
|
||||
#?(:cljs
|
||||
(defn array?
|
||||
[o]
|
||||
(.isArray js/Array o)))
|
||||
|
||||
#?(:cljs
|
||||
(defn into-array
|
||||
[o]
|
||||
(js/Array.from o)))
|
||||
|
||||
#?(:cljs
|
||||
(defn create [] #js {}))
|
||||
|
||||
#?(:cljs
|
||||
(defn get
|
||||
([obj k]
|
||||
(when (some? obj)
|
||||
(unchecked-get obj k)))
|
||||
([obj k default]
|
||||
(let [result (get obj k)]
|
||||
(if (undefined? result) default result)))))
|
||||
|
||||
#?(:cljs
|
||||
(defn contains?
|
||||
[obj k]
|
||||
(when (some? obj)
|
||||
(js/Object.hasOwn obj k))))
|
||||
|
||||
#?(:cljs
|
||||
(defn clone
|
||||
[a]
|
||||
(js/Object.assign #js {} a)))
|
||||
|
||||
#?(:cljs
|
||||
(defn merge!
|
||||
([a b]
|
||||
(js/Object.assign a b))
|
||||
([a b & more]
|
||||
(reduce merge! (merge! a b) more))))
|
||||
|
||||
#?(:cljs
|
||||
(defn merge
|
||||
([a b]
|
||||
(js/Object.assign #js {} a b))
|
||||
([a b & more]
|
||||
(reduce merge! (merge a b) more))))
|
||||
|
||||
#?(:cljs
|
||||
(defn set!
|
||||
[obj key value]
|
||||
(unchecked-set obj key value)
|
||||
obj))
|
||||
|
||||
#?(:cljs
|
||||
(defn unset!
|
||||
[obj key]
|
||||
(js-delete obj key)
|
||||
obj))
|
||||
|
||||
#?(:cljs
|
||||
(def ^:private not-found-sym
|
||||
(js/Symbol "not-found")))
|
||||
|
||||
#?(:cljs
|
||||
(defn update!
|
||||
[obj key f & args]
|
||||
(let [found (c/get obj key not-found-sym)]
|
||||
(when-not ^boolean (identical? found not-found-sym)
|
||||
(unchecked-set obj key (apply f found args)))
|
||||
obj)))
|
||||
|
||||
#?(:cljs
|
||||
(defn ^boolean in?
|
||||
[obj prop]
|
||||
(js* "~{} in ~{}" prop obj)))
|
||||
|
||||
#?(:cljs
|
||||
(defn without-empty
|
||||
[^js obj]
|
||||
(when (some? obj)
|
||||
(js* "Object.entries(~{}).reduce((a, [k,v]) => (v == null ? a : (a[k]=v, a)), {}) " obj))))
|
||||
|
||||
(defmacro add-properties!
|
||||
"Adds properties to an object using `.defineProperty`"
|
||||
[rsym & properties]
|
||||
(let [rsym (with-meta rsym {:tag 'js})
|
||||
getf-sym (with-meta (gensym (str rsym "-get-fn-")) {:tag 'js})
|
||||
setf-sym (with-meta (gensym (str rsym "-set-fn-")) {:tag 'js})
|
||||
this-sym (with-meta (gensym (str rsym "-this-")) {:tag 'js})
|
||||
target-sym (with-meta (gensym (str rsym "-target-")) {:tag 'js})]
|
||||
`(let [~target-sym ~rsym]
|
||||
;; Creates the `.defineProperty` per property
|
||||
~@(for [params properties
|
||||
:let [pname (c/get params :name)
|
||||
get-expr (c/get params :get)
|
||||
set-expr (c/get params :set)
|
||||
this? (c/get params :this true)
|
||||
enum? (c/get params :enumerable true)
|
||||
conf? (c/get params :configurable)
|
||||
writ? (c/get params :writable)]]
|
||||
`(let [~@(concat
|
||||
(when get-expr
|
||||
[getf-sym get-expr])
|
||||
(when set-expr
|
||||
[setf-sym set-expr]))]
|
||||
(.defineProperty
|
||||
js/Object
|
||||
~target-sym
|
||||
~pname
|
||||
(cljs.core/js-obj
|
||||
~@(concat
|
||||
["enumerable" (boolean enum?)]
|
||||
|
||||
(when conf?
|
||||
["configurable" true])
|
||||
|
||||
(when (some? writ?)
|
||||
["writable" true])
|
||||
|
||||
(when get-expr
|
||||
(if this?
|
||||
["get" `(fn [] (cljs.core/this-as ~this-sym (~getf-sym ~this-sym)))]
|
||||
["get" getf-sym]))
|
||||
|
||||
(when set-expr
|
||||
(if this?
|
||||
["set" `(fn [v#] (cljs.core/this-as ~this-sym (~setf-sym ~this-sym v#)))]
|
||||
["set" setf-sym])))))))
|
||||
|
||||
;; Returns the object
|
||||
~target-sym)))
|
||||
|
||||
(defn- collect-properties
|
||||
[params]
|
||||
(let [[tmeta params] (if (map? (first params))
|
||||
[(first params) (rest params)]
|
||||
[{} params])]
|
||||
(loop [params (seq params)
|
||||
props []
|
||||
defs {}
|
||||
curr :start
|
||||
ckey nil]
|
||||
(cond
|
||||
(= curr :start)
|
||||
(let [candidate (first params)]
|
||||
(cond
|
||||
(keyword? candidate)
|
||||
(recur (rest params) props defs :property candidate)
|
||||
|
||||
(nil? candidate)
|
||||
(recur (rest params) props defs :end nil)
|
||||
|
||||
:else
|
||||
(recur (rest params) props defs :definition candidate)))
|
||||
|
||||
(= :end curr)
|
||||
[tmeta props defs]
|
||||
|
||||
(= :property curr)
|
||||
(let [definition (first params)]
|
||||
(if (some? definition)
|
||||
(let [definition (if (map? definition)
|
||||
(c/merge {:this false} (assoc definition :name (name ckey)))
|
||||
(-> {:enumerable false}
|
||||
(c/merge (meta definition))
|
||||
(assoc :name (name ckey))
|
||||
(assoc :this false)
|
||||
(assoc :get `(fn [] ~definition))))]
|
||||
(recur (rest params)
|
||||
(conj props definition)
|
||||
defs
|
||||
:start
|
||||
nil))
|
||||
(let [hint (str "expected property definition for: " curr)]
|
||||
(throw (ex-info hint {:key curr})))))
|
||||
|
||||
(= :definition curr)
|
||||
(let [[params props defs curr ckey]
|
||||
(loop [params params
|
||||
defs (update defs ckey #(or % []))]
|
||||
(let [candidate (first params)
|
||||
params (rest params)]
|
||||
(cond
|
||||
(nil? candidate)
|
||||
[params props defs :end]
|
||||
|
||||
(keyword? candidate)
|
||||
[params props defs :property candidate]
|
||||
|
||||
(symbol? candidate)
|
||||
[params props defs :definition candidate]
|
||||
|
||||
:else
|
||||
(recur params (update defs ckey conj candidate)))))]
|
||||
(recur params props defs curr ckey))
|
||||
|
||||
:else
|
||||
(throw (ex-info "invalid params" {}))))))
|
||||
|
||||
#?(:cljs
|
||||
(def type-symbol
|
||||
(js/Symbol.for "penpot.reify:type")))
|
||||
|
||||
#?(:cljs
|
||||
(defn type-of?
|
||||
[o t]
|
||||
(let [o (get o type-symbol)]
|
||||
(= o t))))
|
||||
|
||||
(defmacro reify
|
||||
"A domain specific variation of reify that creates anonymous objects
|
||||
on demand with the ability to assign protocol implementations and
|
||||
custom properties"
|
||||
[& params]
|
||||
(let [[tmeta properties definitions] (collect-properties params)
|
||||
obj-sym (gensym "obj-")]
|
||||
`(let [~obj-sym (cljs.core/js-obj)]
|
||||
(add-properties! ~obj-sym
|
||||
~@(when-let [tname (:name tmeta)]
|
||||
[`{:name ~'js/Symbol.toStringTag
|
||||
:this false
|
||||
:enumerable false
|
||||
:get (fn [] ~tname)}
|
||||
`{:name type-symbol
|
||||
:this false
|
||||
:enumerable false
|
||||
:get (fn [] ~tname)}])
|
||||
~@properties)
|
||||
(let [~obj-sym ~(if-let [definitions (seq definitions)]
|
||||
`(cljs.core/specify! ~obj-sym
|
||||
~@(mapcat (fn [[k v]] (cons k v)) definitions))
|
||||
obj-sym)]
|
||||
|
||||
(cljs.core/specify! ~obj-sym)))))
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user