Merge branch 'staging' into develop

This commit is contained in:
Andrey Antukh 2024-11-27 08:33:16 +01:00
commit 25a2d3b1fb
28 changed files with 4750 additions and 4411 deletions

View file

@ -409,12 +409,14 @@
;; Resize parent containers that need to ;; Resize parent containers that need to
(pcb/resize-parents parents)))) (pcb/resize-parents parents))))
(defn change-show-in-viewer [shape hide?] (defn change-show-in-viewer
[shape hide?]
(assoc shape :hide-in-viewer hide?)) (assoc shape :hide-in-viewer hide?))
(defn add-new-interaction [shape interaction] (defn add-new-interaction
(-> shape [shape interaction]
(update :interactions ctsi/add-interaction interaction))) (update shape :interactions ctsi/add-interaction interaction))
(defn show-in-viewer [shape] (defn show-in-viewer
[shape]
(dissoc shape :hide-in-viewer)) (dissoc shape :hide-in-viewer))

View file

@ -43,7 +43,9 @@
{:extra-paths ["dev"] {:extra-paths ["dev"]
:extra-deps :extra-deps
{thheller/shadow-cljs {:mvn/version "2.28.18"} {thheller/shadow-cljs {:mvn/version "2.28.18"}
com.bhauman/rebel-readline {:mvn/version "RELEASE"}
org.clojure/tools.namespace {:mvn/version "RELEASE"} org.clojure/tools.namespace {:mvn/version "RELEASE"}
criterium/criterium {:mvn/version "RELEASE"}
cider/cider-nrepl {:mvn/version "0.48.0"}}} cider/cider-nrepl {:mvn/version "0.48.0"}}}
:shadow-cljs :shadow-cljs

34
frontend/dev/user.clj Normal file
View file

@ -0,0 +1,34 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns user
(:require
[app.common.data :as d]
[app.common.pprint :as pp]
[clojure.java.io :as io]
[clojure.tools.namespace.repl :as repl]
[clojure.pprint :refer [pprint print-table]]
[clojure.repl :refer :all]
[clojure.walk :refer [macroexpand-all]]
[criterium.core :as crit]))
;; --- Benchmarking Tools
(defmacro run-quick-bench
[& exprs]
`(crit/with-progress-reporting (crit/quick-bench (do ~@exprs) :verbose)))
(defmacro run-quick-bench'
[& exprs]
`(crit/quick-bench (do ~@exprs)))
(defmacro run-bench
[& exprs]
`(crit/with-progress-reporting (crit/bench (do ~@exprs) :verbose)))
(defmacro run-bench'
[& exprs]
`(crit/bench (do ~@exprs)))

6
frontend/scripts/repl Executable file
View file

@ -0,0 +1,6 @@
#!/usr/bin/env bash
export OPTIONS="-A:dev -J-XX:-OmitStackTraceInFastThrow";
set -ex
exec clojure $OPTIONS -M -m rebel-readline.main

View file

@ -7,6 +7,7 @@
(ns app.main.data.exports.assets (ns app.main.data.exports.assets
(:require (:require
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.main.data.events :as ev]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.persistence :as dwp] [app.main.data.persistence :as dwp]
[app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
@ -247,6 +248,12 @@
(rx/map #(clear-export-state @resource-id)) (rx/map #(clear-export-state @resource-id))
(rx/take-until (rx/delay 6000 stopper)))))))) (rx/take-until (rx/delay 6000 stopper))))))))
(defn request-export
[{:keys [exports] :as params}]
(if (= 1 (count exports))
(request-simple-export (assoc params :export (first exports)))
(request-multiple-export params)))
(defn retry-last-export (defn retry-last-export
[] []
(ptk/reify ::retry-last-export (ptk/reify ::retry-last-export
@ -256,3 +263,16 @@
(when (seq params) (when (seq params)
(rx/of (request-multiple-export params))))))) (rx/of (request-multiple-export params)))))))
(defn export-shapes-event
[exports origin]
(let [types (reduce (fn [counts {:keys [type]}]
(if (#{:png :pdf :svg :jpeg} type)
(update counts type inc)
counts))
{:png 0, :pdf 0, :svg 0, :jpeg 0}
exports)]
(ptk/event
::ev/event (merge types
{::ev/name "export-shapes"
::ev/origin origin
:num-shapes (count exports)}))))

View file

@ -168,7 +168,7 @@
objects (get page :objects) objects (get page :objects)
frame (cfh/get-root-frame objects (:id shape)) frame (cfh/get-root-frame objects (:id shape))
flows (get page :objects) flows (get page :flows)
flow (ctp/get-frame-flow flows (:id frame))] flow (ctp/get-frame-flow flows (:id frame))]
(rx/concat (rx/concat
(rx/of (dwsh/update-shapes (rx/of (dwsh/update-shapes
@ -177,14 +177,14 @@
(let [new-interaction (-> ctsi/default-interaction (let [new-interaction (-> ctsi/default-interaction
(ctsi/set-destination destination) (ctsi/set-destination destination)
(assoc :position-relative-to (:id shape)))] (assoc :position-relative-to (:id shape)))]
(cls/add-new-interaction shape new-interaction)))) (cls/add-new-interaction shape new-interaction)))))
(when destination (when destination
(dwsh/update-shapes [destination] cls/show-in-viewer)) (rx/of (dwsh/update-shapes [destination] cls/show-in-viewer)))
(when (and (not (connected-frame? objects (:id frame))) (when (and (not (connected-frame? objects (:id frame)))
(nil? flow)) (nil? flow))
(add-flow (:id frame)))))))))) (rx/of (add-flow (:id frame))))))))))
(defn remove-interaction (defn remove-interaction
([shape index] ([shape index]

View file

@ -12,7 +12,6 @@
[app.common.colors :as clr] [app.common.colors :as clr]
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.main.data.events :as ev]
[app.main.data.exports.assets :as de] [app.main.data.exports.assets :as de]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.refs :as refs] [app.main.refs :as refs]
@ -23,7 +22,6 @@
[app.util.i18n :as i18n :refer [tr c]] [app.util.i18n :as i18n :refer [tr c]]
[app.util.strings :as ust] [app.util.strings :as ust]
[cuerdas.core :as str] [cuerdas.core :as str]
[potok.v2.core :as ptk]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(def ^:private neutral-icon (def ^:private neutral-icon
@ -59,13 +57,8 @@
(fn [event] (fn [event]
(dom/prevent-default event) (dom/prevent-default event)
(st/emit! (modal/hide) (st/emit! (modal/hide)
(de/request-multiple-export (de/request-multiple-export {:exports enabled-exports :cmd cmd})
{:exports enabled-exports (de/export-shapes-event enabled-exports origin)))
:cmd cmd})
(ptk/event
::ev/event {::ev/name "export-shapes"
::ev/origin origin
:num-shapes (count enabled-exports)})))
on-toggle-enabled on-toggle-enabled
(mf/use-fn (mf/use-fn

View file

@ -8,7 +8,6 @@
(:require-macros [app.main.style :as stl]) (:require-macros [app.main.style :as stl])
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.main.data.events :as ev]
[app.main.data.exports.assets :as de] [app.main.data.exports.assets :as de]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.store :as st] [app.main.store :as st]
@ -18,7 +17,6 @@
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :refer [tr c]] [app.util.i18n :refer [tr c]]
[app.util.keyboard :as kbd] [app.util.keyboard :as kbd]
[potok.v2.core :as ptk]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(mf/defc exports (mf/defc exports
@ -63,15 +61,9 @@
:object-id (-> shapes first :id)} :object-id (-> shapes first :id)}
(cond-> share-id (assoc :share-id share-id))) (cond-> share-id (assoc :share-id share-id)))
exports (mapv #(merge % defaults) @exports)] exports (mapv #(merge % defaults) @exports)]
(if (= 1 (count exports))
(st/emit! (st/emit!
(de/request-simple-export {:export (first exports)}) (de/request-export {:exports exports})
(ptk/event (de/export-shapes-event exports "viewer")))))
::ev/event {::ev/name "export-shapes" ::ev/origin "viewer" :num-shapes 1}))
(st/emit!
(de/request-multiple-export {:exports exports})
(ptk/event
::ev/event {::ev/name "export-shapes" ::ev/origin "viewer" :num-shapes (count exports)}))))))
add-export add-export
(mf/use-callback (mf/use-callback

View file

@ -8,7 +8,6 @@
(:require-macros [app.main.style :as stl]) (:require-macros [app.main.style :as stl])
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.main.data.events :as ev]
[app.main.data.exports.assets :as de] [app.main.data.exports.assets :as de]
[app.main.data.workspace.shapes :as dwsh] [app.main.data.workspace.shapes :as dwsh]
[app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
@ -21,7 +20,6 @@
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :refer [tr c]] [app.util.i18n :refer [tr c]]
[app.util.keyboard :as kbd] [app.util.keyboard :as kbd]
[potok.v2.core :as ptk]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(def exports-attrs (def exports-attrs
@ -68,19 +66,18 @@
(if (and (= 1 (count shapes-with-exports)) (= 1 (-> shapes-with-exports first :exports count))) (if (and (= 1 (count shapes-with-exports)) (= 1 (-> shapes-with-exports first :exports count)))
(let [shape (-> shapes-with-exports first) (let [shape (-> shapes-with-exports first)
export (-> shape :exports first) export (-> shape :exports first)
sname (:name shape)
suffix (:suffix export) suffix (:suffix export)
sname (cond-> (:name shape)
(some? suffix)
(str suffix))
defaults {:page-id page-id defaults {:page-id page-id
:file-id file-id :file-id file-id
:name sname :name sname
:object-id (:id (first shapes-with-exports))}] :object-id (:id (first shapes-with-exports))}
(cond-> sname full-export (merge export defaults)]
(some? suffix)
(str suffix))
(st/emit! (st/emit!
(de/request-simple-export {:export (merge export defaults)}) (de/request-simple-export {:export full-export})
(ptk/event (de/export-shapes-event [full-export] "workspace:sidebar")))
::ev/event {::ev/name "export-shapes" ::ev/origin "workspace:sidebar" :num-shapes 1})))
(st/emit! (st/emit!
(de/show-workspace-export-dialog {:selected (reverse ids) :origin "workspace:sidebar"}))) (de/show-workspace-export-dialog {:selected (reverse ids) :origin "workspace:sidebar"})))
@ -92,16 +89,11 @@
:name sname :name sname
:object-id (first ids)} :object-id (first ids)}
exports (mapv #(merge % defaults) exports)] exports (mapv #(merge % defaults) exports)]
(if (= 1 (count exports))
(let [export (first exports)]
(st/emit! (st/emit!
(de/request-simple-export {:export export}) (de/request-export {:exports exports})
(ptk/event (de/export-shapes-event exports "workspace:sidebar"))))))
::ev/event {::ev/name "export-shapes" ::ev/origin "workspace:sidebar" :num-shapes 1})))
(st/emit!
(de/request-multiple-export {:exports exports})
(ptk/event
::ev/event {::ev/name "export-shapes" ::ev/origin "workspace:sidebar" :num-shapes (count exports)})))))))
;; TODO: maybe move to specific events for avoid to have this logic here? ;; TODO: maybe move to specific events for avoid to have this logic here?
add-export add-export

View file

@ -12,7 +12,6 @@
[app.common.files.changes-builder :as cb] [app.common.files.changes-builder :as cb]
[app.common.files.helpers :as cfh] [app.common.files.helpers :as cfh]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.record :as cr]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.text :as txt] [app.common.text :as txt]
[app.common.types.color :as ctc] [app.common.types.color :as ctc]
@ -59,39 +58,96 @@
(st/emit! (ch/commit-changes changes)) (st/emit! (ch/commit-changes changes))
(shape/shape-proxy plugin-id (:id shape)))) (shape/shape-proxy plugin-id (:id shape))))
(deftype PenpotContext [$plugin] (defn create-context
Object [plugin-id]
(addListener (obj/reify {:name "PenpotContext"}
[_ type callback props] ;; Private properties
(events/add-listener type $plugin callback props)) :$plugin {:enumerable false :get (fn [] plugin-id)}
(removeListener ;; Public properties
[_ listener-id] :root
{:this true
:get #(.getRoot ^js %)}
:currentFile
{:this true
:get #(.getFile ^js %)}
:currentPage
{:this true
:get #(.getPage ^js %)}
:theme
{:this true
:get #(.getTheme ^js %)}
: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)
:else
(let [ids (into (d/ordered-set) (map #(obj/get % "$id")) shapes)]
(st/emit! (dws/select-shapes ids)))))}
:viewport
{:this true
:get #(.getViewport ^js %)}
:currentUser
{:this true
:get #(.getCurrentUser ^js %)}
:activeUsers
{:this true
:get #(.getActiveUsers ^js %)}
: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)) (events/remove-listener listener-id))
(getViewport :getViewport
[_] (fn []
(viewport/viewport-proxy $plugin)) (viewport/viewport-proxy plugin-id))
(getFile :getFile
[_] (fn []
(when (some? (:current-file-id @st/state)) (when (some? (:current-file-id @st/state))
(file/file-proxy $plugin (:current-file-id @st/state)))) (file/file-proxy plugin-id (:current-file-id @st/state))))
(getPage :getPage
[_] (fn []
(let [file-id (:current-file-id @st/state) (let [file-id (:current-file-id @st/state)
page-id (:current-page-id @st/state)] page-id (:current-page-id @st/state)]
(when (and (some? file-id) (some? page-id)) (when (and (some? file-id) (some? page-id))
(page/page-proxy $plugin file-id page-id)))) (page/page-proxy plugin-id file-id page-id))))
(getSelectedShapes :getSelectedShapes
[_] (fn []
(let [selection (get-in @st/state [:workspace-local :selected])] (let [selection (get-in @st/state [:workspace-local :selected])]
(apply array (sequence (map (partial shape/shape-proxy $plugin)) selection)))) (apply array (sequence (map (partial shape/shape-proxy plugin-id)) selection))))
(shapesColors :shapesColors
[_ shapes] (fn [shapes]
(cond (cond
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes))) (or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
(u/display-not-valid :shapesColors-shapes shapes) (u/display-not-valid :shapesColors-shapes shapes)
@ -108,9 +164,8 @@
(group-by :attrs) (group-by :attrs)
(format/format-array format/format-color-result))))) (format/format-array format/format-color-result)))))
(replaceColor :replaceColor
[_ shapes old-color new-color] (fn [shapes old-color new-color]
(let [old-color (parser/parse-color old-color) (let [old-color (parser/parse-color old-color)
new-color (parser/parse-color new-color)] new-color (parser/parse-color new-color)]
(cond (cond
@ -137,33 +192,33 @@
(group-by :attrs))] (group-by :attrs))]
(st/emit! (dwc/change-color-in-selected new-color (get shapes-by-color old-color) old-color)))))) (st/emit! (dwc/change-color-in-selected new-color (get shapes-by-color old-color) old-color))))))
(getRoot :getRoot
[_] (fn []
(when (and (some? (:current-file-id @st/state)) (when (and (some? (:current-file-id @st/state))
(some? (:current-page-id @st/state))) (some? (:current-page-id @st/state)))
(shape/shape-proxy $plugin uuid/zero))) (shape/shape-proxy plugin-id uuid/zero)))
(getTheme :getTheme
[_] (fn []
(let [theme (get-in @st/state [:profile :theme])] (let [theme (get-in @st/state [:profile :theme])]
(if (or (not theme) (= theme "default")) (if (or (not theme) (= theme "default"))
"dark" "dark"
(get-in @st/state [:profile :theme])))) (get-in @st/state [:profile :theme]))))
(getCurrentUser :getCurrentUser
[_] (fn []
(user/current-user-proxy $plugin (:session-id @st/state))) (user/current-user-proxy plugin-id (:session-id @st/state)))
(getActiveUsers :getActiveUsers
[_] (fn []
(apply array (apply array
(->> (:workspace-presence @st/state) (->> (:workspace-presence @st/state)
(vals) (vals)
(remove #(= (:id %) (:session-id @st/state))) (remove #(= (:id %) (:session-id @st/state)))
(map #(user/active-user-proxy $plugin (:id %)))))) (map #(user/active-user-proxy plugin-id (:id %))))))
(uploadMediaUrl :uploadMediaUrl
[_ name url] (fn [name url]
(cond (cond
(not (string? name)) (not (string? name))
(u/display-not-valid :uploadMedia-name name) (u/display-not-valid :uploadMedia-name name)
@ -180,8 +235,8 @@
(rx/map format/format-image) (rx/map format/format-image)
(rx/subs! resolve reject))))))) (rx/subs! resolve reject)))))))
(uploadMediaData :uploadMediaData
[_ name data mime-type] (fn [name data mime-type]
(let [file-id (:current-file-id @st/state)] (let [file-id (:current-file-id @st/state)]
(js/Promise. (js/Promise.
(fn [resolve reject] (fn [resolve reject]
@ -196,8 +251,8 @@
(rx/map format/format-image) (rx/map format/format-image)
(rx/subs! resolve reject)))))) (rx/subs! resolve reject))))))
(group :group
[_ shapes] (fn [shapes]
(cond (cond
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes))) (or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
(u/display-not-valid :group-shapes shapes) (u/display-not-valid :group-shapes shapes)
@ -208,11 +263,10 @@
id (uuid/next) id (uuid/next)
ids (into #{} (map #(obj/get % "$id")) shapes)] ids (into #{} (map #(obj/get % "$id")) shapes)]
(st/emit! (dwg/group-shapes id ids)) (st/emit! (dwg/group-shapes id ids))
(shape/shape-proxy $plugin file-id page-id id)))) (shape/shape-proxy plugin-id file-id page-id id))))
(ungroup
[_ group & rest]
:ungroup
(fn [group & rest]
(cond (cond
(not (shape/shape-proxy? group)) (not (shape/shape-proxy? group))
(u/display-not-valid :ungroup group) (u/display-not-valid :ungroup group)
@ -225,20 +279,20 @@
ids (into #{} (map #(obj/get % "$id")) shapes)] ids (into #{} (map #(obj/get % "$id")) shapes)]
(st/emit! (dwg/ungroup-shapes ids))))) (st/emit! (dwg/ungroup-shapes ids)))))
(createBoard :createBoard
[_] (fn []
(create-shape $plugin :frame)) (create-shape plugin-id :frame))
(createRectangle :createRectangle
[_] (fn []
(create-shape $plugin :rect)) (create-shape plugin-id :rect))
(createEllipse :createEllipse
[_] (fn []
(create-shape $plugin :circle)) (create-shape plugin-id :circle))
(createPath :createPath
[_] (fn []
(let [page-id (:current-page-id @st/state) (let [page-id (:current-page-id @st/state)
page (dm/get-in @st/state [:workspace-data :pages-index page-id]) page (dm/get-in @st/state [:workspace-data :pages-index page-id])
shape (cts/setup-shape shape (cts/setup-shape
@ -251,10 +305,10 @@
(cb/with-objects (:objects page)) (cb/with-objects (:objects page))
(cb/add-object shape))] (cb/add-object shape))]
(st/emit! (ch/commit-changes changes)) (st/emit! (ch/commit-changes changes))
(shape/shape-proxy $plugin (:id shape)))) (shape/shape-proxy plugin-id (:id shape))))
(createText :createText
[_ text] (fn [text]
(cond (cond
(or (not (string? text)) (empty? text)) (or (not (string? text)) (empty? text))
(u/display-not-valid :createText text) (u/display-not-valid :createText text)
@ -272,10 +326,10 @@
(cb/with-objects (:objects page)) (cb/with-objects (:objects page))
(cb/add-object shape))] (cb/add-object shape))]
(st/emit! (ch/commit-changes changes)) (st/emit! (ch/commit-changes changes))
(shape/shape-proxy $plugin file-id page-id (:id shape))))) (shape/shape-proxy plugin-id file-id page-id (:id shape)))))
(createShapeFromSvg :createShapeFromSvg
[_ svg-string] (fn [svg-string]
(cond (cond
(or (not (string? svg-string)) (empty? svg-string)) (or (not (string? svg-string)) (empty? svg-string))
(u/display-not-valid :createShapeFromSvg svg-string) (u/display-not-valid :createShapeFromSvg svg-string)
@ -285,9 +339,10 @@
file-id (:current-file-id @st/state) file-id (:current-file-id @st/state)
page-id (:current-page-id @st/state)] page-id (:current-page-id @st/state)]
(st/emit! (dwm/create-svg-shape id "svg" svg-string (gpt/point 0 0))) (st/emit! (dwm/create-svg-shape id "svg" svg-string (gpt/point 0 0)))
(shape/shape-proxy $plugin file-id page-id id)))) (shape/shape-proxy plugin-id file-id page-id id))))
(createBoolean [_ bool-type shapes] :createBoolean
(fn [bool-type shapes]
(let [bool-type (keyword bool-type)] (let [bool-type (keyword bool-type)]
(cond (cond
(not (contains? cts/bool-types bool-type)) (not (contains? cts/bool-types bool-type))
@ -300,10 +355,10 @@
(let [ids (into #{} (map #(obj/get % "$id")) shapes) (let [ids (into #{} (map #(obj/get % "$id")) shapes)
id-ret (atom nil)] id-ret (atom nil)]
(st/emit! (dwb/create-bool bool-type ids {:id-ret id-ret})) (st/emit! (dwb/create-bool bool-type ids {:id-ret id-ret}))
(shape/shape-proxy $plugin @id-ret))))) (shape/shape-proxy plugin-id @id-ret)))))
(generateMarkup :generateMarkup
[_ shapes options] (fn [shapes options]
(let [type (d/nilv (obj/get options "type") "html")] (let [type (d/nilv (obj/get options "type") "html")]
(cond (cond
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes))) (or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
@ -317,8 +372,8 @@
shapes (into [] (map u/proxy->shape) shapes)] shapes (into [] (map u/proxy->shape) shapes)]
(cg/generate-markup-code objects type shapes))))) (cg/generate-markup-code objects type shapes)))))
(generateStyle :generateStyle
[_ shapes options] (fn [shapes options]
(let [type (d/nilv (obj/get options "type") "css") (let [type (d/nilv (obj/get options "type") "css")
prelude? (d/nilv (obj/get options "withPrelude") false) prelude? (d/nilv (obj/get options "withPrelude") false)
children? (d/nilv (obj/get options "includeChildren") true)] children? (d/nilv (obj/get options "includeChildren") true)]
@ -349,27 +404,26 @@
(cg/generate-style-code (cg/generate-style-code
objects type shapes shapes-with-children {:with-prelude? prelude?}))))) objects type shapes shapes-with-children {:with-prelude? prelude?})))))
(openViewer :openViewer
[_] (fn []
(let [params {:page-id (:current-page-id @st/state) (let [params {:page-id (:current-page-id @st/state)
:file-id (:current-file-id @st/state) :file-id (:current-file-id @st/state)
:section "interactions"}] :section "interactions"}]
(st/emit! (dw/go-to-viewer params)))) (st/emit! (dw/go-to-viewer params))))
(createPage :createPage
[_] (fn []
(let [file-id (:current-file-id @st/state) (let [file-id (:current-file-id @st/state)
id (uuid/next)] id (uuid/next)]
(st/emit! (dw/create-page {:page-id id :file-id file-id})) (st/emit! (dw/create-page {:page-id id :file-id file-id}))
(page/page-proxy $plugin file-id id))) (page/page-proxy plugin-id file-id id)))
:openPage
(openPage (fn [page]
[_ page]
(let [id (obj/get page "$id")] (let [id (obj/get page "$id")]
(st/emit! (dw/go-to-page id)))) (st/emit! (dw/go-to-page id))))
(alignHorizontal :alignHorizontal
[_ shapes direction] (fn [shapes direction]
(let [dir (case direction (let [dir (case direction
"left" :hleft "left" :hleft
"center" :hcenter "center" :hcenter
@ -386,8 +440,8 @@
(let [ids (into #{} (map #(obj/get % "$id")) shapes)] (let [ids (into #{} (map #(obj/get % "$id")) shapes)]
(st/emit! (dw/align-objects dir ids)))))) (st/emit! (dw/align-objects dir ids))))))
(alignVertical :alignVertical
[_ shapes direction] (fn [shapes direction]
(let [dir (case direction (let [dir (case direction
"top" :vtop "top" :vtop
"center" :vcenter "center" :vcenter
@ -404,8 +458,8 @@
(let [ids (into #{} (map #(obj/get % "$id")) shapes)] (let [ids (into #{} (map #(obj/get % "$id")) shapes)]
(st/emit! (dw/align-objects dir ids)))))) (st/emit! (dw/align-objects dir ids))))))
(distributeHorizontal :distributeHorizontal
[_ shapes] (fn [shapes]
(cond (cond
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes))) (or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
(u/display-not-valid :distributeHorizontal-shapes "Not valid shapes") (u/display-not-valid :distributeHorizontal-shapes "Not valid shapes")
@ -414,8 +468,8 @@
(let [ids (into #{} (map #(obj/get % "$id")) shapes)] (let [ids (into #{} (map #(obj/get % "$id")) shapes)]
(st/emit! (dw/distribute-objects :horizontal ids))))) (st/emit! (dw/distribute-objects :horizontal ids)))))
(distributeVertical :distributeVertical
[_ shapes] (fn [shapes]
(cond (cond
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes))) (or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
(u/display-not-valid :distributeVertical-shapes "Not valid shapes") (u/display-not-valid :distributeVertical-shapes "Not valid shapes")
@ -424,41 +478,12 @@
(let [ids (into #{} (map #(obj/get % "$id")) shapes)] (let [ids (into #{} (map #(obj/get % "$id")) shapes)]
(st/emit! (dw/distribute-objects :vertical ids))))) (st/emit! (dw/distribute-objects :vertical ids)))))
(flatten :flatten
[_ shapes] (fn [shapes]
(cond (cond
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes))) (or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
(u/display-not-valid :flatten-shapes "Not valid shapes") (u/display-not-valid :flatten-shapes "Not valid shapes")
:else :else
(let [ids (into #{} (map #(obj/get % "$id")) shapes)] (let [ids (into #{} (map #(obj/get % "$id")) shapes)]
(st/emit! (dw/convert-selected-to-path ids)))))) (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]
(cond
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
(u/display-not-valid :selection 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))}))

View file

@ -7,7 +7,6 @@
(ns app.plugins.comments (ns app.plugins.comments
(:require (:require
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.record :as crc]
[app.common.spec :as us] [app.common.spec :as us]
[app.main.data.comments :as dc] [app.main.data.comments :as dc]
[app.main.data.workspace.comments :as dwc] [app.main.data.workspace.comments :as dwc]
@ -19,45 +18,38 @@
[app.plugins.shape :as shape] [app.plugins.shape :as shape]
[app.plugins.user :as user] [app.plugins.user :as user]
[app.plugins.utils :as u] [app.plugins.utils :as u]
[app.util.object :as obj]
[beicon.v2.core :as rx])) [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] (defn comment-proxy? [p]
(instance? CommentProxy p)) (obj/type-of? p "CommentProxy"))
(defn comment-proxy (defn comment-proxy
[plugin-id file-id page-id thread-id users data] [plugin-id file-id page-id thread-id users data]
(let [data* (atom data)] (let [data* (atom data)]
(crc/add-properties! (obj/reify {:name "CommentProxy"}
(CommentProxy. plugin-id file-id page-id thread-id (:id data)) ;; Private properties
{:name "$plugin" :enumerable false :get (constantly plugin-id)} :$plugin {:enumerable false :get (fn [] plugin-id)}
{:name "$file" :enumerable false :get (constantly file-id)} :$file {:enumerable false :get (fn [] file-id)}
{:name "$page" :enumerable false :get (constantly page-id)} :$page {:enumerable false :get (fn [] page-id)}
{:name "$thread" :enumerable false :get (constantly thread-id)} :$thread {:enumerable false :get (fn [] thread-id)}
{:name "$id" :enumerable false :get (constantly (:id data))} :$id {:enumerable false :get (fn [] (:id data))}
{:name "user" :get (fn [_] (user/user-proxy plugin-id (get users (:owner-id data))))} ;; Public properties
{:name "date" :get (fn [_] (:created-at data))} :user
{:get
(fn [] (user/user-proxy plugin-id (get users (:owner-id data))))}
:date
{:get
(fn [] (:created-at data))}
:content
{:get
(fn [] (:content @data*))
{:name "content"
:get (fn [_] (:content @data*))
:set :set
(fn [_ content] (fn [content]
(let [profile (:profile @st/state)] (let [profile (:profile @st/state)]
(cond (cond
(or (not (string? content)) (empty? content)) (or (not (string? content)) (empty? content))
@ -72,82 +64,49 @@
:else :else
(->> (rp/cmd! :update-comment {:id (:id data) :content content}) (->> (rp/cmd! :update-comment {:id (:id data) :content content})
(rx/tap #(st/emit! (dc/retrieve-comment-threads file-id))) (rx/tap #(st/emit! (dc/retrieve-comment-threads file-id)))
(rx/subs! #(swap! data* assoc :content content))))))}))) (rx/subs! #(swap! data* assoc :content content))))))}
(deftype CommentThreadProxy [$plugin $file $page $users $id owner] ;; Public methods
Object :remove
(findComments (fn []
[_]
(js/Promise. (js/Promise.
(fn [resolve reject] (fn [resolve reject]
(cond (cond
(not (r/check-permission $plugin "comment:read")) (not (r/check-permission plugin-id "comment:write"))
(do (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})
(rx/subs!
(fn [comments]
(resolve
(format/format-array
#(comment-proxy $plugin $file $page $id $users %) comments)))
reject))))))
(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") (u/display-not-valid :remove "Plugin doesn't have 'comment:write' permission")
(reject "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 :else
(js/Promise. (->> (rp/cmd! :delete-comment {:id (:id data)})
(fn [resolve] (rx/tap #(st/emit! (dc/retrieve-comment-threads file-id)))
(js/Promise. (rx/subs! #(resolve) reject)))))))))
(st/emit! (dc/delete-comment-thread-on-workspace {:id $id} #(resolve))))))))))
(defn comment-thread-proxy? [p] (defn comment-thread-proxy? [p]
(instance? CommentThreadProxy p)) (obj/type-of? p "CommentThreadProxy"))
(defn comment-thread-proxy (defn comment-thread-proxy
[plugin-id file-id page-id users data] [plugin-id file-id page-id users data]
(let [data* (atom data)] (let [data* (atom data)]
(crc/add-properties! (obj/reify {:name "CommentThreadProxy"}
(CommentThreadProxy. plugin-id file-id page-id users (:id data) (:owner-id data)) :$plugin {:enumerable false :get (fn [] plugin-id)}
{:name "$plugin" :enumerable false :get (constantly plugin-id)} :$file {:enumerable false :get (fn [] file-id)}
{:name "$file" :enumerable false :get (constantly file-id)} :$page {:enumerable false :get (fn [] page-id)}
{:name "$page" :enumerable false :get (constantly page-id)} :$id {:enumerable false :get (fn [] (:id data))}
{:name "$id" :enumerable false :get (constantly (:id data))} :$users {:enumerable false :get (fn [] users)}
{:name "$users" :enumerable false :get (constantly users)}
{:name "page" :enumerable false :get (fn [_] (u/locate-page file-id page-id))}
{:name "seqNumber" :get (fn [_] (:seqn data))} :page {:enumerable false :get #(u/locate-page file-id page-id)}
{:name "owner" :get (fn [_] (user/user-proxy plugin-id (get users (:owner-id data))))} :seqNumber {:get #(:seqn data)}
{:name "board" :get (fn [_] (shape/shape-proxy plugin-id file-id page-id (:frame-id 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))}
:position
{:get
(fn []
(format/format-point (:position @data*)))
{:name "position"
:get (fn [_] (format/format-point (:position @data*)))
:set :set
(fn [_ position] (fn [position]
(let [position (parser/parse-point position)] (let [position (parser/parse-point position)]
(cond (cond
(or (not (us/safe-number? (:x position))) (not (us/safe-number? (:y position)))) (or (not (us/safe-number? (:x position))) (not (us/safe-number? (:y position))))
@ -160,10 +119,12 @@
(do (st/emit! (dwc/update-comment-thread-position @data* [(:x position) (:y position)])) (do (st/emit! (dwc/update-comment-thread-position @data* [(:x position) (:y position)]))
(swap! data* assoc :position (gpt/point position))))))} (swap! data* assoc :position (gpt/point position))))))}
{:name "resolved" :resolved
:get (fn [_] (:is-resolved @data*)) {:get
(fn [] (:is-resolved @data*))
:set :set
(fn [_ is-resolved] (fn [is-resolved]
(cond (cond
(not (boolean? is-resolved)) (not (boolean? is-resolved))
(u/display-not-valid :resolved "Not a boolean type") (u/display-not-valid :resolved "Not a boolean type")
@ -173,5 +134,54 @@
:else :else
(do (st/emit! (dc/update-comment-thread (assoc @data* :is-resolved is-resolved))) (do (st/emit! (dc/update-comment-thread (assoc @data* :is-resolved is-resolved)))
(swap! data* assoc :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 (r/check-permission plugin-id "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 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)))))))))))

View file

@ -62,15 +62,16 @@
(defmethod handle-state-change "shapechange" (defmethod handle-state-change "shapechange"
[_ plugin-id old-val new-val props] [_ plugin-id old-val new-val props]
(let [shape-id (-> (obj/get props "shapeId") parser/parse-id) (if-let [shape-id (-> (obj/get props "shapeId") parser/parse-id)]
old-shape (wsh/lookup-shape old-val shape-id) (let [old-shape (wsh/lookup-shape old-val shape-id)
new-shape (wsh/lookup-shape new-val shape-id) new-shape (wsh/lookup-shape new-val shape-id)
file-id (:current-file-id new-val) file-id (:current-file-id new-val)
page-id (:current-page-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)) (if (and (identical? old-shape new-shape) (some? plugin-id) (some? file-id) (some? page-id) (some? shape-id))
::not-changed ::not-changed
(shape/shape-proxy plugin-id file-id page-id shape-id)))) (shape/shape-proxy plugin-id file-id page-id shape-id)))
::not-changed))
(defmethod handle-state-change "contentsave" (defmethod handle-state-change "contentsave"
[_ _ old-val new-val _] [_ _ old-val new-val _]

View file

@ -8,7 +8,6 @@
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.record :as crc]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.config :as cf] [app.config :as cf]
[app.main.data.exports.files :as exports.files] [app.main.data.exports.files :as exports.files]
@ -18,6 +17,7 @@
[app.main.repo :as rp] [app.main.repo :as rp]
[app.main.store :as st] [app.main.store :as st]
[app.main.worker :as uw] [app.main.worker :as uw]
[app.plugins.format :as format]
[app.plugins.page :as page] [app.plugins.page :as page]
[app.plugins.parser :as parser] [app.plugins.parser :as parser]
[app.plugins.register :as r] [app.plugins.register :as r]
@ -28,67 +28,21 @@
[app.util.time :as dt] [app.util.time :as dt]
[beicon.v2.core :as rx])) [beicon.v2.core :as rx]))
(declare file-version-proxy) (defn file-version-proxy?
[proxy]
(deftype FileVersionProxy [$plugin $file $version $data] (obj/type-of? proxy "FileVersionProxy"))
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 (defn file-version-proxy
[plugin-id file-id users data] [plugin-id file-id users data]
(let [data (atom data)] (let [data (atom data)]
(crc/add-properties! (obj/reify {:name "FileVersionProxy"}
(FileVersionProxy. plugin-id file-id (:id @data) data) :$plugin {:enumerable false :get (fn [] plugin-id)}
{:name "$plugin" :enumerable false :get (constantly plugin-id)} :$file {:enumerable false :get (fn [] file-id)}
{:name "$file" :enumerable false :get (constantly file-id)}
{:name "$version" :enumerable false :get (constantly (:id @data))}
{:name "$data" :enumerable false :get (constantly @data)}
{:name "label" :label
:get (fn [_] (:label @data)) {:get #(:label @data)
:set :set
(fn [_ value] (fn [value]
(cond (cond
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :label "Plugin doesn't have 'content:write' permission") (u/display-not-valid :label "Plugin doesn't have 'content:write' permission")
@ -102,58 +56,121 @@
(rx/take 1) (rx/take 1)
(rx/subs! identity)))))} (rx/subs! identity)))))}
{:name "createdBy" :createdBy
:get (fn [_] {:get
(fn []
(when-let [user-data (get users (:profile-id @data))] (when-let [user-data (get users (:profile-id @data))]
(user/user-proxy plugin-id user-data)))} (user/user-proxy plugin-id user-data)))}
{:name "createdAt" :createdAt
:get (fn [_] {:get #(.toJSDate ^js (:created-at @data))}
(.toJSDate ^js (:created-at @data)))}
{:name "isAutosave" :isAutosave
:get (fn [_] {:get #(= "system" (:created-by @data))}
(= "system" (:created-by @data)))})))
(deftype FileProxy [$plugin $id] :restore
Object (fn []
(getPages [_] (cond
(let [file (u/locate-file $id)] (not (r/check-permission plugin-id "content:write"))
(apply array (sequence (map #(page/page-proxy $plugin $id %)) (dm/get-in file [:data :pages]))))) (u/display-not-valid :restore "Plugin doesn't have 'content:write' permission")
:else
(let [project-id (:current-project-id @st/state)
version-id (get @data :id)]
(st/emit! (dwv/restore-version project-id file-id version-id :plugin)))))
:remove
(fn []
(js/Promise.
(fn [resolve reject]
(cond
(not (r/check-permission plugin-id "content:write"))
(u/reject-not-valid reject :remove "Plugin doesn't have 'content:write' permission")
:else
(let [version-id (:id @data)]
(->> (rp/cmd! :delete-file-snapshot {:id version-id})
(rx/subs! #(resolve) reject)))))))
:pin
(fn []
(js/Promise.
(fn [resolve reject]
(cond
(not (r/check-permission plugin-id "content:write"))
(u/reject-not-valid reject :pin "Plugin doesn't have 'content:write' permission")
(not= "system" (:created-by @data))
(u/reject-not-valid reject :pin "Only auto-saved versions can be pinned")
:else
(let [params {:id (:id @data)
:label (dt/format (:created-at @data) :date-full)}]
(->> (rx/zip (rp/cmd! :get-team-users {:file-id file-id})
(rp/cmd! :update-file-snapshot params))
(rx/subs! (fn [[users data]]
(let [users (d/index-by :id users)]
(resolve (file-version-proxy plugin-id file-id users @data))))
reject))))))))))
(defn file-proxy? [p]
(obj/type-of? p "FileProxy"))
(defn file-proxy
[plugin-id id]
(obj/reify {:name "FileProxy"}
:$plugin {:enumerable false :get (fn [] plugin-id)}
:$id {:enumerable false :get (fn [] id)}
:id
{:get #(format/format-id id)}
: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 ;; Plugin data
(getPluginData :getPluginData
[self key] (fn [key]
(cond (cond
(not (string? key)) (not (string? key))
(u/display-not-valid :getPluginData-key key) (u/display-not-valid :getPluginData-key key)
:else :else
(let [file (u/proxy->file self)] (let [file (u/locate-file id)]
(dm/get-in file [:data :plugin-data (keyword "plugin" (str $plugin)) key])))) (dm/get-in file [:data :plugin-data (keyword "plugin" (str plugin-id)) key]))))
(setPluginData :setPluginData
[_ key value] (fn [key value]
(cond (cond
(or (not (string? key)) (empty? key)) (or (not (string? key)) (empty? key))
(u/display-not-valid :setPluginData-key key) (u/display-not-valid :setPluginData-key key)
(and (some? value) (not (string? value))) (not (string? value))
(u/display-not-valid :setPluginData-value value) (u/display-not-valid :setPluginData-value value)
(not (r/check-permission $plugin "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :setPluginData "Plugin doesn't have 'content:write' permission") (u/display-not-valid :setPluginData "Plugin doesn't have 'content:write' permission")
:else :else
(st/emit! (dw/set-plugin-data $id :file (keyword "plugin" (str $plugin)) key value)))) (st/emit! (dw/set-plugin-data id :file (keyword "plugin" (str plugin-id)) key value))))
(getPluginDataKeys :getPluginDataKeys
[self] (fn []
(let [file (u/proxy->file self)] (let [file (u/locate-file id)]
(apply array (keys (dm/get-in file [:data :plugin-data (keyword "plugin" (str $plugin))]))))) (apply array (keys (dm/get-in file [:data :plugin-data (keyword "plugin" (dm/str plugin-id))])))))
(getSharedPluginData :getSharedPluginData
[self namespace key] (fn [namespace key]
(cond (cond
(not (string? namespace)) (not (string? namespace))
(u/display-not-valid :getSharedPluginData-namespace namespace) (u/display-not-valid :getSharedPluginData-namespace namespace)
@ -162,12 +179,11 @@
(u/display-not-valid :getSharedPluginData-key key) (u/display-not-valid :getSharedPluginData-key key)
:else :else
(let [file (u/proxy->file self)] (let [file (u/locate-file id)]
(dm/get-in file [:data :plugin-data (keyword "shared" namespace) key])))) (dm/get-in file [:data :plugin-data (keyword "shared" namespace) key]))))
(setSharedPluginData :setSharedPluginData
[_ namespace key value] (fn [namespace key value]
(cond (cond
(or (not (string? namespace)) (empty? namespace)) (or (not (string? namespace)) (empty? namespace))
(u/display-not-valid :setSharedPluginData-namespace namespace) (u/display-not-valid :setSharedPluginData-namespace namespace)
@ -175,57 +191,58 @@
(or (not (string? key)) (empty? key)) (or (not (string? key)) (empty? key))
(u/display-not-valid :setSharedPluginData-key key) (u/display-not-valid :setSharedPluginData-key key)
(and (some? value) (not (string? value))) (not (string? value))
(u/display-not-valid :setSharedPluginData-value value) (u/display-not-valid :setSharedPluginData-value value)
(not (r/check-permission $plugin "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'content:write' permission") (u/display-not-valid :setSharedPluginData "Plugin doesn't have 'content:write' permission")
:else :else
(st/emit! (dw/set-plugin-data $id :file (keyword "shared" namespace) key value)))) (st/emit! (dw/set-plugin-data id :file (keyword "shared" namespace) key value))))
(getSharedPluginDataKeys :getSharedPluginDataKeys
[self namespace] (fn [namespace]
(cond (cond
(not (string? namespace)) (not (string? namespace))
(u/display-not-valid :getSharedPluginDataKeys namespace) (u/display-not-valid :getSharedPluginDataKeys namespace)
:else :else
(let [file (u/proxy->file self)] (let [file (u/locate-file id)]
(apply array (keys (dm/get-in file [:data :plugin-data (keyword "shared" namespace)])))))) (apply array (keys (dm/get-in file [:data :plugin-data (keyword "shared" namespace)]))))))
(createPage :createPage
[_] (fn []
(cond (cond
(not (r/check-permission $plugin "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :createPage "Plugin doesn't have 'content:write' permission") (u/display-not-valid :createPage "Plugin doesn't have 'content:write' permission")
:else :else
(let [page-id (uuid/next)] (let [page-id (uuid/next)]
(st/emit! (dw/create-page {:page-id page-id :file-id $id})) (st/emit! (dw/create-page {:page-id page-id :file-id id}))
(page/page-proxy $plugin $id page-id)))) (page/page-proxy plugin-id id page-id))))
(export :export
[self format type] (fn [format type]
(js/Promise.
(fn [resolve reject]
(let [type (or (parser/parse-keyword type) :all)] (let [type (or (parser/parse-keyword type) :all)]
(cond (cond
(not (contains? #{"penpot" "zip"} format)) (and (some? format) (not (contains? #{"penpot" "zip"} format)))
(u/display-not-valid :format type) (u/reject-not-valid reject :format (dm/str "Invalid format: " format))
(not (contains? (set exports.files/valid-types) type)) (not (contains? (set exports.files/valid-types) type))
(u/display-not-valid :type type) (u/reject-not-valid reject :format (dm/str "Invalid type: " type))
:else :else
(let [file (u/proxy->file self) (let [file (u/locate-file id)
features (features/get-team-enabled-features @st/state) features (features/get-team-enabled-features @st/state)
team-id (:current-team-id @st/state) team-id (:current-team-id @st/state)
format (case format format (case format
"penpot" (if (contains? cf/flags :export-file-v3) "zip" :legacy-zip
(if (contains? cf/flags :export-file-v3)
:binfile-v3 :binfile-v3
:binfile-v1) :binfile-v1))]
"zip" :legacy-zip)]
(js/Promise.
(fn [resolve reject]
(->> (uw/ask-many! (->> (uw/ask-many!
{:cmd :export-files {:cmd :export-files
:format format :format format
@ -235,6 +252,7 @@
:files [file]}) :files [file]})
(rx/mapcat (rx/mapcat
(fn [msg] (fn [msg]
(.log js/console msg)
(case (:type msg) (case (:type msg)
:error :error
(rx/throw (ex-info "cannot export file" {:type :export-file})) (rx/throw (ex-info "cannot export file" {:type :export-file}))
@ -248,80 +266,56 @@
:mode :no-cors :mode :no-cors
:response-type :buffer})))) :response-type :buffer}))))
(rx/take 1) (rx/take 1)
(rx/map (fn [data] (js/Uint8Array. data))) (rx/map #(js/Uint8Array. (:body %)))
(rx/subs! resolve reject)))))))) (rx/subs! resolve reject))))))))
:findVersions
(fn [criteria]
(findVersions
[_ criteria]
(let [user (obj/get criteria "createdBy" nil)] (let [user (obj/get criteria "createdBy" nil)]
(js/Promise. (js/Promise.
(fn [resolve reject] (fn [resolve reject]
(cond (cond
(not (r/check-permission $plugin "content:read")) (not (r/check-permission plugin-id "content:read"))
(u/reject-not-valid reject :findVersions "Plugin doesn't have 'content:read' permission") (u/reject-not-valid reject :findVersions "Plugin doesn't have 'content:read' permission")
(and (not user) (not (user/user-proxy? user))) (and (some? user) (not (user/user-proxy? user)))
(u/reject-not-valid reject :findVersions-user "Created by user is not a valid user object") (u/reject-not-valid reject :findVersions-user "Created by user is not a valid user object")
:else :else
(->> (rx/zip (rp/cmd! :get-team-users {:file-id $id}) (->> (rx/zip (rp/cmd! :get-team-users {:file-id id})
(rp/cmd! :get-file-snapshots {:file-id $id})) (rp/cmd! :get-file-snapshots {:file-id id}))
(rx/take 1) (rx/take 1)
(rx/subs! (rx/subs!
(fn [[users snapshots]] (fn [[users snapshots]]
(let [users (d/index-by :id users)] (let [users (d/index-by :id users)]
(->> snapshots (->> snapshots
(filter #(= (dm/str (:profile-id %)) (obj/get user "id"))) (filter #(or (not (obj/get user "id"))
(map #(file-version-proxy $plugin $id users %)) (= (dm/str (:profile-id %))
(obj/get user "id"))))
(map #(file-version-proxy plugin-id id users %))
(sequence) (sequence)
(apply array) (apply array)
(resolve)))) (resolve))))
reject))))))) reject)))))))
(saveVersion :saveVersion
[_ label] (fn [label]
(let [users-promise (let [users-promise
(js/Promise. (js/Promise.
(fn [resolve reject] (fn [resolve reject]
(->> (rp/cmd! :get-team-users {:file-id $id}) (->> (rp/cmd! :get-team-users {:file-id id})
(rx/subs! resolve reject)))) (rx/subs! resolve reject))))
create-version-promise create-version-promise
(js/Promise. (js/Promise.
(fn [resolve reject] (fn [resolve reject]
(cond (cond
(not (r/check-permission $plugin "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/reject-not-valid reject :findVersions "Plugin doesn't have 'content:write' permission") (u/reject-not-valid reject :findVersions "Plugin doesn't have 'content:write' permission")
:else :else
(st/emit! (dwv/create-version-from-plugins $id label resolve reject)))))] (st/emit! (dwv/create-version-from-plugins id label resolve reject)))))]
(-> (js/Promise.all #js [users-promise create-version-promise]) (-> (js/Promise.all #js [users-promise create-version-promise])
(.then (.then
(fn [[users data]] (fn [[users data]]
(let [users (d/index-by :id users)] (let [users (d/index-by :id users)]
(file-version-proxy $plugin $id users data)))))))) (file-version-proxy plugin-id id users data)))))))))
(crc/define-properties!
FileProxy
{:name js/Symbol.toStringTag
:get (fn [] (str "FileProxy"))})
(defn file-proxy? [p]
(instance? FileProxy p))
(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"))}
{:name "name"
:get #(-> % u/proxy->file :name)}
{:name "pages"
:get #(.getPages ^js %)}))

View file

@ -7,7 +7,6 @@
(ns app.plugins.flex (ns app.plugins.flex
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.record :as crc]
[app.common.spec :as us] [app.common.spec :as us]
[app.common.types.shape.layout :as ctl] [app.common.types.shape.layout :as ctl]
[app.main.data.workspace.shape-layout :as dwsl] [app.main.data.workspace.shape-layout :as dwsl]
@ -21,39 +20,22 @@
;; Define in `app.plugins.shape` we do this way to prevent circular dependency ;; Define in `app.plugins.shape` we do this way to prevent circular dependency
(def shape-proxy? nil) (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] (defn flex-layout-proxy? [p]
(instance? FlexLayout p)) (obj/type-of? p "FlexLayoutProxy"))
(defn flex-layout-proxy (defn flex-layout-proxy
[plugin-id file-id page-id id] [plugin-id file-id page-id id]
(-> (FlexLayout. plugin-id file-id page-id id) (obj/reify {:name "FlexLayoutProxy"}
(crc/add-properties! :$plugin {:enumerable false :get (fn [] plugin-id)}
{:name "$plugin" :enumerable false :get (constantly plugin-id)} :$id {:enumerable false :get (fn [] id)}
{:name "$id" :enumerable false :get (constantly id)} :$file {:enumerable false :get (fn [] file-id)}
{:name "$file" :enumerable false :get (constantly file-id)} :$page {:enumerable false :get (fn [] page-id)}
{:name "$page" :enumerable false :get (constantly page-id)}
{:name "dir" :dir
{:this true
:get #(-> % u/proxy->shape :layout-flex-dir d/name) :get #(-> % u/proxy->shape :layout-flex-dir d/name)
:set :set
(fn [self value] (fn [_ value]
(let [value (keyword value)] (let [value (keyword value)]
(cond (cond
(not (contains? ctl/flex-direction-types value)) (not (contains? ctl/flex-direction-types value))
@ -63,13 +45,13 @@
(u/display-not-valid :dir "Plugin doesn't have 'content:write' permission") (u/display-not-valid :dir "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-flex-dir value})))))}
(st/emit! (dwsl/update-layout #{id} {:layout-flex-dir value}))))))}
{:name "wrap" :wrap
{:this true
:get #(-> % u/proxy->shape :layout-wrap-type d/name) :get #(-> % u/proxy->shape :layout-wrap-type d/name)
:set :set
(fn [self value] (fn [_ value]
(let [value (keyword value)] (let [value (keyword value)]
(cond (cond
(not (contains? ctl/wrap-types value)) (not (contains? ctl/wrap-types value))
@ -79,13 +61,13 @@
(u/display-not-valid :wrap "Plugin doesn't have 'content:write' permission") (u/display-not-valid :wrap "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-wrap-type value})))))}
(st/emit! (dwsl/update-layout #{id} {:layout-wrap-type value}))))))}
{:name "alignItems" :alignItems
{:this true
:get #(-> % u/proxy->shape :layout-align-items d/name) :get #(-> % u/proxy->shape :layout-align-items d/name)
:set :set
(fn [self value] (fn [_ value]
(let [value (keyword value)] (let [value (keyword value)]
(cond (cond
(not (contains? ctl/align-items-types value)) (not (contains? ctl/align-items-types value))
@ -95,13 +77,13 @@
(u/display-not-valid :alignItems "Plugin doesn't have 'content:write' permission") (u/display-not-valid :alignItems "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-align-items value})))))}
(st/emit! (dwsl/update-layout #{id} {:layout-align-items value}))))))}
{:name "alignContent" :alignContent
{:this true
:get #(-> % u/proxy->shape :layout-align-content d/name) :get #(-> % u/proxy->shape :layout-align-content d/name)
:set :set
(fn [self value] (fn [_ value]
(let [value (keyword value)] (let [value (keyword value)]
(cond (cond
(not (contains? ctl/align-content-types value)) (not (contains? ctl/align-content-types value))
@ -111,13 +93,13 @@
(u/display-not-valid :alignContent "Plugin doesn't have 'content:write' permission") (u/display-not-valid :alignContent "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-align-content value})))))}
(st/emit! (dwsl/update-layout #{id} {:layout-align-content value}))))))}
{:name "justifyItems" :justifyItems
{:this true
:get #(-> % u/proxy->shape :layout-justify-items d/name) :get #(-> % u/proxy->shape :layout-justify-items d/name)
:set :set
(fn [self value] (fn [_ value]
(let [value (keyword value)] (let [value (keyword value)]
(cond (cond
(not (contains? ctl/justify-items-types value)) (not (contains? ctl/justify-items-types value))
@ -127,13 +109,13 @@
(u/display-not-valid :justifyItems "Plugin doesn't have 'content:write' permission") (u/display-not-valid :justifyItems "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-justify-items value})))))}
(st/emit! (dwsl/update-layout #{id} {:layout-justify-items value}))))))}
{:name "justifyContent" :justifyContent
{:this true
:get #(-> % u/proxy->shape :layout-justify-content d/name) :get #(-> % u/proxy->shape :layout-justify-content d/name)
:set :set
(fn [self value] (fn [_ value]
(let [value (keyword value)] (let [value (keyword value)]
(cond (cond
(not (contains? ctl/justify-content-types value)) (not (contains? ctl/justify-content-types value))
@ -143,13 +125,13 @@
(u/display-not-valid :justifyContent "Plugin doesn't have 'content:write' permission") (u/display-not-valid :justifyContent "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-justify-content value})))))}
(st/emit! (dwsl/update-layout #{id} {:layout-justify-content value}))))))}
{:name "rowGap" :rowGap
{:this true
:get #(-> % u/proxy->shape :layout-gap :row-gap (d/nilv 0)) :get #(-> % u/proxy->shape :layout-gap :row-gap (d/nilv 0))
:set :set
(fn [self value] (fn [_ value]
(cond (cond
(not (us/safe-int? value)) (not (us/safe-int? value))
(u/display-not-valid :rowGap value) (u/display-not-valid :rowGap value)
@ -158,13 +140,13 @@
(u/display-not-valid :rowGap "Plugin doesn't have 'content:write' permission") (u/display-not-valid :rowGap "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-gap {:row-gap value}}))))}
(st/emit! (dwsl/update-layout #{id} {:layout-gap {:row-gap value}})))))}
{:name "columnGap" :columnGap
{:this true
:get #(-> % u/proxy->shape :layout-gap :column-gap (d/nilv 0)) :get #(-> % u/proxy->shape :layout-gap :column-gap (d/nilv 0))
:set :set
(fn [self value] (fn [_ value]
(cond (cond
(not (us/safe-int? value)) (not (us/safe-int? value))
(u/display-not-valid :columnGap value) (u/display-not-valid :columnGap value)
@ -173,13 +155,13 @@
(u/display-not-valid :columnGap "Plugin doesn't have 'content:write' permission") (u/display-not-valid :columnGap "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-gap {:column-gap value}}))))}
(st/emit! (dwsl/update-layout #{id} {:layout-gap {:column-gap value}})))))}
{:name "verticalPadding" :verticalPadding
{:this true
:get #(-> % u/proxy->shape :layout-padding :p1 (d/nilv 0)) :get #(-> % u/proxy->shape :layout-padding :p1 (d/nilv 0))
:set :set
(fn [self value] (fn [_ value]
(cond (cond
(not (us/safe-int? value)) (not (us/safe-int? value))
(u/display-not-valid :verticalPadding value) (u/display-not-valid :verticalPadding value)
@ -188,13 +170,13 @@
(u/display-not-valid :verticalPadding "Plugin doesn't have 'content:write' permission") (u/display-not-valid :verticalPadding "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value :p3 value}}))))}
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value :p3 value}})))))}
{:name "horizontalPadding" :horizontalPadding
{:this true
:get #(-> % u/proxy->shape :layout-padding :p2 (d/nilv 0)) :get #(-> % u/proxy->shape :layout-padding :p2 (d/nilv 0))
:set :set
(fn [self value] (fn [_ value]
(cond (cond
(not (us/safe-int? value)) (not (us/safe-int? value))
(u/display-not-valid :horizontalPadding value) (u/display-not-valid :horizontalPadding value)
@ -203,14 +185,14 @@
(u/display-not-valid :horizontalPadding "Plugin doesn't have 'content:write' permission") (u/display-not-valid :horizontalPadding "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value :p4 value}}))))}
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value :p4 value}})))))}
{:name "topPadding" :topPadding
{:this true
:get #(-> % u/proxy->shape :layout-padding :p1 (d/nilv 0)) :get #(-> % u/proxy->shape :layout-padding :p1 (d/nilv 0))
:set :set
(fn [self value] (fn [_ value]
(cond (cond
(not (us/safe-int? value)) (not (us/safe-int? value))
(u/display-not-valid :topPadding value) (u/display-not-valid :topPadding value)
@ -219,13 +201,13 @@
(u/display-not-valid :topPadding "Plugin doesn't have 'content:write' permission") (u/display-not-valid :topPadding "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value}}))))}
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value}})))))}
{:name "rightPadding" :rightPadding
{:this true
:get #(-> % u/proxy->shape :layout-padding :p2 (d/nilv 0)) :get #(-> % u/proxy->shape :layout-padding :p2 (d/nilv 0))
:set :set
(fn [self value] (fn [_ value]
(cond (cond
(not (us/safe-int? value)) (not (us/safe-int? value))
(u/display-not-valid :rightPadding value) (u/display-not-valid :rightPadding value)
@ -234,13 +216,13 @@
(u/display-not-valid :rightPadding "Plugin doesn't have 'content:write' permission") (u/display-not-valid :rightPadding "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value}}))))}
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value}})))))}
{:name "bottomPadding" :bottomPadding
{:this true
:get #(-> % u/proxy->shape :layout-padding :p3 (d/nilv 0)) :get #(-> % u/proxy->shape :layout-padding :p3 (d/nilv 0))
:set :set
(fn [self value] (fn [_ value]
(cond (cond
(not (us/safe-int? value)) (not (us/safe-int? value))
(u/display-not-valid :bottomPadding value) (u/display-not-valid :bottomPadding value)
@ -249,13 +231,13 @@
(u/display-not-valid :bottomPadding "Plugin doesn't have 'content:write' permission") (u/display-not-valid :bottomPadding "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-padding {:p3 value}}))))}
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p3 value}})))))}
{:name "leftPadding" :leftPadding
{:this true
:get #(-> % u/proxy->shape :layout-padding :p4 (d/nilv 0)) :get #(-> % u/proxy->shape :layout-padding :p4 (d/nilv 0))
:set :set
(fn [self value] (fn [_ value]
(cond (cond
(not (us/safe-int? value)) (not (us/safe-int? value))
(u/display-not-valid :leftPadding value) (u/display-not-valid :leftPadding value)
@ -264,28 +246,40 @@
(u/display-not-valid :leftPadding "Plugin doesn't have 'content:write' permission") (u/display-not-valid :leftPadding "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-padding {:p4 value}}))))}
(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] (defn layout-child-proxy? [p]
(instance? LayoutChildProxy p)) (obj/type-of? p "LayoutChildProxy"))
(defn layout-child-proxy (defn layout-child-proxy
[plugin-id file-id page-id id] [plugin-id file-id page-id id]
(-> (LayoutChildProxy. plugin-id file-id page-id id) (obj/reify {:name "LayoutChildProxy"}
(crc/add-properties! :$plugin {:enumerable false :get (fn [] plugin-id)}
{:name "$plugin" :enumerable false :get (constantly plugin-id)} :$id {:enumerable false :get (fn [] id)}
{:name "$id" :enumerable false :get (constantly id)} :$file {:enumerable false :get (fn [] file-id)}
{:name "$file" :enumerable false :get (constantly file-id)} :$page {:enumerable false :get (fn [] page-id)}
{:name "$page" :enumerable false :get (constantly page-id)}
{:name "absolute" :absolute
{:this true
:get #(-> % u/proxy->shape :layout-item-absolute boolean) :get #(-> % u/proxy->shape :layout-item-absolute boolean)
:set :set
(fn [self value] (fn [_ value]
(cond (cond
(not (boolean? value)) (not (boolean? value))
(u/display-not-valid :absolute value) (u/display-not-valid :absolute value)
@ -294,13 +288,13 @@
(u/display-not-valid :absolute "Plugin doesn't have 'content:write' permission") (u/display-not-valid :absolute "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-item-absolute value}))))}
(st/emit! (dwsl/update-layout #{id} {:layout-item-absolute value})))))}
{:name "zIndex" :zIndex
{:this true
:get #(-> % u/proxy->shape :layout-item-z-index (d/nilv 0)) :get #(-> % u/proxy->shape :layout-item-z-index (d/nilv 0))
:set :set
(fn [self value] (fn [_ value]
(cond (cond
(us/safe-int? value) (us/safe-int? value)
(u/display-not-valid :zIndex value) (u/display-not-valid :zIndex value)
@ -309,13 +303,13 @@
(u/display-not-valid :zIndex "Plugin doesn't have 'content:write' permission") (u/display-not-valid :zIndex "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-z-index value}))))}
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-z-index value})))))}
{:name "horizontalSizing" :horizontalSizing
{:this true
:get #(-> % u/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 :set
(fn [self value] (fn [_ value]
(let [value (keyword value)] (let [value (keyword value)]
(cond (cond
(not (contains? ctl/item-h-sizing-types value)) (not (contains? ctl/item-h-sizing-types value))
@ -325,13 +319,13 @@
(u/display-not-valid :horizontalPadding "Plugin doesn't have 'content:write' permission") (u/display-not-valid :horizontalPadding "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-h-sizing value})))))}
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-h-sizing value}))))))}
{:name "verticalSizing" :verticalSizing
{:this true
:get #(-> % u/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 :set
(fn [self value] (fn [_ value]
(let [value (keyword value)] (let [value (keyword value)]
(cond (cond
(not (contains? ctl/item-v-sizing-types value)) (not (contains? ctl/item-v-sizing-types value))
@ -341,13 +335,13 @@
(u/display-not-valid :verticalSizing "Plugin doesn't have 'content:write' permission") (u/display-not-valid :verticalSizing "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-v-sizing value})))))}
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-v-sizing value}))))))}
{:name "alignSelf" :alignSelf
{:this true
:get #(-> % u/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 :set
(fn [self value] (fn [_ value]
(let [value (keyword value)] (let [value (keyword value)]
(cond (cond
(not (contains? ctl/item-align-self-types value)) (not (contains? ctl/item-align-self-types value))
@ -357,13 +351,13 @@
(u/display-not-valid :alignSelf "Plugin doesn't have 'content:write' permission") (u/display-not-valid :alignSelf "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-align-self value})))))}
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-align-self value}))))))}
{:name "verticalMargin" :verticalMargin
{:this true
:get #(-> % u/proxy->shape :layout-item-margin :m1 (d/nilv 0)) :get #(-> % u/proxy->shape :layout-item-margin :m1 (d/nilv 0))
:set :set
(fn [self value] (fn [_ value]
(cond (cond
(not (us/safe-number? value)) (not (us/safe-number? value))
(u/display-not-valid :verticalMargin value) (u/display-not-valid :verticalMargin value)
@ -372,13 +366,13 @@
(u/display-not-valid :verticalMargin "Plugin doesn't have 'content:write' permission") (u/display-not-valid :verticalMargin "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m1 value :m3 value}}))))}
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m1 value :m3 value}})))))}
{:name "horizontalMargin" :horizontalMargin
{:this true
:get #(-> % u/proxy->shape :layout-item-margin :m2 (d/nilv 0)) :get #(-> % u/proxy->shape :layout-item-margin :m2 (d/nilv 0))
:set :set
(fn [self value] (fn [_ value]
(cond (cond
(not (us/safe-number? value)) (not (us/safe-number? value))
(u/display-not-valid :horizontalMargin value) (u/display-not-valid :horizontalMargin value)
@ -387,13 +381,13 @@
(u/display-not-valid :horizontalMargin "Plugin doesn't have 'content:write' permission") (u/display-not-valid :horizontalMargin "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m2 value :m4 value}}))))}
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m2 value :m4 value}})))))}
{:name "topMargin" :topMargin
{:this true
:get #(-> % u/proxy->shape :layout-item-margin :m1 (d/nilv 0)) :get #(-> % u/proxy->shape :layout-item-margin :m1 (d/nilv 0))
:set :set
(fn [self value] (fn [_ value]
(cond (cond
(not (us/safe-number? value)) (not (us/safe-number? value))
(u/display-not-valid :topMargin value) (u/display-not-valid :topMargin value)
@ -402,13 +396,13 @@
(u/display-not-valid :topMargin "Plugin doesn't have 'content:write' permission") (u/display-not-valid :topMargin "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m1 value}}))))}
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m1 value}})))))}
{:name "rightMargin" :rightMargin
{:this true
:get #(-> % u/proxy->shape :layout-item-margin :m2 (d/nilv 0)) :get #(-> % u/proxy->shape :layout-item-margin :m2 (d/nilv 0))
:set :set
(fn [self value] (fn [_ value]
(cond (cond
(not (us/safe-number? value)) (not (us/safe-number? value))
(u/display-not-valid :rightMargin value) (u/display-not-valid :rightMargin value)
@ -417,13 +411,13 @@
(u/display-not-valid :rightMargin "Plugin doesn't have 'content:write' permission") (u/display-not-valid :rightMargin "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m2 value}}))))}
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m2 value}})))))}
{:name "bottomMargin" :bottomMargin
{:this true
:get #(-> % u/proxy->shape :layout-item-margin :m3 (d/nilv 0)) :get #(-> % u/proxy->shape :layout-item-margin :m3 (d/nilv 0))
:set :set
(fn [self value] (fn [_ value]
(cond (cond
(not (us/safe-number? value)) (not (us/safe-number? value))
(u/display-not-valid :bottomMargin value) (u/display-not-valid :bottomMargin value)
@ -432,13 +426,13 @@
(u/display-not-valid :bottomMargin "Plugin doesn't have 'content:write' permission") (u/display-not-valid :bottomMargin "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m3 value}}))))}
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m3 value}})))))}
{:name "leftMargin" :leftMargin
{:this true
:get #(-> % u/proxy->shape :layout-item-margin :m4 (d/nilv 0)) :get #(-> % u/proxy->shape :layout-item-margin :m4 (d/nilv 0))
:set :set
(fn [self value] (fn [_ value]
(cond (cond
(not (us/safe-number? value)) (not (us/safe-number? value))
(u/display-not-valid :leftMargin value) (u/display-not-valid :leftMargin value)
@ -447,13 +441,13 @@
(u/display-not-valid :leftMargin "Plugin doesn't have 'content:write' permission") (u/display-not-valid :leftMargin "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m4 value}}))))}
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m4 value}})))))}
{:name "maxWidth" :maxWidth
{:this true
:get #(-> % u/proxy->shape :layout-item-max-w) :get #(-> % u/proxy->shape :layout-item-max-w)
:set :set
(fn [self value] (fn [_ value]
(cond (cond
(not (us/safe-number? value)) (not (us/safe-number? value))
(u/display-not-valid :maxWidth value) (u/display-not-valid :maxWidth value)
@ -462,13 +456,13 @@
(u/display-not-valid :maxWidth "Plugin doesn't have 'content:write' permission") (u/display-not-valid :maxWidth "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-max-w value}))))}
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-max-w value})))))}
{:name "minWidth" :minWidth
{:this true
:get #(-> % u/proxy->shape :layout-item-min-w) :get #(-> % u/proxy->shape :layout-item-min-w)
:set :set
(fn [self value] (fn [_ value]
(cond (cond
(not (us/safe-number? value)) (not (us/safe-number? value))
(u/display-not-valid :minWidth value) (u/display-not-valid :minWidth value)
@ -477,13 +471,13 @@
(u/display-not-valid :minWidth "Plugin doesn't have 'content:write' permission") (u/display-not-valid :minWidth "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-min-w value}))))}
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-min-w value})))))}
{:name "maxHeight" :maxHeight
{:this true
:get #(-> % u/proxy->shape :layout-item-max-h) :get #(-> % u/proxy->shape :layout-item-max-h)
:set :set
(fn [self value] (fn [_ value]
(cond (cond
(not (us/safe-number? value)) (not (us/safe-number? value))
(u/display-not-valid :maxHeight value) (u/display-not-valid :maxHeight value)
@ -492,13 +486,13 @@
(u/display-not-valid :maxHeight "Plugin doesn't have 'content:write' permission") (u/display-not-valid :maxHeight "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-max-h value}))))}
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-max-h value})))))}
{:name "minHeight" :minHeight
{:this true
:get #(-> % u/proxy->shape :layout-item-min-h) :get #(-> % u/proxy->shape :layout-item-min-h)
:set :set
(fn [self value] (fn [_ value]
(cond (cond
(not (us/safe-number? value)) (not (us/safe-number? value))
(u/display-not-valid :minHeight value) (u/display-not-valid :minHeight value)
@ -507,5 +501,4 @@
(u/display-not-valid :minHeight "Plugin doesn't have 'content:write' permission") (u/display-not-valid :minHeight "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-min-h value}))))}))
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-min-h value})))))})))

View file

@ -7,10 +7,10 @@
(ns app.plugins.fonts (ns app.plugins.fonts
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.record :as cr]
[app.main.data.workspace.texts :as dwt] [app.main.data.workspace.texts :as dwt]
[app.main.fonts :as fonts] [app.main.fonts :as fonts]
[app.main.store :as st] [app.main.store :as st]
[app.plugins.format :as format]
[app.plugins.register :as r] [app.plugins.register :as r]
[app.plugins.shape :as shape] [app.plugins.shape :as shape]
[app.plugins.text :as text] [app.plugins.text :as text]
@ -18,15 +18,41 @@
[app.util.object :as obj] [app.util.object :as obj]
[cuerdas.core :as str])) [cuerdas.core :as str]))
(deftype PenpotFontVariant [name fontVariantId fontWeight fontStyle]) (defn font-variant-proxy? [p]
(obj/type-of? p "FontVariantProxy"))
(defn variant-proxy? [p] (defn font-variant-proxy [name id weight style]
(instance? PenpotFontVariant p)) (obj/reify {:name "FontVariantProxy"}
:name {:get (fn [] name)}
:fontVariantId {:get (fn [] id)}
:fontWeight {:get (fn [] weight)}
:fontStyle {:get (fn [] style)}))
(deftype PenpotFont [name fontId fontFamily fontStyle fontVariantId fontWeight variants] (defn font-proxy? [p]
Object (obj/type-of? p "FontProxy"))
(applyToText [_ text variant] (defn font-proxy
[{:keys [id family name variants] :as font}]
(when (some? font)
(let [default-variant (fonts/get-default-variant font)]
(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))}
:variants
{:get
(fn []
(format/format-array
(fn [{:keys [id name style weight]}]
(font-variant-proxy name id weight style))
variants))}
:applyToText
(fn [text variant]
(cond (cond
(not (shape/shape-proxy? text)) (not (shape/shape-proxy? text))
(u/display-not-valid :applyToText text) (u/display-not-valid :applyToText text)
@ -36,16 +62,17 @@
:else :else
(let [id (obj/get text "$id") (let [id (obj/get text "$id")
values {:font-id fontId values {:font-id id
:font-family fontFamily :font-family family
:font-style (d/nilv (obj/get variant "fontStyle") fontStyle) :font-style (d/nilv (obj/get variant "fontStyle") (:style default-variant))
:font-variant-id (d/nilv (obj/get variant "fontVariantId") fontVariantId) :font-variant-id (d/nilv (obj/get variant "fontVariantId") (:id default-variant))
:font-weight (d/nilv (obj/get variant "fontWeight") fontWeight)}] :font-weight (d/nilv (obj/get variant "fontWeight") (:wegith default-variant))}]
(st/emit! (dwt/update-attrs id values))))) (st/emit! (dwt/update-attrs id values)))))
(applyToRange [_ range variant] :applyToRange
(fn [range variant]
(cond (cond
(not (text/text-range? range)) (not (text/text-range-proxy? range))
(u/display-not-valid :applyToRange range) (u/display-not-valid :applyToRange range)
(not (r/check-permission (obj/get range "$plugin") "content:write")) (not (r/check-permission (obj/get range "$plugin") "content:write"))
@ -55,80 +82,69 @@
(let [id (obj/get range "$id") (let [id (obj/get range "$id")
start (obj/get range "start") start (obj/get range "start")
end (obj/get range "end") end (obj/get range "end")
values {:font-id fontId values {:font-id id
:font-family fontFamily :font-family family
:font-style (d/nilv (obj/get variant "fontStyle") fontStyle) :font-style (d/nilv (obj/get variant "fontStyle") (:style default-variant))
:font-variant-id (d/nilv (obj/get variant "fontVariantId") fontVariantId) :font-variant-id (d/nilv (obj/get variant "fontVariantId") (:id default-variant))
:font-weight (d/nilv (obj/get variant "fontWeight") fontWeight)}] :font-weight (d/nilv (obj/get variant "fontWeight") (:weight default-variant))}]
(st/emit! (dwt/update-text-range id start end values)))))) (st/emit! (dwt/update-text-range id start end values)))))))))
(defn font-proxy? [p] (defn fonts-subcontext
(instance? PenpotFont p)) [plugin-id]
(obj/reify {:name "PenpotFontsSubcontext"}
:$plugin {:name "" :enumerable false :get (constantly plugin-id)}
(defn font-proxy :all
[{:keys [id family name variants] :as font}] {:get
(when (some? font) (fn []
(let [default-variant (fonts/get-default-variant font)] (format/format-array
(PenpotFont. font-proxy
name (vals @fonts/fontsdb)))}
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)))))))))
(deftype PenpotFontsSubcontext [$plugin] :findById
Object (fn [id]
(findById
[_ id]
(cond (cond
(not (string? id)) (not (string? id))
(u/display-not-valid :findbyId id) (u/display-not-valid :findbyId id)
:else :else
(font-proxy (d/seek #(str/includes? (str/lower (:id %)) (str/lower id)) (vals @fonts/fontsdb))))) (->> (vals @fonts/fontsdb)
(d/seek #(str/includes? (str/lower (:id %)) (str/lower id)))
(font-proxy))))
(findByName :findByName
[_ name] (fn [name]
(cond (cond
(not (string? name)) (not (string? name))
(u/display-not-valid :findByName name) (u/display-not-valid :findByName name)
:else :else
(font-proxy (d/seek #(str/includes? (str/lower (:name %)) (str/lower name)) (vals @fonts/fontsdb))))) (->> (vals @fonts/fontsdb)
(d/seek #(str/includes? (str/lower (:name %)) (str/lower name)))
(font-proxy))))
(findAllById :findAllById
[_ id] (fn [id]
(cond (cond
(not (string? id)) (not (string? id))
(u/display-not-valid :findAllById name) (u/display-not-valid :findAllById name)
:else :else
(apply array (->> (vals @fonts/fontsdb) (format/format-array
(filter #(str/includes? (str/lower (:id %)) (str/lower id))) (fn [font]
(map font-proxy))))) (when (str/includes? (str/lower (:id font)) (str/lower id))
(font-proxy font)))
(vals @fonts/fontsdb))))
(findAllByName :findAllByName
[_ name] (fn [name]
(cond (cond
(not (string? name)) (not (string? name))
(u/display-not-valid :findAllByName name) (u/display-not-valid :findAllByName name)
:else :else
(apply array (->> (vals @fonts/fontsdb) (format/format-array
(filter #(str/includes? (str/lower (:name %)) (str/lower name))) (fn [font]
(map font-proxy)))))) (when (str/includes? (str/lower (:name font)) (str/lower name))
(font-proxy font)))
(defn fonts-subcontext (vals @fonts/fontsdb))))))
[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))))}))

View file

@ -7,7 +7,6 @@
(ns app.plugins.grid (ns app.plugins.grid
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.record :as crc]
[app.common.spec :as us] [app.common.spec :as us]
[app.common.types.shape.layout :as ctl] [app.common.types.shape.layout :as ctl]
[app.main.data.workspace.shape-layout :as dwsl] [app.main.data.workspace.shape-layout :as dwsl]
@ -22,11 +21,227 @@
;; Define in `app.plugins.shape` we do this way to prevent circular dependency ;; Define in `app.plugins.shape` we do this way to prevent circular dependency
(def shape-proxy? nil) (def shape-proxy? nil)
(deftype GridLayout [$plugin $file $page $id] (defn grid-layout-proxy? [p]
Object (obj/type-of? p "GridLayoutProxy"))
(addRow (defn grid-layout-proxy
[_ type value] [plugin-id file-id page-id 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)}
: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")
:else
(st/emit! (dwsl/update-layout #{id} {:layout-grid-dir value})))))}
:rows
{:this true
:get #(-> % u/proxy->shape :layout-grid-rows format/format-tracks)}
:columns
{:this true
:get #(-> % u/proxy->shape :layout-grid-columns format/format-tracks)}
: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")
:else
(st/emit! (dwsl/update-layout #{id} {:layout-align-items 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")
:else
(st/emit! (dwsl/update-layout #{id} {:layout-align-content 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")
:else
(st/emit! (dwsl/update-layout #{id} {:layout-justify-items 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")
:else
(st/emit! (dwsl/update-layout #{id} {:layout-justify-content 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")
:else
(st/emit! (dwsl/update-layout #{id} {:layout-gap {:row-gap 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")
:else
(st/emit! (dwsl/update-layout #{id} {:layout-gap {:column-gap 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")
:else
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value :p3 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")
:else
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value :p4 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")
:else
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 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")
:else
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 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")
:else
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p3 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")
:else
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p4 value}}))))}
:addRow
(fn [type value]
(let [type (keyword type)] (let [type (keyword type)]
(cond (cond
(not (contains? ctl/grid-track-types type)) (not (contains? ctl/grid-track-types type))
@ -36,14 +251,14 @@
(not (us/safe-number? value))) (not (us/safe-number? value)))
(u/display-not-valid :addRow-value value) (u/display-not-valid :addRow-value value)
(not (r/check-permission $plugin "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :addRow "Plugin doesn't have 'content:write' permission") (u/display-not-valid :addRow "Plugin doesn't have 'content:write' permission")
:else :else
(st/emit! (dwsl/add-layout-track #{$id} :row {:type type :value value}))))) (st/emit! (dwsl/add-layout-track #{id} :row {:type type :value value})))))
(addRowAtIndex :addRowAtIndex
[_ index type value] (fn [index type value]
(let [type (keyword type)] (let [type (keyword type)]
(cond (cond
(not (us/safe-int? index)) (not (us/safe-int? index))
@ -56,14 +271,14 @@
(not (us/safe-number? value))) (not (us/safe-number? value)))
(u/display-not-valid :addRowAtIndex-value value) (u/display-not-valid :addRowAtIndex-value value)
(not (r/check-permission $plugin "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :addRowAtIndex "Plugin doesn't have 'content:write' permission") (u/display-not-valid :addRowAtIndex "Plugin doesn't have 'content:write' permission")
:else :else
(st/emit! (dwsl/add-layout-track #{$id} :row {:type type :value value} index))))) (st/emit! (dwsl/add-layout-track #{id} :row {:type type :value value} index)))))
(addColumn :addColumn
[_ type value] (fn [type value]
(let [type (keyword type)] (let [type (keyword type)]
(cond (cond
(not (contains? ctl/grid-track-types type)) (not (contains? ctl/grid-track-types type))
@ -73,14 +288,14 @@
(not (us/safe-number? value))) (not (us/safe-number? value)))
(u/display-not-valid :addColumn-value value) (u/display-not-valid :addColumn-value value)
(not (r/check-permission $plugin "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :addColumn "Plugin doesn't have 'content:write' permission") (u/display-not-valid :addColumn "Plugin doesn't have 'content:write' permission")
:else :else
(st/emit! (dwsl/add-layout-track #{$id} :column {:type type :value value}))))) (st/emit! (dwsl/add-layout-track #{id} :column {:type type :value value})))))
(addColumnAtIndex :addColumnAtIndex
[_ index type value] (fn [index type value]
(cond (cond
(not (us/safe-int? index)) (not (us/safe-int? index))
(u/display-not-valid :addColumnAtIndex-index index) (u/display-not-valid :addColumnAtIndex-index index)
@ -92,39 +307,39 @@
(not (us/safe-number? value))) (not (us/safe-number? value)))
(u/display-not-valid :addColumnAtIndex-value value) (u/display-not-valid :addColumnAtIndex-value value)
(not (r/check-permission $plugin "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :addColumnAtIndex "Plugin doesn't have 'content:write' permission") (u/display-not-valid :addColumnAtIndex "Plugin doesn't have 'content:write' permission")
:else :else
(let [type (keyword type)] (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 :removeRow
[_ index] (fn [index]
(cond (cond
(not (us/safe-int? index)) (not (us/safe-int? index))
(u/display-not-valid :removeRow index) (u/display-not-valid :removeRow index)
(not (r/check-permission $plugin "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :removeRow "Plugin doesn't have 'content:write' permission") (u/display-not-valid :removeRow "Plugin doesn't have 'content:write' permission")
:else :else
(st/emit! (dwsl/remove-layout-track #{$id} :row index)))) (st/emit! (dwsl/remove-layout-track #{id} :row index))))
(removeColumn :removeColumn
[_ index] (fn [index]
(cond (cond
(not (us/safe-int? index)) (not (us/safe-int? index))
(u/display-not-valid :removeColumn index) (u/display-not-valid :removeColumn index)
(not (r/check-permission $plugin "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :removeColumn "Plugin doesn't have 'content:write' permission") (u/display-not-valid :removeColumn "Plugin doesn't have 'content:write' permission")
:else :else
(st/emit! (dwsl/remove-layout-track #{$id} :column index)))) (st/emit! (dwsl/remove-layout-track #{id} :column index))))
(setColumn :setColumn
[_ index type value] (fn [index type value]
(let [type (keyword type)] (let [type (keyword type)]
(cond (cond
(not (us/safe-int? index)) (not (us/safe-int? index))
@ -137,14 +352,14 @@
(not (us/safe-number? value))) (not (us/safe-number? value)))
(u/display-not-valid :setColumn-value value) (u/display-not-valid :setColumn-value value)
(not (r/check-permission $plugin "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :setColumn "Plugin doesn't have 'content:write' permission") (u/display-not-valid :setColumn "Plugin doesn't have 'content:write' permission")
:else :else
(st/emit! (dwsl/change-layout-track #{$id} :column index (d/without-nils {:type type :value value})))))) (st/emit! (dwsl/change-layout-track #{id} :column index (d/without-nils {:type type :value value}))))))
(setRow :setRow
[_ index type value] (fn [index type value]
(let [type (keyword type)] (let [type (keyword type)]
(cond (cond
(not (us/safe-int? index)) (not (us/safe-int? index))
@ -157,23 +372,23 @@
(not (us/safe-number? value))) (not (us/safe-number? value)))
(u/display-not-valid :setRow-value value) (u/display-not-valid :setRow-value value)
(not (r/check-permission $plugin "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :setRow "Plugin doesn't have 'content:write' permission") (u/display-not-valid :setRow "Plugin doesn't have 'content:write' permission")
:else :else
(st/emit! (dwsl/change-layout-track #{$id} :row index (d/without-nils {:type type :value value})))))) (st/emit! (dwsl/change-layout-track #{id} :row index (d/without-nils {:type type :value value}))))))
(remove :remove
[_] (fn []
(cond (cond
(not (r/check-permission $plugin "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :remove "Plugin doesn't have 'content:write' permission") (u/display-not-valid :remove "Plugin doesn't have 'content:write' permission")
:else :else
(st/emit! (dwsl/remove-layout #{$id})))) (st/emit! (dwsl/remove-layout #{id}))))
(appendChild :appendChild
[_ child row column] (fn [child row column]
(cond (cond
(not (shape-proxy? child)) (not (shape-proxy? child))
(u/display-not-valid :appendChild-child child) (u/display-not-valid :appendChild-child child)
@ -184,236 +399,16 @@
(or (< column 0) (not (us/safe-int? column))) (or (< column 0) (not (us/safe-int? column)))
(u/display-not-valid :appendChild-column column) (u/display-not-valid :appendChild-column column)
(not (r/check-permission $plugin "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :appendChild "Plugin doesn't have 'content:write' permission") (u/display-not-valid :appendChild "Plugin doesn't have 'content:write' permission")
:else :else
(let [child-id (obj/get child "$id")] (let [child-id (obj/get child "$id")]
(st/emit! (dwt/move-shapes-to-frame #{child-id} $id nil [row column]) (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))
(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)}
{: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)
(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}))))))}
{:name "rows"
:get #(-> % u/proxy->shape :layout-grid-rows format/format-tracks)}
{:name "columns"
: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)
(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}))))))}
{: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)
(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}))))))}
{: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)
(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}))))))}
{: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)
(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}))))))}
{: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)
(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}})))))}
{: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)
(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}})))))}
{: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)
(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}})))))}
{: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)
(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}})))))}
{: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)
(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}})))))}
{: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)
(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}})))))}
{: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)
(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}})))))}
{: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)
(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}})))))})))
(deftype GridCellProxy [$plugin $file $page $id])
(defn layout-cell-proxy? [p] (defn layout-cell-proxy? [p]
(instance? GridCellProxy p)) (obj/type-of? p "GridCellProxy"))
(defn layout-cell-proxy (defn layout-cell-proxy
[plugin-id file-id page-id id] [plugin-id file-id page-id id]
@ -422,14 +417,14 @@
parent (u/locate-shape file-id page-id (:parent-id shape))] parent (u/locate-shape file-id page-id (:parent-id shape))]
(ctl/get-cell-by-shape-id parent id)))] (ctl/get-cell-by-shape-id parent id)))]
(-> (GridCellProxy. plugin-id file-id page-id id) (obj/reify {:name "GridCellProxy"}
(crc/add-properties! :$plugin {:enumerable false :get (constantly plugin-id)}
{:name "$plugin" :enumerable false :get (constantly plugin-id)} :$id {:enumerable false :get (constantly id)}
{:name "$id" :enumerable false :get (constantly id)} :$file {:enumerable false :get (constantly file-id)}
{:name "$file" :enumerable false :get (constantly file-id)} :$page {:enumerable false :get (constantly page-id)}
{:name "$page" :enumerable false :get (constantly page-id)}
{:name "row" :row
{:this true
:get #(-> % locate-cell :row) :get #(-> % locate-cell :row)
:set :set
(fn [self value] (fn [self value]
@ -448,7 +443,8 @@
:else :else
(st/emit! (dwsl/update-grid-cell-position (:parent-id shape) (:id cell) {:row value})))))} (st/emit! (dwsl/update-grid-cell-position (:parent-id shape) (:id cell) {:row value})))))}
{:name "rowSpan" :rowSpan
{:this true
:get #(-> % locate-cell :row-span) :get #(-> % locate-cell :row-span)
:set :set
(fn [self value] (fn [self value]
@ -467,7 +463,8 @@
:else :else
(st/emit! (dwsl/update-grid-cell-position (:parent-id shape) (:id cell) {:row-span value})))))} (st/emit! (dwsl/update-grid-cell-position (:parent-id shape) (:id cell) {:row-span value})))))}
{:name "column" :column
{:this true
:get #(-> % locate-cell :column) :get #(-> % locate-cell :column)
:set :set
(fn [self value] (fn [self value]
@ -486,7 +483,8 @@
:else :else
(st/emit! (dwsl/update-grid-cell-position (:parent-id shape) (:id cell) {:column value})))))} (st/emit! (dwsl/update-grid-cell-position (:parent-id shape) (:id cell) {:column value})))))}
{:name "columnSpan" :columnSpan
{:this true
:get #(-> % locate-cell :column-span) :get #(-> % locate-cell :column-span)
:set :set
(fn [self value] (fn [self value]
@ -505,7 +503,8 @@
:else :else
(st/emit! (dwsl/update-grid-cell-position (:parent-id shape) (:id cell) {:column-span value})))))} (st/emit! (dwsl/update-grid-cell-position (:parent-id shape) (:id cell) {:column-span value})))))}
{:name "areaName" :areaName
{:this true
:get #(-> % locate-cell :area-name) :get #(-> % locate-cell :area-name)
:set :set
(fn [self value] (fn [self value]
@ -524,7 +523,8 @@
:else :else
(st/emit! (dwsl/update-grid-cells (:parent-id shape) #{(:id cell)} {:area-name value})))))} (st/emit! (dwsl/update-grid-cells (:parent-id shape) #{(:id cell)} {:area-name value})))))}
{:name "position" :position
{:this true
:get #(-> % locate-cell :position d/name) :get #(-> % locate-cell :position d/name)
:set :set
(fn [self value] (fn [self value]
@ -544,7 +544,8 @@
:else :else
(st/emit! (dwsl/change-cells-mode (:parent-id shape) #{(:id cell)} value)))))} (st/emit! (dwsl/change-cells-mode (:parent-id shape) #{(:id cell)} value)))))}
{:name "alignSelf" :alignSelf
{:this true
:get #(-> % locate-cell :align-self d/name) :get #(-> % locate-cell :align-self d/name)
:set :set
(fn [self value] (fn [self value]
@ -564,7 +565,8 @@
:else :else
(st/emit! (dwsl/update-grid-cells (:parent-id shape) #{(:id cell)} {:align-self value})))))} (st/emit! (dwsl/update-grid-cells (:parent-id shape) #{(:id cell)} {:align-self value})))))}
{:name "justifySelf" :justifySelf
{:this true
:get #(-> % locate-cell :justify-self d/name) :get #(-> % locate-cell :justify-self d/name)
:set :set
(fn [self value] (fn [self value]
@ -582,4 +584,4 @@
(u/display-not-valid :justifySelf "Plugin doesn't have 'content:write' permission") (u/display-not-valid :justifySelf "Plugin doesn't have 'content:write' permission")
:else :else
(st/emit! (dwsl/update-grid-cells (:parent-id shape) #{(:id cell)} {:justify-self value})))))})))) (st/emit! (dwsl/update-grid-cells (:parent-id shape) #{(:id cell)} {:justify-self value})))))})))

View file

@ -6,18 +6,24 @@
(ns app.plugins.history (ns app.plugins.history
(:require (:require
[app.common.record :as crc]
[app.main.data.workspace.undo :as dwu] [app.main.data.workspace.undo :as dwu]
[app.main.store :as st] [app.main.store :as st]
[app.plugins.register :as r] [app.plugins.register :as r]
[app.plugins.utils :as u])) [app.plugins.utils :as u]
[app.util.object :as obj]))
(deftype HistorySubcontext [$plugin] (defn history-subcontext? [p]
Object (obj/type-of? p "HistorySubcontext"))
(undoBlockBegin
[_] (defn history-subcontext
[plugin-id]
(obj/reify {:name "HistorySubcontext"}
:$plugin {:enumerable false :get (fn [] plugin-id)}
:undoBlockBegin
(fn []
(cond (cond
(not (r/check-permission $plugin "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :resize "Plugin doesn't have 'content:write' permission") (u/display-not-valid :resize "Plugin doesn't have 'content:write' permission")
:else :else
@ -25,28 +31,16 @@
(st/emit! (dwu/start-undo-transaction id)) (st/emit! (dwu/start-undo-transaction id))
id))) id)))
(undoBlockFinish :undoBlockFinish
[_ block-id] (fn [block-id]
(cond (cond
(not (r/check-permission $plugin "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :resize "Plugin doesn't have 'content:write' permission") (u/display-not-valid :resize "Plugin doesn't have 'content:write' permission")
(not block-id) (not block-id)
(u/display-not-valid :undoBlockFinish block-id) (u/display-not-valid :undoBlockFinish block-id)
:else :else
(st/emit! (dwu/commit-undo-transaction block-id))))) (st/emit! (dwu/commit-undo-transaction block-id))))))
(crc/define-properties!
HistorySubcontext
{:name js/Symbol.toStringTag
:get (fn [] (str "HistorySubcontext"))})
(defn history-subcontext? [p]
(instance? HistorySubcontext p))
(defn history-subcontext
[plugin-id]
(HistorySubcontext. plugin-id))

File diff suppressed because it is too large Load diff

View file

@ -11,7 +11,6 @@
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.files.helpers :as cfh] [app.common.files.helpers :as cfh]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.record :as crc]
[app.common.spec :as us] [app.common.spec :as us]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.main.data.comments :as dc] [app.main.data.comments :as dc]
@ -31,25 +30,27 @@
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[cuerdas.core :as str])) [cuerdas.core :as str]))
(deftype FlowProxy [$plugin $file $page $id] (declare page-proxy)
Object
(remove [_]
(st/emit! (dwi/remove-flow $page $id))))
(defn flow-proxy? [p] (defn flow-proxy? [p]
(instance? FlowProxy p)) (obj/type-of? p "FlowProxy"))
(defn flow-proxy (defn flow-proxy
[plugin-id file-id page-id id] [plugin-id file-id page-id id]
(crc/add-properties! (obj/reify {:name "FlowProxy"}
(FlowProxy. plugin-id file-id page-id id) :$plugin {:enumerable false :get (fn [] plugin-id)}
{:name "$plugin" :enumerable false :get (constantly plugin-id)} :$file {:enumerable false :get (fn [] file-id)}
{:name "$file" :enumerable false :get (constantly file-id)} :$page {:enumerable false :get (fn [] page-id)}
{:name "$page" :enumerable false :get (constantly page-id)} :$id {:enumerable false :get (fn [] id)}
{:name "$id" :enumerable false :get (constantly id)}
{:name "page" :enumerable false :get (fn [_] (u/locate-page file-id page-id))}
{:name "name" :page
{:enumerable false
:get
(fn []
(page-proxy plugin-id file-id page-id))}
:name
{:this true
:get #(-> % u/proxy->flow :name) :get #(-> % u/proxy->flow :name)
:set :set
(fn [_ value] (fn [_ value]
@ -60,11 +61,12 @@
:else :else
(st/emit! (dwi/update-flow page-id id #(assoc % :name value)))))} (st/emit! (dwi/update-flow page-id id #(assoc % :name value)))))}
{:name "startingBoard" :startingBoard
{:this true
:get :get
(fn [self] (fn [self]
(let [frame (-> self u/proxy->flow :starting-frame)] (when-let [frame (-> self u/proxy->flow :starting-frame)]
(u/locate-shape file-id page-id frame))) (shape/shape-proxy file-id page-id frame)))
:set :set
(fn [_ value] (fn [_ value]
(cond (cond
@ -72,28 +74,97 @@
(u/display-not-valid :startingBoard value) (u/display-not-valid :startingBoard value)
:else :else
(st/emit! (dwi/update-flow page-id id #(assoc % :starting-frame (obj/get value "$id"))))))})) (st/emit! (dwi/update-flow page-id id #(assoc % :starting-frame (obj/get value "$id"))))))}
(deftype PageProxy [$plugin $file $id] :remove
Object (fn []
(getShapeById (st/emit! (dwi/remove-flow page-id id)))))
[_ shape-id]
(defn page-proxy? [proxy]
(obj/type-of? proxy "PageProxy"))
(defn page-proxy
[plugin-id file-id 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)}
:id
{:get #(dm/str id)}
: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 (cond
(not (string? shape-id)) (not (string? shape-id))
(u/display-not-valid :getShapeById shape-id) (u/display-not-valid :getShapeById shape-id)
:else :else
(let [shape-id (uuid/uuid shape-id) (let [shape-id (uuid/uuid shape-id)
shape (u/locate-shape $file $id shape-id)] shape (u/locate-shape file-id id shape-id)]
(when (some? shape) (when (some? shape)
(shape/shape-proxy $plugin $file $id shape-id))))) (shape/shape-proxy plugin-id file-id id shape-id)))))
(getRoot :findShapes
[_] (fn [criteria]
(shape/shape-proxy $plugin $file $id uuid/zero))
(findShapes
[_ criteria]
;; Returns a lazy (iterable) of all available shapes ;; Returns a lazy (iterable) of all available shapes
(let [criteria (parser/parse-criteria criteria) (let [criteria (parser/parse-criteria criteria)
match-criteria? match-criteria?
@ -109,26 +180,26 @@
(or (not (:type criteria)) (or (not (:type criteria))
(= (:type criteria) (:type shape))))) (= (:type criteria) (:type shape)))))
identity)] identity)]
(when (and (some? $file) (some? $id)) (when (and (some? file-id) (some? id))
(let [page (u/locate-page $file $id) (let [page (u/locate-page file-id id)
xf (comp xf (comp
(filter match-criteria?) (filter match-criteria?)
(map #(shape/shape-proxy $plugin $file $id (first %))))] (map #(shape/shape-proxy plugin-id file-id id (first %))))]
(apply array (sequence xf (:objects page))))))) (apply array (sequence xf (:objects page)))))))
;; Plugin data ;; Plugin data
(getPluginData :getPluginData
[self key] (fn [key]
(cond (cond
(not (string? key)) (not (string? key))
(u/display-not-valid :page-plugin-data-key key) (u/display-not-valid :page-plugin-data-key key)
:else :else
(let [page (u/proxy->page self)] (let [page (u/locate-page file-id id)]
(dm/get-in page [:plugin-data (keyword "plugin" (str $plugin)) key])))) (dm/get-in page [:plugin-data (keyword "plugin" (str plugin-id)) key]))))
(setPluginData :setPluginData
[_ key value] (fn [key value]
(cond (cond
(not (string? key)) (not (string? key))
(u/display-not-valid :setPluginData-key key) (u/display-not-valid :setPluginData-key key)
@ -136,19 +207,19 @@
(and (some? value) (not (string? value))) (and (some? value) (not (string? value)))
(u/display-not-valid :setPluginData-value value) (u/display-not-valid :setPluginData-value value)
(not (r/check-permission $plugin "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :setPluginData "Plugin doesn't have 'content:write' permission") (u/display-not-valid :setPluginData "Plugin doesn't have 'content:write' permission")
:else :else
(st/emit! (dw/set-plugin-data $file :page $id (keyword "plugin" (str $plugin)) key value)))) (st/emit! (dw/set-plugin-data file-id :page id (keyword "plugin" (str plugin-id)) key value))))
(getPluginDataKeys :getPluginDataKeys
[self] (fn []
(let [page (u/proxy->page self)] (let [page (u/locate-page file-id id)]
(apply array (keys (dm/get-in page [:plugin-data (keyword "plugin" (str $plugin))]))))) (apply array (keys (dm/get-in page [:plugin-data (keyword "plugin" (str plugin-id))])))))
(getSharedPluginData :getSharedPluginData
[self namespace key] (fn [namespace key]
(cond (cond
(not (string? namespace)) (not (string? namespace))
(u/display-not-valid :page-plugin-data-namespace namespace) (u/display-not-valid :page-plugin-data-namespace namespace)
@ -157,12 +228,11 @@
(u/display-not-valid :page-plugin-data-key key) (u/display-not-valid :page-plugin-data-key key)
:else :else
(let [page (u/proxy->page self)] (let [page (u/locate-page file-id id)]
(dm/get-in page [:plugin-data (keyword "shared" namespace) key])))) (dm/get-in page [:plugin-data (keyword "shared" namespace) key]))))
(setSharedPluginData :setSharedPluginData
[_ namespace key value] (fn [namespace key value]
(cond (cond
(not (string? namespace)) (not (string? namespace))
(u/display-not-valid :setSharedPluginData-namespace namespace) (u/display-not-valid :setSharedPluginData-namespace namespace)
@ -173,14 +243,14 @@
(and (some? value) (not (string? value))) (and (some? value) (not (string? value)))
(u/display-not-valid :setSharedPluginData-value value) (u/display-not-valid :setSharedPluginData-value value)
(not (r/check-permission $plugin "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'content:write' permission") (u/display-not-valid :setSharedPluginData "Plugin doesn't have 'content:write' permission")
:else :else
(st/emit! (dw/set-plugin-data $file :page $id (keyword "shared" namespace) key value)))) (st/emit! (dw/set-plugin-data file-id :page id (keyword "shared" namespace) key value))))
(getSharedPluginDataKeys :getSharedPluginDataKeys
[self namespace] (fn [self namespace]
(cond (cond
(not (string? namespace)) (not (string? namespace))
(u/display-not-valid :page-plugin-data-namespace namespace) (u/display-not-valid :page-plugin-data-namespace namespace)
@ -189,17 +259,17 @@
(let [page (u/proxy->page self)] (let [page (u/proxy->page self)]
(apply array (keys (dm/get-in page [:plugin-data (keyword "shared" namespace)])))))) (apply array (keys (dm/get-in page [:plugin-data (keyword "shared" namespace)]))))))
(openPage :openPage
[_] (fn []
(cond (cond
(not (r/check-permission $plugin "content:read")) (not (r/check-permission plugin-id "content:read"))
(u/display-not-valid :openPage "Plugin doesn't have 'content:read' permission") (u/display-not-valid :openPage "Plugin doesn't have 'content:read' permission")
:else :else
(st/emit! (dw/go-to-page $id)))) (st/emit! (dw/go-to-page id))))
(createFlow :createFlow
[_ name frame] (fn [name frame]
(cond (cond
(or (not (string? name)) (empty? name)) (or (not (string? name)) (empty? name))
(u/display-not-valid :createFlow-name name) (u/display-not-valid :createFlow-name name)
@ -209,20 +279,20 @@
:else :else
(let [flow-id (uuid/next)] (let [flow-id (uuid/next)]
(st/emit! (dwi/add-flow flow-id $id name (obj/get frame "$id"))) (st/emit! (dwi/add-flow flow-id id name (obj/get frame "$id")))
(flow-proxy $plugin $file $id flow-id)))) (flow-proxy plugin-id file-id id flow-id))))
(removeFlow :removeFlow
[_ flow] (fn [flow]
(cond (cond
(not (flow-proxy? flow)) (not (flow-proxy? flow))
(u/display-not-valid :removeFlow-flow flow) (u/display-not-valid :removeFlow-flow flow)
:else :else
(st/emit! (dwi/remove-flow $id (obj/get flow "$id"))))) (st/emit! (dwi/remove-flow id (obj/get flow "$id")))))
(addRulerGuide :addRulerGuide
[_ orientation value board] (fn [orientation value board]
(let [shape (u/proxy->shape board)] (let [shape (u/proxy->shape board)]
(cond (cond
(not (us/safe-number? value)) (not (us/safe-number? value))
@ -236,35 +306,35 @@
(not (cfh/frame-shape? shape)))) (not (cfh/frame-shape? shape))))
(u/display-not-valid :addRulerGuide "The shape is not a board") (u/display-not-valid :addRulerGuide "The shape is not a board")
(not (r/check-permission $plugin "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :addRulerGuide "Plugin doesn't have 'content:write' permission") (u/display-not-valid :addRulerGuide "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (uuid/next)] (let [ruler-id (uuid/next)]
(st/emit! (st/emit!
(dwgu/update-guides (dwgu/update-guides
(d/without-nils (d/without-nils
{:id id {:id ruler-id
:axis (parser/orientation->axis orientation) :axis (parser/orientation->axis orientation)
:position value :position value
:frame-id (when board (obj/get board "$id"))}))) :frame-id (when board (obj/get board "$id"))})))
(rg/ruler-guide-proxy $plugin $file $id id))))) (rg/ruler-guide-proxy plugin-id file-id id ruler-id)))))
(removeRulerGuide :removeRulerGuide
[_ value] (fn [value]
(cond (cond
(not (rg/ruler-guide-proxy? value)) (not (rg/ruler-guide-proxy? value))
(u/display-not-valid :removeRulerGuide "Guide not provided") (u/display-not-valid :removeRulerGuide "Guide not provided")
(not (r/check-permission $plugin "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :removeRulerGuide "Plugin doesn't have 'comment:write' permission") (u/display-not-valid :removeRulerGuide "Plugin doesn't have 'comment:write' permission")
:else :else
(let [guide (u/proxy->ruler-guide value)] (let [guide (u/proxy->ruler-guide value)]
(st/emit! (dwgu/remove-guide guide))))) (st/emit! (dwgu/remove-guide guide)))))
(addCommentThread :addCommentThread
[_ content position board] (fn [content position board]
(let [shape (when board (u/proxy->shape board)) (let [shape (when board (u/proxy->shape board))
position (parser/parse-point position)] position (parser/parse-point position)]
(cond (cond
@ -278,7 +348,7 @@
(and (some? board) (or (not (shape/shape-proxy? board)) (not (cfh/frame-shape? shape)))) (and (some? board) (or (not (shape/shape-proxy? board)) (not (cfh/frame-shape? shape))))
(u/display-not-valid :addCommentThread "Board not valid") (u/display-not-valid :addCommentThread "Board not valid")
(not (r/check-permission $plugin "comment:write")) (not (r/check-permission plugin-id "comment:write"))
(u/display-not-valid :addCommentThread "Plugin doesn't have 'comment:write' permission") (u/display-not-valid :addCommentThread "Plugin doesn't have 'comment:write' permission")
:else :else
@ -291,26 +361,26 @@
(fn [resolve] (fn [resolve]
(st/emit! (st/emit!
(dc/create-thread-on-workspace (dc/create-thread-on-workspace
{:file-id $file {:file-id file-id
:page-id $id :page-id id
:position (gpt/point position) :position (gpt/point position)
:content content} :content content}
(fn [data] (fn [data]
(->> (rp/cmd! :get-team-users {:file-id $file}) (->> (rp/cmd! :get-team-users {:file-id file-id})
(rx/subs! (rx/subs!
(fn [users] (fn [users]
(let [users (d/index-by :id users)] (let [users (d/index-by :id users)]
(resolve (pc/comment-thread-proxy $plugin $file $id users data))))))) (resolve (pc/comment-thread-proxy plugin-id file-id id users data)))))))
false)))))))) false))))))))
(removeCommentThread :removeCommentThread
[_ thread] (fn [thread]
(cond (cond
(not (pc/comment-thread-proxy? thread)) (not (pc/comment-thread-proxy? thread))
(u/display-not-valid :removeCommentThread "Comment thread not valid") (u/display-not-valid :removeCommentThread "Comment thread not valid")
(not (r/check-permission $plugin "comment:write")) (not (r/check-permission plugin-id "comment:write"))
(u/display-not-valid :removeCommentThread "Plugin doesn't have 'content:write' permission") (u/display-not-valid :removeCommentThread "Plugin doesn't have 'content:write' permission")
:else :else
@ -320,22 +390,22 @@
(js/Promise. (js/Promise.
(st/emit! (dc/delete-comment-thread-on-workspace {:id thread-id} #(resolve))))))))) (st/emit! (dc/delete-comment-thread-on-workspace {:id thread-id} #(resolve)))))))))
(findCommentThreads :findCommentThreads
[_ criteria] (fn [criteria]
(let [only-yours (boolean (obj/get criteria "onlyYours" false)) (let [only-yours (boolean (obj/get criteria "onlyYours" false))
show-resolved (boolean (obj/get criteria "showResolved" true)) show-resolved (boolean (obj/get criteria "showResolved" true))
user-id (-> @st/state :profile :id)] user-id (-> @st/state :profile :id)]
(js/Promise. (js/Promise.
(fn [resolve reject] (fn [resolve reject]
(cond (cond
(not (r/check-permission $plugin "comment:read")) (not (r/check-permission plugin-id "comment:read"))
(do (do
(u/display-not-valid :findCommentThreads "Plugin doesn't have 'comment:read' permission") (u/display-not-valid :findCommentThreads "Plugin doesn't have 'comment:read' permission")
(reject "Plugin doesn't have 'comment:read' permission")) (reject "Plugin doesn't have 'comment:read' permission"))
:else :else
(->> (rx/zip (rp/cmd! :get-team-users {:file-id $file}) (->> (rx/zip (rp/cmd! :get-team-users {:file-id file-id})
(rp/cmd! :get-comment-threads {:file-id $file})) (rp/cmd! :get-comment-threads {:file-id file-id}))
(rx/take 1) (rx/take 1)
(rx/subs! (rx/subs!
(fn [[users comments]] (fn [[users comments]]
@ -349,72 +419,5 @@
(filter #(contains? (:participants %) user-id)))] (filter #(contains? (:participants %) user-id)))]
(resolve (resolve
(format/format-array (format/format-array
#(pc/comment-thread-proxy $plugin $file $id users %) comments)))) #(pc/comment-thread-proxy plugin-id file-id id users %) comments))))
reject)))))))) reject)))))))))
(crc/define-properties!
PageProxy
{:name js/Symbol.toStringTag
:get (fn [] (str "PageProxy"))})
(defn page-proxy? [p]
(instance? PageProxy p))
(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)}
{:name "id"
:get #(dm/str (obj/get % "$id"))}
{:name "name"
: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))))}
{:name "root"
:enumerable false
:get #(.getRoot ^js %)}
{:name "background"
:enumerable false
: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}))))}
{: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)))}
{: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 %))))))}))

View file

@ -45,7 +45,7 @@
(conj "content:read") (conj "content:read")
(contains? permissions "library:write") (contains? permissions "library:write")
(conj "content:write") (conj "library:read")
(contains? permissions "comment:write") (contains? permissions "comment:write")
(conj "comment:read")) (conj "comment:read"))

View file

@ -8,7 +8,6 @@
(:require (:require
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.files.helpers :as cfh] [app.common.files.helpers :as cfh]
[app.common.record :as crc]
[app.common.spec :as us] [app.common.spec :as us]
[app.main.data.workspace.guides :as dwgu] [app.main.data.workspace.guides :as dwgu]
[app.main.store :as st] [app.main.store :as st]
@ -20,25 +19,20 @@
(def shape-proxy identity) (def shape-proxy identity)
(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] (defn ruler-guide-proxy? [p]
(instance? RulerGuideProxy p)) (obj/type-of? p "RulerGuideProxy"))
(defn ruler-guide-proxy (defn ruler-guide-proxy
[plugin-id file-id page-id id] [plugin-id file-id page-id id]
(crc/add-properties! (obj/reify {:name "RuleGuideProxy"}
(RulerGuideProxy. plugin-id file-id page-id id) :$plugin {:enumerable false :get (constantly plugin-id)}
{:name "$plugin" :enumerable false :get (constantly plugin-id)} :$file {:enumerable false :get (constantly file-id)}
{:name "$file" :enumerable false :get (constantly file-id)} :$page {:enumerable false :get (constantly page-id)}
{:name "$page" :enumerable false :get (constantly page-id)} :$id {:enumerable false :get (constantly id)}
{:name "$id" :enumerable false :get (constantly id)}
{:name "board" :enumerable false :board
{:this true
:enumerable false
:get :get
(fn [self] (fn [self]
(let [board-id (-> self u/proxy->ruler-guide :frame-id)] (let [board-id (-> self u/proxy->ruler-guide :frame-id)]
@ -63,10 +57,12 @@
guide (-> self u/proxy->ruler-guide)] guide (-> self u/proxy->ruler-guide)]
(st/emit! (dwgu/update-guides (assoc guide :frame-id board-id)))))))} (st/emit! (dwgu/update-guides (assoc guide :frame-id board-id)))))))}
{:name "orientation" :orientation
{:this true
:get #(-> % u/proxy->ruler-guide :axis format/axis->orientation)} :get #(-> % u/proxy->ruler-guide :axis format/axis->orientation)}
{:name "position" :position
{:this true
:get :get
(fn [self] (fn [self]
(let [guide (u/proxy->ruler-guide self)] (let [guide (u/proxy->ruler-guide self)]
@ -96,4 +92,9 @@
(+ board-pos value)) (+ board-pos value))
value)] value)]
(st/emit! (dwgu/update-guides (assoc guide :position position))))))})) (st/emit! (dwgu/update-guides (assoc guide :position position))))))}
:remove
(fn []
(let [guide (u/locate-ruler-guide file-id page-id id)]
(st/emit! (dwgu/remove-guide guide))))))

File diff suppressed because it is too large Load diff

View file

@ -27,21 +27,16 @@
;; This regex seems duplicated but probably in the future when we support diferent units ;; This regex seems duplicated but probably in the future when we support diferent units
;; this will need to reflect changes for each property ;; this will need to reflect changes for each property
(def font-size-re #"^\d*\.?\d*$") (def ^:private font-size-re #"^\d*\.?\d*$")
(def line-height-re #"^\d*\.?\d*$") (def ^:private line-height-re #"^\d*\.?\d*$")
(def letter-spacing-re #"^\d*\.?\d*$") (def ^:private letter-spacing-re #"^\d*\.?\d*$")
(def text-transform-re #"uppercase|capitalize|lowercase|none") (def ^:private text-transform-re #"uppercase|capitalize|lowercase|none")
(def text-decoration-re #"underline|line-through|none") (def ^:private text-decoration-re #"underline|line-through|none")
(def text-direction-re #"ltr|rtl") (def ^:private text-direction-re #"ltr|rtl")
(def text-align-re #"left|center|right|justify") (def ^:private text-align-re #"left|center|right|justify")
(def vertical-align-re #"top|center|bottom") (def ^:private vertical-align-re #"top|center|bottom")
(defn mixed-value (defn- font-data
[values]
(let [s (set values)]
(if (= (count s) 1) (first s) "mixed")))
(defn font-data
[font variant] [font variant]
(d/without-nils (d/without-nils
{:font-id (:id font) {:font-id (:id font)
@ -50,55 +45,51 @@
:font-style (:style variant) :font-style (:style variant)
:font-weight (:weight variant)})) :font-weight (:weight variant)}))
(defn variant-data (defn- variant-data
[variant] [variant]
(d/without-nils (d/without-nils
{:font-variant-id (:id variant) {:font-variant-id (:id variant)
:font-style (:style variant) :font-style (:style variant)
:font-weight (:weight variant)})) :font-weight (:weight variant)}))
(deftype TextRange [$plugin $file $page $id start end] (defn- text-props
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
[shape] [shape]
(d/merge (d/merge
(dwt/current-root-values {:shape shape :attrs txt/root-attrs}) (dwt/current-root-values {:shape shape :attrs txt/root-attrs})
(dwt/current-paragraph-values {:shape shape :attrs txt/paragraph-attrs}) (dwt/current-paragraph-values {:shape shape :attrs txt/paragraph-attrs})
(dwt/current-text-values {:shape shape :attrs txt/text-node-attrs}))) (dwt/current-text-values {:shape shape :attrs txt/text-node-attrs})))
(defn text-range (defn text-range-proxy?
[plugin-id file-id page-id id start end] [range]
(-> (TextRange. plugin-id file-id page-id id start end) (obj/type-of? range "TextRange"))
(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)}
{:name "shape" (defn text-range-proxy
[plugin-id file-id page-id id start end]
(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)}
:shape
{:this true
:get #(-> % u/proxy->shape)} :get #(-> % u/proxy->shape)}
{:name "characters" :characters
:get #(let [range-data {:this true
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))] :get
(->> range-data (map :text) (str/join "")))} (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" :fontId
:get #(let [range-data {:this true
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))] :get
(->> range-data (map :font-id) mixed-value)) (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 :set
(fn [_ value] (fn [_ value]
@ -114,10 +105,13 @@
:else :else
(st/emit! (dwt/update-text-range id start end (font-data font variant))))))} (st/emit! (dwt/update-text-range id start end (font-data font variant))))))}
{:name "fontFamily" :fontFamily
:get #(let [range-data {:this true
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))] :get
(->> range-data (map :font-family) mixed-value)) (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 :set
(fn [_ value] (fn [_ value]
@ -133,10 +127,13 @@
:else :else
(st/emit! (dwt/update-text-range id start end (font-data font variant))))))} (st/emit! (dwt/update-text-range id start end (font-data font variant))))))}
{:name "fontVariantId" :fontVariantId
:get #(let [range-data {:this true
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))] :get
(->> range-data (map :font-variant-id) mixed-value)) (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 :set
(fn [self value] (fn [self value]
(let [font (fonts/get-font-data (obj/get self "fontId")) (let [font (fonts/get-font-data (obj/get self "fontId"))
@ -151,10 +148,13 @@
:else :else
(st/emit! (dwt/update-text-range id start end (variant-data variant))))))} (st/emit! (dwt/update-text-range id start end (variant-data variant))))))}
{:name "fontSize" :fontSize
:get #(let [range-data {:this true
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))] :get
(->> range-data (map :font-size) mixed-value)) (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 :set
(fn [_ value] (fn [_ value]
(let [value (str/trim (dm/str value))] (let [value (str/trim (dm/str value))]
@ -168,10 +168,14 @@
:else :else
(st/emit! (dwt/update-text-range id start end {:font-size value})))))} (st/emit! (dwt/update-text-range id start end {:font-size value})))))}
{:name "fontWeight" :fontWeight
:get #(let [range-data {:this true
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))] :get
(->> range-data (map :font-weight) mixed-value)) (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)))
:set :set
(fn [self value] (fn [self value]
(let [font (fonts/get-font-data (obj/get self "fontId")) (let [font (fonts/get-font-data (obj/get self "fontId"))
@ -191,10 +195,13 @@
:else :else
(st/emit! (dwt/update-text-range id start end (variant-data variant))))))} (st/emit! (dwt/update-text-range id start end (variant-data variant))))))}
{:name "fontStyle" :fontStyle
:get #(let [range-data {:this true
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))] :get
(->> range-data (map :font-style) mixed-value)) (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 :set
(fn [self value] (fn [self value]
(let [font (fonts/get-font-data (obj/get self "fontId")) (let [font (fonts/get-font-data (obj/get self "fontId"))
@ -214,10 +221,13 @@
:else :else
(st/emit! (dwt/update-text-range id start end (variant-data variant))))))} (st/emit! (dwt/update-text-range id start end (variant-data variant))))))}
{:name "lineHeight" :lineHeight
:get #(let [range-data {:this true
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))] :get
(->> range-data (map :line-height) mixed-value)) (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 :set
(fn [_ value] (fn [_ value]
(let [value (str/trim (dm/str value))] (let [value (str/trim (dm/str value))]
@ -231,10 +241,13 @@
:else :else
(st/emit! (dwt/update-text-range id start end {:line-height value})))))} (st/emit! (dwt/update-text-range id start end {:line-height value})))))}
{:name "letterSpacing" :letterSpacing
:get #(let [range-data {:this true
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))] :get
(->> range-data (map :letter-spacing) mixed-value)) (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 :set
(fn [_ value] (fn [_ value]
(let [value (str/trim (dm/str value))] (let [value (str/trim (dm/str value))]
@ -248,14 +261,17 @@
:else :else
(st/emit! (dwt/update-text-range id start end {:letter-spacing value})))))} (st/emit! (dwt/update-text-range id start end {:letter-spacing value})))))}
{:name "textTransform" :textTransform
:get #(let [range-data {:this true
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))] :get
(->> range-data (map :text-transform) mixed-value)) (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 :set
(fn [_ value] (fn [_ value]
(cond (cond
(and (string? value) (re-matches text-transform-re value)) (and (string? value) (not (re-matches text-transform-re value)))
(u/display-not-valid :textTransform value) (u/display-not-valid :textTransform value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
@ -264,10 +280,13 @@
:else :else
(st/emit! (dwt/update-text-range id start end {:text-transform value}))))} (st/emit! (dwt/update-text-range id start end {:text-transform value}))))}
{:name "textDecoration" :textDecoration
:get #(let [range-data {:this true
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))] :get
(->> range-data (map :text-decoration) mixed-value)) (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 :set
(fn [_ value] (fn [_ value]
(cond (cond
@ -280,10 +299,13 @@
:else :else
(st/emit! (dwt/update-text-range id start end {:text-decoration value}))))} (st/emit! (dwt/update-text-range id start end {:text-decoration value}))))}
{:name "direction" :direction
:get #(let [range-data {:this true
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))] :get
(->> range-data (map :direction) mixed-value)) (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 :set
(fn [_ value] (fn [_ value]
(cond (cond
@ -296,10 +318,13 @@
:else :else
(st/emit! (dwt/update-text-range id start end {:direction value}))))} (st/emit! (dwt/update-text-range id start end {:direction value}))))}
{:name "align" :align
:get #(let [range-data {:this true
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))] :get
(->> range-data (map :text-align) mixed-value)) (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 :set
(fn [_ value] (fn [_ value]
(cond (cond
@ -312,10 +337,13 @@
:else :else
(st/emit! (dwt/update-text-range id start end {:text-align value}))))} (st/emit! (dwt/update-text-range id start end {:text-align value}))))}
{:name "fills" :fills
:get #(let [range-data {:this true
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))] :get
(->> range-data (map :fills) mixed-value format/format-fills)) (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 :set
(fn [_ value] (fn [_ value]
(let [value (parser/parse-fills value)] (let [value (parser/parse-fills value)]
@ -327,7 +355,16 @@
(u/display-not-valid :fills "Plugin doesn't have 'content:write' permission") (u/display-not-valid :fills "Plugin doesn't have 'content:write' permission")
:else :else
(st/emit! (dwt/update-text-range id start end {:fills value})))))}))) (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 (defn add-text-props
[shape-proxy plugin-id] [shape-proxy plugin-id]

View file

@ -41,23 +41,28 @@
(defn current-user-proxy? [p] (defn current-user-proxy? [p]
(instance? CurrentUserProxy p)) (obj/type-of? p "CurrentUserProxy"))
(defn current-user-proxy (defn current-user-proxy
[plugin-id session-id] [plugin-id session-id]
(-> (CurrentUserProxy. plugin-id) (-> (obj/reify {:name "CurrentUserProxy"}
:$plugin {:enumerable false :get (fn [] plugin-id)})
(add-session-properties session-id))) (add-session-properties session-id)))
(defn active-user-proxy? [p] (defn active-user-proxy? [p]
(instance? ActiveUserProxy p)) (obj/type-of? p "ActiveUserProxy"))
(defn active-user-proxy (defn active-user-proxy
[plugin-id session-id] [plugin-id session-id]
(-> (ActiveUserProxy. plugin-id) (-> (obj/reify {:name "ActiveUserProxy"}
(add-session-properties session-id) :$plugin {:enumerable false :get (fn [] plugin-id)}
(crc/add-properties!
{:name "position" :get (fn [_] (-> (u/locate-presence session-id) :point format/format-point))} :position
{:name "zoom" :get (fn [_] (-> (u/locate-presence session-id) :zoom))}))) {: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 (defn- add-user-properties
[user-proxy data] [user-proxy data]
@ -75,13 +80,14 @@
{:name "avatarUrl" {:name "avatarUrl"
:get (fn [_] (cfg/resolve-profile-photo-url data))}))) :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 (defn user-proxy
[plugin-id data] [plugin-id data]
(-> (UserProxy. plugin-id) (-> (obj/reify {:name "UserProxy"}
:$plugin {:enumerable false :get (fn [] plugin-id)})
(add-user-properties data))) (add-user-properties data)))
(defn user-proxy?
[p]
(or (obj/type-of? p "UserProxy")
(current-user-proxy? p)
(active-user-proxy? p)))

View file

@ -119,26 +119,33 @@
flow-id (obj/get proxy "$id") flow-id (obj/get proxy "$id")
page (locate-page file-id page-id)] page (locate-page file-id page-id)]
(when (some? page) (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 (defn proxy->ruler-guide
[proxy] [proxy]
(let [file-id (obj/get proxy "$file") (let [file-id (obj/get proxy "$file")
page-id (obj/get proxy "$page") page-id (obj/get proxy "$page")
ruler-id (obj/get proxy "$id") ruler-id (obj/get proxy "$id")]
page (locate-page file-id page-id)] (locate-ruler-guide file-id page-id ruler-id)))
(when (some? page)
(d/seek #(= (:id %) ruler-id) (-> page :guides vals))))) (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 (defn proxy->interaction
[proxy] [proxy]
(let [file-id (obj/get proxy "$file") (let [file-id (obj/get proxy "$file")
page-id (obj/get proxy "$page") page-id (obj/get proxy "$page")
shape-id (obj/get proxy "$shape") shape-id (obj/get proxy "$shape")
index (obj/get proxy "$index") index (obj/get proxy "$index")]
shape (locate-shape file-id page-id shape-id)] (locate-interaction file-id page-id shape-id index)))
(when (some? shape)
(get-in shape [:interactions index]))))
(defn get-data (defn get-data
([self attr] ([self attr]
@ -193,3 +200,8 @@
(let [msg (dm/str "[PENPOT PLUGIN] Value not valid: " value ". Code: " code)] (let [msg (dm/str "[PENPOT PLUGIN] Value not valid: " value ". Code: " code)]
(.error js/console msg) (.error js/console msg)
(reject msg))) (reject msg)))
(defn mixed-value
[values]
(let [s (set values)]
(if (= (count s) 1) (first s) "mixed")))

View file

@ -7,7 +7,6 @@
(ns app.plugins.viewport (ns app.plugins.viewport
(:require (:require
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.record :as crc]
[app.common.spec :as us] [app.common.spec :as us]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.main.data.workspace.viewport :as dwv] [app.main.data.workspace.viewport :as dwv]
@ -17,45 +16,24 @@
[app.plugins.utils :as u] [app.plugins.utils :as u]
[app.util.object :as obj])) [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] (defn viewport-proxy? [p]
(instance? ViewportProxy p)) (obj/type-of? p "ViewportProxy"))
(defn viewport-proxy (defn viewport-proxy
[plugin-id] [plugin-id]
(crc/add-properties! (obj/reify {:name "ViewportProxy"}
(ViewportProxy. plugin-id) :$plugin {:enumerable false :get (fn [] plugin-id)}
{:name "center"
:get :center
(fn [_] {:get
(fn []
(let [vp (dm/get-in @st/state [:workspace-local :vbox]) (let [vp (dm/get-in @st/state [:workspace-local :vbox])
x (+ (:x vp) (/ (:width vp) 2)) x (+ (:x vp) (/ (:width vp) 2))
y (+ (:y vp) (/ (:height vp) 2))] y (+ (:y vp) (/ (:height vp) 2))]
(.freeze js/Object #js {:x x :y y}))) (.freeze js/Object #js {:x x :y y})))
:set :set
(fn [_ value] (fn [value]
(let [new-x (obj/get value "x") (let [new-x (obj/get value "x")
new-y (obj/get value "y")] new-y (obj/get value "y")]
(cond (cond
@ -76,12 +54,13 @@
:y #(+ % delta-y)}] :y #(+ % delta-y)}]
(st/emit! (dwv/update-viewport-position to-position))))))} (st/emit! (dwv/update-viewport-position to-position))))))}
{:name "zoom" :zoom
:get {:get
(fn [_] (fn []
(dm/get-in @st/state [:workspace-local :zoom])) (dm/get-in @st/state [:workspace-local :zoom]))
:set :set
(fn [_ value] (fn [value]
(cond (cond
(not (us/safe-number? value)) (not (us/safe-number? value))
(u/display-not-valid :zoom value) (u/display-not-valid :zoom value)
@ -90,10 +69,28 @@
(let [z (dm/get-in @st/state [:workspace-local :zoom])] (let [z (dm/get-in @st/state [:workspace-local :zoom])]
(st/emit! (dwz/set-zoom (/ value z))))))} (st/emit! (dwz/set-zoom (/ value z))))))}
{:name "bounds" :bounds
:get {:get
(fn [_] (fn []
(let [vbox (dm/get-in @st/state [:workspace-local :vbox])] (let [vbox (dm/get-in @st/state [:workspace-local :vbox])]
(.freeze js/Object (format/format-bounds 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))))))

View file

@ -0,0 +1,247 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.util.object
"A collection of helpers for work with javascript objects."
(:refer-clojure :exclude [set! new get merge clone contains? array? into-array reify])
#?(:cljs (:require-macros [app.util.object]))
(:require
[clojure.core :as c]))
#?(:cljs
(defn array?
[o]
(.isArray js/Array o)))
#?(:cljs
(defn into-array
[o]
(js/Array.from o)))
#?(:cljs
(defn create [] #js {}))
#?(:cljs
(defn get
([obj k]
(when (some? obj)
(unchecked-get obj k)))
([obj k default]
(let [result (get obj k)]
(if (undefined? result) default result)))))
#?(:cljs
(defn contains?
[obj k]
(when (some? obj)
(js/Object.hasOwn obj k))))
#?(:cljs
(defn clone
[a]
(js/Object.assign #js {} a)))
#?(:cljs
(defn merge!
([a b]
(js/Object.assign a b))
([a b & more]
(reduce merge! (merge! a b) more))))
#?(:cljs
(defn merge
([a b]
(js/Object.assign #js {} a b))
([a b & more]
(reduce merge! (merge a b) more))))
#?(:cljs
(defn set!
[obj key value]
(unchecked-set obj key value)
obj))
#?(:cljs
(defn unset!
[obj key]
(js-delete obj key)
obj))
#?(:cljs
(def ^:private not-found-sym
(js/Symbol "not-found")))
#?(:cljs
(defn update!
[obj key f & args]
(let [found (c/get obj key not-found-sym)]
(when-not ^boolean (identical? found not-found-sym)
(unchecked-set obj key (apply f found args)))
obj)))
#?(:cljs
(defn ^boolean in?
[obj prop]
(js* "~{} in ~{}" prop obj)))
#?(:cljs
(defn without-empty
[^js obj]
(when (some? obj)
(js* "Object.entries(~{}).reduce((a, [k,v]) => (v == null ? a : (a[k]=v, a)), {}) " obj))))
(defmacro add-properties!
"Adds properties to an object using `.defineProperty`"
[rsym & properties]
(let [rsym (with-meta rsym {:tag 'js})
getf-sym (with-meta (gensym (str rsym "-get-fn-")) {:tag 'js})
setf-sym (with-meta (gensym (str rsym "-set-fn-")) {:tag 'js})
this-sym (with-meta (gensym (str rsym "-this-")) {:tag 'js})
target-sym (with-meta (gensym (str rsym "-target-")) {:tag 'js})]
`(let [~target-sym ~rsym]
;; Creates the `.defineProperty` per property
~@(for [params properties
:let [pname (c/get params :name)
get-expr (c/get params :get)
set-expr (c/get params :set)
this? (c/get params :this true)
enum? (c/get params :enumerable true)
conf? (c/get params :configurable)
writ? (c/get params :writable)]]
`(let [~@(concat
(when get-expr
[getf-sym get-expr])
(when set-expr
[setf-sym set-expr]))]
(.defineProperty
js/Object
~target-sym
~pname
(cljs.core/js-obj
~@(concat
["enumerable" (boolean enum?)]
(when conf?
["configurable" true])
(when (some? writ?)
["writable" true])
(when get-expr
(if this?
["get" `(fn [] (cljs.core/this-as ~this-sym (~getf-sym ~this-sym)))]
["get" getf-sym]))
(when set-expr
(if this?
["set" `(fn [v#] (cljs.core/this-as ~this-sym (~setf-sym ~this-sym v#)))]
["set" setf-sym])))))))
;; Returns the object
~target-sym)))
(defn- collect-properties
[params]
(let [[tmeta params] (if (map? (first params))
[(first params) (rest params)]
[{} params])]
(loop [params (seq params)
props []
defs {}
curr :start
ckey nil]
(cond
(= curr :start)
(let [candidate (first params)]
(cond
(keyword? candidate)
(recur (rest params) props defs :property candidate)
(nil? candidate)
(recur (rest params) props defs :end nil)
:else
(recur (rest params) props defs :definition candidate)))
(= :end curr)
[tmeta props defs]
(= :property curr)
(let [definition (first params)]
(if (some? definition)
(let [definition (if (map? definition)
(c/merge {:this false} (assoc definition :name (name ckey)))
(-> {:enumerable false}
(c/merge (meta definition))
(assoc :name (name ckey))
(assoc :this false)
(assoc :get `(fn [] ~definition))))]
(recur (rest params)
(conj props definition)
defs
:start
nil))
(let [hint (str "expected property definition for: " curr)]
(throw (ex-info hint {:key curr})))))
(= :definition curr)
(let [[params props defs curr ckey]
(loop [params params
defs (update defs ckey #(or % []))]
(let [candidate (first params)
params (rest params)]
(cond
(nil? candidate)
[params props defs :end]
(keyword? candidate)
[params props defs :property candidate]
(symbol? candidate)
[params props defs :definition candidate]
:else
(recur params (update defs ckey conj candidate)))))]
(recur params props defs curr ckey))
:else
(throw (ex-info "invalid params" {}))))))
#?(:cljs
(def type-symbol
(js/Symbol.for "penpot.reify:type")))
#?(:cljs
(defn type-of?
[o t]
(let [o (get o type-symbol)]
(= o t))))
(defmacro reify
"A domain specific variation of reify that creates anonymous objects
on demand with the ability to assign protocol implementations and
custom properties"
[& params]
(let [[tmeta properties definitions] (collect-properties params)
obj-sym (gensym "obj-")]
`(let [~obj-sym (cljs.core/js-obj)]
(add-properties! ~obj-sym
~@(when-let [tname (:name tmeta)]
[`{:name ~'js/Symbol.toStringTag
:this false
:enumerable false
:get (fn [] ~tname)}
`{:name type-symbol
:this false
:enumerable false
:get (fn [] ~tname)}])
~@properties)
(let [~obj-sym ~(if-let [definitions (seq definitions)]
`(cljs.core/specify! ~obj-sym
~@(mapcat (fn [[k v]] (cons k v)) definitions))
obj-sym)]
(cljs.core/specify! ~obj-sym)))))

View file

@ -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)))