mirror of
https://github.com/penpot/penpot.git
synced 2025-02-09 00:28:20 -05:00
commit
46ed61f070
8 changed files with 963 additions and 875 deletions
File diff suppressed because it is too large
Load diff
|
@ -30,7 +30,7 @@
|
||||||
[app.main.ui.hooks.resize :as r]
|
[app.main.ui.hooks.resize :as r]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
[app.main.ui.workspace.plugins :as uwp]
|
[app.main.ui.workspace.plugins :as uwp]
|
||||||
[app.plugins :as plugins]
|
[app.plugins.register :as preg]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
[app.util.keyboard :as kbd]
|
[app.util.keyboard :as kbd]
|
||||||
|
@ -609,7 +609,7 @@
|
||||||
::mf/wrap [mf/memo]}
|
::mf/wrap [mf/memo]}
|
||||||
[{:keys [open-plugins on-close]}]
|
[{:keys [open-plugins on-close]}]
|
||||||
(when (features/active-feature? @st/state "plugins/runtime")
|
(when (features/active-feature? @st/state "plugins/runtime")
|
||||||
(let [plugins @plugins/pluginsdb]
|
(let [plugins (preg/plugins-list)]
|
||||||
[:& dropdown-menu {:show true
|
[:& dropdown-menu {:show true
|
||||||
:list-class (stl/css-case :sub-menu true :plugins true)
|
:list-class (stl/css-case :sub-menu true :plugins true)
|
||||||
:on-close on-close}
|
:on-close on-close}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
[app.main.ui.components.search-bar :refer [search-bar]]
|
[app.main.ui.components.search-bar :refer [search-bar]]
|
||||||
[app.main.ui.components.title-bar :refer [title-bar]]
|
[app.main.ui.components.title-bar :refer [title-bar]]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
[app.plugins :as plugins]
|
[app.plugins.register :as preg]
|
||||||
[app.util.avatars :as avatars]
|
[app.util.avatars :as avatars]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.http :as http]
|
[app.util.http :as http]
|
||||||
|
@ -80,7 +80,7 @@
|
||||||
::mf/register-as :plugin-management}
|
::mf/register-as :plugin-management}
|
||||||
[]
|
[]
|
||||||
|
|
||||||
(let [plugins-state* (mf/use-state @plugins/pluginsdb)
|
(let [plugins-state* (mf/use-state #(preg/plugins-list))
|
||||||
plugins-state @plugins-state*
|
plugins-state @plugins-state*
|
||||||
|
|
||||||
plugin-url* (mf/use-state "")
|
plugin-url* (mf/use-state "")
|
||||||
|
@ -117,14 +117,14 @@
|
||||||
(rx/subs!
|
(rx/subs!
|
||||||
(fn [body]
|
(fn [body]
|
||||||
(reset! fetching-manifest? false)
|
(reset! fetching-manifest? false)
|
||||||
(let [plugin (plugins/parser-manifest plugin-url body)]
|
(let [plugin (preg/parse-manifest plugin-url body)]
|
||||||
(st/emit! (ptk/event ::ev/event {::ev/name "install-plugin" :name (:name plugin) :url plugin-url}))
|
(st/emit! (ptk/event ::ev/event {::ev/name "install-plugin" :name (:name plugin) :url plugin-url}))
|
||||||
(modal/show!
|
(modal/show!
|
||||||
:plugin-permissions
|
:plugin-permissions
|
||||||
{:plugin plugin
|
{:plugin plugin
|
||||||
:on-accept
|
:on-accept
|
||||||
#(do
|
#(do
|
||||||
(plugins/install-plugin! plugin)
|
(preg/install-plugin! plugin)
|
||||||
(modal/show! :plugin-management {}))})
|
(modal/show! :plugin-management {}))})
|
||||||
(reset! input-status* :success)
|
(reset! input-status* :success)
|
||||||
(reset! plugin-url* "")))
|
(reset! plugin-url* "")))
|
||||||
|
@ -146,12 +146,13 @@
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps plugins-state)
|
(mf/deps plugins-state)
|
||||||
(fn [plugin-index]
|
(fn [plugin-index]
|
||||||
(let [plugin (nth @plugins/pluginsdb plugin-index)]
|
(let [plugins-list (preg/plugins-list)
|
||||||
|
plugin (nth plugins-list plugin-index)]
|
||||||
(st/emit! (ptk/event ::ev/event {::ev/name "remove-plugin"
|
(st/emit! (ptk/event ::ev/event {::ev/name "remove-plugin"
|
||||||
:name (:name plugin)
|
:name (:name plugin)
|
||||||
:host (:host plugin)}))
|
:host (:host plugin)}))
|
||||||
(plugins/remove-plugin! plugin)
|
(preg/remove-plugin! plugin)
|
||||||
(reset! plugins-state* @plugins/pluginsdb))))]
|
(reset! plugins-state* (preg/plugins-list)))))]
|
||||||
|
|
||||||
[:div {:class (stl/css :modal-overlay)}
|
[:div {:class (stl/css :modal-overlay)}
|
||||||
[:div {:class (stl/css :modal-dialog :plugin-management)}
|
[:div {:class (stl/css :modal-dialog :plugin-management)}
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
(ns app.plugins
|
(ns app.plugins
|
||||||
"RPC for plugins runtime."
|
"RPC for plugins runtime."
|
||||||
(:require
|
(:require
|
||||||
[app.common.uuid :as uuid]
|
|
||||||
[app.main.features :as features]
|
[app.main.features :as features]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.plugins.api :as api]
|
[app.plugins.api :as api]
|
||||||
|
@ -18,10 +17,6 @@
|
||||||
[beicon.v2.core :as rx]
|
[beicon.v2.core :as rx]
|
||||||
[potok.v2.core :as ptk]))
|
[potok.v2.core :as ptk]))
|
||||||
|
|
||||||
(def pluginsdb register/pluginsdb)
|
|
||||||
(def install-plugin! register/install-plugin!)
|
|
||||||
(def remove-plugin! register/remove-plugin!)
|
|
||||||
|
|
||||||
(defn init-plugins-runtime!
|
(defn init-plugins-runtime!
|
||||||
[]
|
[]
|
||||||
(when-let [init-runtime (obj/get global "initPluginsRuntime")]
|
(when-let [init-runtime (obj/get global "initPluginsRuntime")]
|
||||||
|
@ -41,28 +36,3 @@
|
||||||
(rx/tap init-plugins-runtime!)
|
(rx/tap init-plugins-runtime!)
|
||||||
(rx/ignore)))))
|
(rx/ignore)))))
|
||||||
|
|
||||||
(defn parser-manifest
|
|
||||||
[plugin-url ^js manifest]
|
|
||||||
(let [name (obj/get manifest "name")
|
|
||||||
desc (obj/get manifest "description")
|
|
||||||
code (obj/get manifest "code")
|
|
||||||
icon (obj/get manifest "icon")
|
|
||||||
|
|
||||||
permissions (into #{} (obj/get manifest "permissions" []))
|
|
||||||
permissions
|
|
||||||
(cond-> permissions
|
|
||||||
(contains? permissions "content:write")
|
|
||||||
(conj "content:read")
|
|
||||||
|
|
||||||
(contains? permissions "library:write")
|
|
||||||
(conj "content:write"))
|
|
||||||
|
|
||||||
origin (obj/get (js/URL. plugin-url) "origin")
|
|
||||||
plugin-id (str (uuid/next))]
|
|
||||||
{:plugin-id plugin-id
|
|
||||||
:name name
|
|
||||||
:description desc
|
|
||||||
:host origin
|
|
||||||
:code code
|
|
||||||
:icon icon
|
|
||||||
:permissions (->> permissions (mapv str))}))
|
|
||||||
|
|
|
@ -61,8 +61,8 @@
|
||||||
(deftype PenpotContext [$plugin]
|
(deftype PenpotContext [$plugin]
|
||||||
Object
|
Object
|
||||||
(addListener
|
(addListener
|
||||||
[_ type callback]
|
[_ type callback props]
|
||||||
(events/add-listener type $plugin callback))
|
(events/add-listener type $plugin callback props))
|
||||||
|
|
||||||
(removeListener
|
(removeListener
|
||||||
[_ listener-id]
|
[_ listener-id]
|
||||||
|
|
|
@ -6,15 +6,20 @@
|
||||||
|
|
||||||
(ns app.plugins.events
|
(ns app.plugins.events
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
|
[app.main.data.workspace.state-helpers :as wsh]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.plugins.file :as file]
|
[app.plugins.file :as file]
|
||||||
[app.plugins.page :as page]
|
[app.plugins.page :as page]
|
||||||
|
[app.plugins.parser :as parser]
|
||||||
|
[app.plugins.shape :as shape]
|
||||||
|
[app.util.object :as obj]
|
||||||
[goog.functions :as gf]))
|
[goog.functions :as gf]))
|
||||||
|
|
||||||
(defmulti handle-state-change (fn [type _] type))
|
(defmulti handle-state-change (fn [type _] type))
|
||||||
|
|
||||||
(defmethod handle-state-change "finish"
|
(defmethod handle-state-change "finish"
|
||||||
[_ _ old-val new-val]
|
[_ _ old-val new-val _]
|
||||||
(let [old-file-id (:current-file-id old-val)
|
(let [old-file-id (:current-file-id old-val)
|
||||||
new-file-id (:current-file-id new-val)]
|
new-file-id (:current-file-id new-val)]
|
||||||
(if (and (some? old-file-id) (nil? new-file-id))
|
(if (and (some? old-file-id) (nil? new-file-id))
|
||||||
|
@ -22,7 +27,7 @@
|
||||||
::not-changed)))
|
::not-changed)))
|
||||||
|
|
||||||
(defmethod handle-state-change "filechange"
|
(defmethod handle-state-change "filechange"
|
||||||
[_ plugin-id old-val new-val]
|
[_ plugin-id old-val new-val _]
|
||||||
(let [old-file-id (:current-file-id old-val)
|
(let [old-file-id (:current-file-id old-val)
|
||||||
new-file-id (:current-file-id new-val)]
|
new-file-id (:current-file-id new-val)]
|
||||||
(if (identical? old-file-id new-file-id)
|
(if (identical? old-file-id new-file-id)
|
||||||
|
@ -30,7 +35,7 @@
|
||||||
(file/file-proxy plugin-id new-file-id))))
|
(file/file-proxy plugin-id new-file-id))))
|
||||||
|
|
||||||
(defmethod handle-state-change "pagechange"
|
(defmethod handle-state-change "pagechange"
|
||||||
[_ plugin-id old-val new-val]
|
[_ plugin-id old-val new-val _]
|
||||||
(let [old-page-id (:current-page-id old-val)
|
(let [old-page-id (:current-page-id old-val)
|
||||||
new-page-id (:current-page-id new-val)]
|
new-page-id (:current-page-id new-val)]
|
||||||
(if (identical? old-page-id new-page-id)
|
(if (identical? old-page-id new-page-id)
|
||||||
|
@ -38,7 +43,7 @@
|
||||||
(page/page-proxy plugin-id (:current-file-id new-val) new-page-id))))
|
(page/page-proxy plugin-id (:current-file-id new-val) new-page-id))))
|
||||||
|
|
||||||
(defmethod handle-state-change "selectionchange"
|
(defmethod handle-state-change "selectionchange"
|
||||||
[_ _ old-val new-val]
|
[_ _ old-val new-val _]
|
||||||
(let [old-selection (get-in old-val [:workspace-local :selected])
|
(let [old-selection (get-in old-val [:workspace-local :selected])
|
||||||
new-selection (get-in new-val [:workspace-local :selected])]
|
new-selection (get-in new-val [:workspace-local :selected])]
|
||||||
(if (identical? old-selection new-selection)
|
(if (identical? old-selection new-selection)
|
||||||
|
@ -46,7 +51,7 @@
|
||||||
(apply array (map str new-selection)))))
|
(apply array (map str new-selection)))))
|
||||||
|
|
||||||
(defmethod handle-state-change "themechange"
|
(defmethod handle-state-change "themechange"
|
||||||
[_ _ old-val new-val]
|
[_ _ old-val new-val _]
|
||||||
(let [old-theme (get-in old-val [:profile :theme])
|
(let [old-theme (get-in old-val [:profile :theme])
|
||||||
new-theme (get-in new-val [:profile :theme])]
|
new-theme (get-in new-val [:profile :theme])]
|
||||||
(if (identical? old-theme new-theme)
|
(if (identical? old-theme new-theme)
|
||||||
|
@ -55,23 +60,55 @@
|
||||||
"dark"
|
"dark"
|
||||||
new-theme))))
|
new-theme))))
|
||||||
|
|
||||||
|
(defmethod handle-state-change "shapechange"
|
||||||
|
[_ plugin-id old-val new-val props]
|
||||||
|
(let [shape-id (-> (obj/get props "shapeId") parser/parse-id)
|
||||||
|
old-shape (wsh/lookup-shape old-val shape-id)
|
||||||
|
new-shape (wsh/lookup-shape new-val shape-id)
|
||||||
|
|
||||||
|
file-id (:current-file-id new-val)
|
||||||
|
page-id (:current-page-id new-val)]
|
||||||
|
(if (and (identical? old-shape new-shape) (some? plugin-id) (some? file-id) (some? page-id) (some? shape-id))
|
||||||
|
::not-changed
|
||||||
|
(shape/shape-proxy plugin-id file-id page-id shape-id))))
|
||||||
|
|
||||||
|
(defmethod handle-state-change "contentsave"
|
||||||
|
[_ _ old-val new-val _]
|
||||||
|
(let [old-status (dm/get-in old-val [:persistence :status])
|
||||||
|
new-status (dm/get-in new-val [:persistence :status])]
|
||||||
|
(if (and (= :saved new-status) (not= new-status old-status))
|
||||||
|
::void ;; Changed but void
|
||||||
|
::not-changed)))
|
||||||
|
|
||||||
(defmethod handle-state-change :default
|
(defmethod handle-state-change :default
|
||||||
[_ _ _ _]
|
[_ _ _ _]
|
||||||
::not-changed)
|
::not-changed)
|
||||||
|
|
||||||
(defn add-listener
|
(defn add-listener
|
||||||
[type plugin-id callback]
|
[type plugin-id callback props]
|
||||||
(let [key (js/Symbol)
|
(let [plugin-id (parser/parse-id plugin-id)
|
||||||
callback (gf/debounce callback 10)]
|
key (js/Symbol)
|
||||||
|
|
||||||
|
;; We wrap the callback in an exception handler so the plugins
|
||||||
|
;; don't crash the application
|
||||||
|
safe-callback
|
||||||
|
(fn [value]
|
||||||
|
(try
|
||||||
|
(if (= ::void value)
|
||||||
|
(callback)
|
||||||
|
(callback value))
|
||||||
|
(catch :default cause
|
||||||
|
(.error js/console cause))))
|
||||||
|
|
||||||
|
;; We also debounce the callbacks so we don't get too many at the same time
|
||||||
|
debounced-callback (gf/debounce safe-callback 10)]
|
||||||
|
|
||||||
(add-watch
|
(add-watch
|
||||||
st/state key
|
st/state key
|
||||||
(fn [_ _ old-val new-val]
|
(fn [_ _ old-val new-val]
|
||||||
(let [result (handle-state-change type plugin-id old-val new-val)]
|
(let [result (handle-state-change type plugin-id old-val new-val props)]
|
||||||
(when (not= ::not-changed result)
|
(when (not= ::not-changed result)
|
||||||
(try
|
(debounced-callback result)))))
|
||||||
(callback result)
|
|
||||||
(catch :default cause
|
|
||||||
(.error js/console cause)))))))
|
|
||||||
|
|
||||||
;; return the generated key
|
;; return the generated key
|
||||||
key))
|
key))
|
||||||
|
|
|
@ -7,54 +7,126 @@
|
||||||
(ns app.plugins.register
|
(ns app.plugins.register
|
||||||
"RPC for plugins runtime."
|
"RPC for plugins runtime."
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]))
|
[app.common.data :as d]
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
|
[app.util.object :as obj]))
|
||||||
|
|
||||||
;; TODO: Remove clj->js and parse into a better data structure for accessing the permissions
|
;; Stores the installed plugins information
|
||||||
|
(defonce ^:private registry (atom {}))
|
||||||
|
|
||||||
(def pluginsdb (atom nil))
|
(defn plugins-list
|
||||||
|
"Retrieves the plugin data as an ordered list of plugin elements"
|
||||||
|
[]
|
||||||
|
(->> (:ids @registry)
|
||||||
|
(mapv #(dm/get-in @registry [:data %]))))
|
||||||
|
|
||||||
|
(defn parse-manifest
|
||||||
|
"Read the manifest.json defined by the plugins definition and transforms it into an
|
||||||
|
object that will be stored in the register."
|
||||||
|
[plugin-url ^js manifest]
|
||||||
|
(let [name (obj/get manifest "name")
|
||||||
|
desc (obj/get manifest "description")
|
||||||
|
code (obj/get manifest "code")
|
||||||
|
icon (obj/get manifest "icon")
|
||||||
|
|
||||||
|
permissions (into #{} (obj/get manifest "permissions" []))
|
||||||
|
permissions
|
||||||
|
(cond-> permissions
|
||||||
|
(contains? permissions "content:write")
|
||||||
|
(conj "content:read")
|
||||||
|
|
||||||
|
(contains? permissions "library:write")
|
||||||
|
(conj "content:write"))
|
||||||
|
|
||||||
|
origin (obj/get (js/URL. plugin-url) "origin")
|
||||||
|
|
||||||
|
prev-plugin
|
||||||
|
(->> (:data @registry)
|
||||||
|
(vals)
|
||||||
|
(d/seek (fn [plugin]
|
||||||
|
(and (= name (:name plugin))
|
||||||
|
(= origin (:host plugin))))))
|
||||||
|
|
||||||
|
plugin-id (d/nilv (:plugin-id prev-plugin) (str (uuid/next)))]
|
||||||
|
{:plugin-id plugin-id
|
||||||
|
:name name
|
||||||
|
:description desc
|
||||||
|
:host origin
|
||||||
|
:code code
|
||||||
|
:icon icon
|
||||||
|
:permissions (into #{} (map str) permissions)}))
|
||||||
|
|
||||||
|
(defn format-plugin-data
|
||||||
|
"Format into a JS object the plugin data. This will be used to be stored in the local storage."
|
||||||
|
[{:keys [plugin-id name description host code icon permissions]}]
|
||||||
|
#js {:plugin-id plugin-id
|
||||||
|
:name name
|
||||||
|
:description description
|
||||||
|
:host host
|
||||||
|
:code code
|
||||||
|
:icon icon
|
||||||
|
:permissions (apply array permissions)})
|
||||||
|
|
||||||
|
(defn parse-plugin-data
|
||||||
|
"Parsers the JS plugin data into a CLJS data structure. This will be used primarily when the local storage
|
||||||
|
data is retrieved"
|
||||||
|
[^js data]
|
||||||
|
{:plugin-id (obj/get data "plugin-id")
|
||||||
|
:name (obj/get data "name")
|
||||||
|
:description (obj/get data "description")
|
||||||
|
:host (obj/get data "host")
|
||||||
|
:code (obj/get data "code")
|
||||||
|
:icon (obj/get data "icon")
|
||||||
|
:permissions (into #{} (obj/get data "permissions"))})
|
||||||
|
|
||||||
(defn load-from-store
|
(defn load-from-store
|
||||||
[]
|
[]
|
||||||
(let [ls (.-localStorage js/window)
|
(let [ls (.-localStorage js/window)
|
||||||
plugins-val (.getItem ls "plugins")]
|
plugins-val (.getItem ls "plugins")]
|
||||||
(when plugins-val
|
(when plugins-val
|
||||||
(let [plugins-js (.parse js/JSON plugins-val)]
|
(let [stored (->> (.parse js/JSON plugins-val)
|
||||||
(js->clj plugins-js {:keywordize-keys true})))))
|
(map parse-plugin-data))]
|
||||||
|
(reset! registry
|
||||||
|
{:ids (->> stored (map :plugin-id))
|
||||||
|
:data (d/index-by :plugin-id stored)})))))
|
||||||
|
|
||||||
(defn save-to-store
|
(defn save-to-store
|
||||||
[plugins]
|
[]
|
||||||
(let [ls (.-localStorage js/window)
|
(->> (:ids @registry)
|
||||||
plugins-js (clj->js plugins)
|
(map #(dm/get-in @registry [:data %]))
|
||||||
plugins-val (.stringify js/JSON plugins-js)]
|
(map format-plugin-data)
|
||||||
(.setItem ls "plugins" plugins-val)))
|
(apply array)
|
||||||
|
(.stringify js/JSON)
|
||||||
|
(.setItem (.-localStorage js/window) "plugins")))
|
||||||
|
|
||||||
(defn init
|
(defn init
|
||||||
[]
|
[]
|
||||||
(reset! pluginsdb (load-from-store)))
|
(load-from-store))
|
||||||
|
|
||||||
(defn install-plugin!
|
(defn install-plugin!
|
||||||
[plugin]
|
[plugin]
|
||||||
(let [plugins (as-> @pluginsdb $
|
(letfn [(update-ids [ids]
|
||||||
(remove (fn [{:keys [name host]}]
|
(conj
|
||||||
(and (= name (:name plugin))
|
(->> ids (remove #(= % (:plugin-id plugin))))
|
||||||
(= host (:host plugin)))) $)
|
(:plugin-id plugin)))]
|
||||||
(conj $ plugin)
|
(swap! registry #(-> %
|
||||||
(vec $))]
|
(update :ids update-ids)
|
||||||
(reset! pluginsdb plugins)
|
(update :data assoc (:plugin-id plugin) plugin)))
|
||||||
(save-to-store plugins)))
|
(save-to-store)))
|
||||||
|
|
||||||
(defn remove-plugin!
|
(defn remove-plugin!
|
||||||
[{:keys [plugin-id]}]
|
[{:keys [plugin-id]}]
|
||||||
(let [plugins
|
(letfn [(update-ids [ids]
|
||||||
(into []
|
(->> ids
|
||||||
(keep (fn [plugin]
|
(remove #(= % plugin-id))))]
|
||||||
(when (not= plugin-id (:plugin-id plugin)) plugin)))
|
(swap! registry #(-> %
|
||||||
@pluginsdb)]
|
(update :ids update-ids)
|
||||||
(reset! pluginsdb plugins)
|
(update :data dissoc plugin-id)))
|
||||||
(save-to-store plugins)))
|
(save-to-store)))
|
||||||
|
|
||||||
(defn check-permission
|
(defn check-permission
|
||||||
[plugin-id permission]
|
[plugin-id permission]
|
||||||
(or (= plugin-id "TEST")
|
(or (= plugin-id "TEST")
|
||||||
(let [{:keys [permissions]} (->> @pluginsdb (d/seek #(= (:plugin-id %) plugin-id)))]
|
(let [{:keys [permissions]} (dm/get-in @registry [:data plugin-id])]
|
||||||
(->> permissions (d/seek #(= % permission))))))
|
(contains? permissions permission))))
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.main.data.workspace :as dw]
|
[app.main.data.workspace :as dw]
|
||||||
[app.main.data.workspace.groups :as dwg]
|
[app.main.data.workspace.groups :as dwg]
|
||||||
|
[app.main.data.workspace.libraries :as dwl]
|
||||||
[app.main.data.workspace.selection :as dws]
|
[app.main.data.workspace.selection :as dws]
|
||||||
[app.main.data.workspace.shape-layout :as dwsl]
|
[app.main.data.workspace.shape-layout :as dwsl]
|
||||||
[app.main.data.workspace.shapes :as dwsh]
|
[app.main.data.workspace.shapes :as dwsh]
|
||||||
|
@ -441,6 +442,10 @@
|
||||||
(let [[root component] (u/locate-component objects shape)]
|
(let [[root component] (u/locate-component objects shape)]
|
||||||
(lib-component-proxy $plugin (:component-file root) (:id component))))))
|
(lib-component-proxy $plugin (:component-file root) (:id component))))))
|
||||||
|
|
||||||
|
(detach
|
||||||
|
[_]
|
||||||
|
(st/emit! (dwl/detach-component $id)))
|
||||||
|
|
||||||
(export
|
(export
|
||||||
[self value]
|
[self value]
|
||||||
(let [value (parser/parse-export value)]
|
(let [value (parser/parse-export value)]
|
||||||
|
@ -819,6 +824,13 @@
|
||||||
:else
|
:else
|
||||||
(st/emit! (dw/update-position id {:y value})))))}
|
(st/emit! (dw/update-position id {:y value})))))}
|
||||||
|
|
||||||
|
{:name "parent"
|
||||||
|
;; not enumerable so there are no infinite loops
|
||||||
|
:enumerable false
|
||||||
|
:get (fn [self]
|
||||||
|
(let [shape (u/proxy->shape self)
|
||||||
|
parent-id (:parent-id shape)]
|
||||||
|
(shape-proxy (obj/get self "$file") (obj/get self "$page") parent-id)))}
|
||||||
{:name "parentX"
|
{:name "parentX"
|
||||||
:get (fn [self]
|
:get (fn [self]
|
||||||
(let [shape (u/proxy->shape self)
|
(let [shape (u/proxy->shape self)
|
||||||
|
|
Loading…
Add table
Reference in a new issue