Review input validation for plugins

This commit is contained in:
alonso.torres 2024-06-19 11:17:04 +02:00
parent c5c8be4b4a
commit 1794859468
12 changed files with 1532 additions and 497 deletions

View file

@ -8,6 +8,8 @@
(:require
[app.common.schema :as sm]))
(def export-types #{:png :jpeg :svg :pdf})
(sm/def! ::export
[:map {:title "ShapeExport"}
[:type :keyword]

View file

@ -72,6 +72,7 @@
(defn open-plugin!
[{:keys [plugin-id name description host code icon permissions]}]
(try
(.ɵloadPlugin
js/window
#js {:pluginId plugin-id
@ -80,7 +81,9 @@
:host host
:code code
:icon icon
:permissions (apply array permissions)}))
:permissions (apply array permissions)})
(catch :default e
(.error js/console "Error" e))))
(mf/defc plugin-management-dialog
{::mf/register modal/components

View file

@ -35,7 +35,7 @@
(setPluginData
[_ key value]
(cond
(not (string? key))
(or (not (string? key)) (empty? key))
(u/display-not-valid :file-plugin-data-key key)
(and (some? value) (not (string? value)))
@ -66,10 +66,10 @@
[_ namespace key value]
(cond
(not (string? namespace))
(or (not (string? namespace)) (empty? namespace))
(u/display-not-valid :file-plugin-data-namespace namespace)
(not (string? key))
(or (not (string? key)) (empty? key))
(u/display-not-valid :file-plugin-data-key key)
(and (some? value) (not (string? value)))

View file

@ -13,10 +13,13 @@
[app.main.data.workspace.shape-layout :as dwsl]
[app.main.data.workspace.transforms :as dwt]
[app.main.store :as st]
[app.plugins.utils :as utils :refer [proxy->shape]]
[app.plugins.utils :as u]
[app.util.object :as obj]
[potok.v2.core :as ptk]))
;; 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
@ -25,9 +28,14 @@
(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]})))))
(ptk/data-event :layout/update {:ids [$id]}))))))
(defn flex-layout-proxy? [p]
(instance? FlexLayout p))
@ -42,113 +50,165 @@
{:name "$page" :enumerable false :get (constantly page-id)}
{:name "dir"
:get #(-> % proxy->shape :layout-flex-dir d/name)
:get #(-> % u/proxy->shape :layout-flex-dir d/name)
:set
(fn [self value]
(let [id (obj/get self "$id")
value (keyword value)]
(when (contains? ctl/flex-direction-types value)
(st/emit! (dwsl/update-layout #{id} {:layout-flex-dir value})))))}
(let [value (keyword value)]
(cond
(not (contains? ctl/flex-direction-types value))
(u/display-not-valid :dir value)
:else
(let [id (obj/get self "$id")]
(st/emit! (dwsl/update-layout #{id} {:layout-flex-dir value}))))))}
{:name "alignItems"
:get #(-> % proxy->shape :layout-align-items d/name)
:get #(-> % u/proxy->shape :layout-align-items d/name)
:set
(fn [self value]
(let [id (obj/get self "$id")
value (keyword value)]
(when (contains? ctl/align-items-types value)
(st/emit! (dwsl/update-layout #{id} {:layout-align-items value})))))}
(let [value (keyword value)]
(cond
(not (contains? ctl/align-items-types value))
(u/display-not-valid :alignItems value)
:else
(let [id (obj/get self "$id")]
(st/emit! (dwsl/update-layout #{id} {:layout-align-items value}))))))}
{:name "alignContent"
:get #(-> % proxy->shape :layout-align-content d/name)
:get #(-> % u/proxy->shape :layout-align-content d/name)
:set
(fn [self value]
(let [id (obj/get self "$id")
value (keyword value)]
(when (contains? ctl/align-content-types value)
(st/emit! (dwsl/update-layout #{id} {:layout-align-content value})))))}
(let [value (keyword value)]
(cond
(not (contains? ctl/align-content-types value))
(u/display-not-valid :alignContent value)
:else
(let [id (obj/get self "$id")]
(st/emit! (dwsl/update-layout #{id} {:layout-align-content value}))))))}
{:name "justifyItems"
:get #(-> % proxy->shape :layout-justify-items d/name)
:get #(-> % u/proxy->shape :layout-justify-items d/name)
:set
(fn [self value]
(let [id (obj/get self "$id")
value (keyword value)]
(when (contains? ctl/justify-items-types value)
(st/emit! (dwsl/update-layout #{id} {:layout-justify-items value})))))}
(let [value (keyword value)]
(cond
(not (contains? ctl/justify-items-types value))
(u/display-not-valid :justifyItems value)
:else
(let [id (obj/get self "$id")]
(st/emit! (dwsl/update-layout #{id} {:layout-justify-items value}))))))}
{:name "justifyContent"
:get #(-> % proxy->shape :layout-justify-content d/name)
:get #(-> % u/proxy->shape :layout-justify-content d/name)
:set
(fn [self value]
(let [id (obj/get self "$id")
value (keyword value)]
(when (contains? ctl/justify-content-types value)
(st/emit! (dwsl/update-layout #{id} {:layout-justify-content value})))))}
(let [value (keyword value)]
(cond
(not (contains? ctl/justify-content-types value))
(u/display-not-valid :justifyContent value)
:else
(let [id (obj/get self "$id")]
(st/emit! (dwsl/update-layout #{id} {:layout-justify-content value}))))))}
{:name "rowGap"
:get #(-> % proxy->shape :layout-gap :row-gap)
: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)
:else
(let [id (obj/get self "$id")]
(when (us/safe-int? value)
(st/emit! (dwsl/update-layout #{id} {:layout-gap {:row-gap value}})))))}
{:name "columnGap"
:get #(-> % proxy->shape :layout-gap :column-gap)
: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)
:else
(let [id (obj/get self "$id")]
(when (us/safe-int? value)
(st/emit! (dwsl/update-layout #{id} {:layout-gap {:column-gap value}})))))}
{:name "verticalPadding"
:get #(-> % proxy->shape :layout-padding :p1)
: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)
:else
(let [id (obj/get self "$id")]
(when (us/safe-int? value)
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value :p3 value}})))))}
{:name "horizontalPadding"
:get #(-> % proxy->shape :layout-padding :p2)
: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)
:else
(let [id (obj/get self "$id")]
(when (us/safe-int? value)
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value :p4 value}})))))}
{:name "topPadding"
:get #(-> % proxy->shape :layout-padding :p1)
: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)
:else
(let [id (obj/get self "$id")]
(when (us/safe-int? value)
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value}})))))}
{:name "rightPadding"
:get #(-> % proxy->shape :layout-padding :p2)
: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)
:else
(let [id (obj/get self "$id")]
(when (us/safe-int? value)
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value}})))))}
{:name "bottomPadding"
:get #(-> % proxy->shape :layout-padding :p3)
: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)
:else
(let [id (obj/get self "$id")]
(when (us/safe-int? value)
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p3 value}})))))}
{:name "leftPadding"
:get #(-> % proxy->shape :layout-padding :p4)
: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)
:else
(let [id (obj/get self "$id")]
(when (us/safe-int? value)
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p4 value}})))))})))
@ -167,124 +227,184 @@
{:name "$page" :enumerable false :get (constantly page-id)}
{:name "absolute"
:get #(-> % proxy->shape :layout-item-absolute boolean)
:get #(-> % u/proxy->shape :layout-item-absolute boolean)
:set
(fn [self value]
(cond
(not (boolean? value))
(u/display-not-valid :absolute value)
:else
(let [id (obj/get self "$id")]
(when (boolean? value)
(st/emit! (dwsl/update-layout #{id} {:layout-item-absolute value})))))}
{:name "zIndex"
:get #(-> % proxy->shape :layout-item-z-index (d/nilv 0))
: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)
:else
(let [id (obj/get self "$id")]
(when (us/safe-int? value)
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-z-index value})))))}
{:name "horizontalSizing"
:get #(-> % proxy->shape :layout-item-h-sizing (d/nilv :fix) d/name)
:get #(-> % u/proxy->shape :layout-item-h-sizing (d/nilv :fix) d/name)
:set
(fn [self value]
(let [id (obj/get self "$id")
value (keyword value)]
(when (contains? ctl/item-h-sizing-types value)
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-h-sizing value})))))}
(let [value (keyword value)]
(cond
(not (contains? ctl/item-h-sizing-types value))
(u/display-not-valid :horizontalPadding value)
:else
(let [id (obj/get self "$id")]
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-h-sizing value}))))))}
{:name "verticalSizing"
:get #(-> % proxy->shape :layout-item-v-sizing (d/nilv :fix) d/name)
:get #(-> % u/proxy->shape :layout-item-v-sizing (d/nilv :fix) d/name)
:set
(fn [self value]
(let [id (obj/get self "$id")
value (keyword value)]
(when (contains? ctl/item-v-sizing-types value)
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-v-sizing value})))))}
(let [value (keyword value)]
(cond
(not (contains? ctl/item-v-sizing-types value))
(u/display-not-valid :verticalSizing value)
:else
(let [id (obj/get self "$id")]
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-v-sizing value}))))))}
{:name "alignSelf"
:get #(-> % proxy->shape :layout-item-align-self (d/nilv :auto) d/name)
:get #(-> % u/proxy->shape :layout-item-align-self (d/nilv :auto) d/name)
:set
(fn [self value]
(let [id (obj/get self "$id")
value (keyword value)]
(when (contains? ctl/item-align-self-types value)
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-align-self value})))))}
(let [value (keyword value)]
(cond
(not (contains? ctl/item-align-self-types value))
(u/display-not-valid :alignSelf value)
:else
(let [id (obj/get self "$id")]
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-align-self value}))))))}
{:name "verticalMargin"
:get #(-> % proxy->shape :layout-item-margin :m1 (d/nilv 0))
: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)
:else
(let [id (obj/get self "$id")]
(when (us/safe-number? value)
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m1 value :m3 value}})))))}
{:name "horizontalMargin"
:get #(-> % proxy->shape :layout-item-margin :m2 (d/nilv 0))
: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)
:else
(let [id (obj/get self "$id")]
(when (us/safe-number? value)
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m2 value :m4 value}})))))}
{:name "topMargin"
:get #(-> % proxy->shape :layout-item-margin :m1 (d/nilv 0))
: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)
:else
(let [id (obj/get self "$id")]
(when (us/safe-number? value)
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m1 value}})))))}
{:name "rightMargin"
:get #(-> % proxy->shape :layout-item-margin :m2 (d/nilv 0))
: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)
:else
(let [id (obj/get self "$id")]
(when (us/safe-number? value)
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m2 value}})))))}
{:name "bottomMargin"
:get #(-> % proxy->shape :layout-item-margin :m3 (d/nilv 0))
: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)
:else
(let [id (obj/get self "$id")]
(when (us/safe-number? value)
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m3 value}})))))}
{:name "leftMargin"
:get #(-> % proxy->shape :layout-item-margin :m4 (d/nilv 0))
: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)
:else
(let [id (obj/get self "$id")]
(when (us/safe-number? value)
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m4 value}})))))}
{:name "maxWidth"
:get #(-> % proxy->shape :layout-item-max-w)
:get #(-> % u/proxy->shape :layout-item-max-w)
:set
(fn [self value]
(cond
(not (us/safe-number? value))
(u/display-not-valid :maxWidth value)
:else
(let [id (obj/get self "$id")]
(when (us/safe-number? value)
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-max-w value})))))}
{:name "minWidth"
:get #(-> % proxy->shape :layout-item-min-w)
:get #(-> % u/proxy->shape :layout-item-min-w)
:set
(fn [self value]
(cond
(not (us/safe-number? value))
(u/display-not-valid :minWidth value)
:else
(let [id (obj/get self "$id")]
(when (us/safe-number? value)
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-min-w value})))))}
{:name "maxHeight"
:get #(-> % proxy->shape :layout-item-max-h)
:get #(-> % u/proxy->shape :layout-item-max-h)
:set
(fn [self value]
(cond
(not (us/safe-number? value))
(u/display-not-valid :maxHeight value)
:else
(let [id (obj/get self "$id")]
(when (us/safe-number? value)
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-max-h value})))))}
{:name "minHeight"
:get #(-> % proxy->shape :layout-item-min-h)
:get #(-> % u/proxy->shape :layout-item-min-h)
:set
(fn [self value]
(cond
(not (us/safe-number? value))
(u/display-not-valid :minHeight value)
:else
(let [id (obj/get self "$id")]
(when (us/safe-number? value)
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-min-h value})))))})))

View file

@ -11,6 +11,8 @@
[app.main.data.workspace.texts :as dwt]
[app.main.fonts :as fonts]
[app.main.store :as st]
[app.plugins.shape :as shape]
[app.plugins.utils :as u]
[app.util.object :as obj]
[cuerdas.core :as str]))
@ -20,15 +22,29 @@
Object
(applyToText [_ text variant]
(cond
(not (shape/shape-proxy? text))
(u/display-not-valid :applyToText text)
;; TODO: Check variant inside font variants
: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))))
(st/emit! (dwt/update-attrs id values)))))
(applyToRange [_ range variant]
(cond
(not (shape/text-range? range))
(u/display-not-valid :applyToRange range)
;; TODO: Check variant inside font variants
:else
(let [id (obj/get range "$id")
start (obj/get range "start")
end (obj/get range "end")
@ -37,7 +53,7 @@
: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)))))
(st/emit! (dwt/update-text-range id start end values))))))
(defn font-proxy? [p]
(instance? PenpotFont p))
@ -63,23 +79,43 @@
Object
(findById
[_ id]
(font-proxy (d/seek #(str/includes? (str/lower (:id %)) (str/lower id)) (vals @fonts/fontsdb))))
(cond
(not (string? id))
(u/display-not-valid :findbyId id)
:else
(font-proxy (d/seek #(str/includes? (str/lower (:id %)) (str/lower id)) (vals @fonts/fontsdb)))))
(findByName
[_ name]
(font-proxy (d/seek #(str/includes? (str/lower (:name %)) (str/lower name)) (vals @fonts/fontsdb))))
(cond
(not (string? name))
(u/display-not-valid :findByName name)
:else
(font-proxy (d/seek #(str/includes? (str/lower (:name %)) (str/lower name)) (vals @fonts/fontsdb)))))
(findAllById
[_ id]
(cond
(not (string? id))
(u/display-not-valid :findAllById name)
:else
(apply array (->> (vals @fonts/fontsdb)
(filter #(str/includes? (str/lower (:id %)) (str/lower id)))
(map font-proxy))))
(map font-proxy)))))
(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)))))
(map font-proxy))))))
(defn fonts-subcontext
[plugin-id]

View file

@ -13,15 +13,18 @@
[app.main.data.workspace.shape-layout :as dwsl]
[app.main.data.workspace.transforms :as dwt]
[app.main.store :as st]
[app.plugins.utils :as utils :refer [proxy->shape locate-shape]]
[app.plugins.utils :as u]
[app.util.object :as obj]
[potok.v2.core :as ptk]))
;; Define in `app.plugins.shape` we do this way to prevent circular dependency
(def shape-proxy? nil)
(defn- make-tracks
[tracks]
(.freeze
js/Object
(apply array (->> tracks (map utils/to-js)))))
(apply array (->> tracks (map u/to-js)))))
(deftype GridLayout [$plugin $file $page $id]
Object
@ -29,40 +32,116 @@
(addRow
[_ type value]
(let [type (keyword type)]
(st/emit! (dwsl/add-layout-track #{$id} :row {:type type :value value}))))
(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)
:else
(st/emit! (dwsl/add-layout-track #{$id} :row {:type type :value value})))))
(addRowAtIndex
[_ index type value]
(let [type (keyword type)]
(st/emit! (dwsl/add-layout-track #{$id} :row {:type type :value value} index))))
(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)
:else
(st/emit! (dwsl/add-layout-track #{$id} :row {:type type :value value} index)))))
(addColumn
[_ type value]
(let [type (keyword type)]
(st/emit! (dwsl/add-layout-track #{$id} :column {:type type :value value}))))
(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)
: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)
:else
(let [type (keyword type)]
(st/emit! (dwsl/add-layout-track #{$id} :column {:type type :value value} index))))
(st/emit! (dwsl/add-layout-track #{$id} :column {:type type :value value} index)))))
(removeRow
[_ index]
(st/emit! (dwsl/remove-layout-track #{$id} :row index)))
(cond
(not (us/safe-int? index))
(u/display-not-valid :removeRow index)
:else
(st/emit! (dwsl/remove-layout-track #{$id} :row index))))
(removeColumn
[_ index]
(st/emit! (dwsl/remove-layout-track #{$id} :column index)))
(cond
(not (us/safe-int? index))
(u/display-not-valid :removeColumn index)
:else
(st/emit! (dwsl/remove-layout-track #{$id} :column index))))
(setColumn
[_ index type value]
(let [type (keyword type)]
(st/emit! (dwsl/change-layout-track #{$id} :column index (d/without-nils {:type type :value value})))))
(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)
: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)]
(st/emit! (dwsl/change-layout-track #{$id} :row index (d/without-nils {:type type :value value})))))
(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)
:else
(st/emit! (dwsl/change-layout-track #{$id} :row index (d/without-nils {:type type :value value}))))))
(remove
[_]
@ -70,9 +149,20 @@
(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)
: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]})))))
(ptk/data-event :layout/update {:ids [$id]}))))))
(defn grid-layout-proxy? [p]
(instance? GridLayout p))
@ -85,119 +175,172 @@
{:name "$id" :enumerable false :get (constantly id)}
{:name "$file" :enumerable false :get (constantly file-id)}
{:name "$page" :enumerable false :get (constantly page-id)}
{:name "dir"
:get #(-> % proxy->shape :layout-grid-dir d/name)
:get #(-> % u/proxy->shape :layout-grid-dir d/name)
:set
(fn [self value]
(let [id (obj/get self "$id")
value (keyword value)]
(when (contains? ctl/grid-direction-types value)
(st/emit! (dwsl/update-layout #{id} {:layout-grid-dir value})))))}
(let [value (keyword value)]
(cond
(not (contains? ctl/grid-direction-types value))
(u/display-not-valid :dir value)
:else
(let [id (obj/get self "$id")]
(st/emit! (dwsl/update-layout #{id} {:layout-grid-dir value}))))))}
{:name "rows"
:get #(-> % proxy->shape :layout-grid-rows make-tracks)}
:get #(-> % u/proxy->shape :layout-grid-rows make-tracks)}
{:name "columns"
:get #(-> % proxy->shape :layout-grid-columns make-tracks)}
:get #(-> % u/proxy->shape :layout-grid-columns make-tracks)}
{:name "alignItems"
:get #(-> % proxy->shape :layout-align-items d/name)
:get #(-> % u/proxy->shape :layout-align-items d/name)
:set
(fn [self value]
(let [id (obj/get self "$id")
value (keyword value)]
(when (contains? ctl/align-items-types value)
(st/emit! (dwsl/update-layout #{id} {:layout-align-items value})))))}
(let [value (keyword value)]
(cond
(not (contains? ctl/align-items-types value))
(u/display-not-valid :alignItems value)
:else
(let [id (obj/get self "$id")]
(st/emit! (dwsl/update-layout #{id} {:layout-align-items value}))))))}
{:name "alignContent"
:get #(-> % proxy->shape :layout-align-content d/name)
:get #(-> % u/proxy->shape :layout-align-content d/name)
:set
(fn [self value]
(let [id (obj/get self "$id")
value (keyword value)]
(when (contains? ctl/align-content-types value)
(st/emit! (dwsl/update-layout #{id} {:layout-align-content value})))))}
(let [value (keyword value)]
(cond
(not (contains? ctl/align-content-types value))
(u/display-not-valid :alignContent value)
:else
(let [id (obj/get self "$id")]
(st/emit! (dwsl/update-layout #{id} {:layout-align-content value}))))))}
{:name "justifyItems"
:get #(-> % proxy->shape :layout-justify-items d/name)
:get #(-> % u/proxy->shape :layout-justify-items d/name)
:set
(fn [self value]
(let [id (obj/get self "$id")
value (keyword value)]
(when (contains? ctl/justify-items-types value)
(st/emit! (dwsl/update-layout #{id} {:layout-justify-items value})))))}
(let [value (keyword value)]
(cond
(not (contains? ctl/justify-items-types value))
(u/display-not-valid :justifyItems value)
:else
(let [id (obj/get self "$id")]
(st/emit! (dwsl/update-layout #{id} {:layout-justify-items value}))))))}
{:name "justifyContent"
:get #(-> % proxy->shape :layout-justify-content d/name)
:get #(-> % u/proxy->shape :layout-justify-content d/name)
:set
(fn [self value]
(let [id (obj/get self "$id")
value (keyword value)]
(when (contains? ctl/justify-content-types value)
(st/emit! (dwsl/update-layout #{id} {:layout-justify-content value})))))}
(let [value (keyword value)]
(cond
(not (contains? ctl/justify-content-types value))
(u/display-not-valid :justifyContent value)
:else
(let [id (obj/get self "$id")]
(st/emit! (dwsl/update-layout #{id} {:layout-justify-content value}))))))}
{:name "rowGap"
:get #(-> % proxy->shape :layout-gap :row-gap)
: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)
:else
(let [id (obj/get self "$id")]
(when (us/safe-int? value)
(st/emit! (dwsl/update-layout #{id} {:layout-gap {:row-gap value}})))))}
{:name "columnGap"
:get #(-> % proxy->shape :layout-gap :column-gap)
: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)
:else
(let [id (obj/get self "$id")]
(when (us/safe-int? value)
(st/emit! (dwsl/update-layout #{id} {:layout-gap {:column-gap value}})))))}
{:name "verticalPadding"
:get #(-> % proxy->shape :layout-padding :p1)
: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)
:else
(let [id (obj/get self "$id")]
(when (us/safe-int? value)
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value :p3 value}})))))}
{:name "horizontalPadding"
:get #(-> % proxy->shape :layout-padding :p2)
: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)
:else
(let [id (obj/get self "$id")]
(when (us/safe-int? value)
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value :p4 value}})))))}
{:name "topPadding"
:get #(-> % proxy->shape :layout-padding :p1)
: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)
:else
(let [id (obj/get self "$id")]
(when (us/safe-int? value)
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value}})))))}
{:name "rightPadding"
:get #(-> % proxy->shape :layout-padding :p2)
: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)
:else
(let [id (obj/get self "$id")]
(when (us/safe-int? value)
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value}})))))}
{:name "bottomPadding"
:get #(-> % proxy->shape :layout-padding :p3)
: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)
:else
(let [id (obj/get self "$id")]
(when (us/safe-int? value)
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p3 value}})))))}
{:name "leftPadding"
:get #(-> % proxy->shape :layout-padding :p4)
: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)
:else
(let [id (obj/get self "$id")]
(when (us/safe-int? value)
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p4 value}})))))})))
(deftype GridCellProxy [$plugin $file $page $id])
@ -208,8 +351,8 @@
(defn layout-cell-proxy
[plugin-id file-id page-id id]
(letfn [(locate-cell [_]
(let [shape (locate-shape file-id page-id id)
parent (locate-shape file-id page-id (:parent-id shape))]
(let [shape (u/locate-shape file-id page-id id)
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)
@ -223,73 +366,129 @@
:get #(-> % locate-cell :row)
:set
(fn [self value]
(let [shape (proxy->shape self)
cell (locate-cell self)]
(when (us/safe-int? value)
(let [cell (locate-cell self)
shape (u/proxy->shape self)]
(cond
(not (us/safe-int? value))
(u/display-not-valid :row value)
(nil? cell)
(u/display-not-valid :cell "cell not found")
: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 (proxy->shape self)
(let [shape (u/proxy->shape self)
cell (locate-cell self)]
(when (us/safe-int? value)
(cond
(not (us/safe-int? value))
(u/display-not-valid :rowSpan-value value)
(nil? cell)
(u/display-not-valid :rowSpan-cell "cell not found")
: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 (proxy->shape self)
(let [shape (u/proxy->shape self)
cell (locate-cell self)]
(when (us/safe-int? value)
(cond
(not (us/safe-int? value))
(u/display-not-valid :column-value value)
(nil? cell)
(u/display-not-valid :column-cell "cell not found")
: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 (proxy->shape self)
(let [shape (u/proxy->shape self)
cell (locate-cell self)]
(when (us/safe-int? value)
(cond
(not (us/safe-int? value))
(u/display-not-valid :columnSpan-value value)
(nil? cell)
(u/display-not-valid :columnSpan-cell "cell not found")
: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 (proxy->shape self)
(let [shape (u/proxy->shape self)
cell (locate-cell self)]
(when (string? value)
(cond
(not (string? value))
(u/display-not-valid :areaName-value value)
(nil? cell)
(u/display-not-valid :areaName-cell "cell not found")
: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 (proxy->shape self)
(let [shape (u/proxy->shape self)
cell (locate-cell self)
value (keyword value)]
(when (contains? ctl/grid-position-types 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")
: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 (proxy->shape self)
(let [shape (u/proxy->shape self)
value (keyword value)
cell (locate-cell self)]
(when (contains? ctl/grid-cell-align-self-types value)
(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")
: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 (proxy->shape self)
(let [shape (u/proxy->shape self)
value (keyword value)
cell (locate-cell self)]
(when (contains? ctl/grid-cell-justify-self-types value)
(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")
:else
(st/emit! (dwsl/update-grid-cells (:parent-id shape) #{(:id cell)} {:justify-self value})))))}))))

View file

@ -20,7 +20,7 @@
[app.main.data.workspace.libraries :as dwl]
[app.main.data.workspace.texts :as dwt]
[app.main.store :as st]
[app.plugins.shape :as shapes]
[app.plugins.shape :as shape]
[app.plugins.utils :as u]
[app.util.object :as obj]))
@ -157,63 +157,81 @@
:get #(-> % u/proxy->library-color :name)
:set
(fn [self value]
(if (and (some? value) (string? value))
(cond
(not (string? value))
(u/display-not-valid :library-color-name value)
: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)))
(u/display-not-valid :library-color-name value)))}
(st/emit! (dwl/rename-color file-id id value)))))}
{:name "path"
:get #(-> % u/proxy->library-color :path)
:set
(fn [self value]
(if (and (some? value) (string? value))
(cond
(not (string? value))
(u/display-not-valid :library-color-path value)
:else
(let [color (-> (u/proxy->library-color self)
(update :name #(str value " / " %)))]
(st/emit! (dwl/update-color color file-id)))
(u/display-not-valid :library-color-path value)))}
(st/emit! (dwl/update-color color file-id)))))}
{:name "color"
:get #(-> % u/proxy->library-color :color)
:set
(fn [self value]
(if (and (some? value) (string? value) (cc/valid-hex-color? value))
(cond
(or (not (string? value)) (not (cc/valid-hex-color? value)))
(u/display-not-valid :library-color-color value)
:else
(let [color (-> (u/proxy->library-color self)
(assoc :color value))]
(st/emit! (dwl/update-color color file-id)))
(u/display-not-valid :library-color-color value)))}
(st/emit! (dwl/update-color color file-id)))))}
{:name "opacity"
:get #(-> % u/proxy->library-color :opacity)
:set
(fn [self value]
(if (and (some? value) (number? value) (>= value 0) (<= value 1))
(cond
(or (not (number? value)) (< value 0) (> value 1))
(u/display-not-valid :library-color-opacity value)
:else
(let [color (-> (u/proxy->library-color self)
(assoc :opacity value))]
(st/emit! (dwl/update-color color file-id)))
(u/display-not-valid :library-color-opacity value)))}
(st/emit! (dwl/update-color color file-id)))))}
{:name "gradient"
:get #(-> % u/proxy->library-color :gradient u/to-js)
:set
(fn [self value]
(let [value (u/from-js value)]
(if (sm/fast-check! ::ctc/gradient value)
(cond
(not (sm/validate ::ctc/gradient value))
(u/display-not-valid :library-color-gradient value)
:else
(let [color (-> (u/proxy->library-color self)
(assoc :gradient value))]
(st/emit! (dwl/update-color color file-id)))
(u/display-not-valid :library-color-gradient value))))}
(st/emit! (dwl/update-color color file-id))))))}
{:name "image"
:get #(-> % u/proxy->library-color :image u/to-js)
:set
(fn [self value]
(let [value (u/from-js value)]
(if (sm/fast-check! ::ctc/image-color value)
(cond
(not (sm/validate ::ctc/image-color value))
(u/display-not-valid :library-color-image value)
:else
(let [color (-> (u/proxy->library-color self)
(assoc :image value))]
(st/emit! (dwl/update-color color file-id)))
(u/display-not-valid :library-color-image value))))}))
(st/emit! (dwl/update-color color file-id))))))}))
(deftype LibraryTypographyProxy [$plugin $file $id]
Object
@ -231,12 +249,22 @@
(applyToText
[_ shape]
(cond
(not (shape/shape-proxy? shape))
(u/display-not-valid :applyToText shape)
:else
(let [shape-id (obj/get shape "$id")
typography (u/locate-library-typography $file $id)]
(st/emit! (dwt/apply-typography #{shape-id} typography $file))))
(st/emit! (dwt/apply-typography #{shape-id} typography $file)))))
(applyToTextRange
[self range]
(cond
(not (shape/text-range? range))
(u/display-not-valid :applyToText range)
:else
(let [shape-id (obj/get range "$id")
start (obj/get range "start")
end (obj/get range "end")
@ -245,7 +273,7 @@
(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))))
(st/emit! (dwt/update-text-range shape-id start end attrs)))))
;; PLUGIN DATA
(getPluginData
@ -322,6 +350,8 @@
(defn lib-typography-proxy? [p]
(instance? LibraryTypographyProxy p))
(set! shape/lib-typography-proxy? lib-typography-proxy?)
(defn lib-typography-proxy
[plugin-id file-id id]
(assert (uuid? file-id))
@ -338,111 +368,144 @@
:get #(-> % u/proxy->library-typography :name)
:set
(fn [self value]
(if (and (some? value) (string? value))
(cond
(not (string? value))
(u/display-not-valid :library-typography-name value)
: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)))
(u/display-not-valid :library-typography-name value)))}
(st/emit! (dwl/rename-typography file-id id value)))))}
{:name "path"
:get #(-> % u/proxy->library-typography :path)
:set
(fn [self value]
(if (and (some? value) (string? value))
(cond
(not (string? value))
(u/display-not-valid :library-typography-path value)
:else
(let [typo (-> (u/proxy->library-typography self)
(update :name #(str value " / " %)))]
(st/emit! (dwl/update-typography typo file-id)))
(u/display-not-valid :library-typography-path value)))}
(st/emit! (dwl/update-typography typo file-id)))))}
{:name "fontId"
:get #(-> % u/proxy->library-typography :font-id)
:set
(fn [self value]
(if (and (some? value) (string? value))
(cond
(not (string? value))
(u/display-not-valid :library-typography-font-id value)
:else
(let [typo (-> (u/proxy->library-typography self)
(assoc :font-id value))]
(st/emit! (dwl/update-typography typo file-id)))
(u/display-not-valid :library-typography-font-id value)))}
(st/emit! (dwl/update-typography typo file-id)))))}
{:name "fontFamily"
:get #(-> % u/proxy->library-typography :font-family)
:set
(fn [self value]
(if (and (some? value) (string? value))
(cond
(not (string? value))
(u/display-not-valid :library-typography-font-family value)
:else
(let [typo (-> (u/proxy->library-typography self)
(assoc :font-family value))]
(st/emit! (dwl/update-typography typo file-id)))
(u/display-not-valid :library-typography-font-family value)))}
(st/emit! (dwl/update-typography typo file-id)))))}
{:name "fontVariantId"
:get #(-> % u/proxy->library-typography :font-variant-id)
:set
(fn [self value]
(if (and (some? value) (string? value))
(cond
(not (string? value))
(u/display-not-valid :library-typography-font-variant-id value)
:else
(let [typo (-> (u/proxy->library-typography self)
(assoc :font-variant-id value))]
(st/emit! (dwl/update-typography typo file-id)))
(u/display-not-valid :library-typography-font-variant-id value)))}
(st/emit! (dwl/update-typography typo file-id)))))}
{:name "fontSize"
:get #(-> % u/proxy->library-typography :font-size)
:set
(fn [self value]
(if (and (some? value) (string? value))
(cond
(not (string? value))
(u/display-not-valid :library-typography-font-size value)
:else
(let [typo (-> (u/proxy->library-typography self)
(assoc :font-size value))]
(st/emit! (dwl/update-typography typo file-id)))
(u/display-not-valid :library-typography-font-size value)))}
(st/emit! (dwl/update-typography typo file-id)))))}
{:name "fontWeight"
:get #(-> % u/proxy->library-typography :font-weight)
:set
(fn [self value]
(if (and (some? value) (string? value))
(cond
(not (string? value))
(u/display-not-valid :library-typography-font-weight value)
:else
(let [typo (-> (u/proxy->library-typography self)
(assoc :font-weight value))]
(st/emit! (dwl/update-typography typo file-id)))
(u/display-not-valid :library-typography-font-weight value)))}
(st/emit! (dwl/update-typography typo file-id)))))}
{:name "fontStyle"
:get #(-> % u/proxy->library-typography :font-style)
:set
(fn [self value]
(if (and (some? value) (string? value))
(cond
(not (string? value))
(u/display-not-valid :library-typography-font-style value)
:else
(let [typo (-> (u/proxy->library-typography self)
(assoc :font-style value))]
(st/emit! (dwl/update-typography typo file-id)))
(u/display-not-valid :library-typography-font-style value)))}
(st/emit! (dwl/update-typography typo file-id)))))}
{:name "lineHeight"
:get #(-> % u/proxy->library-typography :font-height)
:set
(fn [self value]
(if (and (some? value) (string? value))
(cond
(not (string? value))
(u/display-not-valid :library-typography-font-height value)
:else
(let [typo (-> (u/proxy->library-typography self)
(assoc :font-height value))]
(st/emit! (dwl/update-typography typo file-id)))
(u/display-not-valid :library-typography-font-height value)))}
(st/emit! (dwl/update-typography typo file-id)))))}
{:name "letterSpacing"
:get #(-> % u/proxy->library-typography :letter-spacing)
:set
(fn [self value]
(if (and (some? value) (string? value))
(cond
(not (string? value))
(u/display-not-valid :library-typography-letter-spacing value)
:else
(let [typo (-> (u/proxy->library-typography self)
(assoc :letter-spacing value))]
(st/emit! (dwl/update-typography typo file-id)))
(u/display-not-valid :library-typography-letter-spacing value)))}
(st/emit! (dwl/update-typography typo file-id)))))}
{:name "textTransform"
:get #(-> % u/proxy->library-typography :text-transform)
:set
(fn [self value]
(if (and (some? value) (string? value))
(cond
(not (string? value))
(u/display-not-valid :library-typography-text-transform value)
:else
(let [typo (-> (u/proxy->library-typography self)
(assoc :text-transform value))]
(st/emit! (dwl/update-typography typo file-id)))
(u/display-not-valid :library-typography-text-transform value)))}))
(st/emit! (dwl/update-typography typo file-id)))))}))
(deftype LibraryComponentProxy [$plugin $file $id]
Object
@ -455,7 +518,7 @@
[_]
(let [id-ref (atom nil)]
(st/emit! (dwl/instantiate-component $file $id (gpt/point 0 0) {:id-ref id-ref}))
(shapes/shape-proxy $plugin @id-ref)))
(shape/shape-proxy $plugin @id-ref)))
(getPluginData
[self key]
@ -547,21 +610,27 @@
:get #(-> % u/proxy->library-component :name)
:set
(fn [self value]
(if (and (some? value) (string? value))
(cond
(not (string? value))
(u/display-not-valid :library-component-name value)
:else
(let [component (u/proxy->library-component self)
value (dm/str (d/nilv (:path component) "") " / " value)]
(st/emit! (dwl/rename-component id value)))
(u/display-not-valid :library-component-name value)))}
(st/emit! (dwl/rename-component id value)))))}
{:name "path"
:get #(-> % u/proxy->library-component :path)
:set
(fn [self value]
(if (and (some? value) (string? value))
(cond
(not (string? value))
(u/display-not-valid :library-component-path value)
:else
(let [component (u/proxy->library-component self)
value (dm/str value " / " (:name component))]
(st/emit! (dwl/rename-component id value)))
(u/display-not-valid :library-component-path value)))}))
(st/emit! (dwl/rename-component id value)))))}))
(deftype Library [$plugin $id]
Object

View file

@ -21,8 +21,13 @@
Object
(getShapeById
[_ shape-id]
(cond
(not (string? shape-id))
(u/display-not-valid :getShapeById shape-id)
:else
(let [shape-id (uuid/uuid shape-id)]
(shape/shape-proxy $plugin $file $id shape-id)))
(shape/shape-proxy $plugin $file $id shape-id))))
(getRoot
[_]
@ -125,9 +130,12 @@
:get #(-> % u/proxy->page :name)
:set
(fn [_ value]
(if (string? value)
(st/emit! (dw/rename-page id value))
(u/display-not-valid :page-name value)))}
(cond
(not (string? value))
(u/display-not-valid :page-name value)
:else
(st/emit! (dw/rename-page id value))))}
{:name "root"
:enumerable false
@ -138,6 +146,9 @@
:get #(or (-> % u/proxy->page :options :background) cc/canvas)
:set
(fn [_ value]
(if (and (some? value) (string? value) (cc/valid-hex-color? value))
(st/emit! (dw/change-canvas-color id {:color value}))
(u/display-not-valid :page-background-color value)))}))
(cond
(or (not (string? value)) (not (cc/valid-hex-color? value)))
(u/display-not-valid :page-background-color value)
:else
(st/emit! (dw/change-canvas-color id {:color value}))))}))

View file

@ -9,11 +9,17 @@
(:require
[app.common.geom.rect :as grc]
[app.common.geom.shapes :as gsh]
[app.plugins.shape :as shape]
[app.plugins.utils :as u]))
(defn ^:export centerShapes
[shapes]
(cond
(not (every? shape/shape-proxy? shapes))
(u/display-not-valid :centerShapes shapes)
:else
(let [shapes (->> shapes (map u/proxy->shape))]
(-> (gsh/shapes->rect shapes)
(grc/rect->center)
(u/to-js))))
(u/to-js)))))

View file

@ -14,12 +14,18 @@
[app.common.geom.rect :as grc]
[app.common.geom.shapes :as gsh]
[app.common.record :as crc]
[app.common.schema :as sm]
[app.common.spec :as us]
[app.common.svg.path.legacy-parser2 :as spp]
[app.common.text :as txt]
[app.common.types.grid :as ctg]
[app.common.types.shape :as cts]
[app.common.types.shape.blur :as ctsb]
[app.common.types.shape.export :as ctse]
[app.common.types.shape.layout :as ctl]
[app.common.types.shape.path :as ctsp]
[app.common.types.shape.radius :as ctsr]
[app.common.types.shape.shadow :as ctss]
[app.common.uuid :as uuid]
[app.main.data.workspace :as dw]
[app.main.data.workspace.groups :as dwg]
@ -36,6 +42,7 @@
[app.util.text-editor :as ted]
[cuerdas.core :as str]))
(def lib-typography-proxy? nil)
(deftype TextRange [$plugin $file $page $id start end]
Object
@ -52,7 +59,10 @@
(let [s (set values)]
(if (= (count s) 1) (first s) "mixed")))
;; TODO Validate inputs
(defn text-range?
[range]
(instance? TextRange range))
(defn text-range
[plugin-id file-id page-id id start end]
(-> (TextRange. plugin-id file-id page-id id start end)
@ -77,7 +87,12 @@
:set
(fn [_ value]
(st/emit! (dwt/update-text-range id start end {:font-id value})))}
(cond
(not (string? value))
(u/display-not-valid :fontId value)
:else
(st/emit! (dwt/update-text-range id start end {:font-id value}))))}
{:name "fontFamily"
:get #(let [range-data
@ -86,7 +101,12 @@
:set
(fn [_ value]
(st/emit! (dwt/update-text-range id start end {:font-family value})))}
(cond
(not (string? value))
(u/display-not-valid :fontFamily value)
:else
(st/emit! (dwt/update-text-range id start end {:font-family value}))))}
{:name "fontVariantId"
:get #(let [range-data
@ -94,7 +114,12 @@
(->> range-data (map :font-variant-id) mixed-value))
:set
(fn [_ value]
(st/emit! (dwt/update-text-range id start end {:font-variant-id value})))}
(cond
(not (string? value))
(u/display-not-valid :fontVariantId value)
:else
(st/emit! (dwt/update-text-range id start end {:font-variant-id value}))))}
{:name "fontSize"
:get #(let [range-data
@ -102,7 +127,12 @@
(->> range-data (map :font-size) mixed-value))
:set
(fn [_ value]
(st/emit! (dwt/update-text-range id start end {:font-size value})))}
(cond
(not (string? value))
(u/display-not-valid :fontSize value)
:else
(st/emit! (dwt/update-text-range id start end {:font-size value}))))}
{:name "fontWeight"
:get #(let [range-data
@ -110,7 +140,12 @@
(->> range-data (map :font-weight) mixed-value))
:set
(fn [_ value]
(st/emit! (dwt/update-text-range id start end {:font-weight value})))}
(cond
(not (string? value))
(u/display-not-valid :fontWeight value)
:else
(st/emit! (dwt/update-text-range id start end {:font-weight value}))))}
{:name "fontStyle"
:get #(let [range-data
@ -118,7 +153,12 @@
(->> range-data (map :font-style) mixed-value))
:set
(fn [_ value]
(st/emit! (dwt/update-text-range id start end {:font-style value})))}
(cond
(not (string? value))
(u/display-not-valid :fontStyle value)
:else
(st/emit! (dwt/update-text-range id start end {:font-style value}))))}
{:name "lineHeight"
:get #(let [range-data
@ -126,7 +166,12 @@
(->> range-data (map :line-height) mixed-value))
:set
(fn [_ value]
(st/emit! (dwt/update-text-range id start end {:line-height value})))}
(cond
(not (string? value))
(u/display-not-valid :lineHeight value)
:else
(st/emit! (dwt/update-text-range id start end {:line-height value}))))}
{:name "letterSpacing"
:get #(let [range-data
@ -134,7 +179,12 @@
(->> range-data (map :letter-spacing) mixed-value))
:set
(fn [_ value]
(st/emit! (dwt/update-text-range id start end {:letter-spacing value})))}
(cond
(not (string? value))
(u/display-not-valid :letterSpacing value)
:else
(st/emit! (dwt/update-text-range id start end {:letter-spacing value}))))}
{:name "textTransform"
:get #(let [range-data
@ -142,7 +192,12 @@
(->> range-data (map :text-transform) mixed-value))
:set
(fn [_ value]
(st/emit! (dwt/update-text-range id start end {:text-transform value})))}
(cond
(not (string? value))
(u/display-not-valid :textTransform value)
:else
(st/emit! (dwt/update-text-range id start end {:text-transform value}))))}
{:name "textDecoration"
:get #(let [range-data
@ -150,7 +205,12 @@
(->> range-data (map :text-decoration) mixed-value))
:set
(fn [_ value]
(st/emit! (dwt/update-text-range id start end {:text-decoration value})))}
(cond
(not (string? value))
(u/display-not-valid :textDecoration value)
:else
(st/emit! (dwt/update-text-range id start end {:text-decoration value}))))}
{:name "direction"
:get #(let [range-data
@ -158,7 +218,12 @@
(->> range-data (map :direction) mixed-value))
:set
(fn [_ value]
(st/emit! (dwt/update-text-range id start end {:direction value})))}
(cond
(not (string? value))
(u/display-not-valid :direction value)
:else
(st/emit! (dwt/update-text-range id start end {:direction value}))))}
{:name "fills"
:get #(let [range-data
@ -167,7 +232,12 @@
:set
(fn [_ value]
(let [value (mapv #(u/from-js %) value)]
(st/emit! (dwt/update-text-range id start end {: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})))))})))
(declare shape-proxy)
@ -199,8 +269,16 @@
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)
:else
(st/emit! (dw/update-dimensions [$id] :width width)
(dw/update-dimensions [$id] :height height)))
(dw/update-dimensions [$id] :height height))))
(rotate
[self angle center]
@ -363,17 +441,32 @@
(getRange
[_ start end]
(let [shape (u/locate-shape $file $page $id)]
(if (cfh/text-shape? shape)
(text-range $plugin $file $page $id start end)
(u/display-not-valid :makeMask (:type shape)))))
(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-range $plugin $file $page $id start end))))
(applyTypography
[_ typography]
(let [shape (u/locate-shape $file $page $id)]
(if (cfh/text-shape? shape)
(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))
:else
(let [typography (u/proxy->library-typography typography)]
(st/emit! (dwt/apply-typography #{$id} typography $file)))
(u/display-not-valid :applyTypography (:type shape))))))
(st/emit! (dwt/apply-typography #{$id} typography $file)))))))
(crc/define-properties!
ShapeProxy
@ -383,6 +476,10 @@
(defn shape-proxy? [p]
(instance? ShapeProxy p))
;; Prevent circular dependency
(do (set! flex/shape-proxy? shape-proxy?)
(set! grid/shape-proxy? shape-proxy?))
(defn shape-proxy
([plugin-id id]
(shape-proxy plugin-id (:current-file-id @st/state) (:current-page-id @st/state) id))
@ -411,118 +508,197 @@
{:name "name"
:get #(-> % u/proxy->shape :name)
:set (fn [self value]
: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)))]
(if valid?
(st/emit! (dwsh/update-shapes [id] #(assoc % :name value)))
(u/display-not-valid :shape-name value))))}
(cond
(not valid?)
(u/display-not-valid :shape-name value)
:else
(st/emit! (dwsh/update-shapes [id] #(assoc % :name value))))))}
{:name "blocked"
:get #(-> % u/proxy->shape :blocked boolean)
:set (fn [self value]
:set
(fn [self value]
(cond
(not (boolean? value))
(u/display-not-valid :blocked value)
:else
(let [id (obj/get self "$id")]
(st/emit! (dwsh/update-shapes [id] #(assoc % :blocked value)))))}
(st/emit! (dwsh/update-shapes [id] #(assoc % :blocked value))))))}
{:name "hidden"
:get #(-> % u/proxy->shape :hidden boolean)
:set (fn [self value]
:set
(fn [self value]
(cond
(not (boolean? value))
(u/display-not-valid :hidden value)
:else
(let [id (obj/get self "$id")]
(st/emit! (dwsh/update-shapes [id] #(assoc % :hidden value)))))}
(st/emit! (dwsh/update-shapes [id] #(assoc % :hidden value))))))}
{:name "proportionLock"
:get #(-> % u/proxy->shape :proportion-lock boolean)
:set (fn [self value]
:set
(fn [self value]
(cond
(not (boolean? value))
(u/display-not-valid :proportionLock value)
:else
(let [id (obj/get self "$id")]
(st/emit! (dwsh/update-shapes [id] #(assoc % :proportion-lock value)))))}
(st/emit! (dwsh/update-shapes [id] #(assoc % :proportion-lock value))))))}
{:name "constraintsHorizontal"
:get #(-> % u/proxy->shape :constraints-h d/name)
:set (fn [self value]
:set
(fn [self value]
(let [id (obj/get self "$id")
value (keyword value)]
(when (contains? cts/horizontal-constraint-types value)
(cond
(not (contains? cts/horizontal-constraint-types value))
(u/display-not-valid :constraintsHorizontal value)
:else
(st/emit! (dwsh/update-shapes [id] #(assoc % :constraints-h value))))))}
{:name "constraintsVertical"
:get #(-> % u/proxy->shape :constraints-v d/name)
:set (fn [self value]
:set
(fn [self value]
(let [id (obj/get self "$id")
value (keyword value)]
(when (contains? cts/vertical-constraint-types value)
(cond
(not (contains? cts/vertical-constraint-types value))
(u/display-not-valid :constraintsVertical value)
:else
(st/emit! (dwsh/update-shapes [id] #(assoc % :constraints-v value))))))}
{:name "borderRadius"
:get #(-> % u/proxy->shape :rx)
:set (fn [self value]
:set
(fn [self value]
(let [id (obj/get self "$id")
shape (u/proxy->shape self)]
(when (us/safe-int? value)
(when (or (not (ctsr/has-radius? shape)) (ctsr/radius-4? shape))
(st/emit! (dwsh/update-shapes [id] ctsr/switch-to-radius-1)))
(cond
(or (not (us/safe-int? value)) (< value 0))
(u/display-not-valid :borderRadius value)
(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))))))}
{:name "borderRadiusTopLeft"
:get #(-> % u/proxy->shape :r1)
:set (fn [self value]
:set
(fn [self value]
(let [id (obj/get self "$id")
shape (u/proxy->shape self)]
(when (us/safe-int? value)
(when (or (not (ctsr/has-radius? shape)) (not (ctsr/radius-4? shape)))
(st/emit! (dwsh/update-shapes [id] ctsr/switch-to-radius-4)))
(cond
(not (us/safe-int? value))
(u/display-not-valid :borderRadiusTopLeft value)
(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))))))}
{:name "borderRadiusTopRight"
:get #(-> % u/proxy->shape :r2)
:set (fn [self value]
:set
(fn [self value]
(let [id (obj/get self "$id")
shape (u/proxy->shape self)]
(when (us/safe-int? value)
(when (or (not (ctsr/has-radius? shape)) (not (ctsr/radius-4? shape)))
(st/emit! (dwsh/update-shapes [id] ctsr/switch-to-radius-4)))
(cond
(not (us/safe-int? value))
(u/display-not-valid :borderRadiusTopRight value)
(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))))))}
{:name "borderRadiusBottomRight"
:get #(-> % u/proxy->shape :r3)
:set (fn [self value]
:set
(fn [self value]
(let [id (obj/get self "$id")
shape (u/proxy->shape self)]
(when (us/safe-int? value)
(when (or (not (ctsr/has-radius? shape)) (not (ctsr/radius-4? shape)))
(st/emit! (dwsh/update-shapes [id] ctsr/switch-to-radius-4)))
(cond
(not (us/safe-int? value))
(u/display-not-valid :borderRadiusBottomRight value)
(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))))))}
{:name "borderRadiusBottomLeft"
:get #(-> % u/proxy->shape :r4)
:set (fn [self value]
:set
(fn [self value]
(let [id (obj/get self "$id")
shape (u/proxy->shape self)]
(when (us/safe-int? value)
(when (or (not (ctsr/has-radius? shape)) (not (ctsr/radius-4? shape)))
(st/emit! (dwsh/update-shapes [id] ctsr/switch-to-radius-4)))
(cond
(not (us/safe-int? value))
(u/display-not-valid :borderRadiusBottomLeft value)
(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))))))}
{:name "opacity"
:get #(-> % u/proxy->shape :opacity)
:set (fn [self value]
:set
(fn [self value]
(let [id (obj/get self "$id")]
(when (and (us/safe-number? value) (>= value 0) (<= value 1))
(st/emit! (dwsh/update-shapes [id] #(assoc % :opacity value))))))}
{:name "blendMode"
:get #(-> % u/proxy->shape :blend-mode (d/nilv :normal) d/name)
:set (fn [self value]
:set
(fn [self value]
(let [id (obj/get self "$id")
value (keyword value)]
(when (contains? cts/blend-modes value)
(cond
(not (contains? cts/blend-modes value))
(u/display-not-valid :blendMode value)
:else
(st/emit! (dwsh/update-shapes [id] #(assoc % :blend-mode value))))))}
{:name "shadows"
:get #(-> % u/proxy->shape :shadow u/array-to-js)
:set (fn [self value]
:set
(fn [self value]
(let [id (obj/get self "$id")
value (mapv (fn [val]
;; Merge default shadow properties
@ -537,11 +713,17 @@
:hidden false}
(u/from-js val #{:style :type})))
value)]
(st/emit! (dwsh/update-shapes [id] #(assoc % :shadow value)))))}
(cond
(not (sm/validate [:vector ::ctss/shadow] value))
(u/display-not-valid :shadows value)
:else
(st/emit! (dwsh/update-shapes [id] #(assoc % :shadow value))))))}
{:name "blur"
:get #(-> % u/proxy->shape :blur u/to-js)
:set (fn [self value]
:set
(fn [self value]
(if (nil? value)
(st/emit! (dwsh/update-shapes [id] #(dissoc % :blur)))
(let [id (obj/get self "$id")
@ -552,14 +734,25 @@
:value 4
:hidden false}
(u/from-js value))]
(st/emit! (dwsh/update-shapes [id] #(assoc % :blur value))))))}
(cond
(not (sm/validate ::ctsb/blur value))
(u/display-not-valid :blur value)
:else
(st/emit! (dwsh/update-shapes [id] #(assoc % :blur value)))))))}
{:name "exports"
:get #(-> % u/proxy->shape :exports u/array-to-js)
:set (fn [self value]
:set
(fn [self value]
(let [id (obj/get self "$id")
value (mapv #(u/from-js %) value)]
(st/emit! (dwsh/update-shapes [id] #(assoc % :exports value)))))}
(cond
(not (sm/validate [:vector ::ctse/export] value))
(u/display-not-valid :exports value)
:else
(st/emit! (dwsh/update-shapes [id] #(assoc % :exports value))))))}
;; Geometry properties
{:name "x"
@ -567,14 +760,24 @@
:set
(fn [self value]
(let [id (obj/get self "$id")]
(st/emit! (dw/update-position id {:x value}))))}
(cond
(not (us/safe-number? value))
(u/display-not-valid :x value)
:else
(st/emit! (dw/update-position id {:x value})))))}
{:name "y"
:get #(-> % u/proxy->shape :y)
:set
(fn [self value]
(let [id (obj/get self "$id")]
(st/emit! (dw/update-position id {:y value}))))}
(cond
(not (us/safe-number? value))
(u/display-not-valid :y value)
:else
(st/emit! (dw/update-position id {:y value})))))}
{:name "parentX"
:get (fn [self]
@ -584,11 +787,16 @@
(- (:x shape) (:x parent))))
:set
(fn [self value]
(cond
(not (us/safe-number? value))
(u/display-not-valid :parentX value)
: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)}))))}
(st/emit! (dw/update-position id {:x (+ parent-x value)})))))}
{:name "parentY"
:get (fn [self]
@ -599,11 +807,16 @@
(- (:y shape) parent-y)))
:set
(fn [self value]
(cond
(not (us/safe-number? value))
(u/display-not-valid :parentY value)
: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)}))))}
(st/emit! (dw/update-position id {:y (+ parent-y value)})))))}
{:name "frameX"
:get (fn [self]
@ -614,11 +827,16 @@
(- (:x shape) frame-x)))
:set
(fn [self value]
(cond
(not (us/safe-number? value))
(u/display-not-valid :frameX value)
: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)}))))}
(st/emit! (dw/update-position id {:x (+ frame-x value)})))))}
{:name "frameY"
:get (fn [self]
@ -629,11 +847,16 @@
(- (:y shape) frame-y)))
:set
(fn [self value]
(cond
(not (us/safe-number? value))
(u/display-not-valid :frameY value)
: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)}))))}
(st/emit! (dw/update-position id {:y (+ frame-y value)})))))}
{:name "width"
:get #(-> % u/proxy->shape :width)}
@ -645,49 +868,70 @@
:get #(-> % u/proxy->shape :rotation)
:set
(fn [self value]
(if (number? value)
(cond
(not (number? value))
(u/display-not-valid :rotation value)
:else
(let [shape (u/proxy->shape self)]
(st/emit! (dw/increase-rotation #{(:id shape)} value)))
(u/display-not-valid :rotation value)))}
(st/emit! (dw/increase-rotation #{(:id shape)} value)))))}
{:name "flipX"
:get #(-> % u/proxy->shape :flip-x boolean)
:set
(fn [self value]
(if (boolean? value)
(cond
(not (boolean? value))
(u/display-not-valid :flipX value)
:else
(let [id (obj/get self "$id")]
(st/emit! (dw/flip-horizontal-selected #{id})))
(u/display-not-valid :flipX value)))}
(st/emit! (dw/flip-horizontal-selected #{id})))))}
{:name "flipY"
:get #(-> % u/proxy->shape :flip-y boolean)
:set
(fn [self value]
(if (boolean? value)
(cond
(not (boolean? value))
(u/display-not-valid :flipY value)
:else
(let [id (obj/get self "$id")]
(st/emit! (dw/flip-vertical-selected #{id})))
(u/display-not-valid :flipY value)))}
(st/emit! (dw/flip-vertical-selected #{id})))))}
;; Strokes and fills
;; TODO: Validate fills input
{:name "fills"
:get #(if (cfh/text-shape? data)
(-> % u/proxy->shape text-props :fills u/array-to-js)
(-> % u/proxy->shape :fills u/array-to-js))
:set (fn [self value]
:set
(fn [self value]
(let [shape (u/proxy->shape self)
id (:id shape)
value (mapv #(u/from-js %) value)]
(if (cfh/text-shape? shape)
(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}))
:else
(st/emit! (dwsh/update-shapes [id] #(assoc % :fills value))))))}
{:name "strokes"
:get #(-> % u/proxy->shape :strokes u/array-to-js)
:set (fn [self value]
:set
(fn [self value]
(let [id (obj/get self "$id")
value (mapv #(u/from-js % #{:stroke-style :stroke-alignment}) value)]
(st/emit! (dwsh/update-shapes [id] #(assoc % :strokes value)))))}
(cond
(not (sm/validate [:vector ::cts/stroke] value))
(u/display-not-valid :strokes value)
:else
(st/emit! (dwsh/update-shapes [id] #(assoc % :strokes value))))))}
{:name "layoutChild"
:get
@ -742,7 +986,12 @@
:set (fn [self value]
(let [id (obj/get self "$id")
value (mapv #(u/from-js %) value)]
(st/emit! (dwsh/update-shapes [id] #(assoc % :grids value)))))}
(cond
(not (sm/validate [:vector ::ctg/grid] value))
(u/display-not-valid :guides value)
:else
(st/emit! (dwsh/update-shapes [id] #(assoc % :grids value))))))}
{:name "horizontalSizing"
:get #(-> % u/proxy->shape :layout-item-h-sizing (d/nilv :fix) d/name)
@ -750,7 +999,11 @@
(fn [self value]
(let [id (obj/get self "$id")
value (keyword value)]
(when (contains? #{:fix :auto} value)
(cond
(not (contains? #{:fix :auto} value))
(u/display-not-valid :horizontalSizing value)
:else
(st/emit! (dwsl/update-layout #{id} {:layout-item-h-sizing value})))))}
{:name "verticalSizing"
@ -759,7 +1012,11 @@
(fn [self value]
(let [id (obj/get self "$id")
value (keyword value)]
(when (contains? #{:fix :auto} value)
(cond
(not (contains? #{:fix :auto} value))
(u/display-not-valid :verticalSizing value)
:else
(st/emit! (dwsl/update-layout #{id} {:layout-item-v-sizing value})))))})))
(cond-> (cfh/text-shape? data)
@ -771,7 +1028,11 @@
(let [id (obj/get self "$id")]
;; The user is currently editing the text. We need to update the
;; editor as well
(when (contains? (:workspace-editor-state @st/state) id)
(cond
(or (not (string? value)) (empty? value))
(u/display-not-valid :characters value)
(contains? (:workspace-editor-state @st/state) id)
(let [shape (u/proxy->shape self)
editor
(-> shape
@ -779,8 +1040,10 @@
:content
ted/import-content
ted/create-editor-state)]
(st/emit! (dwt/update-editor-state shape editor))))
(st/emit! (dwsh/update-shapes [id] #(txt/change-text % value)))))}
(st/emit! (dwt/update-editor-state shape editor)))
:else
(st/emit! (dwsh/update-shapes [id] #(txt/change-text % value))))))}
{:name "growType"
:get #(-> % u/proxy->shape :grow-type d/name)
@ -788,7 +1051,11 @@
(fn [self value]
(let [id (obj/get self "$id")
value (keyword value)]
(when (contains? #{:auto-width :auto-height :fixed} value)
(cond
(not (contains? #{:auto-width :auto-height :fixed} value))
(u/display-not-valid :growType value)
:else
(st/emit! (dwsh/update-shapes [id] #(assoc % :grow-type value))))))}
{:name "fontId"
@ -796,63 +1063,108 @@
:set
(fn [self value]
(let [id (obj/get self "$id")]
(st/emit! (dwt/update-attrs id {:font-id value}))))}
(cond
(not (string? value))
(u/display-not-valid :fontId value)
:else
(st/emit! (dwt/update-attrs id {:font-id value})))))}
{:name "fontFamily"
:get #(-> % u/proxy->shape text-props :font-family)
:set
(fn [self value]
(let [id (obj/get self "$id")]
(st/emit! (dwt/update-attrs id {:font-id value}))))}
(cond
(not (string? value))
(u/display-not-valid :fontFamily value)
:else
(st/emit! (dwt/update-attrs id {:font-family value})))))}
{:name "fontVariantId"
:get #(-> % u/proxy->shape text-props :font-variant-id)
:set
(fn [self value]
(let [id (obj/get self "$id")]
(st/emit! (dwt/update-attrs id {:font-id value}))))}
(cond
(not (string? value))
(u/display-not-valid :fontVariantId value)
:else
(st/emit! (dwt/update-attrs id {:font-variant-id value})))))}
{:name "fontSize"
:get #(-> % u/proxy->shape text-props :font-size)
:set
(fn [self value]
(let [id (obj/get self "$id")]
(st/emit! (dwt/update-attrs id {:font-size value}))))}
(cond
(not (string? value))
(u/display-not-valid :fontSize value)
:else
(st/emit! (dwt/update-attrs id {:font-size value})))))}
{:name "fontWeight"
:get #(-> % u/proxy->shape text-props :font-weight)
:set
(fn [self value]
(let [id (obj/get self "$id")]
(st/emit! (dwt/update-attrs id {:font-id value}))))}
(cond
(not (string? value))
(u/display-not-valid :fontWeight value)
:else
(st/emit! (dwt/update-attrs id {:font-weight value})))))}
{:name "fontStyle"
:get #(-> % u/proxy->shape text-props :font-style)
:set
(fn [self value]
(let [id (obj/get self "$id")]
(st/emit! (dwt/update-attrs id {:font-style value}))))}
(cond
(not (string? value))
(u/display-not-valid :fontStyle value)
:else
(st/emit! (dwt/update-attrs id {:font-style value})))))}
{:name "lineHeight"
:get #(-> % u/proxy->shape text-props :line-height)
:set
(fn [self value]
(let [id (obj/get self "$id")]
(st/emit! (dwt/update-attrs id {:line-height value}))))}
(cond
(not (string? value))
(u/display-not-valid :lineHeight value)
:else
(st/emit! (dwt/update-attrs id {:line-height value})))))}
{:name "letterSpacing"
:get #(-> % u/proxy->shape text-props :letter-spacing)
:set
(fn [self value]
(let [id (obj/get self "$id")]
(st/emit! (dwt/update-attrs id {:letter-spacing value}))))}
(cond
(not (string? value))
(u/display-not-valid :letterSpacing value)
:else
(st/emit! (dwt/update-attrs id {:letter-spacing value})))))}
{:name "textTransform"
:get #(-> % u/proxy->shape text-props :text-transform)
:set
(fn [self value]
(let [id (obj/get self "$id")]
(st/emit! (dwt/update-attrs id {:text-transform value}))))}))
(cond
(not (string? value))
(u/display-not-valid :textTransform value)
:else
(st/emit! (dwt/update-attrs id {:text-transform value})))))}))
(cond-> (or (cfh/path-shape? data) (cfh/bool-shape? data))
(crc/add-properties!
@ -864,7 +1176,12 @@
(->> value
(map u/from-js)
(mapv parse-command)
(spp/simplify-commands))
selrect (gsh/content->selrect content)
(spp/simplify-commands))]
(cond
(not (sm/validate ::ctsp/content content))
(u/display-not-valid :content value)
:else
(let [selrect (gsh/content->selrect content)
points (grc/rect->points selrect)]
(st/emit! (dwsh/update-shapes [id] (fn [shape] (assoc shape :content content :selrect selrect :points points))))))}))))))
(st/emit! (dwsh/update-shapes [id] (fn [shape] (assoc shape :content content :selrect selrect :points points))))))))}))))))

View file

@ -14,6 +14,7 @@
[app.main.data.workspace.viewport :as dwv]
[app.main.data.workspace.zoom :as dwz]
[app.main.store :as st]
[app.plugins.utils :as u]
[app.util.object :as obj]))
(deftype ViewportProxy [$plugin]
@ -51,7 +52,14 @@
(fn [_ value]
(let [new-x (obj/get value "x")
new-y (obj/get value "y")]
(when (and (us/safe-number? new-x) (us/safe-number? new-y))
(cond
(not (us/safe-number? new-x))
(u/display-not-valid :center-x new-x)
(not (us/safe-number? new-y))
(u/display-not-valid :center-y new-y)
: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))
@ -68,7 +76,11 @@
(dm/get-in @st/state [:workspace-local :zoom]))
:set
(fn [_ value]
(when (us/safe-number? 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))))))}

View file

@ -0,0 +1,260 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns frontend-tests.plugins.context-shapes-test
(:require
[app.common.math :as m]
[app.common.test-helpers.files :as cthf]
[app.common.test-helpers.ids-map :as cthi]
[app.common.test-helpers.shapes :as cths]
[app.common.uuid :as uuid]
[app.main.store :as st]
[app.plugins.api :as api]
[cljs.test :as t :include-macros true]
[frontend-tests.helpers.state :as ths]))
(t/deftest test-common-shape-properties
(let [;; ==== Setup
store
(ths/setup-store (cthf/sample-file :file1 :page-label :page1))
_ (set! st/state store)
context (api/create-context "tests")
page (. context -currentPage)
shape (.createRectangle context)
get-shape-path
#(vector :workspace-data :pages-index (aget page "$id") :objects (aget shape "$id") %)]
(t/testing "Basic shape properites"
(t/testing " - name"
(set! (.-name shape) "TEST")
(t/is (= (.-name shape) "TEST"))
(t/is (= (get-in @store (get-shape-path :name)) "TEST")))
(t/testing " - x"
(set! (.-x shape) 10)
(t/is (= (.-x shape) 10))
(t/is (= (get-in @store (get-shape-path :x)) 10))
(set! (.-x shape) "fail")
(t/is (= (.-x shape) 10))
(t/is (= (get-in @store (get-shape-path :x)) 10)))
(t/testing " - y"
(set! (.-y shape) 50)
(t/is (= (.-y shape) 50))
(t/is (= (get-in @store (get-shape-path :y)) 50))
(set! (.-y shape) "fail")
(t/is (= (.-y shape) 50))
(t/is (= (get-in @store (get-shape-path :y)) 50)))
(t/testing " - resize"
(.resize shape 250 300)
(t/is (= (.-width shape) 250))
(t/is (= (.-height shape) 300))
(t/is (= (get-in @store (get-shape-path :width)) 250))
(t/is (= (get-in @store (get-shape-path :height)) 300))
(.resize shape 0 0)
(t/is (= (.-width shape) 250))
(t/is (= (.-height shape) 300))
(t/is (= (get-in @store (get-shape-path :width)) 250))
(t/is (= (get-in @store (get-shape-path :height)) 300)))
(t/testing " - blocked"
(set! (.-blocked shape) true)
(t/is (= (.-blocked shape) true))
(t/is (= (get-in @store (get-shape-path :blocked)) true))
(set! (.-blocked shape) false)
(t/is (= (.-blocked shape) false))
(t/is (= (get-in @store (get-shape-path :blocked)) false)))
(t/testing " - hidden"
(set! (.-hidden shape) true)
(t/is (= (.-hidden shape) true))
(t/is (= (get-in @store (get-shape-path :hidden)) true))
(set! (.-hidden shape) false)
(t/is (= (.-hidden shape) false))
(t/is (= (get-in @store (get-shape-path :hidden)) false)))
(t/testing " - proportionLock"
(set! (.-proportionLock shape) true)
(t/is (= (.-proportionLock shape) true))
(t/is (= (get-in @store (get-shape-path :proportion-lock)) true)))
(t/testing " - constraintsHorizontal"
(set! (.-constraintsHorizontal shape) "fail")
(t/is (not= (.-constraintsHorizontal shape) "fail"))
(t/is (not= (get-in @store (get-shape-path :constraints-h)) "fail"))
(set! (.-constraintsHorizontal shape) "right")
(t/is (= (.-constraintsHorizontal shape) "right"))
(t/is (= (get-in @store (get-shape-path :constraints-h)) :right)))
(t/testing " - constraintsVertical"
(set! (.-constraintsVertical shape) "fail")
(t/is (not= (.-constraintsVertical shape) "fail"))
(t/is (not= (get-in @store (get-shape-path :constraints-v)) "fail"))
(set! (.-constraintsVertical shape) "bottom")
(t/is (= (.-constraintsVertical shape) "bottom"))
(t/is (= (get-in @store (get-shape-path :constraints-v)) :bottom)))
(t/testing " - borderRadius"
(set! (.-borderRadius shape) 10)
(t/is (= (.-borderRadius shape) 10))
(t/is (= (get-in @store (get-shape-path :rx)) 10))
(set! (.-borderRadiusTopLeft shape) 20)
(t/is (= (.-borderRadiusTopLeft shape) 20))
(t/is (= (get-in @store (get-shape-path :rx)) nil))
(t/is (= (get-in @store (get-shape-path :r1)) 20))
(t/is (= (get-in @store (get-shape-path :r2)) 10))
(t/is (= (get-in @store (get-shape-path :r3)) 10))
(t/is (= (get-in @store (get-shape-path :r4)) 10))
(set! (.-borderRadiusTopRight shape) 30)
(set! (.-borderRadiusBottomRight shape) 40)
(set! (.-borderRadiusBottomLeft shape) 50)
(t/is (= (.-borderRadiusTopRight shape) 30))
(t/is (= (.-borderRadiusBottomRight shape) 40))
(t/is (= (.-borderRadiusBottomLeft shape) 50))
(t/is (= (get-in @store (get-shape-path :rx)) nil))
(t/is (= (get-in @store (get-shape-path :r1)) 20))
(t/is (= (get-in @store (get-shape-path :r2)) 30))
(t/is (= (get-in @store (get-shape-path :r3)) 40))
(t/is (= (get-in @store (get-shape-path :r4)) 50)))
(t/testing " - opacity"
(set! (.-opacity shape) 0.5)
(t/is (= (.-opacity shape) 0.5))
(t/is (= (get-in @store (get-shape-path :opacity)) 0.5)))
(t/testing " - blendMode"
(set! (.-blendMode shape) "multiply")
(t/is (= (.-blendMode shape) "multiply"))
(t/is (= (get-in @store (get-shape-path :blend-mode)) :multiply))
(set! (.-blendMode shape) "fail")
(t/is (= (.-blendMode shape) "multiply"))
(t/is (= (get-in @store (get-shape-path :blend-mode)) :multiply)))
(t/testing " - shadows"
(let [shadow #js {:style "drop-shadow"
:color #js {:color "#FABADA" :opacity 1}}]
(set! (.-shadows shape) #js [shadow])
(let [shadow-id (uuid/uuid (aget (aget (aget shape "shadows") 0) "id"))]
(t/is (= (-> (. shape -shadows) (aget 0) (aget "style")) "drop-shadow"))
(t/is (= (get-in @store (get-shape-path :shadow)) [{:id shadow-id
:style :drop-shadow
:offset-x 4
:offset-y 4
:blur 4
:spread 0
:color {:color "#FABADA" :opacity 1}
:hidden false}]))))
(let [shadow #js {:style "fail"}]
(set! (.-shadows shape) #js [shadow])
(t/is (= (-> (. shape -shadows) (aget 0) (aget "style")) "drop-shadow"))))
(t/testing " - blur"
(set! (.-blur shape) #js {:value 10})
(t/is (= (-> (. shape -blur) (aget "type")) "layer-blur"))
(t/is (= (-> (. shape -blur) (aget "value")) 10))
(t/is (= (-> (. shape -blur) (aget "hidden")) false))
(let [id (-> (. shape -blur) (aget "id") uuid/uuid)]
(t/is (= (get-in @store (get-shape-path :blur)) {:id id :type :layer-blur :value 10 :hidden false}))))
(t/testing " - exports"
(set! (.-exports shape) #js [#js {:type "pdf" :scale 2 :suffix "test"}])
(t/is (= (-> (. shape -exports) (aget 0) (aget "type")) "pdf"))
(t/is (= (-> (. shape -exports) (aget 0) (aget "scale")) 2))
(t/is (= (-> (. shape -exports) (aget 0) (aget "suffix")) "test"))
(t/is (= (get-in @store (get-shape-path :exports)) [{:type :pdf :scale 2 :suffix "test"}]))
(set! (.-exports shape) #js [#js {:type 10 :scale 2 :suffix "test"}])
(t/is (= (get-in @store (get-shape-path :exports)) [{:type :pdf :scale 2 :suffix "test"}])))
(t/testing " - flipX"
(set! (.-flipX shape) true)
(t/is (= (.-flipX shape) true))
(t/is (= (get-in @store (get-shape-path :flip-x)) true)))
(t/testing " - flipY"
(set! (.-flipY shape) true)
(t/is (= (.-flipY shape) true))
(t/is (= (get-in @store (get-shape-path :flip-y)) true)))
(t/testing " - rotation"
(set! (.-rotation shape) 45)
(t/is (= (.-rotation shape) 45))
(t/is (= (get-in @store (get-shape-path :rotation)) 45))
(set! (.-rotation shape) 0)
(t/is (= (.-rotation shape) 0))
(t/is (= (get-in @store (get-shape-path :rotation)) 0)))
(t/testing " - fills"
(set! (.-fills shape) #js [#js {:fillColor 100}])
(t/is (= (-> (. shape -fills) (aget 0) (aget "fillColor")) "#B1B2B5"))
(t/is (= (get-in @store (get-shape-path :fills)) [{:fill-color "#B1B2B5" :fill-opacity 1}]))
(set! (.-fills shape) #js [#js {:fillColor "#FABADA" :fillOpacity 1}])
(t/is (= (-> (. shape -fills) (aget 0) (aget "fillColor")) "#FABADA"))
(t/is (= (-> (. shape -fills) (aget 0) (aget "fillOpacity")) 1))
(t/is (= (get-in @store (get-shape-path :fills)) [{:fill-color "#FABADA" :fill-opacity 1}])))
(t/testing " - strokes"
(set! (.-fills shape) #js [#js {:strokeColor "#FABADA" :strokeOpacity 1 :stroke-width 5}])
(t/is (= (-> (. shape -fills) (aget 0) (aget "strokeColor")) "#FABADA"))
(t/is (= (-> (. shape -fills) (aget 0) (aget "strokeOpacity")) 1))
(t/is (= (-> (. shape -fills) (aget 0) (aget "strokeWidth")) 5))
(t/is (= (get-in @store (get-shape-path :fills)) [{:stroke-color "#FABADA" :stroke-opacity 1 :stroke-width 5}]))))
(t/testing "Relative properties"
(let [frame (.createFrame context)]
(set! (.-x frame) 100)
(set! (.-y frame) 200)
(t/is (= (.-x frame) 100))
(t/is (= (.-y frame) 200))
(.appendChild frame shape)
(t/testing " - frameX"
(set! (.-frameX shape) 10)
(t/is (m/close? (.-frameX shape) 10))
(t/is (m/close? (.-x shape) 110))
(t/is (m/close? (get-in @store (get-shape-path :x)) 110)))
(t/testing " - frameY"
(set! (.-frameY shape) 20)
(t/is (m/close? (.-frameY shape) 20))
(t/is (m/close? (.-y shape) 220))
(t/is (m/close? (get-in @store (get-shape-path :y)) 220)))
(t/testing " - parentX"
(set! (.-parentX shape) 30)
(t/is (m/close? (.-parentX shape) 30))
(t/is (m/close? (.-x shape) 130))
(t/is (m/close? (get-in @store (get-shape-path :x)) 130)))
(t/testing " - parentY"
(set! (.-parentY shape) 40)
(t/is (m/close? (.-parentY shape) 40))
(t/is (m/close? (.-y shape) 240))
(t/is (m/close? (get-in @store (get-shape-path :y)) 240)))))
(t/testing "Clone")
(t/testing "Remove")))