From e16ec9c7195607b71836ac1b3fc25d89d563f34d Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 21 Nov 2024 15:25:52 +0100 Subject: [PATCH 1/2] :sparkles: Add facility for create anonymous objects Speciailly designed to be work in plugins where code is submited to hard deep freeze on the sandboxing process. --- frontend/deps.edn | 2 + frontend/dev/user.clj | 34 +++++ frontend/scripts/repl | 6 + frontend/src/app/plugins/file.cljs | 146 +++++++++--------- frontend/src/app/util/object.cljc | 235 +++++++++++++++++++++++++++++ frontend/src/app/util/object.cljs | 76 ---------- 6 files changed, 348 insertions(+), 151 deletions(-) create mode 100644 frontend/dev/user.clj create mode 100755 frontend/scripts/repl create mode 100644 frontend/src/app/util/object.cljc delete mode 100644 frontend/src/app/util/object.cljs diff --git a/frontend/deps.edn b/frontend/deps.edn index e847b4fbe..394c49bf3 100644 --- a/frontend/deps.edn +++ b/frontend/deps.edn @@ -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 diff --git a/frontend/dev/user.clj b/frontend/dev/user.clj new file mode 100644 index 000000000..763c2a009 --- /dev/null +++ b/frontend/dev/user.clj @@ -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))) diff --git a/frontend/scripts/repl b/frontend/scripts/repl new file mode 100755 index 000000000..bf9f4065f --- /dev/null +++ b/frontend/scripts/repl @@ -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 diff --git a/frontend/src/app/plugins/file.cljs b/frontend/src/app/plugins/file.cljs index 07bedf5e7..ea7d9b1be 100644 --- a/frontend/src/app/plugins/file.cljs +++ b/frontend/src/app/plugins/file.cljs @@ -28,92 +28,88 @@ [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 :plugin))))) - - (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 [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 {:get (fn [] plugin-id) :enumerable false} + :$file {:get (fn [] file-id) :enumerable false} + :$version {:get (fn [] (:id @data)) :enumerable false} + :$data {:get (fn [] @data) :enumerable false} - {:name "label" - :get (fn [_] (:label @data)) - :set - (fn [_ value] + :label + {:get #(: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)))))} + + :createdBy + {:get + (fn [] + (when-let [user-data (get users (:profile-id @data))] + (user/user-proxy plugin-id user-data)))} + + :createdAt + {:get #(.toJSDate ^js (:created-at @data))} + + :isAutosave + {:get #(= "system" (:created-by @data))} + + :restore + (fn [] (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) + (u/display-not-valid :restore "Plugin doesn't have 'content:write' permission") :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)))))} + (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))))) - {:name "createdBy" - :get (fn [_] - (when-let [user-data (get users (:profile-id @data))] - (user/user-proxy plugin-id user-data)))} + :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") - {:name "createdAt" - :get (fn [_] - (.toJSDate ^js (:created-at @data)))} + :else + (let [version-id (:id @data)] + (->> (rp/cmd! :delete-file-snapshot {:id version-id}) + (rx/subs! #(resolve) reject))))))) - {:name "isAutosave" - :get (fn [_] - (= "system" (:created-by @data)))}))) + :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)))))))))) (deftype FileProxy [$plugin $id] Object diff --git a/frontend/src/app/util/object.cljc b/frontend/src/app/util/object.cljc new file mode 100644 index 000000000..b287650b5 --- /dev/null +++ b/frontend/src/app/util/object.cljc @@ -0,0 +1,235 @@ +;; 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) + (assoc definition :name (name ckey) :this false) + (-> {: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" {})))))) + +(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)}]) + ~@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 + cljs.core/IMeta + (~'-meta [_#] ~tmeta)))))) diff --git a/frontend/src/app/util/object.cljs b/frontend/src/app/util/object.cljs deleted file mode 100644 index 6066eb952..000000000 --- a/frontend/src/app/util/object.cljs +++ /dev/null @@ -1,76 +0,0 @@ -;; This Source Code Form is subject to the terms of the Mozilla Public -;; License, v. 2.0. If a copy of the MPL was not distributed with this -;; file, You can obtain one at http://mozilla.org/MPL/2.0/. -;; -;; Copyright (c) KALEIDOS INC - -(ns app.util.object - "A collection of helpers for work with javascript objects." - (:refer-clojure :exclude [set! new get merge clone contains? array? into-array])) - -(defn array? - [o] - (.isArray js/Array o)) - -(defn into-array - [o] - (js/Array.from o)) - -(defn create [] #js {}) - -(defn get - ([obj k] - (when (some? obj) - (unchecked-get obj k))) - ([obj k default] - (let [result (get obj k)] - (if (undefined? result) default result)))) - -(defn contains? - [obj k] - (when (some? obj) - (js/Object.hasOwn obj k))) - -(defn clone - [a] - (js/Object.assign #js {} a)) - -(defn merge! - ([a b] - (js/Object.assign a b)) - ([a b & more] - (reduce merge! (merge! a b) more))) - -(defn merge - ([a b] - (js/Object.assign #js {} a b)) - ([a b & more] - (reduce merge! (merge a b) more))) - -(defn set! - [obj key value] - (unchecked-set obj key value) - obj) - -(defn unset! - [obj key] - (js-delete obj key) - obj) - -(def ^:private not-found-sym (js/Symbol "not-found")) - -(defn update! - [obj key f & args] - (let [found (get obj key not-found-sym)] - (when-not ^boolean (identical? found not-found-sym) - (unchecked-set obj key (apply f found args))) - obj)) - -(defn ^boolean in? - [obj prop] - (js* "~{} in ~{}" prop obj)) - -(defn without-empty - [^js obj] - (when (some? obj) - (js* "Object.entries(~{}).reduce((a, [k,v]) => (v == null ? a : (a[k]=v, a)), {}) " obj))) From eccc4226c79adf94a356e4eaa361c752f31e6269 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 22 Nov 2024 11:05:10 +0100 Subject: [PATCH 2/2] :sparkles: Migrate proxies to new format --- frontend/src/app/plugins/api.cljs | 697 +++---- frontend/src/app/plugins/comments.cljs | 260 +-- frontend/src/app/plugins/events.cljs | 17 +- frontend/src/app/plugins/file.cljs | 414 ++-- frontend/src/app/plugins/flex.cljs | 773 ++++---- frontend/src/app/plugins/fonts.cljs | 210 +- frontend/src/app/plugins/grid.cljs | 950 ++++----- frontend/src/app/plugins/history.cljs | 62 +- frontend/src/app/plugins/library.cljs | 1583 +++++++-------- frontend/src/app/plugins/page.cljs | 725 +++---- frontend/src/app/plugins/register.cljs | 2 +- frontend/src/app/plugins/ruler_guides.cljs | 127 +- frontend/src/app/plugins/shape.cljs | 2017 ++++++++++---------- frontend/src/app/plugins/text.cljs | 509 ++--- frontend/src/app/plugins/user.cljs | 36 +- frontend/src/app/plugins/utils.cljs | 30 +- frontend/src/app/plugins/viewport.cljs | 135 +- frontend/src/app/util/object.cljc | 20 +- 18 files changed, 4355 insertions(+), 4212 deletions(-) diff --git a/frontend/src/app/plugins/api.cljs b/frontend/src/app/plugins/api.cljs index 15dc990e4..7bc0fd1fc 100644 --- a/frontend/src/app/plugins/api.cljs +++ b/frontend/src/app/plugins/api.cljs @@ -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))))))) diff --git a/frontend/src/app/plugins/comments.cljs b/frontend/src/app/plugins/comments.cljs index 79f8e9c39..623dc68a0 100644 --- a/frontend/src/app/plugins/comments.cljs +++ b/frontend/src/app/plugins/comments.cljs @@ -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))))))))))) diff --git a/frontend/src/app/plugins/events.cljs b/frontend/src/app/plugins/events.cljs index fee2c3089..313f4c3b6 100644 --- a/frontend/src/app/plugins/events.cljs +++ b/frontend/src/app/plugins/events.cljs @@ -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 _] diff --git a/frontend/src/app/plugins/file.cljs b/frontend/src/app/plugins/file.cljs index ea7d9b1be..22f4049cc 100644 --- a/frontend/src/app/plugins/file.cljs +++ b/frontend/src/app/plugins/file.cljs @@ -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,14 +28,16 @@ [app.util.time :as dt] [beicon.v2.core :as rx])) +(defn file-version-proxy? + [proxy] + (obj/type-of? proxy "FileVersionProxy")) + (defn file-version-proxy [plugin-id file-id users data] (let [data (atom data)] (obj/reify {:name "FileVersionProxy"} - :$plugin {:get (fn [] plugin-id) :enumerable false} - :$file {:get (fn [] file-id) :enumerable false} - :$version {:get (fn [] (:id @data)) :enumerable false} - :$data {:get (fn [] @data) :enumerable false} + :$plugin {:enumerable false :get (fn [] plugin-id)} + :$file {:enumerable false :get (fn [] file-id)} :label {:get #(:label @data) @@ -111,213 +113,209 @@ (resolve (file-version-proxy plugin-id file-id users @data)))) reject)))))))))) -(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] - (cond - (not (r/check-permission $plugin "content:read")) - (u/reject-not-valid reject :findVersions "Plugin doesn't have 'content:read' 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") - - :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))))))) - - (saveVersion - [_ 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 "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 users data)))))))) - -(crc/define-properties! - FileProxy - {:name js/Symbol.toStringTag - :get (fn [] (str "FileProxy"))}) - (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))))))))) diff --git a/frontend/src/app/plugins/flex.cljs b/frontend/src/app/plugins/flex.cljs index 9f65e6f34..a4119008c 100644 --- a/frontend/src/app/plugins/flex.cljs +++ b/frontend/src/app/plugins/flex.cljs @@ -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}))))})) diff --git a/frontend/src/app/plugins/fonts.cljs b/frontend/src/app/plugins/fonts.cljs index e07ea77ba..b36c7731d 100644 --- a/frontend/src/app/plugins/fonts.cljs +++ b/frontend/src/app/plugins/fonts.cljs @@ -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)))))) diff --git a/frontend/src/app/plugins/grid.cljs b/frontend/src/app/plugins/grid.cljs index 3035948e2..7fedf0014 100644 --- a/frontend/src/app/plugins/grid.cljs +++ b/frontend/src/app/plugins/grid.cljs @@ -7,7 +7,6 @@ (ns app.plugins.grid (: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] @@ -22,398 +21,394 @@ ;; Define in `app.plugins.shape` we do this way to prevent circular dependency (def shape-proxy? nil) -(deftype GridLayout [$plugin $file $page $id] - Object - - (addRow - [_ type value] - (let [type (keyword type)] - (cond - (not (contains? ctl/grid-track-types type)) - (u/display-not-valid :addRow-type type) - - (and (or (= :percent type) (= :flex type) (= :fixed type)) - (not (us/safe-number? value))) - (u/display-not-valid :addRow-value value) - - (not (r/check-permission $plugin "content:write")) - (u/display-not-valid :addRow "Plugin doesn't have 'content:write' permission") - - :else - (st/emit! (dwsl/add-layout-track #{$id} :row {:type type :value value}))))) - - (addRowAtIndex - [_ index type value] - (let [type (keyword type)] - (cond - (not (us/safe-int? index)) - (u/display-not-valid :addRowAtIndex-index index) - - (not (contains? ctl/grid-track-types type)) - (u/display-not-valid :addRowAtIndex-type type) - - (and (or (= :percent type) (= :flex type) (= :fixed type)) - (not (us/safe-number? value))) - (u/display-not-valid :addRowAtIndex-value value) - - (not (r/check-permission $plugin "content:write")) - (u/display-not-valid :addRowAtIndex "Plugin doesn't have 'content:write' permission") - - :else - (st/emit! (dwsl/add-layout-track #{$id} :row {:type type :value value} index))))) - - (addColumn - [_ type value] - (let [type (keyword type)] - (cond - (not (contains? ctl/grid-track-types type)) - (u/display-not-valid :addColumn-type type) - - (and (or (= :percent type) (= :flex type) (= :lex type)) - (not (us/safe-number? value))) - (u/display-not-valid :addColumn-value value) - - (not (r/check-permission $plugin "content:write")) - (u/display-not-valid :addColumn "Plugin doesn't have 'content:write' permission") - - :else - (st/emit! (dwsl/add-layout-track #{$id} :column {:type type :value value}))))) - - (addColumnAtIndex - [_ index type value] - (cond - (not (us/safe-int? index)) - (u/display-not-valid :addColumnAtIndex-index index) - - (not (contains? ctl/grid-track-types type)) - (u/display-not-valid :addColumnAtIndex-type type) - - (and (or (= :percent type) (= :flex type) (= :fixed type)) - (not (us/safe-number? value))) - (u/display-not-valid :addColumnAtIndex-value value) - - (not (r/check-permission $plugin "content:write")) - (u/display-not-valid :addColumnAtIndex "Plugin doesn't have 'content:write' permission") - - :else - (let [type (keyword type)] - (st/emit! (dwsl/add-layout-track #{$id} :column {:type type :value value} index))))) - - (removeRow - [_ index] - (cond - (not (us/safe-int? index)) - (u/display-not-valid :removeRow index) - - (not (r/check-permission $plugin "content:write")) - (u/display-not-valid :removeRow "Plugin doesn't have 'content:write' permission") - - :else - (st/emit! (dwsl/remove-layout-track #{$id} :row index)))) - - (removeColumn - [_ index] - (cond - (not (us/safe-int? index)) - (u/display-not-valid :removeColumn index) - - (not (r/check-permission $plugin "content:write")) - (u/display-not-valid :removeColumn "Plugin doesn't have 'content:write' permission") - - :else - (st/emit! (dwsl/remove-layout-track #{$id} :column index)))) - - (setColumn - [_ index type value] - (let [type (keyword type)] - (cond - (not (us/safe-int? index)) - (u/display-not-valid :setColumn-index index) - - (not (contains? ctl/grid-track-types type)) - (u/display-not-valid :setColumn-type type) - - (and (or (= :percent type) (= :flex type) (= :fixed type)) - (not (us/safe-number? value))) - (u/display-not-valid :setColumn-value value) - - (not (r/check-permission $plugin "content:write")) - (u/display-not-valid :setColumn "Plugin doesn't have 'content:write' permission") - - :else - (st/emit! (dwsl/change-layout-track #{$id} :column index (d/without-nils {:type type :value value})))))) - - (setRow - [_ index type value] - (let [type (keyword type)] - (cond - (not (us/safe-int? index)) - (u/display-not-valid :setRow-index index) - - (not (contains? ctl/grid-track-types type)) - (u/display-not-valid :setRow-type type) - - (and (or (= :percent type) (= :flex type) (= :fixed type)) - (not (us/safe-number? value))) - (u/display-not-valid :setRow-value value) - - (not (r/check-permission $plugin "content:write")) - (u/display-not-valid :setRow "Plugin doesn't have 'content:write' permission") - - :else - (st/emit! (dwsl/change-layout-track #{$id} :row index (d/without-nils {:type type :value value})))))) - - (remove - [_] - (cond - (not (r/check-permission $plugin "content:write")) - (u/display-not-valid :remove "Plugin doesn't have 'content:write' permission") - - :else - (st/emit! (dwsl/remove-layout #{$id})))) - - (appendChild - [_ child row column] - (cond - (not (shape-proxy? child)) - (u/display-not-valid :appendChild-child child) - - (or (< row 0) (not (us/safe-int? row))) - (u/display-not-valid :appendChild-row row) - - (or (< column 0) (not (us/safe-int? column))) - (u/display-not-valid :appendChild-column column) - - (not (r/check-permission $plugin "content:write")) - (u/display-not-valid :appendChild "Plugin doesn't have 'content:write' permission") - - :else - (let [child-id (obj/get child "$id")] - (st/emit! (dwt/move-shapes-to-frame #{child-id} $id nil [row column]) - (ptk/data-event :layout/update {:ids [$id]})))))) - (defn grid-layout-proxy? [p] - (instance? GridLayout p)) + (obj/type-of? p "GridLayoutProxy")) (defn grid-layout-proxy [plugin-id file-id page-id id] - (-> (GridLayout. 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 "GridLayoutProxy"} + :$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 "dir" - :get #(-> % u/proxy->shape :layout-grid-dir d/name) - :set - (fn [self value] - (let [value (keyword value)] - (cond - (not (contains? ctl/grid-direction-types value)) - (u/display-not-valid :dir value) + :dir + {:this true + :get #(-> % u/proxy->shape :layout-grid-dir d/name) + :set + (fn [_ value] + (let [value (keyword value)] + (cond + (not (contains? ctl/grid-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-grid-dir value}))))))} + :else + (st/emit! (dwsl/update-layout #{id} {:layout-grid-dir value})))))} - {:name "rows" - :get #(-> % u/proxy->shape :layout-grid-rows format/format-tracks)} + :rows + {:this true + :get #(-> % u/proxy->shape :layout-grid-rows format/format-tracks)} - {:name "columns" - :get #(-> % u/proxy->shape :layout-grid-columns format/format-tracks)} + :columns + {:this true + :get #(-> % u/proxy->shape :layout-grid-columns format/format-tracks)} - {: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 :righPadding "Plugin doesn't have 'content:write' permission") + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :righPadding "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}}))))} -(deftype GridCellProxy [$plugin $file $page $id]) + :addRow + (fn [type value] + (let [type (keyword type)] + (cond + (not (contains? ctl/grid-track-types type)) + (u/display-not-valid :addRow-type type) + + (and (or (= :percent type) (= :flex type) (= :fixed type)) + (not (us/safe-number? value))) + (u/display-not-valid :addRow-value value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :addRow "Plugin doesn't have 'content:write' permission") + + :else + (st/emit! (dwsl/add-layout-track #{id} :row {:type type :value value}))))) + + :addRowAtIndex + (fn [index type value] + (let [type (keyword type)] + (cond + (not (us/safe-int? index)) + (u/display-not-valid :addRowAtIndex-index index) + + (not (contains? ctl/grid-track-types type)) + (u/display-not-valid :addRowAtIndex-type type) + + (and (or (= :percent type) (= :flex type) (= :fixed type)) + (not (us/safe-number? value))) + (u/display-not-valid :addRowAtIndex-value value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :addRowAtIndex "Plugin doesn't have 'content:write' permission") + + :else + (st/emit! (dwsl/add-layout-track #{id} :row {:type type :value value} index))))) + + :addColumn + (fn [type value] + (let [type (keyword type)] + (cond + (not (contains? ctl/grid-track-types type)) + (u/display-not-valid :addColumn-type type) + + (and (or (= :percent type) (= :flex type) (= :lex type)) + (not (us/safe-number? value))) + (u/display-not-valid :addColumn-value value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :addColumn "Plugin doesn't have 'content:write' permission") + + :else + (st/emit! (dwsl/add-layout-track #{id} :column {:type type :value value}))))) + + :addColumnAtIndex + (fn [index type value] + (cond + (not (us/safe-int? index)) + (u/display-not-valid :addColumnAtIndex-index index) + + (not (contains? ctl/grid-track-types type)) + (u/display-not-valid :addColumnAtIndex-type type) + + (and (or (= :percent type) (= :flex type) (= :fixed type)) + (not (us/safe-number? value))) + (u/display-not-valid :addColumnAtIndex-value value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :addColumnAtIndex "Plugin doesn't have 'content:write' permission") + + :else + (let [type (keyword type)] + (st/emit! (dwsl/add-layout-track #{id} :column {:type type :value value} index))))) + + :removeRow + (fn [index] + (cond + (not (us/safe-int? index)) + (u/display-not-valid :removeRow index) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :removeRow "Plugin doesn't have 'content:write' permission") + + :else + (st/emit! (dwsl/remove-layout-track #{id} :row index)))) + + :removeColumn + (fn [index] + (cond + (not (us/safe-int? index)) + (u/display-not-valid :removeColumn index) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :removeColumn "Plugin doesn't have 'content:write' permission") + + :else + (st/emit! (dwsl/remove-layout-track #{id} :column index)))) + + :setColumn + (fn [index type value] + (let [type (keyword type)] + (cond + (not (us/safe-int? index)) + (u/display-not-valid :setColumn-index index) + + (not (contains? ctl/grid-track-types type)) + (u/display-not-valid :setColumn-type type) + + (and (or (= :percent type) (= :flex type) (= :fixed type)) + (not (us/safe-number? value))) + (u/display-not-valid :setColumn-value value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :setColumn "Plugin doesn't have 'content:write' permission") + + :else + (st/emit! (dwsl/change-layout-track #{id} :column index (d/without-nils {:type type :value value})))))) + + :setRow + (fn [index type value] + (let [type (keyword type)] + (cond + (not (us/safe-int? index)) + (u/display-not-valid :setRow-index index) + + (not (contains? ctl/grid-track-types type)) + (u/display-not-valid :setRow-type type) + + (and (or (= :percent type) (= :flex type) (= :fixed type)) + (not (us/safe-number? value))) + (u/display-not-valid :setRow-value value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :setRow "Plugin doesn't have 'content:write' permission") + + :else + (st/emit! (dwsl/change-layout-track #{id} :row index (d/without-nils {:type type :value value})))))) + + :remove + (fn [] + (cond + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :remove "Plugin doesn't have 'content:write' permission") + + :else + (st/emit! (dwsl/remove-layout #{id})))) + + :appendChild + (fn [child row column] + (cond + (not (shape-proxy? child)) + (u/display-not-valid :appendChild-child child) + + (or (< row 0) (not (us/safe-int? row))) + (u/display-not-valid :appendChild-row row) + + (or (< column 0) (not (us/safe-int? column))) + (u/display-not-valid :appendChild-column column) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :appendChild "Plugin doesn't have 'content:write' permission") + + :else + (let [child-id (obj/get child "$id")] + (st/emit! (dwt/move-shapes-to-frame #{child-id} id nil [row column]) + (ptk/data-event :layout/update {:ids [id]}))))))) (defn layout-cell-proxy? [p] - (instance? GridCellProxy p)) + (obj/type-of? p "GridCellProxy")) (defn layout-cell-proxy [plugin-id file-id page-id id] @@ -422,164 +417,171 @@ parent (u/locate-shape file-id page-id (:parent-id shape))] (ctl/get-cell-by-shape-id parent id)))] - (-> (GridCellProxy. 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 "GridCellProxy"} + :$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 "row" - :get #(-> % locate-cell :row) - :set - (fn [self value] - (let [cell (locate-cell self) - shape (u/proxy->shape self)] - (cond - (not (us/safe-int? value)) - (u/display-not-valid :row-value value) + :row + {:this true + :get #(-> % locate-cell :row) + :set + (fn [self value] + (let [cell (locate-cell self) + shape (u/proxy->shape self)] + (cond + (not (us/safe-int? value)) + (u/display-not-valid :row-value value) - (nil? cell) - (u/display-not-valid :row-cell "cell not found") + (nil? cell) + (u/display-not-valid :row-cell "cell not found") - (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :row "Plugin doesn't have 'content:write' permission") + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :row "Plugin doesn't have 'content:write' permission") - :else - (st/emit! (dwsl/update-grid-cell-position (:parent-id shape) (:id cell) {:row value})))))} + :else + (st/emit! (dwsl/update-grid-cell-position (:parent-id shape) (:id cell) {:row value})))))} - {:name "rowSpan" - :get #(-> % locate-cell :row-span) - :set - (fn [self value] - (let [shape (u/proxy->shape self) - cell (locate-cell self)] - (cond - (not (us/safe-int? value)) - (u/display-not-valid :rowSpan-value value) + :rowSpan + {:this true + :get #(-> % locate-cell :row-span) + :set + (fn [self value] + (let [shape (u/proxy->shape self) + cell (locate-cell self)] + (cond + (not (us/safe-int? value)) + (u/display-not-valid :rowSpan-value value) - (nil? cell) - (u/display-not-valid :rowSpan-cell "cell not found") + (nil? cell) + (u/display-not-valid :rowSpan-cell "cell not found") - (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :rowSpan "Plugin doesn't have 'content:write' permission") + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :rowSpan "Plugin doesn't have 'content:write' permission") - :else - (st/emit! (dwsl/update-grid-cell-position (:parent-id shape) (:id cell) {:row-span value})))))} + :else + (st/emit! (dwsl/update-grid-cell-position (:parent-id shape) (:id cell) {:row-span value})))))} - {:name "column" - :get #(-> % locate-cell :column) - :set - (fn [self value] - (let [shape (u/proxy->shape self) - cell (locate-cell self)] - (cond - (not (us/safe-int? value)) - (u/display-not-valid :column-value value) + :column + {:this true + :get #(-> % locate-cell :column) + :set + (fn [self value] + (let [shape (u/proxy->shape self) + cell (locate-cell self)] + (cond + (not (us/safe-int? value)) + (u/display-not-valid :column-value value) - (nil? cell) - (u/display-not-valid :column-cell "cell not found") + (nil? cell) + (u/display-not-valid :column-cell "cell not found") - (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :column "Plugin doesn't have 'content:write' permission") + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :column "Plugin doesn't have 'content:write' permission") - :else - (st/emit! (dwsl/update-grid-cell-position (:parent-id shape) (:id cell) {:column value})))))} + :else + (st/emit! (dwsl/update-grid-cell-position (:parent-id shape) (:id cell) {:column value})))))} - {:name "columnSpan" - :get #(-> % locate-cell :column-span) - :set - (fn [self value] - (let [shape (u/proxy->shape self) - cell (locate-cell self)] - (cond - (not (us/safe-int? value)) - (u/display-not-valid :columnSpan-value value) + :columnSpan + {:this true + :get #(-> % locate-cell :column-span) + :set + (fn [self value] + (let [shape (u/proxy->shape self) + cell (locate-cell self)] + (cond + (not (us/safe-int? value)) + (u/display-not-valid :columnSpan-value value) - (nil? cell) - (u/display-not-valid :columnSpan-cell "cell not found") + (nil? cell) + (u/display-not-valid :columnSpan-cell "cell not found") - (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :columnSpan "Plugin doesn't have 'content:write' permission") + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :columnSpan "Plugin doesn't have 'content:write' permission") - :else - (st/emit! (dwsl/update-grid-cell-position (:parent-id shape) (:id cell) {:column-span value})))))} + :else + (st/emit! (dwsl/update-grid-cell-position (:parent-id shape) (:id cell) {:column-span value})))))} - {:name "areaName" - :get #(-> % locate-cell :area-name) - :set - (fn [self value] - (let [shape (u/proxy->shape self) - cell (locate-cell self)] - (cond - (not (string? value)) - (u/display-not-valid :areaName-value value) + :areaName + {:this true + :get #(-> % locate-cell :area-name) + :set + (fn [self value] + (let [shape (u/proxy->shape self) + cell (locate-cell self)] + (cond + (not (string? value)) + (u/display-not-valid :areaName-value value) - (nil? cell) - (u/display-not-valid :areaName-cell "cell not found") + (nil? cell) + (u/display-not-valid :areaName-cell "cell not found") - (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :areaName "Plugin doesn't have 'content:write' permission") + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :areaName "Plugin doesn't have 'content:write' permission") - :else - (st/emit! (dwsl/update-grid-cells (:parent-id shape) #{(:id cell)} {:area-name value})))))} + :else + (st/emit! (dwsl/update-grid-cells (:parent-id shape) #{(:id cell)} {:area-name value})))))} - {:name "position" - :get #(-> % locate-cell :position d/name) - :set - (fn [self value] - (let [shape (u/proxy->shape self) - cell (locate-cell self) - value (keyword value)] - (cond - (not (contains? ctl/grid-position-types value)) - (u/display-not-valid :position-value value) + :position + {:this true + :get #(-> % locate-cell :position d/name) + :set + (fn [self value] + (let [shape (u/proxy->shape self) + cell (locate-cell self) + value (keyword value)] + (cond + (not (contains? ctl/grid-position-types value)) + (u/display-not-valid :position-value value) - (nil? cell) - (u/display-not-valid :position-cell "cell not found") + (nil? cell) + (u/display-not-valid :position-cell "cell not found") - (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 - (st/emit! (dwsl/change-cells-mode (:parent-id shape) #{(:id cell)} value)))))} + :else + (st/emit! (dwsl/change-cells-mode (:parent-id shape) #{(:id cell)} value)))))} - {:name "alignSelf" - :get #(-> % locate-cell :align-self d/name) - :set - (fn [self value] - (let [shape (u/proxy->shape self) - value (keyword value) - cell (locate-cell self)] - (cond - (not (contains? ctl/grid-cell-align-self-types value)) - (u/display-not-valid :alignSelf-value value) + :alignSelf + {:this true + :get #(-> % locate-cell :align-self d/name) + :set + (fn [self value] + (let [shape (u/proxy->shape self) + value (keyword value) + cell (locate-cell self)] + (cond + (not (contains? ctl/grid-cell-align-self-types value)) + (u/display-not-valid :alignSelf-value value) - (nil? cell) - (u/display-not-valid :alignSelf-cell "cell not found") + (nil? cell) + (u/display-not-valid :alignSelf-cell "cell not found") - (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 - (st/emit! (dwsl/update-grid-cells (:parent-id shape) #{(:id cell)} {:align-self value})))))} + :else + (st/emit! (dwsl/update-grid-cells (:parent-id shape) #{(:id cell)} {:align-self value})))))} - {:name "justifySelf" - :get #(-> % locate-cell :justify-self d/name) - :set - (fn [self value] - (let [shape (u/proxy->shape self) - value (keyword value) - cell (locate-cell self)] - (cond - (not (contains? ctl/grid-cell-justify-self-types value)) - (u/display-not-valid :justifySelf-value value) + :justifySelf + {:this true + :get #(-> % locate-cell :justify-self d/name) + :set + (fn [self value] + (let [shape (u/proxy->shape self) + value (keyword value) + cell (locate-cell self)] + (cond + (not (contains? ctl/grid-cell-justify-self-types value)) + (u/display-not-valid :justifySelf-value value) - (nil? cell) - (u/display-not-valid :justifySelf-cell "cell not found") + (nil? cell) + (u/display-not-valid :justifySelf-cell "cell not found") - (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :justifySelf "Plugin doesn't have 'content:write' permission") + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :justifySelf "Plugin doesn't have 'content:write' permission") - :else - (st/emit! (dwsl/update-grid-cells (:parent-id shape) #{(:id cell)} {:justify-self value})))))})))) + :else + (st/emit! (dwsl/update-grid-cells (:parent-id shape) #{(:id cell)} {:justify-self value})))))}))) diff --git a/frontend/src/app/plugins/history.cljs b/frontend/src/app/plugins/history.cljs index decc741a8..191dcc0d7 100644 --- a/frontend/src/app/plugins/history.cljs +++ b/frontend/src/app/plugins/history.cljs @@ -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)))))) diff --git a/frontend/src/app/plugins/library.cljs b/frontend/src/app/plugins/library.cljs index bce7fa071..b1c38e76d 100644 --- a/frontend/src/app/plugins/library.cljs +++ b/frontend/src/app/plugins/library.cljs @@ -11,7 +11,6 @@ [app.common.data :as d] [app.common.data.macros :as dm] [app.common.geom.point :as gpt] - [app.common.record :as cr] [app.common.schema :as sm] [app.common.types.color :as ctc] [app.common.types.file :as ctf] @@ -35,935 +34,943 @@ (declare lib-color-proxy) (declare lib-typography-proxy) -(deftype LibraryColorProxy [$plugin $file $id] - Object - - (remove - [_] - (cond - (not (r/check-permission $plugin "library:write")) - (u/display-not-valid :remove "Plugin doesn't have 'library:write' permission") - - :else - (st/emit! (dwl/delete-color {:id $id})))) - - (clone - [_] - (cond - (not (r/check-permission $plugin "library:write")) - (u/display-not-valid :clone "Plugin doesn't have 'library:write' permission") - - :else - (let [color-id (uuid/next) - color (-> (u/locate-library-color $file $id) - (assoc :id color-id))] - (st/emit! (dwl/add-color color {:rename? false})) - (lib-color-proxy $plugin $id color-id)))) - - (asFill [_] - (let [color (u/locate-library-color $file $id)] - (format/format-fill - {:fill-color (:color color) - :fill-opacity (:opacity color) - :fill-color-gradient (:gradient color) - :fill-color-ref-file $file - :fill-color-ref-id $id - :fill-image (:image color)}))) - - (asStroke [_] - (let [color (u/locate-library-color $file $id)] - (format/format-stroke - {:stroke-color (:color color) - :stroke-opacity (:opacity color) - :stroke-color-gradient (:gradient color) - :stroke-color-ref-file $file - :stroke-color-ref-id $id - :stroke-image (:image color) - :stroke-style :solid - :stroke-alignment :inner}))) - - (getPluginData - [self key] - (cond - (not (string? key)) - (u/display-not-valid :getPluginData-key key) - - :else - (let [color (u/proxy->library-color self)] - (dm/get-in color [:plugin-data (keyword "plugin" (str $plugin)) key])))) - - (setPluginData - [_ key value] - (cond - (not= $file (:current-file-id @st/state)) - (u/display-not-valid :setPluginData-non-local-library $file) - - (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 "library:write")) - (u/display-not-valid :setPluginData "Plugin doesn't have 'library:write' permission") - - :else - (st/emit! (dw/set-plugin-data $file :color $id (keyword "plugin" (str $plugin)) key value)))) - - (getPluginDataKeys - [self] - (let [color (u/proxy->library-color self)] - (apply array (keys (dm/get-in color [: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 [color (u/proxy->library-color self)] - (dm/get-in color [:plugin-data (keyword "shared" namespace) key])))) - - (setSharedPluginData - [_ namespace key value] - (cond - (not= $file (:current-file-id @st/state)) - (u/display-not-valid :setSharedPluginData-non-local-library $file) - - (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 "library:write")) - (u/display-not-valid :setSharedPluginData "Plugin doesn't have 'library:write' permission") - - :else - (st/emit! (dw/set-plugin-data $file :color $id (keyword "shared" namespace) key value)))) - - (getSharedPluginDataKeys - [self namespace] - (cond - (not (string? namespace)) - (u/display-not-valid :getSharedPluginDataKeys-namespace namespace) - - :else - (let [color (u/proxy->library-color self)] - (apply array (keys (dm/get-in color [:plugin-data (keyword "shared" namespace)]))))))) - (defn lib-color-proxy? [p] - (instance? LibraryColorProxy p)) + (obj/type-of? p "LibraryColorProxy")) (defn lib-color-proxy [plugin-id file-id id] (assert (uuid? file-id)) (assert (uuid? id)) - (cr/add-properties! - (LibraryColorProxy. 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 "LibraryColorProxy"} + :$plugin {:enumerable false :get (constantly plugin-id)} + :$id {:enumerable false :get (constantly id)} + :$file {:enumerable false :get (constantly file-id)} - {:name "id" :get (fn [_] (dm/str id))} + :id {:get (fn [] (dm/str id))} - {:name "name" - :get #(-> % u/proxy->library-color :name) - :set - (fn [self value] + :name + {:this true + :get #(-> % u/proxy->library-color :name) + :set + (fn [self value] + (cond + (not (string? value)) + (u/display-not-valid :name value) + + (not (r/check-permission plugin-id "library:write")) + (u/display-not-valid :name "Plugin doesn't have 'library:write' permission") + + :else + (let [color (u/proxy->library-color self) + value (dm/str (d/nilv (:path color) "") " / " value)] + (st/emit! (dwl/rename-color file-id id value)))))} + + :path + {:this true + :get #(-> % u/proxy->library-color :path) + :set + (fn [self value] + (cond + (not (string? value)) + (u/display-not-valid :path value) + + (not (r/check-permission plugin-id "library:write")) + (u/display-not-valid :path "Plugin doesn't have 'library:write' permission") + + :else + (let [color (-> (u/proxy->library-color self) + (update :name #(str value " / " %)))] + (st/emit! (dwl/update-color color file-id)))))} + + :color + {:this true + :get #(-> % u/proxy->library-color :color) + :set + (fn [self value] + (cond + (or (not (string? value)) (not (cc/valid-hex-color? value))) + (u/display-not-valid :color value) + + (not (r/check-permission plugin-id "library:write")) + (u/display-not-valid :color "Plugin doesn't have 'library:write' permission") + + :else + (let [color (-> (u/proxy->library-color self) + (assoc :color value))] + (st/emit! (dwl/update-color color file-id)))))} + + :opacity + {:this true + :get #(-> % u/proxy->library-color :opacity) + :set + (fn [self value] + (cond + (or (not (number? value)) (< value 0) (> value 1)) + (u/display-not-valid :opacity value) + + (not (r/check-permission plugin-id "library:write")) + (u/display-not-valid :opacity "Plugin doesn't have 'library:write' permission") + + :else + (let [color (-> (u/proxy->library-color self) + (assoc :opacity value))] + (st/emit! (dwl/update-color color file-id)))))} + + :gradient + {:this true + :get #(-> % u/proxy->library-color :gradient format/format-gradient) + :set + (fn [self value] + (let [value (parser/parse-gradient value)] + (cond + (not (sm/validate ::ctc/gradient value)) + (u/display-not-valid :gradient value) + + (not (r/check-permission plugin-id "library:write")) + (u/display-not-valid :gradient "Plugin doesn't have 'library:write' permission") + + :else + (let [color (-> (u/proxy->library-color self) + (assoc :gradient value))] + (st/emit! (dwl/update-color color file-id))))))} + + :image + {:this true + :get #(-> % u/proxy->library-color :image format/format-image) + :set + (fn [self value] + (let [value (parser/parse-image-data value)] + (cond + (not (sm/validate ::ctc/image-color value)) + (u/display-not-valid :image value) + + (not (r/check-permission plugin-id "library:write")) + (u/display-not-valid :image "Plugin doesn't have 'library:write' permission") + + :else + (let [color (-> (u/proxy->library-color self) + (assoc :image value))] + (st/emit! (dwl/update-color color file-id))))))} + + :remove + (fn [] (cond - (not (string? value)) - (u/display-not-valid :name value) - (not (r/check-permission plugin-id "library:write")) - (u/display-not-valid :name "Plugin doesn't have 'library:write' permission") + (u/display-not-valid :remove "Plugin doesn't have 'library:write' permission") :else - (let [color (u/proxy->library-color self) - value (dm/str (d/nilv (:path color) "") " / " value)] - (st/emit! (dwl/rename-color file-id id value)))))} + (st/emit! (dwl/delete-color {:id id})))) - {:name "path" - :get #(-> % u/proxy->library-color :path) - :set - (fn [self value] + :clone + (fn [] (cond - (not (string? value)) - (u/display-not-valid :path value) - (not (r/check-permission plugin-id "library:write")) - (u/display-not-valid :path "Plugin doesn't have 'library:write' permission") + (u/display-not-valid :clone "Plugin doesn't have 'library:write' permission") :else - (let [color (-> (u/proxy->library-color self) - (update :name #(str value " / " %)))] - (st/emit! (dwl/update-color color file-id)))))} + (let [color-id (uuid/next) + color (-> (u/locate-library-color file-id id) + (assoc :id color-id))] + (st/emit! (dwl/add-color color {:rename? false})) + (lib-color-proxy plugin-id id color-id)))) - {:name "color" - :get #(-> % u/proxy->library-color :color) - :set - (fn [self value] + :asFill + (fn [] + (let [color (u/locate-library-color file-id id)] + (format/format-fill + {:fill-color (:color color) + :fill-opacity (:opacity color) + :fill-color-gradient (:gradient color) + :fill-color-ref-file file-id + :fill-color-ref-id id + :fill-image (:image color)}))) + + :asStroke + (fn [] + (let [color (u/locate-library-color file-id id)] + (format/format-stroke + {:stroke-color (:color color) + :stroke-opacity (:opacity color) + :stroke-color-gradient (:gradient color) + :stroke-color-ref-file file-id + :stroke-color-ref-id id + :stroke-image (:image color) + :stroke-style :solid + :stroke-alignment :inner}))) + + :getPluginData + (fn [key] (cond - (or (not (string? value)) (not (cc/valid-hex-color? value))) - (u/display-not-valid :color value) - - (not (r/check-permission plugin-id "library:write")) - (u/display-not-valid :color "Plugin doesn't have 'library:write' permission") + (not (string? key)) + (u/display-not-valid :getPluginData-key key) :else - (let [color (-> (u/proxy->library-color self) - (assoc :color value))] - (st/emit! (dwl/update-color color file-id)))))} + (let [color (u/locate-library-color file-id id)] + (dm/get-in color [:plugin-data (keyword "plugin" (str plugin-id)) key])))) - {:name "opacity" - :get #(-> % u/proxy->library-color :opacity) - :set - (fn [self value] + :setPluginData + (fn [key value] (cond - (or (not (number? value)) (< value 0) (> value 1)) - (u/display-not-valid :opacity value) + (not= file-id (:current-file-id @st/state)) + (u/display-not-valid :setPluginData-non-local-library file-id) + + (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-id "library:write")) - (u/display-not-valid :opacity "Plugin doesn't have 'library:write' permission") + (u/display-not-valid :setPluginData "Plugin doesn't have 'library:write' permission") :else - (let [color (-> (u/proxy->library-color self) - (assoc :opacity value))] - (st/emit! (dwl/update-color color file-id)))))} + (st/emit! (dw/set-plugin-data file-id :color id (keyword "plugin" (str plugin-id)) key value)))) - {:name "gradient" - :get #(-> % u/proxy->library-color :gradient format/format-gradient) - :set - (fn [self value] - (let [value (parser/parse-gradient value)] - (cond - (not (sm/validate ::ctc/gradient value)) - (u/display-not-valid :gradient value) + :getPluginDataKeys + (fn [] + (let [color (u/locate-library-color file-id id)] + (apply array (keys (dm/get-in color [:plugin-data (keyword "plugin" (str plugin-id))]))))) - (not (r/check-permission plugin-id "library:write")) - (u/display-not-valid :gradient "Plugin doesn't have 'library:write' permission") + :getSharedPluginData + (fn [namespace key] + (cond + (not (string? namespace)) + (u/display-not-valid :getSharedPluginData-namespace namespace) - :else - (let [color (-> (u/proxy->library-color self) - (assoc :gradient value))] - (st/emit! (dwl/update-color color file-id))))))} + (not (string? key)) + (u/display-not-valid :getSharedPluginData-key key) - {:name "image" - :get #(-> % u/proxy->library-color :image format/format-image) - :set - (fn [self value] - (let [value (parser/parse-image-data value)] - (cond - (not (sm/validate ::ctc/image-color value)) - (u/display-not-valid :image value) + :else + (let [color (u/locate-library-color file-id id)] + (dm/get-in color [:plugin-data (keyword "shared" namespace) key])))) - (not (r/check-permission plugin-id "library:write")) - (u/display-not-valid :image "Plugin doesn't have 'library:write' permission") + :setSharedPluginData + (fn [namespace key value] + (cond + (not= file-id (:current-file-id @st/state)) + (u/display-not-valid :setSharedPluginData-non-local-library file-id) - :else - (let [color (-> (u/proxy->library-color self) - (assoc :image value))] - (st/emit! (dwl/update-color color file-id))))))})) + (not (string? namespace)) + (u/display-not-valid :setSharedPluginData-namespace namespace) -(deftype LibraryTypographyProxy [$plugin $file $id] - Object - (remove - [_] - (cond - (not (r/check-permission $plugin "library:write")) - (u/display-not-valid :remove "Plugin doesn't have 'library:write' permission") + (not (string? key)) + (u/display-not-valid :setSharedPluginData-key key) - :else - (st/emit! (dwl/delete-typography {:id $id})))) + (and (some? value) (not (string? value))) + (u/display-not-valid :setSharedPluginData-value value) - (clone - [_] - (cond - (not (r/check-permission $plugin "library:write")) - (u/display-not-valid :clone "Plugin doesn't have 'library:write' permission") + (not (r/check-permission plugin-id "library:write")) + (u/display-not-valid :setSharedPluginData "Plugin doesn't have 'library:write' permission") - :else - (let [typo-id (uuid/next) - typo (-> (u/locate-library-typography $file $id) - (assoc :id typo-id))] - (st/emit! (dwl/add-typography typo false)) - (lib-typography-proxy $plugin $id typo-id)))) + :else + (st/emit! (dw/set-plugin-data file-id :color id (keyword "shared" namespace) key value)))) - (applyToText - [_ shape] - (cond - (not (shape/shape-proxy? shape)) - (u/display-not-valid :applyToText shape) + :getSharedPluginDataKeys + (fn [namespace] + (cond + (not (string? namespace)) + (u/display-not-valid :getSharedPluginDataKeys-namespace namespace) - (not (r/check-permission $plugin "content:write")) - (u/display-not-valid :applyToText "Plugin doesn't have 'content:write' permission") - - :else - (let [shape-id (obj/get shape "$id") - typography (u/locate-library-typography $file $id)] - (st/emit! (dwt/apply-typography #{shape-id} typography $file))))) - - (applyToTextRange - [self range] - (cond - (not (text/text-range? range)) - (u/display-not-valid :applyToText range) - - (not (r/check-permission $plugin "content:write")) - (u/display-not-valid :applyToText "Plugin doesn't have 'content:write' permission") - - :else - (let [shape-id (obj/get range "$id") - start (obj/get range "start") - end (obj/get range "end") - typography (u/proxy->library-typography self) - attrs (-> typography - (assoc :typography-ref-file $file) - (assoc :typography-ref-id (:id typography)) - (dissoc :id :name))] - (st/emit! (dwt/update-text-range shape-id start end attrs))))) - - ;; PLUGIN DATA - (getPluginData - [self key] - (cond - (not (string? key)) - (u/display-not-valid :typography-plugin-data-key key) - - :else - (let [typography (u/proxy->library-typography self)] - (dm/get-in typography [:plugin-data (keyword "plugin" (str $plugin)) key])))) - - (setPluginData - [_ key value] - (cond - (not= $file (:current-file-id @st/state)) - (u/display-not-valid :setPluginData-non-local-library $file) - - (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 "library:write")) - (u/display-not-valid :setPluginData "Plugin doesn't have 'library:write' permission") - - :else - (st/emit! (dw/set-plugin-data $file :typography $id (keyword "plugin" (str $plugin)) key value)))) - - (getPluginDataKeys - [self] - (let [typography (u/proxy->library-typography self)] - (apply array (keys (dm/get-in typography [: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 [typography (u/proxy->library-typography self)] - (dm/get-in typography [:plugin-data (keyword "shared" namespace) key])))) - - (setSharedPluginData - [_ namespace key value] - (cond - (not= $file (:current-file-id @st/state)) - (u/display-not-valid :setSharedPluginData-non-local-library $file) - - (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 "library:write")) - (u/display-not-valid :setSharedPluginData "Plugin doesn't have 'library:write' permission") - - :else - (st/emit! (dw/set-plugin-data $file :typography $id (keyword "shared" namespace) key value)))) - - (getSharedPluginDataKeys - [self namespace] - (cond - (not (string? namespace)) - (u/display-not-valid :getSharedPluginDataKeys-namespace namespace) - - :else - (let [typography (u/proxy->library-typography self)] - (apply array (keys (dm/get-in typography [:plugin-data (keyword "shared" namespace)]))))))) + :else + (let [color (u/locate-library-color file-id id)] + (apply array (keys (dm/get-in color [:plugin-data (keyword "shared" namespace)])))))))) (defn lib-typography-proxy? [p] - (instance? LibraryTypographyProxy p)) + (obj/type-of? p "LibraryTypographyProxy")) (defn lib-typography-proxy [plugin-id file-id id] (assert (uuid? file-id)) (assert (uuid? id)) - (cr/add-properties! - (LibraryTypographyProxy. 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)} - {:name "id" :get (fn [_] (dm/str id))} + (obj/reify {:name "LibraryTypographyProxy"} + :$plugin {:name "" :enumerable false :get (constantly plugin-id)} + :$id {:name "" :enumerable false :get (constantly id)} + :$file {:name "" :enumerable false :get (constantly file-id)} + :id {:name "" :get (fn [_] (dm/str id))} - {:name "name" - :get #(-> % u/proxy->library-typography :name) - :set - (fn [self value] + :name + {:this true + :get #(-> % u/proxy->library-typography :name) + :set + (fn [self value] + (cond + (not (string? value)) + (u/display-not-valid :name value) + + (not (r/check-permission plugin-id "library:write")) + (u/display-not-valid :name "Plugin doesn't have 'library:write' permission") + + :else + (let [typo (u/proxy->library-typography self) + value (dm/str (d/nilv (:path typo) "") " / " value)] + (st/emit! (dwl/rename-typography file-id id value)))))} + + :path + {:this true + :get #(-> % u/proxy->library-typography :path) + :set + (fn [self value] + (cond + (not (string? value)) + (u/display-not-valid :path value) + + (not (r/check-permission plugin-id "library:write")) + (u/display-not-valid :path "Plugin doesn't have 'library:write' permission") + + :else + (let [typo (-> (u/proxy->library-typography self) + (update :name #(str value " / " %)))] + (st/emit! (dwl/update-typography typo file-id)))))} + + :fontId + {:this true + :get #(-> % u/proxy->library-typography :font-id) + :set + (fn [self value] + (cond + (not (string? value)) + (u/display-not-valid :fontId value) + + (not (r/check-permission plugin-id "library:write")) + (u/display-not-valid :fontId "Plugin doesn't have 'library:write' permission") + + :else + (let [typo (-> (u/proxy->library-typography self) + (assoc :font-id value))] + (st/emit! (dwl/update-typography typo file-id)))))} + + :fontFamily + {:this true + :get #(-> % u/proxy->library-typography :font-family) + :set + (fn [self value] + (cond + (not (string? value)) + (u/display-not-valid :fontFamily value) + + (not (r/check-permission plugin-id "library:write")) + (u/display-not-valid :fontFamily "Plugin doesn't have 'library:write' permission") + + :else + (let [typo (-> (u/proxy->library-typography self) + (assoc :font-family value))] + (st/emit! (dwl/update-typography typo file-id)))))} + + :fontVariantId + {:this true + :get #(-> % u/proxy->library-typography :font-variant-id) + :set + (fn [self value] + (cond + (not (string? value)) + (u/display-not-valid :fontVariantId value) + + (not (r/check-permission plugin-id "library:write")) + (u/display-not-valid :fontVariantId "Plugin doesn't have 'library:write' permission") + + :else + (let [typo (-> (u/proxy->library-typography self) + (assoc :font-variant-id value))] + (st/emit! (dwl/update-typography typo file-id)))))} + + :fontSize + {:this true + :get #(-> % u/proxy->library-typography :font-size) + :set + (fn [self value] + (cond + (not (string? value)) + (u/display-not-valid :fontSize value) + + (not (r/check-permission plugin-id "library:write")) + (u/display-not-valid :fontSize "Plugin doesn't have 'library:write' permission") + + :else + (let [typo (-> (u/proxy->library-typography self) + (assoc :font-size value))] + (st/emit! (dwl/update-typography typo file-id)))))} + + :fontWeight + {:this true + :get #(-> % u/proxy->library-typography :font-weight) + :set + (fn [self value] + (cond + (not (string? value)) + (u/display-not-valid :fontWeight value) + + (not (r/check-permission plugin-id "library:write")) + (u/display-not-valid :fontWeight "Plugin doesn't have 'library:write' permission") + + :else + (let [typo (-> (u/proxy->library-typography self) + (assoc :font-weight value))] + (st/emit! (dwl/update-typography typo file-id)))))} + + :fontStyle + {:this true + :get #(-> % u/proxy->library-typography :font-style) + :set + (fn [self value] + (cond + (not (string? value)) + (u/display-not-valid :fontStyle value) + + (not (r/check-permission plugin-id "library:write")) + (u/display-not-valid :fontStyle "Plugin doesn't have 'library:write' permission") + + :else + (let [typo (-> (u/proxy->library-typography self) + (assoc :font-style value))] + (st/emit! (dwl/update-typography typo file-id)))))} + + :lineHeight + {:this true + :get #(-> % u/proxy->library-typography :font-height) + :set + (fn [self value] + (cond + (not (string? value)) + (u/display-not-valid :lineHeight value) + + (not (r/check-permission plugin-id "library:write")) + (u/display-not-valid :lineHeight "Plugin doesn't have 'library:write' permission") + + :else + (let [typo (-> (u/proxy->library-typography self) + (assoc :font-height value))] + (st/emit! (dwl/update-typography typo file-id)))))} + + :letterSpacing + {:this true + :get #(-> % u/proxy->library-typography :letter-spacing) + :set + (fn [self value] + (cond + (not (string? value)) + (u/display-not-valid :letterSpacing value) + + (not (r/check-permission plugin-id "library:write")) + (u/display-not-valid :letterSpacing "Plugin doesn't have 'library:write' permission") + + :else + (let [typo (-> (u/proxy->library-typography self) + (assoc :letter-spacing value))] + (st/emit! (dwl/update-typography typo file-id)))))} + + :textTransform + {:this true + :get #(-> % u/proxy->library-typography :text-transform) + :set + (fn [self value] + (cond + (not (string? value)) + (u/display-not-valid :textTransform value) + + (not (r/check-permission plugin-id "library:write")) + (u/display-not-valid :textTransform "Plugin doesn't have 'library:write' permission") + + :else + (let [typo (-> (u/proxy->library-typography self) + (assoc :text-transform value))] + (st/emit! (dwl/update-typography typo file-id)))))} + + :remove + (fn [] (cond - (not (string? value)) - (u/display-not-valid :name value) - (not (r/check-permission plugin-id "library:write")) - (u/display-not-valid :name "Plugin doesn't have 'library:write' permission") + (u/display-not-valid :remove "Plugin doesn't have 'library:write' permission") :else - (let [typo (u/proxy->library-typography self) - value (dm/str (d/nilv (:path typo) "") " / " value)] - (st/emit! (dwl/rename-typography file-id id value)))))} + (st/emit! (dwl/delete-typography {:id id})))) - {:name "path" - :get #(-> % u/proxy->library-typography :path) - :set - (fn [self value] + :clone + (fn [] (cond - (not (string? value)) - (u/display-not-valid :path value) - (not (r/check-permission plugin-id "library:write")) - (u/display-not-valid :path "Plugin doesn't have 'library:write' permission") + (u/display-not-valid :clone "Plugin doesn't have 'library:write' permission") :else - (let [typo (-> (u/proxy->library-typography self) - (update :name #(str value " / " %)))] - (st/emit! (dwl/update-typography typo file-id)))))} + (let [typo-id (uuid/next) + typo (-> (u/locate-library-typography file-id id) + (assoc :id typo-id))] + (st/emit! (dwl/add-typography typo false)) + (lib-typography-proxy plugin-id id typo-id)))) - {:name "fontId" - :get #(-> % u/proxy->library-typography :font-id) - :set - (fn [self value] + :applyToText + (fn [shape] (cond - (not (string? value)) - (u/display-not-valid :fontId value) + (not (shape/shape-proxy? shape)) + (u/display-not-valid :applyToText shape) - (not (r/check-permission plugin-id "library:write")) - (u/display-not-valid :fontId "Plugin doesn't have 'library:write' permission") + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :applyToText "Plugin doesn't have 'content:write' permission") :else - (let [typo (-> (u/proxy->library-typography self) - (assoc :font-id value))] - (st/emit! (dwl/update-typography typo file-id)))))} + (let [shape-id (obj/get shape "$id") + typography (u/locate-library-typography file-id id)] + (st/emit! (dwt/apply-typography #{shape-id} typography file-id))))) - {:name "fontFamily" - :get #(-> % u/proxy->library-typography :font-family) - :set - (fn [self value] + :applyToTextRange + (fn [range] (cond - (not (string? value)) - (u/display-not-valid :fontFamily value) + (not (text/text-range-proxy? range)) + (u/display-not-valid :applyToText range) - (not (r/check-permission plugin-id "library:write")) - (u/display-not-valid :fontFamily "Plugin doesn't have 'library:write' permission") + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :applyToText "Plugin doesn't have 'content:write' permission") :else - (let [typo (-> (u/proxy->library-typography self) - (assoc :font-family value))] - (st/emit! (dwl/update-typography typo file-id)))))} + (let [shape-id (obj/get range "$id") + start (obj/get range "start") + end (obj/get range "end") + typography (u/locate-library-typography file-id id) + attrs (-> typography + (assoc :typography-ref-file file-id) + (assoc :typography-ref-id (:id typography)) + (dissoc :id :name))] + (st/emit! (dwt/update-text-range shape-id start end attrs))))) - {:name "fontVariantId" - :get #(-> % u/proxy->library-typography :font-variant-id) - :set - (fn [self value] + ;; PLUGIN DATA + :getPluginData + (fn [key] (cond - (not (string? value)) - (u/display-not-valid :fontVariantId value) - - (not (r/check-permission plugin-id "library:write")) - (u/display-not-valid :fontVariantId "Plugin doesn't have 'library:write' permission") + (not (string? key)) + (u/display-not-valid :typography-plugin-data-key key) :else - (let [typo (-> (u/proxy->library-typography self) - (assoc :font-variant-id value))] - (st/emit! (dwl/update-typography typo file-id)))))} + (let [typography (u/locate-library-typography file-id id)] + (dm/get-in typography [:plugin-data (keyword "plugin" (str plugin-id)) key])))) - {:name "fontSize" - :get #(-> % u/proxy->library-typography :font-size) - :set - (fn [self value] + :setPluginData + (fn [key value] (cond - (not (string? value)) - (u/display-not-valid :fontSize value) + (not= file-id (:current-file-id @st/state)) + (u/display-not-valid :setPluginData-non-local-library file-id) + + (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-id "library:write")) - (u/display-not-valid :fontSize "Plugin doesn't have 'library:write' permission") + (u/display-not-valid :setPluginData "Plugin doesn't have 'library:write' permission") :else - (let [typo (-> (u/proxy->library-typography self) - (assoc :font-size value))] - (st/emit! (dwl/update-typography typo file-id)))))} + (st/emit! (dw/set-plugin-data file-id :typography id (keyword "plugin" (str plugin-id)) key value)))) - {:name "fontWeight" - :get #(-> % u/proxy->library-typography :font-weight) - :set - (fn [self value] + :getPluginDataKeys + (fn [] + (let [typography (u/locate-library-typography file-id id)] + (apply array (keys (dm/get-in typography [:plugin-data (keyword "plugin" (str plugin-id))]))))) + + :getSharedPluginData + (fn [namespace key] (cond - (not (string? value)) - (u/display-not-valid :fontWeight value) + (not (string? namespace)) + (u/display-not-valid :getSharedPluginData-namespace namespace) + + (not (string? key)) + (u/display-not-valid :getSharedPluginData-key key) + + :else + (let [typography (u/locate-library-typography file-id id)] + (dm/get-in typography [:plugin-data (keyword "shared" namespace) key])))) + + :setSharedPluginData + (fn [namespace key value] + (cond + (not= file-id (:current-file-id @st/state)) + (u/display-not-valid :setSharedPluginData-non-local-library file-id) + + (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 "library:write")) - (u/display-not-valid :fontWeight "Plugin doesn't have 'library:write' permission") + (u/display-not-valid :setSharedPluginData "Plugin doesn't have 'library:write' permission") :else - (let [typo (-> (u/proxy->library-typography self) - (assoc :font-weight value))] - (st/emit! (dwl/update-typography typo file-id)))))} + (st/emit! (dw/set-plugin-data file-id :typography id (keyword "shared" namespace) key value)))) - {:name "fontStyle" - :get #(-> % u/proxy->library-typography :font-style) - :set - (fn [self value] + :getSharedPluginDataKeys + (fn [namespace] (cond - (not (string? value)) - (u/display-not-valid :fontStyle value) - - (not (r/check-permission plugin-id "library:write")) - (u/display-not-valid :fontStyle "Plugin doesn't have 'library:write' permission") + (not (string? namespace)) + (u/display-not-valid :getSharedPluginDataKeys-namespace namespace) :else - (let [typo (-> (u/proxy->library-typography self) - (assoc :font-style value))] - (st/emit! (dwl/update-typography typo file-id)))))} - - {:name "lineHeight" - :get #(-> % u/proxy->library-typography :font-height) - :set - (fn [self value] - (cond - (not (string? value)) - (u/display-not-valid :lineHeight value) - - (not (r/check-permission plugin-id "library:write")) - (u/display-not-valid :lineHeight "Plugin doesn't have 'library:write' permission") - - :else - (let [typo (-> (u/proxy->library-typography self) - (assoc :font-height value))] - (st/emit! (dwl/update-typography typo file-id)))))} - - {:name "letterSpacing" - :get #(-> % u/proxy->library-typography :letter-spacing) - :set - (fn [self value] - (cond - (not (string? value)) - (u/display-not-valid :letterSpacing value) - - (not (r/check-permission plugin-id "library:write")) - (u/display-not-valid :letterSpacing "Plugin doesn't have 'library:write' permission") - - :else - (let [typo (-> (u/proxy->library-typography self) - (assoc :letter-spacing value))] - (st/emit! (dwl/update-typography typo file-id)))))} - - {:name "textTransform" - :get #(-> % u/proxy->library-typography :text-transform) - :set - (fn [self value] - (cond - (not (string? value)) - (u/display-not-valid :textTransform value) - - (not (r/check-permission plugin-id "library:write")) - (u/display-not-valid :textTransform "Plugin doesn't have 'library:write' permission") - - :else - (let [typo (-> (u/proxy->library-typography self) - (assoc :text-transform value))] - (st/emit! (dwl/update-typography typo file-id)))))})) - -(deftype LibraryComponentProxy [$plugin $file $id] - Object - - (remove - [_] - (cond - (not (r/check-permission $plugin "library:write")) - (u/display-not-valid :remove "Plugin doesn't have 'library:write' permission") - - :else - (st/emit! (dwl/delete-component {:id $id})))) - - (instance - [_] - (cond - (not (r/check-permission $plugin "content:write")) - (u/display-not-valid :instance "Plugin doesn't have 'content:write' permission") - - :else - (let [id-ref (atom nil)] - (st/emit! (dwl/instantiate-component $file $id (gpt/point 0 0) {:id-ref id-ref})) - (shape/shape-proxy $plugin @id-ref)))) - - (getPluginData - [self key] - (cond - (not (string? key)) - (u/display-not-valid :component-plugin-data-key key) - - :else - (let [component (u/proxy->library-component self)] - (dm/get-in component [:plugin-data (keyword "plugin" (str $plugin)) key])))) - - (setPluginData - [_ key value] - (cond - (not= $file (:current-file-id @st/state)) - (u/display-not-valid :setPluginData-non-local-library $file) - - (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 "library:write")) - (u/display-not-valid :setPluginData "Plugin doesn't have 'library:write' permission") - - :else - (st/emit! (dw/set-plugin-data $file :component $id (keyword "plugin" (str $plugin)) key value)))) - - (getPluginDataKeys - [self] - (let [component (u/proxy->library-component self)] - (apply array (keys (dm/get-in component [:plugin-data (keyword "plugin" (str $plugin))]))))) - - (getSharedPluginData - [self namespace key] - (cond - (not (string? namespace)) - (u/display-not-valid :component-plugin-data-namespace namespace) - - (not (string? key)) - (u/display-not-valid :component-plugin-data-key key) - - :else - (let [component (u/proxy->library-component self)] - (dm/get-in component [:plugin-data (keyword "shared" namespace) key])))) - - (setSharedPluginData - [_ namespace key value] - (cond - (not= $file (:current-file-id @st/state)) - (u/display-not-valid :setSharedPluginData-non-local-library $file) - - (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 "library:write")) - (u/display-not-valid :setSharedPluginData "Plugin doesn't have 'library:write' permission") - - :else - (st/emit! (dw/set-plugin-data $file :component $id (keyword "shared" namespace) key value)))) - - (getSharedPluginDataKeys - [self namespace] - (cond - (not (string? namespace)) - (u/display-not-valid :component-plugin-data-namespace namespace) - - :else - (let [component (u/proxy->library-component self)] - (apply array (keys (dm/get-in component [:plugin-data (keyword "shared" namespace)])))))) - - (mainInstance - [self] - (let [file (u/locate-file $file) - component (u/proxy->library-component self) - root (ctf/get-component-root (:data file) component)] - (when (some? root) - (shape/shape-proxy $plugin $file (:main-instance-page component) (:id root)))))) + (let [typography (u/locate-library-typography file-id id)] + (apply array (keys (dm/get-in typography [:plugin-data (keyword "shared" namespace)])))))))) (defn lib-component-proxy? [p] - (instance? LibraryComponentProxy p)) + (obj/type-of? p "LibraryComponentProxy")) (defn lib-component-proxy [plugin-id file-id id] (assert (uuid? file-id)) (assert (uuid? id)) - (cr/add-properties! - (LibraryComponentProxy. 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)} - {:name "id" :get (fn [_] (dm/str id))} + (obj/reify {:name "LibraryComponentProxy"} + :$plugin {:enumerable false :get (constantly plugin-id)} + :$id {:enumerable false :get (constantly id)} + :$file {:enumerable false :get (constantly file-id)} + :id {:get (fn [_] (dm/str id))} - {:name "name" - :get #(-> % u/proxy->library-component :name) - :set - (fn [self value] + :name + {:this true + :get #(-> % u/proxy->library-component :name) + :set + (fn [self value] + (cond + (not (string? value)) + (u/display-not-valid :name value) + + (not (r/check-permission plugin-id "library:write")) + (u/display-not-valid :name "Plugin doesn't have 'library:write' permission") + + :else + (let [component (u/proxy->library-component self) + value (dm/str (d/nilv (:path component) "") " / " value)] + (st/emit! (dwl/rename-component id value)))))} + + :path + {:this true + :get #(-> % u/proxy->library-component :path) + :set + (fn [self value] + (cond + (not (string? value)) + (u/display-not-valid :path value) + + (not (r/check-permission plugin-id "library:write")) + (u/display-not-valid :path "Plugin doesn't have 'library:write' permission") + + :else + (let [component (u/proxy->library-component self) + value (dm/str value " / " (:name component))] + (st/emit! (dwl/rename-component id value)))))} + + :remove + (fn [] (cond - (not (string? value)) - (u/display-not-valid :name value) - (not (r/check-permission plugin-id "library:write")) - (u/display-not-valid :name "Plugin doesn't have 'library:write' permission") + (u/display-not-valid :remove "Plugin doesn't have 'library:write' permission") :else - (let [component (u/proxy->library-component self) - value (dm/str (d/nilv (:path component) "") " / " value)] - (st/emit! (dwl/rename-component id value)))))} + (st/emit! (dwl/delete-component {:id id})))) - {:name "path" - :get #(-> % u/proxy->library-component :path) - :set - (fn [self value] + :instance + (fn [] (cond - (not (string? value)) - (u/display-not-valid :path value) - - (not (r/check-permission plugin-id "library:write")) - (u/display-not-valid :path "Plugin doesn't have 'library:write' permission") + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :instance "Plugin doesn't have 'content:write' permission") :else - (let [component (u/proxy->library-component self) - value (dm/str value " / " (:name component))] - (st/emit! (dwl/rename-component id value)))))})) + (let [id-ref (atom nil)] + (st/emit! (dwl/instantiate-component file-id id (gpt/point 0 0) {:id-ref id-ref})) + (shape/shape-proxy plugin-id @id-ref)))) -(deftype Library [$plugin $id] - Object + :getPluginData + (fn [key] + (cond + (not (string? key)) + (u/display-not-valid :component-plugin-data-key key) - (createColor - [_] - (cond - (not (r/check-permission $plugin "library:write")) - (u/display-not-valid :createColor "Plugin doesn't have 'library:write' permission") + :else + (let [component (u/locate-library-component file-id id)] + (dm/get-in component [:plugin-data (keyword "plugin" (str plugin-id)) key])))) - :else - (let [color-id (uuid/next)] - (st/emit! (dwl/add-color {:id color-id :name "Color" :color "#000000" :opacity 1} {:rename? false})) - (lib-color-proxy $plugin $id color-id)))) + :setPluginData + (fn [key value] + (cond + (not= file-id (:current-file-id @st/state)) + (u/display-not-valid :setPluginData-non-local-library file-id) - (createTypography - [_] - (cond - (not (r/check-permission $plugin "library:write")) - (u/display-not-valid :createTypography "Plugin doesn't have 'library:write' permission") + (not (string? key)) + (u/display-not-valid :setPluginData-key key) - :else - (let [typography-id (uuid/next)] - (st/emit! (dwl/add-typography (ctt/make-typography {:id typography-id :name "Typography"}) false)) - (lib-typography-proxy $plugin $id typography-id)))) + (and (some? value) (not (string? value))) + (u/display-not-valid :setPluginData-value value) - (createComponent - [_ shapes] - (cond - (not (r/check-permission $plugin "library:write")) - (u/display-not-valid :createComponent "Plugin doesn't have 'library:write' permission") + (not (r/check-permission plugin-id "library:write")) + (u/display-not-valid :setPluginData "Plugin doesn't have 'library:write' permission") - :else - (let [id-ref (atom nil) - ids (into #{} (map #(obj/get % "$id")) shapes)] - (st/emit! (dwl/add-component id-ref ids)) - (lib-component-proxy $plugin $id @id-ref)))) + :else + (st/emit! (dw/set-plugin-data file-id :component id (keyword "plugin" (str plugin-id)) key value)))) - ;; Plugin data - (getPluginData - [self key] - (cond - (not (string? key)) - (u/display-not-valid :file-plugin-data-key key) + :getPluginDataKeys + (fn [] + (let [component (u/locate-library-component file-id id)] + (apply array (keys (dm/get-in component [:plugin-data (keyword "plugin" (str plugin-id))]))))) - :else - (let [file (u/proxy->file self)] - (dm/get-in file [:data :plugin-data (keyword "plugin" (str $plugin)) key])))) + :getSharedPluginData + (fn [namespace key] + (cond + (not (string? namespace)) + (u/display-not-valid :component-plugin-data-namespace namespace) - (setPluginData - [_ key value] - (cond - (not (string? key)) - (u/display-not-valid :setPluginData-key key) + (not (string? key)) + (u/display-not-valid :component-plugin-data-key key) - (and (some? value) (not (string? value))) - (u/display-not-valid :setPluginData-value value) + :else + (let [component (u/locate-library-component file-id id)] + (dm/get-in component [:plugin-data (keyword "shared" namespace) key])))) - (not (r/check-permission $plugin "library:write")) - (u/display-not-valid :setPluginData "Plugin doesn't have 'library:write' permission") + :setSharedPluginData + (fn [namespace key value] + (cond + (not= file-id (:current-file-id @st/state)) + (u/display-not-valid :setSharedPluginData-non-local-library file-id) - :else - (st/emit! (dw/set-plugin-data $id :file (keyword "plugin" (str $plugin)) key value)))) + (not (string? namespace)) + (u/display-not-valid :setSharedPluginData-namespace namespace) - (getPluginDataKeys - [self] - (let [file (u/proxy->file self)] - (apply array (keys (dm/get-in file [:data :plugin-data (keyword "plugin" (str $plugin))]))))) + (not (string? key)) + (u/display-not-valid :setSharedPluginData-key key) - (getSharedPluginData - [self namespace key] - (cond - (not (string? namespace)) - (u/display-not-valid :file-plugin-data-namespace namespace) + (and (some? value) (not (string? value))) + (u/display-not-valid :setSharedPluginData-value value) - (not (string? key)) - (u/display-not-valid :file-plugin-data-key key) + (not (r/check-permission plugin-id "library:write")) + (u/display-not-valid :setSharedPluginData "Plugin doesn't have 'library:write' permission") - :else - (let [file (u/proxy->file self)] - (dm/get-in file [:data :plugin-data (keyword "shared" namespace) key])))) + :else + (st/emit! (dw/set-plugin-data file-id :component id (keyword "shared" namespace) key value)))) - (setSharedPluginData - [_ namespace key value] + :getSharedPluginDataKeys + (fn [namespace] + (cond + (not (string? namespace)) + (u/display-not-valid :component-plugin-data-namespace namespace) - (cond - (not (string? namespace)) - (u/display-not-valid :setSharedPluginData-namespace namespace) + :else + (let [component (u/locate-library-component file-id id)] + (apply array (keys (dm/get-in component [:plugin-data (keyword "shared" 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 "library:write")) - (u/display-not-valid :setSharedPluginData "Plugin doesn't have 'library: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 :namespace namespace) - - :else - (let [file (u/proxy->file self)] - (apply array (keys (dm/get-in file [:data :plugin-data (keyword "shared" namespace)]))))))) + :mainInstance + (fn [] + (let [file (u/locate-file file-id) + component (u/locate-library-component file-id id) + root (ctf/get-component-root (:data file) component)] + (when (some? root) + (shape/shape-proxy plugin-id file-id (:main-instance-page component) (:id root))))))) (defn library-proxy? [p] - (instance? Library p)) + (obj/type-of? p "LibraryProxy")) (defn library-proxy [plugin-id file-id] (assert (uuid? file-id) "File id not valid") - (cr/add-properties! - (Library. plugin-id file-id) - {:name "$plugin" :enumerable false :get (constantly plugin-id)} - {:name "$id" :enumerable false :get (constantly file-id)} + (obj/reify {:name "LibraryProxy"} + :$plugin {:enumerable false :get (constantly plugin-id)} + :$id {:enumerable false :get (constantly file-id)} - {:name "id" - :get #(-> % u/proxy->file :id str)} + :id + {:this true + :get #(-> % u/proxy->file :id str)} - {:name "name" - :get #(-> % u/proxy->file :name)} + :name + {:this true + :get #(-> % u/proxy->file :name)} - {:name "colors" - :get - (fn [_] - (let [file (u/locate-file file-id) - colors (->> file :data :colors keys (map #(lib-color-proxy plugin-id file-id %)))] - (apply array colors)))} + :colors + {:this true + :get + (fn [_] + (let [file (u/locate-file file-id) + colors (->> file :data :colors keys (map #(lib-color-proxy plugin-id file-id %)))] + (apply array colors)))} - {:name "typographies" - :get - (fn [_] - (let [file (u/locate-file file-id) - typographies (->> file :data :typographies keys (map #(lib-typography-proxy plugin-id file-id %)))] - (apply array typographies)))} + :typographies + {:this true + :get + (fn [_] + (let [file (u/locate-file file-id) + typographies (->> file :data :typographies keys (map #(lib-typography-proxy plugin-id file-id %)))] + (apply array typographies)))} - {:name "components" - :get - (fn [_] - (let [file (u/locate-file file-id) - components (->> file - :data - :components - (remove (comp :deleted second)) - (map first) - (map #(lib-component-proxy plugin-id file-id %)))] - (apply array components)))})) + :components + {:this true + :get + (fn [_] + (let [file (u/locate-file file-id) + components (->> file + :data + :components + (remove (comp :deleted second)) + (map first) + (map #(lib-component-proxy plugin-id file-id %)))] + (apply array components)))} -(deftype PenpotLibrarySubcontext [$plugin] - Object - (availableLibraries - [_] - (let [team-id (:current-team-id @st/state)] - (js/Promise. - (fn [resolve reject] - (let [current-libs (into #{} (map first) (get @st/state :workspace-libraries))] - (->> (rp/cmd! :get-team-shared-files {:team-id team-id}) - (rx/map (fn [result] - (->> result - (filter #(not (contains? current-libs (:id %)))) - (map - (fn [{:keys [id name library-summary]}] - #js {:id (dm/str id) - :name name - :numColors (-> library-summary :colors :count) - :numComponents (-> library-summary :components :count) - :numTypographies (-> library-summary :typographies :count)})) - (apply array)))) - (rx/subs! resolve reject))))))) + :createColor + (fn [] + (cond + (not (r/check-permission plugin-id "library:write")) + (u/display-not-valid :createColor "Plugin doesn't have 'library:write' permission") - (connectLibrary - [_ library-id] - (cond - (not (r/check-permission $plugin "library:write")) - (u/display-not-valid :connectLibrary "Plugin doesn't have 'library:write' permission") + :else + (let [color-id (uuid/next)] + (st/emit! (dwl/add-color {:id color-id :name "Color" :color "#000000" :opacity 1} {:rename? false})) + (lib-color-proxy plugin-id file-id color-id)))) - :else - (js/Promise. - (fn [resolve reject] - (cond - (not (string? library-id)) - (do (u/display-not-valid :connectLibrary library-id) - (reject nil)) + :createTypography + (fn [] + (cond + (not (r/check-permission plugin-id "library:write")) + (u/display-not-valid :createTypography "Plugin doesn't have 'library:write' permission") - :else - (let [file-id (:current-file-id @st/state) - library-id (uuid/uuid library-id)] - (->> st/stream - (rx/filter (ptk/type? ::dwl/attach-library-finished)) - (rx/take 1) - (rx/subs! #(resolve (library-proxy $plugin library-id)) reject)) - (st/emit! (dwl/link-file-to-library file-id library-id))))))))) + :else + (let [typography-id (uuid/next)] + (st/emit! (dwl/add-typography (ctt/make-typography {:id typography-id :name "Typography"}) false)) + (lib-typography-proxy plugin-id file-id typography-id)))) + + :createComponent + (fn [shapes] + (cond + (not (r/check-permission plugin-id "library:write")) + (u/display-not-valid :createComponent "Plugin doesn't have 'library:write' permission") + + :else + (let [id-ref (atom nil) + ids (into #{} (map #(obj/get % "$id")) shapes)] + (st/emit! (dwl/add-component id-ref ids)) + (lib-component-proxy plugin-id file-id @id-ref)))) + + ;; Plugin data + :getPluginData + (fn [key] + (cond + (not (string? key)) + (u/display-not-valid :file-plugin-data-key key) + + :else + (let [file (u/locate-file file-id)] + (dm/get-in file [:data :plugin-data (keyword "plugin" (str plugin-id)) key])))) + + :setPluginData + (fn [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-id "library:write")) + (u/display-not-valid :setPluginData "Plugin doesn't have 'library:write' permission") + + :else + (st/emit! (dw/set-plugin-data file-id :file (keyword "plugin" (str plugin-id)) key value)))) + + :getPluginDataKeys + (fn [] + (let [file (u/locate-file file-id)] + (apply array (keys (dm/get-in file [:data :plugin-data (keyword "plugin" (str plugin-id))]))))) + + :getSharedPluginData + (fn [namespace key] + (cond + (not (string? namespace)) + (u/display-not-valid :file-plugin-data-namespace namespace) + + (not (string? key)) + (u/display-not-valid :file-plugin-data-key key) + + :else + (let [file (u/locate-file file-id)] + (dm/get-in file [:data :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 "library:write")) + (u/display-not-valid :setSharedPluginData "Plugin doesn't have 'library:write' permission") + + :else + (st/emit! (dw/set-plugin-data file-id :file (keyword "shared" namespace) key value)))) + + :getSharedPluginDataKeys + (fn [namespace] + (cond + (not (string? namespace)) + (u/display-not-valid :namespace namespace) + + :else + (let [file (u/locate-file file-id)] + (apply array (keys (dm/get-in file [:data :plugin-data (keyword "shared" namespace)])))))))) (defn library-subcontext [plugin-id] - (cr/add-properties! - (PenpotLibrarySubcontext. plugin-id) - {:name "$plugin" :enumerable false :get (constantly plugin-id)} - {:name "local" :get - (fn [_] - (library-proxy plugin-id (:current-file-id @st/state)))} + (obj/reify {:name "PenpotLibrarySubcontext"} + :$plugin {:enumerable false :get (constantly plugin-id)} - {:name "connected" :get - (fn [_] - (let [libraries (get @st/state :workspace-libraries)] - (apply array (->> libraries keys (map (partial library-proxy plugin-id))))))})) + :local + {:get #(library-proxy plugin-id (:current-file-id @st/state))} + + :connected + {:get + (fn [] + (let [libraries (get @st/state :workspace-libraries)] + (apply array (->> libraries keys (map (partial library-proxy plugin-id))))))} + + :availableLibraries + (fn [] + (let [team-id (:current-team-id @st/state)] + (js/Promise. + (fn [resolve reject] + (let [current-libs (into #{} (map first) (get @st/state :workspace-libraries))] + (->> (rp/cmd! :get-team-shared-files {:team-id team-id}) + (rx/map (fn [result] + (->> result + (filter #(not (contains? current-libs (:id %)))) + (map + (fn [{:keys [id name library-summary]}] + #js {:id (dm/str id) + :name name + :numColors (-> library-summary :colors :count) + :numComponents (-> library-summary :components :count) + :numTypographies (-> library-summary :typographies :count)})) + (apply array)))) + (rx/subs! resolve reject))))))) + + :connectLibrary + (fn [library-id] + (cond + (not (r/check-permission plugin-id "library:write")) + (u/display-not-valid :connectLibrary "Plugin doesn't have 'library:write' permission") + + :else + (js/Promise. + (fn [resolve reject] + (cond + (not (string? library-id)) + (do (u/display-not-valid :connectLibrary library-id) + (reject nil)) + + :else + (let [file-id (:current-file-id @st/state) + library-id (uuid/uuid library-id)] + (->> st/stream + (rx/filter (ptk/type? ::dwl/attach-library-finished)) + (rx/take 1) + (rx/subs! #(resolve (library-proxy plugin-id library-id)) reject)) + (st/emit! (dwl/link-file-to-library file-id library-id)))))))))) diff --git a/frontend/src/app/plugins/page.cljs b/frontend/src/app/plugins/page.cljs index a575ff742..65cbe093e 100644 --- a/frontend/src/app/plugins/page.cljs +++ b/frontend/src/app/plugins/page.cljs @@ -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))))))))) diff --git a/frontend/src/app/plugins/register.cljs b/frontend/src/app/plugins/register.cljs index f8e3e03be..12be74150 100644 --- a/frontend/src/app/plugins/register.cljs +++ b/frontend/src/app/plugins/register.cljs @@ -45,7 +45,7 @@ (conj "content:read") (contains? permissions "library:write") - (conj "content:write") + (conj "library:read") (contains? permissions "comment:write") (conj "comment:read")) diff --git a/frontend/src/app/plugins/ruler_guides.cljs b/frontend/src/app/plugins/ruler_guides.cljs index d0e8addbf..834280c2a 100644 --- a/frontend/src/app/plugins/ruler_guides.cljs +++ b/frontend/src/app/plugins/ruler_guides.cljs @@ -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)))))) diff --git a/frontend/src/app/plugins/shape.cljs b/frontend/src/app/plugins/shape.cljs index 9097cd5b0..b93438b2d 100644 --- a/frontend/src/app/plugins/shape.cljs +++ b/frontend/src/app/plugins/shape.cljs @@ -57,77 +57,79 @@ (declare shape-proxy) (declare shape-proxy?) -(deftype InteractionProxy [$plugin $file $page $shape $index] - Object - (remove [_] - (st/emit! (dwi/remove-interaction {:id $shape} $index)))) - (defn interaction-proxy? [p] - (instance? InteractionProxy p)) + (obj/type-of? p "InteractionProxy")) (defn interaction-proxy [plugin-id file-id page-id shape-id index] - (crc/add-properties! - (InteractionProxy. plugin-id file-id page-id shape-id index) - {: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 "$shape" :enumerable false :get (constantly shape-id)} - {:name "$index" :enumerable false :get (constantly index)} + (obj/reify {:name "InteractionProxy"} + :$plugin {:enumerable false :get (fn [] plugin-id)} + :$file {:enumerable false :get (fn [] file-id)} + :$page {:enumerable false :get (fn [] page-id)} + :$shape {:enumerable false :get (fn [] shape-id)} + :$index {:enumerable false :get (fn [] index)} - ;; Not enumerable so we don't have an infinite loop - {:name "shape" :enumerable false - :get (fn [_] (shape-proxy plugin-id file-id page-id shape-id))} + ;; Not enumerable so we don't have an infinite loop + :shape + {:enumerable false + :get (fn [] (shape-proxy plugin-id file-id page-id shape-id))} - {:name "trigger" - :get #(-> % u/proxy->interaction :event-type format/format-key) - :set - (fn [_ value] - (let [value (parser/parse-keyword value)] - (cond - (not (contains? ctsi/event-types value)) - (u/display-not-valid :trigger value) + :trigger + {:this true + :get #(-> % u/proxy->interaction :event-type format/format-key) + :set + (fn [_ value] + (let [value (parser/parse-keyword value)] + (cond + (not (contains? ctsi/event-types value)) + (u/display-not-valid :trigger value) - :else - (st/emit! (dwi/update-interaction - {:id shape-id} - index - #(assoc % :event-type value) - {:page-id page-id})))))} + :else + (st/emit! (dwi/update-interaction + {:id shape-id} + index + #(assoc % :event-type value) + {:page-id page-id})))))} - {:name "delay" - :get #(-> % u/proxy->interaction :delay) - :set - (fn [_ value] - (cond - (or (not (number? value)) (not (pos? value))) - (u/display-not-valid :delay value) + :delay + {:this true + :get #(-> % u/proxy->interaction :delay) + :set + (fn [_ value] + (cond + (or (not (number? value)) (not (pos? value))) + (u/display-not-valid :delay value) - :else - (st/emit! (dwi/update-interaction - {:id shape-id} - index - #(assoc % :delay value) - {:page-id page-id}))))} + :else + (st/emit! (dwi/update-interaction + {:id shape-id} + index + #(assoc % :delay value) + {:page-id page-id}))))} - {:name "action" - :get #(-> % u/proxy->interaction (format/format-action plugin-id file-id page-id)) - :set - (fn [self value] - (let [params (parser/parse-action value) - interaction - (-> (u/proxy->interaction self) - (d/patch-object params))] - (cond - (not (sm/validate ::ctsi/interaction interaction)) - (u/display-not-valid :action interaction) + :action + {:this true + :get #(-> % u/proxy->interaction (format/format-action plugin-id file-id page-id)) + :set + (fn [self value] + (let [params (parser/parse-action value) + interaction + (-> (u/proxy->interaction self) + (d/patch-object params))] + (cond + (not (sm/validate ::ctsi/interaction interaction)) + (u/display-not-valid :action interaction) - :else - (st/emit! (dwi/update-interaction - {:id shape-id} - index - #(d/patch-object % params) - {:page-id page-id})))))})) + :else + (st/emit! (dwi/update-interaction + {:id shape-id} + index + #(d/patch-object % params) + {:page-id page-id})))))} + + :remove + (fn [] + (st/emit! (dwi/remove-interaction {:id shape-id} index))))) (def lib-typography-proxy? nil) (def lib-component-proxy nil) @@ -139,7 +141,6 @@ (dwt/current-paragraph-values {:shape shape :attrs txt/paragraph-attrs}) (dwt/current-text-values {:shape shape :attrs txt/text-node-attrs}))) - (defn- shadow-defaults [shadow] (d/patch-object @@ -162,469 +163,8 @@ :hidden false} blur)) -(deftype ShapeProxy [$plugin $file $page $id] - Object - (resize - [_ width height] - (cond - (or (not (us/safe-number? width)) (<= width 0)) - (u/display-not-valid :resize width) - - (or (not (us/safe-number? height)) (<= height 0)) - (u/display-not-valid :resize height) - - (not (r/check-permission $plugin "content:write")) - (u/display-not-valid :resize "Plugin doesn't have 'content:write' permission") - - :else - (st/emit! (dw/update-dimensions [$id] :width width) - (dw/update-dimensions [$id] :height height)))) - - (rotate - [self angle center] - (let [center (when center {:x (obj/get center "x") :y (obj/get center "y")})] - (cond - (not (number? angle)) - (u/display-not-valid :rotate-angle angle) - - (and (some? center) (or (not (number? (:x center))) (not (number? (:y center))))) - (u/display-not-valid :rotate-center center) - - (not (r/check-permission $plugin "content:write")) - (u/display-not-valid :rotate "Plugin doesn't have 'content:write' permission") - - :else - (let [id (obj/get self "$id")] - (st/emit! (dw/increase-rotation [id] angle {:center center :delta? true})))))) - - (clone - [_] - (let [ret-v (atom nil)] - (cond - (not (r/check-permission $plugin "content:write")) - (u/display-not-valid :clone "Plugin doesn't have 'content:write' permission") - - :else - (do (st/emit! (dws/duplicate-shapes #{$id} :change-selection? false :return-ref ret-v)) - (shape-proxy $plugin (deref ret-v)))))) - - (remove - [_] - (cond - (not (r/check-permission $plugin "content:write")) - (u/display-not-valid :remove "Plugin doesn't have 'content:write' permission") - - :else - (st/emit! (dwsh/delete-shapes #{$id})))) - - ;; Plugin data - (getPluginData - [self key] - (cond - (not (string? key)) - (u/display-not-valid :getPluginData key) - - :else - (let [shape (u/proxy->shape self)] - (dm/get-in shape [: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 :shape $id $page (keyword "plugin" (str $plugin)) key value)))) - - (getPluginDataKeys - [self] - (let [shape (u/proxy->shape self)] - (apply array (keys (dm/get-in shape [: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 [shape (u/proxy->shape self)] - (dm/get-in shape [: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 :shape $id $page (keyword "shared" namespace) key value)))) - - (getSharedPluginDataKeys - [self namespace] - (cond - (not (string? namespace)) - (u/display-not-valid :getSharedPluginDataKeys namespace) - - :else - (let [shape (u/proxy->shape self)] - (apply array (keys (dm/get-in shape [:plugin-data (keyword "shared" namespace)])))))) - - ;; Only for frames + groups + booleans - (getChildren - [_] - (let [shape (u/locate-shape $file $page $id)] - (cond - - (and (not (cfh/frame-shape? shape)) - (not (cfh/group-shape? shape)) - (not (cfh/svg-raw-shape? shape)) - (not (cfh/bool-shape? shape))) - (u/display-not-valid :getChildren (:type shape)) - - :else - (->> (u/locate-shape $file $page $id) - (:shapes) - (format/format-array #(shape-proxy $plugin $file $page %)))))) - - (appendChild - [_ child] - (let [shape (u/locate-shape $file $page $id)] - (cond - (and (not (cfh/frame-shape? shape)) - (not (cfh/group-shape? shape)) - (not (cfh/svg-raw-shape? shape)) - (not (cfh/bool-shape? shape))) - (u/display-not-valid :appendChild (:type shape)) - - (not (shape-proxy? child)) - (u/display-not-valid :appendChild-child child) - - (not (r/check-permission $plugin "content:write")) - (u/display-not-valid :appendChild "Plugin doesn't have 'content:write' permission") - - :else - (let [child-id (obj/get child "$id")] - (st/emit! (dw/relocate-shapes #{child-id} $id 0)))))) - - (insertChild - [_ index child] - (let [shape (u/locate-shape $file $page $id)] - (cond - (and (not (cfh/frame-shape? shape)) - (not (cfh/group-shape? shape)) - (not (cfh/svg-raw-shape? shape)) - (not (cfh/bool-shape? shape))) - (u/display-not-valid :insertChild (:type shape)) - - (not (shape-proxy? child)) - (u/display-not-valid :insertChild-child child) - - (not (r/check-permission $plugin "content:write")) - (u/display-not-valid :insertChild "Plugin doesn't have 'content:write' permission") - - :else - (let [child-id (obj/get child "$id")] - (st/emit! (dw/relocate-shapes #{child-id} $id index)))))) - - ;; Only for frames - (addFlexLayout - [_] - (let [shape (u/locate-shape $file $page $id)] - (cond - (not (cfh/frame-shape? shape)) - (u/display-not-valid :addFlexLayout (:type shape)) - - (not (r/check-permission $plugin "content:write")) - (u/display-not-valid :addFlexLayout "Plugin doesn't have 'content:write' permission") - - :else - (do (st/emit! (dwsl/create-layout-from-id $id :flex :from-frame? true :calculate-params? false)) - (grid/grid-layout-proxy $plugin $file $page $id))))) - - (addGridLayout - [_] - (let [shape (u/locate-shape $file $page $id)] - (cond - (not (cfh/frame-shape? shape)) - (u/display-not-valid :addGridLayout (:type shape)) - - (not (r/check-permission $plugin "content:write")) - (u/display-not-valid :addGridLayout "Plugin doesn't have 'content:write' permission") - - :else - (do (st/emit! (dwsl/create-layout-from-id $id :grid :from-frame? true :calculate-params? false)) - (grid/grid-layout-proxy $plugin $file $page $id))))) - - ;; Make masks for groups - (makeMask - [_] - (let [shape (u/locate-shape $file $page $id)] - (cond - (not (cfh/group-shape? shape)) - (u/display-not-valid :makeMask (:type shape)) - - (not (r/check-permission $plugin "content:write")) - (u/display-not-valid :makeMask "Plugin doesn't have 'content:write' permission") - - :else - (st/emit! (dwg/mask-group #{$id}))))) - - (removeMask - [_] - (let [shape (u/locate-shape $file $page $id)] - (cond - (not (cfh/mask-shape? shape)) - (u/display-not-valid :removeMask (:type shape)) - - (not (r/check-permission $plugin "content:write")) - (u/display-not-valid :removeMask "Plugin doesn't have 'content:write' permission") - - :else - (st/emit! (dwg/unmask-group #{$id}))))) - - ;; Only for path and bool shapes - (toD - [_] - (let [shape (u/locate-shape $file $page $id)] - (cond - (not (cfh/path-shape? shape)) - (u/display-not-valid :makeMask (:type shape)) - - :else - (upf/format-path (:content shape))))) - - ;; Text shapes - (getRange - [_ start end] - (let [shape (u/locate-shape $file $page $id)] - (cond - (not (cfh/text-shape? shape)) - (u/display-not-valid :getRange-shape "shape is not text") - - (or (not (us/safe-int? start)) (< start 0) (> start end)) - (u/display-not-valid :getRange-start start) - - (not (us/safe-int? end)) - (u/display-not-valid :getRange-end end) - - :else - (text/text-range $plugin $file $page $id start end)))) - - (applyTypography - [_ typography] - (let [shape (u/locate-shape $file $page $id)] - (cond - (not (lib-typography-proxy? typography)) - (u/display-not-valid :applyTypography-typography typography) - - (not (cfh/text-shape? shape)) - (u/display-not-valid :applyTypography-shape (:type shape)) - - (not (r/check-permission $plugin "content:write")) - (u/display-not-valid :applyTypography "Plugin doesn't have 'content:write' permission") - - :else - (let [typography (u/proxy->library-typography typography)] - (st/emit! (dwt/apply-typography #{$id} typography $file)))))) - - ;; COMPONENTS - (isComponentInstance - [self] - (let [shape (u/proxy->shape self) - file-id (obj/get self "$file") - page-id (obj/get self "$page") - objects (u/locate-objects file-id page-id)] - (ctn/in-any-component? objects shape))) - - (isComponentMainInstance - [self] - (let [shape (u/proxy->shape self) - file-id (obj/get self "$file") - page-id (obj/get self "$page") - objects (u/locate-objects file-id page-id)] - (ctn/inside-component-main? objects shape))) - - (isComponentCopyInstance - [self] - (let [shape (u/proxy->shape self)] - (ctk/in-component-copy? shape))) - - (isComponentRoot - [self] - (let [shape (u/proxy->shape self)] - (ctk/instance-root? shape))) - - (isComponentHead - [self] - (let [shape (u/proxy->shape self)] - (ctk/instance-head? shape))) - - (componentRefShape - [self] - (let [objects (u/locate-objects $file $page) - shape (u/proxy->shape self)] - (when (ctn/in-any-component? objects shape) - (let [plugin-id (obj/get self "$plugin") - [root component] (u/locate-component objects shape) - component-page-id (:main-instance-page component) - component-file (u/locate-file (:component-file root)) - ref-shape (ctf/get-ref-shape (:data component-file) component shape)] - (when (and (not (:deleted component)) (some? ref-shape) (some? component-file)) - (shape-proxy plugin-id (:id component-file) component-page-id (:id ref-shape))))))) - - (componentRoot - [self] - (let [objects (u/locate-objects $file $page) - shape (u/proxy->shape self)] - (when (ctn/in-any-component? objects shape) - (let [[root component] (u/locate-component objects shape)] - (shape-proxy $plugin (:component-file root) (:main-instance-page component) (:id root)))))) - - (componentHead - [self] - (let [objects (u/locate-objects $file $page) - shape (u/proxy->shape self)] - (when (ctn/in-any-component? objects shape) - (let [head (ctn/get-head-shape (u/locate-objects $file $page) shape)] - (shape-proxy $plugin $file $page (:id head)))))) - - (component - [self] - (let [objects (u/locate-objects $file $page) - shape (u/proxy->shape self)] - (when (ctn/in-any-component? objects shape) - (let [[root component] (u/locate-component objects shape)] - (lib-component-proxy $plugin (:component-file root) (:id component)))))) - - (detach - [_] - (st/emit! (dwl/detach-component $id))) - - ;; Export - (export - [self value] - (let [value (parser/parse-export value)] - (cond - (not (sm/validate ::ctse/export value)) - (u/display-not-valid :export value) - - :else - (let [payload - {:cmd :export-shapes - :profile-id (:profile-id @st/state) - :wait true - :exports [{:file-id $file - :page-id $page - :object-id $id - :name (obj/get self "name") - :type (:type value :png) - :suffix (:suffix value "") - :scale (:scale value 1)}]}] - (js/Promise. - (fn [resolve reject] - (->> (rp/cmd! :export payload) - (rx/mapcat #(rp/cmd! :export {:cmd :get-resource :wait true :id (:id %) :blob? true})) - (rx/mapcat #(.arrayBuffer %)) - (rx/map #(js/Uint8Array. %)) - (rx/subs! resolve reject)))))))) - - ;; Interactions - (addInteraction - [self trigger action delay] - (let [interaction - (-> ctsi/default-interaction - (d/patch-object (parser/parse-interaction trigger action delay)))] - (cond - (not (sm/validate ::ctsi/interaction interaction)) - (u/display-not-valid :addInteraction interaction) - - :else - (let [index (-> (u/proxy->shape self) (:interactions []) count)] - (st/emit! (dwi/add-interaction $page $id interaction)) - (interaction-proxy $plugin $file $page $id index))))) - - (removeInteraction - [_ interaction] - (cond - (not (interaction-proxy? interaction)) - (u/display-not-valid :removeInteraction interaction) - - :else - (st/emit! (dwi/remove-interaction {:id $id} (obj/get interaction "$index"))))) - - ;; Ruler guides - (addRulerGuide - [self orientation value] - (let [shape (u/proxy->shape self)] - (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'") - - (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) - axis (parser/orientation->axis orientation) - objects (u/locate-objects $file $page) - frame (get objects $id) - board-pos (get frame axis) - position (+ board-pos value)] - (st/emit! - (dwgu/update-guides - {:id id - :axis axis - :position position - :frame-id $id})) - (rg/ruler-guide-proxy $plugin $file $page 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 'content:write' permission") - - :else - (let [guide (u/proxy->ruler-guide value)] - (st/emit! (dwgu/remove-guide guide)))))) - (defn shape-proxy? [p] - (instance? ShapeProxy p)) - -(crc/define-properties! - ShapeProxy - {:name js/Symbol.toStringTag - :get (fn [] (str "ShapeProxy"))}) + (obj/type-of? p "ShapeProxy")) (defn shape-proxy ([plugin-id id] @@ -639,574 +179,1063 @@ (assert (uuid? id)) (let [data (u/locate-shape file-id page-id id)] - (-> (ShapeProxy. 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 "ShapeProxy"} + :$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 "id" - :get #(-> % u/proxy->shape :id str)} + :id + {:this true + :get #(-> % u/proxy->shape :id str)} - {:name "type" - :get #(-> % u/proxy->shape :type format/shape-type)} + :type + {:this true + :get #(-> % u/proxy->shape :type format/shape-type)} - {:name "name" - :get #(-> % u/proxy->shape :name) - :set - (fn [self value] - (let [id (obj/get self "$id") - value (when (string? value) (-> value str/trim cfh/clean-path)) - valid? (and (some? value) - (not (str/ends-with? value "/")) - (not (str/blank? value)))] - (cond - (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :name "Plugin doesn't have 'content:write' permission") + :name + {:this true + :get #(-> % u/proxy->shape :name) + :set + (fn [self value] + (let [id (obj/get self "$id") + value (when (string? value) (-> value str/trim cfh/clean-path)) + valid? (and (some? value) + (not (str/ends-with? value "/")) + (not (str/blank? value)))] + (cond + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :name "Plugin doesn't have 'content:write' permission") - (not valid?) - (u/display-not-valid :name value) + (not valid?) + (u/display-not-valid :name value) - :else - (st/emit! (dwsh/update-shapes [id] #(assoc % :name value))))))} + :else + (st/emit! (dwsh/update-shapes [id] #(assoc % :name value))))))} - {:name "blocked" - :get #(-> % u/proxy->shape :blocked boolean) - :set - (fn [self value] + :blocked + {:this true + :get #(-> % u/proxy->shape :blocked boolean) + :set + (fn [self value] + (cond + (not (boolean? value)) + (u/display-not-valid :blocked value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :blocked "Plugin doesn't have 'content:write' permission") + + :else + (let [id (obj/get self "$id")] + (st/emit! (dwsh/update-shapes [id] #(assoc % :blocked value))))))} + + :hidden + {:this true + :get #(-> % u/proxy->shape :hidden boolean) + :set + (fn [self value] + (cond + (not (boolean? value)) + (u/display-not-valid :hidden value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :hidden "Plugin doesn't have 'content:write' permission") + + :else + (let [id (obj/get self "$id")] + (st/emit! (dwsh/update-shapes [id] #(assoc % :hidden value))))))} + + :visible + {:this true + :get #(-> % u/proxy->shape :hidden boolean not) + :set + (fn [self value] + (cond + (not (boolean? value)) + (u/display-not-valid :visible value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :visible "Plugin doesn't have 'content:write' permission") + + :else + (let [id (obj/get self "$id")] + (st/emit! (dwsh/update-shapes [id] #(assoc % :hidden (not value)))))))} + + :proportionLock + {:this true + :get #(-> % u/proxy->shape :proportion-lock boolean) + :set + (fn [self value] + (cond + (not (boolean? value)) + (u/display-not-valid :proportionLock value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :proportionLock "Plugin doesn't have 'content:write' permission") + + :else + (let [id (obj/get self "$id")] + (st/emit! (dwsh/update-shapes [id] #(assoc % :proportion-lock value))))))} + + :constraintsHorizontal + {:this true + :get #(-> % u/proxy->shape :constraints-h d/name) + :set + (fn [self value] + (let [id (obj/get self "$id") + value (keyword value)] + (cond + (not (contains? cts/horizontal-constraint-types value)) + (u/display-not-valid :constraintsHorizontal value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :constraintsHorizontal "Plugin doesn't have 'content:write' permission") + + :else + (st/emit! (dwsh/update-shapes [id] #(assoc % :constraints-h value))))))} + + :constraintsVertical + {:this true + :get #(-> % u/proxy->shape :constraints-v d/name) + :set + (fn [self value] + (let [id (obj/get self "$id") + value (keyword value)] + (cond + (not (contains? cts/vertical-constraint-types value)) + (u/display-not-valid :constraintsVertical value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :constraintsVertical "Plugin doesn't have 'content:write' permission") + + :else + (st/emit! (dwsh/update-shapes [id] #(assoc % :constraints-v value))))))} + + :borderRadius + {:this true + :get #(-> % u/proxy->shape :rx) + :set + (fn [self value] + (let [id (obj/get self "$id") + shape (u/proxy->shape self)] + (cond + (or (not (us/safe-int? value)) (< value 0)) + (u/display-not-valid :borderRadius value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :borderRadius "Plugin doesn't have 'content:write' permission") + + (or (not (ctsr/has-radius? shape)) (ctsr/radius-4? shape)) + (st/emit! (dwsh/update-shapes [id] #(-> % + ctsr/switch-to-radius-1 + (ctsr/set-radius-1 value)))) + + :else + (st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-1 % value))))))} + + :borderRadiusTopLeft + {:this true + :get #(-> % u/proxy->shape :r1) + :set + (fn [self value] + (let [id (obj/get self "$id") + shape (u/proxy->shape self)] + (cond + (not (us/safe-int? value)) + (u/display-not-valid :borderRadiusTopLeft value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :borderRadiusTopLeft "Plugin doesn't have 'content:write' permission") + + (or (not (ctsr/has-radius? shape)) (not (ctsr/radius-4? shape))) + (st/emit! (dwsh/update-shapes [id] #(-> % + (ctsr/switch-to-radius-4) + (ctsr/set-radius-4 :r1 value)))) + + :else + (st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-4 % :r1 value))))))} + + :borderRadiusTopRight + {:this true + :get #(-> % u/proxy->shape :r2) + :set + (fn [self value] + (let [id (obj/get self "$id") + shape (u/proxy->shape self)] + (cond + (not (us/safe-int? value)) + (u/display-not-valid :borderRadiusTopRight value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :borderRadiusTopRight "Plugin doesn't have 'content:write' permission") + + (or (not (ctsr/has-radius? shape)) (not (ctsr/radius-4? shape))) + (st/emit! (dwsh/update-shapes [id] #(-> % + (ctsr/switch-to-radius-4) + (ctsr/set-radius-4 :r2 value)))) + + :else + (st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-4 % :r2 value))))))} + + :borderRadiusBottomRight + {:this true + :get #(-> % u/proxy->shape :r3) + :set + (fn [self value] + (let [id (obj/get self "$id") + shape (u/proxy->shape self)] + (cond + (not (us/safe-int? value)) + (u/display-not-valid :borderRadiusBottomRight value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :borderRadiusBottomRight "Plugin doesn't have 'content:write' permission") + + (or (not (ctsr/has-radius? shape)) (not (ctsr/radius-4? shape))) + (st/emit! (dwsh/update-shapes [id] #(-> % + (ctsr/switch-to-radius-4) + (ctsr/set-radius-4 :r3 value)))) + + :else + (st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-4 % :r3 value))))))} + + :borderRadiusBottomLeft + {:this true + :get #(-> % u/proxy->shape :r4) + :set + (fn [self value] + (let [id (obj/get self "$id") + shape (u/proxy->shape self)] + (cond + (not (us/safe-int? value)) + (u/display-not-valid :borderRadiusBottomLeft value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :borderRadiusBottomLeft "Plugin doesn't have 'content:write' permission") + + (or (not (ctsr/has-radius? shape)) (not (ctsr/radius-4? shape))) + (st/emit! (dwsh/update-shapes [id] #(-> % + (ctsr/switch-to-radius-4) + (ctsr/set-radius-4 :r4 value)))) + + :else + (st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-4 % :r4 value))))))} + + :opacity + {:this true + :get #(-> % u/proxy->shape :opacity) + :set + (fn [self value] + (let [id (obj/get self "$id")] + (cond + (or (not (us/safe-number? value)) (< value 0) (> value 1)) + (u/display-not-valid :opacity value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :opacity "Plugin doesn't have 'content:write' permission") + + :else + (st/emit! (dwsh/update-shapes [id] #(assoc % :opacity value))))))} + + :blendMode + {:this true + :get #(-> % u/proxy->shape :blend-mode (d/nilv :normal) d/name) + :set + (fn [self value] + (let [id (obj/get self "$id") + value (keyword value)] + (cond + (not (contains? cts/blend-modes value)) + (u/display-not-valid :blendMode value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :blendMode "Plugin doesn't have 'content:write' permission") + + :else + (st/emit! (dwsh/update-shapes [id] #(assoc % :blend-mode value))))))} + + :shadows + {:this true + :get #(-> % u/proxy->shape :shadow format/format-shadows) + :set + (fn [self value] + (let [id (obj/get self "$id") + value (mapv #(shadow-defaults (parser/parse-shadow %)) value)] + (cond + (not (sm/validate [:vector ::ctss/shadow] value)) + (u/display-not-valid :shadows value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :shadows "Plugin doesn't have 'content:write' permission") + + :else + (st/emit! (dwsh/update-shapes [id] #(assoc % :shadow value))))))} + + :blur + {:this true + :get #(-> % u/proxy->shape :blur format/format-blur) + :set + (fn [self value] + (if (nil? value) + (st/emit! (dwsh/update-shapes [id] #(dissoc % :blur))) + (let [id (obj/get self "$id") + value (blur-defaults (parser/parse-blur value))] + (cond + (not (sm/validate ::ctsb/blur value)) + (u/display-not-valid :blur value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :blur "Plugin doesn't have 'content:write' permission") + + :else + (st/emit! (dwsh/update-shapes [id] #(assoc % :blur value)))))))} + + :exports + {:this true + :get #(-> % u/proxy->shape :exports format/format-exports) + :set + (fn [self value] + (let [id (obj/get self "$id") + value (parser/parse-exports value)] + (cond + (not (sm/validate [:vector ::ctse/export] value)) + (u/display-not-valid :exports value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :exports "Plugin doesn't have 'content:write' permission") + + :else + (st/emit! (dwsh/update-shapes [id] #(assoc % :exports value))))))} + + ;; Geometry properties + :x + {:this true + :get #(-> % u/proxy->shape :points grc/points->rect :x) + :set + (fn [self value] + (let [id (obj/get self "$id")] + (cond + (not (us/safe-number? value)) + (u/display-not-valid :x value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :x "Plugin doesn't have 'content:write' permission") + + :else + (st/emit! (dw/update-position id {:x value})))))} + + :y + {:this true + :get #(-> % u/proxy->shape :points grc/points->rect :y) + :set + (fn [self value] + (let [id (obj/get self "$id")] + (cond + (not (us/safe-number? value)) + (u/display-not-valid :y value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :y "Plugin doesn't have 'content:write' permission") + + :else + (st/emit! (dw/update-position id {:y value})))))} + + :parent + {:this true + ;; not enumerable so there are no infinite loops + :enumerable false + :get (fn [self] + (let [shape (u/proxy->shape self) + parent-id (:parent-id shape)] + (shape-proxy plugin-id (obj/get self "$file") (obj/get self "$page") parent-id)))} + + :parentX + {:this true + :get (fn [self] + (let [shape (u/proxy->shape self) + shape-x (-> shape :points grc/points->rect :x) + parent-id (:parent-id shape) + parent (u/locate-shape (obj/get self "$file") (obj/get self "$page") parent-id)] + (- shape-x (:x parent)))) + :set + (fn [self value] + (cond + (not (us/safe-number? value)) + (u/display-not-valid :parentX value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :parentX "Plugin doesn't have 'content:write' permission") + + :else + (let [id (obj/get self "$id") + parent-id (-> self u/proxy->shape :parent-id) + parent (u/locate-shape (obj/get self "$file") (obj/get self "$page") parent-id) + parent-x (:x parent)] + (st/emit! (dw/update-position id {:x (+ parent-x value)})))))} + + :parentY + {:this true + :get (fn [self] + (let [shape (u/proxy->shape self) + shape-y (-> shape :points grc/points->rect :y) + parent-id (:parent-id shape) + parent (u/locate-shape (obj/get self "$file") (obj/get self "$page") parent-id) + parent-y (:y parent)] + (- shape-y parent-y))) + :set + (fn [self value] + (cond + (not (us/safe-number? value)) + (u/display-not-valid :parentY value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :parentY "Plugin doesn't have 'content:write' permission") + + :else + (let [id (obj/get self "$id") + parent-id (-> self u/proxy->shape :parent-id) + parent (u/locate-shape (obj/get self "$file") (obj/get self "$page") parent-id) + parent-y (:y parent)] + (st/emit! (dw/update-position id {:y (+ parent-y value)})))))} + + :boardX + {:this true + :get (fn [self] + (let [shape (u/proxy->shape self) + shape-x (-> shape :points grc/points->rect :x) + frame-id (:parent-id shape) + frame (u/locate-shape (obj/get self "$file") (obj/get self "$page") frame-id) + frame-x (:x frame)] + (- shape-x frame-x))) + :set + (fn [self value] + (cond + (not (us/safe-number? value)) + (u/display-not-valid :frameX value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :frameX "Plugin doesn't have 'content:write' permission") + + :else + (let [id (obj/get self "$id") + frame-id (-> self u/proxy->shape :frame-id) + frame (u/locate-shape (obj/get self "$file") (obj/get self "$page") frame-id) + frame-x (:x frame)] + (st/emit! (dw/update-position id {:x (+ frame-x value)})))))} + + :boardY + {:this true + :get (fn [self] + (let [shape (u/proxy->shape self) + shape-y (-> shape :points grc/points->rect :y) + frame-id (:parent-id shape) + frame (u/locate-shape (obj/get self "$file") (obj/get self "$page") frame-id) + frame-y (:y frame)] + (- shape-y frame-y))) + :set + (fn [self value] + (cond + (not (us/safe-number? value)) + (u/display-not-valid :frameY value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :frameY "Plugin doesn't have 'content:write' permission") + + :else + (let [id (obj/get self "$id") + frame-id (-> self u/proxy->shape :frame-id) + frame (u/locate-shape (obj/get self "$file") (obj/get self "$page") frame-id) + frame-y (:y frame)] + (st/emit! (dw/update-position id {:y (+ frame-y value)})))))} + + :width + {:this true + :get #(-> % u/proxy->shape :selrect :width)} + + :height + {:this true + :get #(-> % u/proxy->shape :selrect :height)} + + :bounds + {:this true + :get #(-> % u/proxy->shape :points grc/points->rect format/format-bounds)} + + :center + {:this true + :get #(-> % u/proxy->shape gsh/shape->center format/format-point)} + + :rotation + {:this true + :get #(-> % u/proxy->shape :rotation) + :set + (fn [self value] + (cond + (not (number? value)) + (u/display-not-valid :rotation value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :rotation "Plugin doesn't have 'content:write' permission") + + :else + (let [shape (u/proxy->shape self)] + (st/emit! (dw/increase-rotation #{(:id shape)} value)))))} + + :flipX + {:this true + :get #(-> % u/proxy->shape :flip-x boolean) + :set + (fn [self value] + (cond + (not (boolean? value)) + (u/display-not-valid :flipX value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :flipX "Plugin doesn't have 'content:write' permission") + + :else + (let [id (obj/get self "$id")] + (st/emit! (dw/flip-horizontal-selected #{id})))))} + + :flipY + {:this true + :get #(-> % u/proxy->shape :flip-y boolean) + :set + (fn [self value] + (cond + (not (boolean? value)) + (u/display-not-valid :flipY value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :flipY "Plugin doesn't have 'content:write' permission") + + :else + (let [id (obj/get self "$id")] + (st/emit! (dw/flip-vertical-selected #{id})))))} + + ;; Strokes and fills + :fills + {:this true + :get #(if (cfh/text-shape? data) + (-> % u/proxy->shape text-props :fills format/format-fills) + (-> % u/proxy->shape :fills format/format-fills)) + :set + (fn [self value] + (let [shape (u/proxy->shape self) + id (:id shape) + value (parser/parse-fills value)] + (cond + (not (sm/validate [:vector ::cts/fill] value)) + (u/display-not-valid :fills value) + + (cfh/text-shape? shape) + (st/emit! (dwt/update-attrs id {: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! (dwsh/update-shapes [id] #(assoc % :fills value))))))} + + :strokes + {:this true + :get #(-> % u/proxy->shape :strokes format/format-strokes) + :set + (fn [self value] + (let [id (obj/get self "$id") + value (parser/parse-strokes value)] + (cond + (not (sm/validate [:vector ::cts/stroke] value)) + (u/display-not-valid :strokes value) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :strokes "Plugin doesn't have 'content:write' permission") + + :else + (st/emit! (dwsh/update-shapes [id] #(assoc % :strokes value))))))} + + :layoutChild + {:this true + :get + (fn [self] + (let [file-id (obj/get self "$file") + page-id (obj/get self "$page") + id (obj/get self "$id") + objects (u/locate-objects file-id page-id)] + (when (ctl/any-layout-immediate-child-id? objects id) + (flex/layout-child-proxy plugin-id file-id page-id id))))} + + :layoutCell + {:this true + :get + (fn [self] + (let [file-id (obj/get self "$file") + page-id (obj/get self "$page") + id (obj/get self "$id") + objects (u/locate-objects file-id page-id)] + (when (ctl/grid-layout-immediate-child-id? objects id) + (grid/layout-cell-proxy plugin-id file-id page-id id))))} + + + ;; Interactions + :interactions + {:this true + :get + (fn [self] + (let [interactions (-> self u/proxy->shape :interactions)] + (format/format-array + #(interaction-proxy plugin-id file-id page-id id %) + (range 0 (count interactions)))))} + + + ;; Methods + + :resize + (fn [width height] (cond - (not (boolean? value)) - (u/display-not-valid :blocked value) + (or (not (us/safe-number? width)) (<= width 0)) + (u/display-not-valid :resize width) + + (or (not (us/safe-number? height)) (<= height 0)) + (u/display-not-valid :resize height) (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :blocked "Plugin doesn't have 'content:write' permission") + (u/display-not-valid :resize "Plugin doesn't have 'content:write' permission") :else - (let [id (obj/get self "$id")] - (st/emit! (dwsh/update-shapes [id] #(assoc % :blocked value))))))} + (st/emit! (dw/update-dimensions [id] :width width) + (dw/update-dimensions [id] :height height)))) - {:name "hidden" - :get #(-> % u/proxy->shape :hidden boolean) - :set - (fn [self value] + :rotate + (fn [angle center] + (let [center (when center {:x (obj/get center "x") :y (obj/get center "y")})] + (cond + (not (number? angle)) + (u/display-not-valid :rotate-angle angle) + + (and (some? center) (or (not (number? (:x center))) (not (number? (:y center))))) + (u/display-not-valid :rotate-center center) + + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :rotate "Plugin doesn't have 'content:write' permission") + + :else + (st/emit! (dw/increase-rotation [id] angle {:center center :delta? true}))))) + + :clone + (fn [] + (let [ret-v (atom nil)] + (cond + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :clone "Plugin doesn't have 'content:write' permission") + + :else + (do (st/emit! (dws/duplicate-shapes #{id} :change-selection? false :return-ref ret-v)) + (shape-proxy plugin-id (deref ret-v)))))) + + :remove + (fn [] (cond - (not (boolean? value)) - (u/display-not-valid :hidden value) + (not (r/check-permission plugin-id "content:write")) + (u/display-not-valid :remove "Plugin doesn't have 'content:write' permission") + + :else + (st/emit! (dwsh/delete-shapes #{id})))) + + ;; Plugin data + :getPluginData + (fn [key] + (cond + (not (string? key)) + (u/display-not-valid :getPluginData key) + + :else + (let [shape (u/locate-shape file-id page-id id)] + (dm/get-in shape [:plugin-data (keyword "plugin" (str plugin-id)) key])))) + + :setPluginData + (fn [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-id "content:write")) - (u/display-not-valid :hidden "Plugin doesn't have 'content:write' permission") + (u/display-not-valid :setPluginData "Plugin doesn't have 'content:write' permission") :else - (let [id (obj/get self "$id")] - (st/emit! (dwsh/update-shapes [id] #(assoc % :hidden value))))))} + (st/emit! (dw/set-plugin-data file-id :shape id page-id (keyword "plugin" (str plugin-id)) key value)))) - {:name "visible" - :get #(-> % u/proxy->shape :hidden boolean not) - :set - (fn [self value] + :getPluginDataKeys + (fn [] + (let [shape (u/locate-shape file-id page-id id)] + (apply array (keys (dm/get-in shape [:plugin-data (keyword "plugin" (str plugin-id))]))))) + + :getSharedPluginData + (fn [namespace key] (cond - (not (boolean? value)) - (u/display-not-valid :visible value) + (not (string? namespace)) + (u/display-not-valid :getSharedPluginData-namespace namespace) + + (not (string? key)) + (u/display-not-valid :getSharedPluginData-key key) + + :else + (let [shape (u/locate-shape file-id page-id id)] + (dm/get-in shape [: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 :visible "Plugin doesn't have 'content:write' permission") + (u/display-not-valid :setSharedPluginData "Plugin doesn't have 'content:write' permission") :else - (let [id (obj/get self "$id")] - (st/emit! (dwsh/update-shapes [id] #(assoc % :hidden (not value)))))))} + (st/emit! (dw/set-plugin-data file-id :shape id page-id (keyword "shared" namespace) key value)))) - {:name "proportionLock" - :get #(-> % u/proxy->shape :proportion-lock boolean) - :set - (fn [self value] + :getSharedPluginDataKeys + (fn [namespace] (cond - (not (boolean? value)) - (u/display-not-valid :proportionLock value) - - (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :proportionLock "Plugin doesn't have 'content:write' permission") + (not (string? namespace)) + (u/display-not-valid :getSharedPluginDataKeys namespace) :else - (let [id (obj/get self "$id")] - (st/emit! (dwsh/update-shapes [id] #(assoc % :proportion-lock value))))))} + (let [shape (u/locate-shape file-id page-id id)] + (apply array (keys (dm/get-in shape [:plugin-data (keyword "shared" namespace)])))))) - {:name "constraintsHorizontal" - :get #(-> % u/proxy->shape :constraints-h d/name) - :set - (fn [self value] - (let [id (obj/get self "$id") - value (keyword value)] + ;; Only for frames + groups + booleans + :getChildren + (fn [] + (let [shape (u/locate-shape file-id page-id id)] (cond - (not (contains? cts/horizontal-constraint-types value)) - (u/display-not-valid :constraintsHorizontal value) - (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :constraintsHorizontal "Plugin doesn't have 'content:write' permission") + (and (not (cfh/frame-shape? shape)) + (not (cfh/group-shape? shape)) + (not (cfh/svg-raw-shape? shape)) + (not (cfh/bool-shape? shape))) + (u/display-not-valid :getChildren (:type shape)) :else - (st/emit! (dwsh/update-shapes [id] #(assoc % :constraints-h value))))))} + (->> (u/locate-shape file-id page-id id) + (:shapes) + (format/format-array #(shape-proxy plugin-id file-id page-id %)))))) - {:name "constraintsVertical" - :get #(-> % u/proxy->shape :constraints-v d/name) - :set - (fn [self value] - (let [id (obj/get self "$id") - value (keyword value)] + :appendChild + (fn [child] + (let [shape (u/locate-shape file-id page-id id)] (cond - (not (contains? cts/vertical-constraint-types value)) - (u/display-not-valid :constraintsVertical value) + (and (not (cfh/frame-shape? shape)) + (not (cfh/group-shape? shape)) + (not (cfh/svg-raw-shape? shape)) + (not (cfh/bool-shape? shape))) + (u/display-not-valid :appendChild (:type shape)) + + (not (shape-proxy? child)) + (u/display-not-valid :appendChild-child child) (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :constraintsVertical "Plugin doesn't have 'content:write' permission") + (u/display-not-valid :appendChild "Plugin doesn't have 'content:write' permission") :else - (st/emit! (dwsh/update-shapes [id] #(assoc % :constraints-v value))))))} + (let [child-id (obj/get child "$id")] + (st/emit! (dw/relocate-shapes #{child-id} id 0)))))) - {:name "borderRadius" - :get #(-> % u/proxy->shape :rx) - :set - (fn [self value] - (let [id (obj/get self "$id") - shape (u/proxy->shape self)] + :insertChild + (fn [index child] + (let [shape (u/locate-shape file-id page-id id)] (cond - (or (not (us/safe-int? value)) (< value 0)) - (u/display-not-valid :borderRadius value) + (and (not (cfh/frame-shape? shape)) + (not (cfh/group-shape? shape)) + (not (cfh/svg-raw-shape? shape)) + (not (cfh/bool-shape? shape))) + (u/display-not-valid :insertChild (:type shape)) + + (not (shape-proxy? child)) + (u/display-not-valid :insertChild-child child) (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :borderRadius "Plugin doesn't have 'content:write' permission") - - (or (not (ctsr/has-radius? shape)) (ctsr/radius-4? shape)) - (st/emit! (dwsh/update-shapes [id] #(-> % - ctsr/switch-to-radius-1 - (ctsr/set-radius-1 value)))) + (u/display-not-valid :insertChild "Plugin doesn't have 'content:write' permission") :else - (st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-1 % value))))))} + (let [child-id (obj/get child "$id")] + (st/emit! (dw/relocate-shapes #{child-id} id index)))))) - {:name "borderRadiusTopLeft" - :get #(-> % u/proxy->shape :r1) - :set - (fn [self value] - (let [id (obj/get self "$id") - shape (u/proxy->shape self)] + ;; Only for frames + :addFlexLayout + (fn [] + (let [shape (u/locate-shape file-id page-id id)] (cond - (not (us/safe-int? value)) - (u/display-not-valid :borderRadiusTopLeft value) + (not (cfh/frame-shape? shape)) + (u/display-not-valid :addFlexLayout (:type shape)) (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :borderRadiusTopLeft "Plugin doesn't have 'content:write' permission") - - (or (not (ctsr/has-radius? shape)) (not (ctsr/radius-4? shape))) - (st/emit! (dwsh/update-shapes [id] #(-> % - (ctsr/switch-to-radius-4) - (ctsr/set-radius-4 :r1 value)))) + (u/display-not-valid :addFlexLayout "Plugin doesn't have 'content:write' permission") :else - (st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-4 % :r1 value))))))} + (do (st/emit! (dwsl/create-layout-from-id id :flex :from-frame? true :calculate-params? false)) + (grid/grid-layout-proxy plugin-id file-id page-id id))))) - {:name "borderRadiusTopRight" - :get #(-> % u/proxy->shape :r2) - :set - (fn [self value] - (let [id (obj/get self "$id") - shape (u/proxy->shape self)] + :addGridLayout + (fn [] + (let [shape (u/locate-shape file-id page-id id)] (cond - (not (us/safe-int? value)) - (u/display-not-valid :borderRadiusTopRight value) + (not (cfh/frame-shape? shape)) + (u/display-not-valid :addGridLayout (:type shape)) (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :borderRadiusTopRight "Plugin doesn't have 'content:write' permission") - - (or (not (ctsr/has-radius? shape)) (not (ctsr/radius-4? shape))) - (st/emit! (dwsh/update-shapes [id] #(-> % - (ctsr/switch-to-radius-4) - (ctsr/set-radius-4 :r2 value)))) + (u/display-not-valid :addGridLayout "Plugin doesn't have 'content:write' permission") :else - (st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-4 % :r2 value))))))} + (do (st/emit! (dwsl/create-layout-from-id id :grid :from-frame? true :calculate-params? false)) + (grid/grid-layout-proxy plugin-id file-id page-id id))))) - {:name "borderRadiusBottomRight" - :get #(-> % u/proxy->shape :r3) - :set - (fn [self value] - (let [id (obj/get self "$id") - shape (u/proxy->shape self)] + ;; Make masks for groups + :makeMask + (fn [] + (let [shape (u/locate-shape file-id page-id id)] (cond - (not (us/safe-int? value)) - (u/display-not-valid :borderRadiusBottomRight value) + (not (cfh/group-shape? shape)) + (u/display-not-valid :makeMask (:type shape)) (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :borderRadiusBottomRight "Plugin doesn't have 'content:write' permission") - - (or (not (ctsr/has-radius? shape)) (not (ctsr/radius-4? shape))) - (st/emit! (dwsh/update-shapes [id] #(-> % - (ctsr/switch-to-radius-4) - (ctsr/set-radius-4 :r3 value)))) + (u/display-not-valid :makeMask "Plugin doesn't have 'content:write' permission") :else - (st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-4 % :r3 value))))))} + (st/emit! (dwg/mask-group #{id}))))) - {:name "borderRadiusBottomLeft" - :get #(-> % u/proxy->shape :r4) - :set - (fn [self value] - (let [id (obj/get self "$id") - shape (u/proxy->shape self)] + :removeMask + (fn [] + (let [shape (u/locate-shape file-id page-id id)] (cond - (not (us/safe-int? value)) - (u/display-not-valid :borderRadiusBottomLeft value) + (not (cfh/mask-shape? shape)) + (u/display-not-valid :removeMask (:type shape)) (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :borderRadiusBottomLeft "Plugin doesn't have 'content:write' permission") - - (or (not (ctsr/has-radius? shape)) (not (ctsr/radius-4? shape))) - (st/emit! (dwsh/update-shapes [id] #(-> % - (ctsr/switch-to-radius-4) - (ctsr/set-radius-4 :r4 value)))) + (u/display-not-valid :removeMask "Plugin doesn't have 'content:write' permission") :else - (st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-4 % :r4 value))))))} + (st/emit! (dwg/unmask-group #{id}))))) - {:name "opacity" - :get #(-> % u/proxy->shape :opacity) - :set - (fn [self value] - (let [id (obj/get self "$id")] + ;; Only for path and bool shapes + :toD + (fn [] + (let [shape (u/locate-shape file-id page-id id)] (cond - (or (not (us/safe-number? value)) (< value 0) (> value 1)) - (u/display-not-valid :opacity value) + (not (cfh/path-shape? shape)) + (u/display-not-valid :makeMask (:type shape)) + + :else + (upf/format-path (:content shape))))) + + ;; Text shapes + :getRange + (fn [start end] + (let [shape (u/locate-shape file-id page-id id)] + (cond + (not (cfh/text-shape? shape)) + (u/display-not-valid :getRange-shape "shape is not text") + + (or (not (us/safe-int? start)) (< start 0) (> start end)) + (u/display-not-valid :getRange-start start) + + (not (us/safe-int? end)) + (u/display-not-valid :getRange-end end) + + :else + (text/text-range-proxy plugin-id file-id page-id id start end)))) + + :applyTypography + (fn [typography] + (let [shape (u/locate-shape file-id page-id id)] + (cond + (not (lib-typography-proxy? typography)) + (u/display-not-valid :applyTypography-typography typography) + + (not (cfh/text-shape? shape)) + (u/display-not-valid :applyTypography-shape (:type shape)) (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :opacity "Plugin doesn't have 'content:write' permission") + (u/display-not-valid :applyTypography "Plugin doesn't have 'content:write' permission") :else - (st/emit! (dwsh/update-shapes [id] #(assoc % :opacity value))))))} + (let [typography (u/proxy->library-typography typography)] + (st/emit! (dwt/apply-typography #{id} typography file-id)))))) - {:name "blendMode" - :get #(-> % u/proxy->shape :blend-mode (d/nilv :normal) d/name) - :set - (fn [self value] - (let [id (obj/get self "$id") - value (keyword value)] + ;; COMPONENTS + :isComponentInstance + (fn [] + (let [shape (u/locate-shape file-id page-id id) + objects (u/locate-objects file-id page-id)] + (ctn/in-any-component? objects shape))) + + :isComponentMainInstance + (fn [] + (let [shape (u/locate-shape file-id page-id id) + objects (u/locate-objects file-id page-id)] + (ctn/inside-component-main? objects shape))) + + :isComponentCopyInstance + (fn [] + (let [shape (u/locate-shape file-id page-id id)] + (ctk/in-component-copy? shape))) + + :isComponentRoot + (fn [] + (let [shape (u/locate-shape file-id page-id id)] + (ctk/instance-root? shape))) + + :isComponentHead + (fn [] + (let [shape (u/locate-shape file-id page-id id)] + (ctk/instance-head? shape))) + + :componentRefShape + (fn [] + (let [objects (u/locate-objects file-id page-id) + shape (u/locate-shape file-id page-id id)] + (when (ctn/in-any-component? objects shape) + (let [[root component] (u/locate-component objects shape) + component-page-id (:main-instance-page component) + component-file (u/locate-file (:component-file root)) + ref-shape (ctf/get-ref-shape (:data component-file) component shape)] + (when (and (not (:deleted component)) (some? ref-shape) (some? component-file)) + (shape-proxy plugin-id (:id component-file) component-page-id (:id ref-shape))))))) + + :componentRoot + (fn [] + (let [objects (u/locate-objects file-id page-id) + shape (u/locate-shape file-id page-id id)] + (when (ctn/in-any-component? objects shape) + (let [[root component] (u/locate-component objects shape)] + (shape-proxy plugin-id (:component-file root) (:main-instance-page component) (:id root)))))) + + :componentHead + (fn [] + (let [objects (u/locate-objects file-id page-id) + shape (u/locate-shape file-id page-id id)] + (when (ctn/in-any-component? objects shape) + (let [head (ctn/get-head-shape (u/locate-objects file-id page-id) shape)] + (shape-proxy plugin-id file-id page-id (:id head)))))) + + :component + (fn [] + (let [objects (u/locate-objects file-id page-id) + shape (u/locate-shape file-id page-id id)] + (when (ctn/in-any-component? objects shape) + (let [[root component] (u/locate-component objects shape)] + (lib-component-proxy plugin-id (:component-file root) (:id component)))))) + + :detach + (fn [] + (st/emit! (dwl/detach-component id))) + + ;; Export + :export + (fn [value] + (let [value (parser/parse-export value)] (cond - (not (contains? cts/blend-modes value)) - (u/display-not-valid :blendMode value) - - (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :blendMode "Plugin doesn't have 'content:write' permission") + (not (sm/validate ::ctse/export value)) + (u/display-not-valid :export value) :else - (st/emit! (dwsh/update-shapes [id] #(assoc % :blend-mode value))))))} + (let [shape (u/locate-shape file-id page-id id) + payload + {:cmd :export-shapes + :profile-id (:profile-id @st/state) + :wait true + :exports [{:file-id file-id + :page-id page-id + :object-id id + :name (:name shape) + :type (:type value :png) + :suffix (:suffix value "") + :scale (:scale value 1)}]}] + (js/Promise. + (fn [resolve reject] + (->> (rp/cmd! :export payload) + (rx/mapcat #(rp/cmd! :export {:cmd :get-resource :wait true :id (:id %) :blob? true})) + (rx/mapcat #(.arrayBuffer %)) + (rx/map #(js/Uint8Array. %)) + (rx/subs! resolve reject)))))))) - {:name "shadows" - :get #(-> % u/proxy->shape :shadow format/format-shadows) - :set - (fn [self value] - (let [id (obj/get self "$id") - value (mapv #(shadow-defaults (parser/parse-shadow %)) value)] + ;; Interactions + :addInteraction + (fn [trigger action delay] + (let [interaction + (-> ctsi/default-interaction + (d/patch-object (parser/parse-interaction trigger action delay)))] (cond - (not (sm/validate [:vector ::ctss/shadow] value)) - (u/display-not-valid :shadows value) - - (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :shadows "Plugin doesn't have 'content:write' permission") + (not (sm/validate ::ctsi/interaction interaction)) + (u/display-not-valid :addInteraction interaction) :else - (st/emit! (dwsh/update-shapes [id] #(assoc % :shadow value))))))} + (let [index (-> (u/locate-shape file-id page-id id) (:interactions []) count)] + (st/emit! (dwi/add-interaction page-id id interaction)) + (interaction-proxy plugin-id file-id page-id id index))))) - {:name "blur" - :get #(-> % u/proxy->shape :blur format/format-blur) - :set - (fn [self value] - (if (nil? value) - (st/emit! (dwsh/update-shapes [id] #(dissoc % :blur))) - (let [id (obj/get self "$id") - value (blur-defaults (parser/parse-blur value))] - (cond - (not (sm/validate ::ctsb/blur value)) - (u/display-not-valid :blur value) + :removeInteraction + (fn [interaction] + (cond + (not (interaction-proxy? interaction)) + (u/display-not-valid :removeInteraction interaction) - (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :blur "Plugin doesn't have 'content:write' permission") + :else + (st/emit! (dwi/remove-interaction {:id id} (obj/get interaction "$index"))))) - :else - (st/emit! (dwsh/update-shapes [id] #(assoc % :blur value)))))))} - - {:name "exports" - :get #(-> % u/proxy->shape :exports format/format-exports) - :set - (fn [self value] - (let [id (obj/get self "$id") - value (parser/parse-exports value)] - (cond - (not (sm/validate [:vector ::ctse/export] value)) - (u/display-not-valid :exports value) - - (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :exports "Plugin doesn't have 'content:write' permission") - - :else - (st/emit! (dwsh/update-shapes [id] #(assoc % :exports value))))))} - - ;; Geometry properties - {:name "x" - :get #(-> % u/proxy->shape :points grc/points->rect :x) - :set - (fn [self value] - (let [id (obj/get self "$id")] + ;; Ruler guides + :addRulerGuide + (fn [orientation value] + (let [shape (u/locate-shape file-id page-id id)] (cond (not (us/safe-number? value)) - (u/display-not-valid :x 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'") + + (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 :x "Plugin doesn't have 'content:write' permission") + (u/display-not-valid :addRulerGuide "Plugin doesn't have 'content:write' permission") :else - (st/emit! (dw/update-position id {:x value})))))} + (let [id (uuid/next) + axis (parser/orientation->axis orientation) + objects (u/locate-objects file-id page-id) + frame (get objects id) + board-pos (get frame axis) + position (+ board-pos value)] + (st/emit! + (dwgu/update-guides + {:id id + :axis axis + :position position + :frame-id id})) + (rg/ruler-guide-proxy plugin-id file-id page-id id))))) - {:name "y" - :get #(-> % u/proxy->shape :points grc/points->rect :y) - :set - (fn [self value] - (let [id (obj/get self "$id")] - (cond - (not (us/safe-number? value)) - (u/display-not-valid :y value) - - (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :y "Plugin doesn't have 'content:write' permission") - - :else - (st/emit! (dw/update-position id {:y value})))))} - - {:name "parent" - ;; not enumerable so there are no infinite loops - :enumerable false - :get (fn [self] - (let [shape (u/proxy->shape self) - parent-id (:parent-id shape)] - (shape-proxy plugin-id (obj/get self "$file") (obj/get self "$page") parent-id)))} - {:name "parentX" - :get (fn [self] - (let [shape (u/proxy->shape self) - shape-x (-> shape :points grc/points->rect :x) - parent-id (:parent-id shape) - parent (u/locate-shape (obj/get self "$file") (obj/get self "$page") parent-id)] - (- shape-x (:x parent)))) - :set - (fn [self value] + :removeRulerGuide + (fn [_ value] (cond - (not (us/safe-number? value)) - (u/display-not-valid :parentX value) + (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 :parentX "Plugin doesn't have 'content:write' permission") + (u/display-not-valid :removeRulerGuide "Plugin doesn't have 'content:write' permission") :else - (let [id (obj/get self "$id") - parent-id (-> self u/proxy->shape :parent-id) - parent (u/locate-shape (obj/get self "$file") (obj/get self "$page") parent-id) - parent-x (:x parent)] - (st/emit! (dw/update-position id {:x (+ parent-x value)})))))} - - {:name "parentY" - :get (fn [self] - (let [shape (u/proxy->shape self) - shape-y (-> shape :points grc/points->rect :y) - parent-id (:parent-id shape) - parent (u/locate-shape (obj/get self "$file") (obj/get self "$page") parent-id) - parent-y (:y parent)] - (- shape-y parent-y))) - :set - (fn [self value] - (cond - (not (us/safe-number? value)) - (u/display-not-valid :parentY value) - - (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :parentY "Plugin doesn't have 'content:write' permission") - - :else - (let [id (obj/get self "$id") - parent-id (-> self u/proxy->shape :parent-id) - parent (u/locate-shape (obj/get self "$file") (obj/get self "$page") parent-id) - parent-y (:y parent)] - (st/emit! (dw/update-position id {:y (+ parent-y value)})))))} - - {:name "boardX" - :get (fn [self] - (let [shape (u/proxy->shape self) - shape-x (-> shape :points grc/points->rect :x) - frame-id (:parent-id shape) - frame (u/locate-shape (obj/get self "$file") (obj/get self "$page") frame-id) - frame-x (:x frame)] - (- shape-x frame-x))) - :set - (fn [self value] - (cond - (not (us/safe-number? value)) - (u/display-not-valid :frameX value) - - (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :frameX "Plugin doesn't have 'content:write' permission") - - :else - (let [id (obj/get self "$id") - frame-id (-> self u/proxy->shape :frame-id) - frame (u/locate-shape (obj/get self "$file") (obj/get self "$page") frame-id) - frame-x (:x frame)] - (st/emit! (dw/update-position id {:x (+ frame-x value)})))))} - - {:name "boardY" - :get (fn [self] - (let [shape (u/proxy->shape self) - shape-y (-> shape :points grc/points->rect :y) - frame-id (:parent-id shape) - frame (u/locate-shape (obj/get self "$file") (obj/get self "$page") frame-id) - frame-y (:y frame)] - (- shape-y frame-y))) - :set - (fn [self value] - (cond - (not (us/safe-number? value)) - (u/display-not-valid :frameY value) - - (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :frameY "Plugin doesn't have 'content:write' permission") - - :else - (let [id (obj/get self "$id") - frame-id (-> self u/proxy->shape :frame-id) - frame (u/locate-shape (obj/get self "$file") (obj/get self "$page") frame-id) - frame-y (:y frame)] - (st/emit! (dw/update-position id {:y (+ frame-y value)})))))} - - {:name "width" - :get #(-> % u/proxy->shape :selrect :width)} - - {:name "height" - :get #(-> % u/proxy->shape :selrect :height)} - - {:name "bounds" - :get #(-> % u/proxy->shape :points grc/points->rect format/format-bounds)} - - {:name "center" - :get #(-> % u/proxy->shape gsh/shape->center format/format-point)} - - {:name "rotation" - :get #(-> % u/proxy->shape :rotation) - :set - (fn [self value] - (cond - (not (number? value)) - (u/display-not-valid :rotation value) - - (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :rotation "Plugin doesn't have 'content:write' permission") - - :else - (let [shape (u/proxy->shape self)] - (st/emit! (dw/increase-rotation #{(:id shape)} value)))))} - - {:name "flipX" - :get #(-> % u/proxy->shape :flip-x boolean) - :set - (fn [self value] - (cond - (not (boolean? value)) - (u/display-not-valid :flipX value) - - (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :flipX "Plugin doesn't have 'content:write' permission") - - :else - (let [id (obj/get self "$id")] - (st/emit! (dw/flip-horizontal-selected #{id})))))} - - {:name "flipY" - :get #(-> % u/proxy->shape :flip-y boolean) - :set - (fn [self value] - (cond - (not (boolean? value)) - (u/display-not-valid :flipY value) - - (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :flipY "Plugin doesn't have 'content:write' permission") - - :else - (let [id (obj/get self "$id")] - (st/emit! (dw/flip-vertical-selected #{id})))))} - - ;; Strokes and fills - {:name "fills" - :get #(if (cfh/text-shape? data) - (-> % u/proxy->shape text-props :fills format/format-fills) - (-> % u/proxy->shape :fills format/format-fills)) - :set - (fn [self value] - (let [shape (u/proxy->shape self) - id (:id shape) - value (parser/parse-fills value)] - (cond - (not (sm/validate [:vector ::cts/fill] value)) - (u/display-not-valid :fills value) - - (cfh/text-shape? shape) - (st/emit! (dwt/update-attrs id {: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! (dwsh/update-shapes [id] #(assoc % :fills value))))))} - - {:name "strokes" - :get #(-> % u/proxy->shape :strokes format/format-strokes) - :set - (fn [self value] - (let [id (obj/get self "$id") - value (parser/parse-strokes value)] - (cond - (not (sm/validate [:vector ::cts/stroke] value)) - (u/display-not-valid :strokes value) - - (not (r/check-permission plugin-id "content:write")) - (u/display-not-valid :strokes "Plugin doesn't have 'content:write' permission") - - :else - (st/emit! (dwsh/update-shapes [id] #(assoc % :strokes value))))))} - - {:name "layoutChild" - :get - (fn [self] - (let [file-id (obj/get self "$file") - page-id (obj/get self "$page") - id (obj/get self "$id") - objects (u/locate-objects file-id page-id)] - (when (ctl/any-layout-immediate-child-id? objects id) - (flex/layout-child-proxy plugin-id file-id page-id id))))} - - {:name "layoutCell" - :get - (fn [self] - (let [file-id (obj/get self "$file") - page-id (obj/get self "$page") - id (obj/get self "$id") - objects (u/locate-objects file-id page-id)] - (when (ctl/grid-layout-immediate-child-id? objects id) - (grid/layout-cell-proxy plugin-id file-id page-id id))))} - - - ;; Interactions - {:name "interactions" - :get - (fn [self] - (let [interactions (-> self u/proxy->shape :interactions)] - (format/format-array - #(interaction-proxy plugin-id file-id page-id id %) - (range 0 (count interactions)))))}) + (let [guide (u/proxy->ruler-guide value)] + (st/emit! (dwgu/remove-guide guide)))))) (cond-> (or (cfh/frame-shape? data) (cfh/group-shape? data) (cfh/svg-raw-shape? data) (cfh/bool-shape? data)) (crc/add-properties! diff --git a/frontend/src/app/plugins/text.cljs b/frontend/src/app/plugins/text.cljs index d2343310a..6c90f658d 100644 --- a/frontend/src/app/plugins/text.cljs +++ b/frontend/src/app/plugins/text.cljs @@ -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] diff --git a/frontend/src/app/plugins/user.cljs b/frontend/src/app/plugins/user.cljs index c2997818f..bf2022435 100644 --- a/frontend/src/app/plugins/user.cljs +++ b/frontend/src/app/plugins/user.cljs @@ -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))) diff --git a/frontend/src/app/plugins/utils.cljs b/frontend/src/app/plugins/utils.cljs index 2d28301f9..63aea0eed 100644 --- a/frontend/src/app/plugins/utils.cljs +++ b/frontend/src/app/plugins/utils.cljs @@ -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"))) diff --git a/frontend/src/app/plugins/viewport.cljs b/frontend/src/app/plugins/viewport.cljs index 4623c375c..ab3622764 100644 --- a/frontend/src/app/plugins/viewport.cljs +++ b/frontend/src/app/plugins/viewport.cljs @@ -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)))))) diff --git a/frontend/src/app/util/object.cljc b/frontend/src/app/util/object.cljc index b287650b5..a04e7e4b7 100644 --- a/frontend/src/app/util/object.cljc +++ b/frontend/src/app/util/object.cljc @@ -173,7 +173,7 @@ (let [definition (first params)] (if (some? definition) (let [definition (if (map? definition) - (assoc definition :name (name ckey) :this false) + (c/merge {:this false} (assoc definition :name (name ckey))) (-> {:enumerable false} (c/merge (meta definition)) (assoc :name (name ckey)) @@ -210,6 +210,16 @@ :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 @@ -221,6 +231,10 @@ (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)}]) @@ -230,6 +244,4 @@ ~@(mapcat (fn [[k v]] (cons k v)) definitions)) obj-sym)] - (cljs.core/specify! ~obj-sym - cljs.core/IMeta - (~'-meta [_#] ~tmeta)))))) + (cljs.core/specify! ~obj-sym)))))