From 00de89197ebd65c1682f55568098cee8c5e7ad0b Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 22 Apr 2024 15:40:01 +0200 Subject: [PATCH] :sparkles: Add write apis to the plugins --- common/src/app/common/text.cljc | 47 +++++++++++ frontend/src/app/plugins/api.cljs | 27 ++++++- frontend/src/app/plugins/events.cljs | 2 - frontend/src/app/plugins/shape.cljs | 113 +++++++++++++++++++++------ 4 files changed, 160 insertions(+), 29 deletions(-) diff --git a/common/src/app/common/text.cljc b/common/src/app/common/text.cljc index 8b301d2d4..b164633a8 100644 --- a/common/src/app/common/text.cljc +++ b/common/src/app/common/text.cljc @@ -381,6 +381,53 @@ (-> (rec-style-text-map [] node {}) reverse))) +(defn content->text + "Given a root node of a text content extracts the texts with its associated styles" + [content] + (letfn [(add-node [acc node] + (cond + (is-paragraph-node? node) + (conj acc []) + + (is-text-node? node) + (let [i (dec (count acc))] + (update acc i conj (:text node))) + + :else + acc))] + (->> (node-seq content) + (reduce add-node []) + (map #(str/join "" %)) + (str/join "\n")))) + +(defn change-text + "Changes the content of the text shape to use the text as argument. Will use the styles of the + first paragraph and text that is present in the shape (and override the rest)" + [shape text] + (let [content (:content shape) + + paragraph-style (select-keys (->> content (node-seq is-paragraph-node?) first) text-all-attrs) + text-style (select-keys (->> content (node-seq is-text-node?) first) text-all-attrs) + + paragraph-texts (str/split text "\n") + + paragraphs + (->> paragraph-texts + (mapv + (fn [pt] + (merge + paragraph-style + {:type "paragraph" + :children [(merge {:text pt} text-style)]})))) + + new-content + {:type "root" + :children + [{:type "paragraph-set" + :children paragraphs}]}] + + (assoc shape :content new-content))) + (defn index-content "Adds a property `$id` that identifies the current node inside" ([content] diff --git a/frontend/src/app/plugins/api.cljs b/frontend/src/app/plugins/api.cljs index 74efdb66e..51ed5155c 100644 --- a/frontend/src/app/plugins/api.cljs +++ b/frontend/src/app/plugins/api.cljs @@ -8,8 +8,11 @@ "RPC for plugins runtime." (:require [app.common.data.macros :as dm] + [app.common.files.changes-builder :as cb] [app.common.record :as cr] + [app.common.types.shape :as cts] [app.common.uuid :as uuid] + [app.main.data.workspace.changes :as ch] [app.main.store :as st] [app.plugins.events :as events] [app.plugins.file :as file] @@ -37,8 +40,9 @@ (getPage [_] - (let [page-id (:current-page-id @st/state)] - (page/data->page-proxy (dm/get-in @st/state [:workspace-data :pages-index page-id])))) + (let [page-id (:current-page-id @st/state) + page (dm/get-in @st/state [:workspace-data :pages-index page-id])] + (page/data->page-proxy page))) (getSelected [_] @@ -64,11 +68,26 @@ (let [theme (get-in @st/state [:profile :theme])] (if (or (not theme) (= theme "default")) "dark" - (get-in @st/state [:profile :theme]))))) + (get-in @st/state [:profile :theme])))) + + (createRectangle + [_] + (let [page-id (:current-page-id @st/state) + page (dm/get-in @st/state [:workspace-data :pages-index page-id]) + shape (cts/setup-shape {:type :rect + :x 0 :y 0 :width 100 :height 100}) + changes + (-> (cb/empty-changes) + (cb/with-page page) + (cb/with-objects (:objects page)) + (cb/add-object shape))] + (st/emit! (ch/commit-changes changes)) + (shape/data->shape-proxy shape)))) (defn create-context [] (cr/add-properties! (PenpotContext.) {:name "root" :get #(.getRoot ^js %)} - {:name "currentPage" :get #(.getPage ^js %)})) + {:name "currentPage" :get #(.getPage ^js %)} + {:name "selection" :get #(.getSelectedShapes ^js %)})) diff --git a/frontend/src/app/plugins/events.cljs b/frontend/src/app/plugins/events.cljs index ae1d17bd0..6963174ab 100644 --- a/frontend/src/app/plugins/events.cljs +++ b/frontend/src/app/plugins/events.cljs @@ -53,12 +53,10 @@ "dark" new-theme)))) - (defmethod handle-state-change :default [_ _ _] ::not-changed) - (defn add-listener [type callback] (let [key (js/Symbol) diff --git a/frontend/src/app/plugins/shape.cljs b/frontend/src/app/plugins/shape.cljs index 2ff971fe5..8a7aea836 100644 --- a/frontend/src/app/plugins/shape.cljs +++ b/frontend/src/app/plugins/shape.cljs @@ -7,8 +7,12 @@ (ns app.plugins.shape "RPC for plugins runtime." (:require + [app.common.data :as d] [app.common.data.macros :as dm] + [app.common.files.helpers :as cfh] [app.common.record :as crc] + [app.common.text :as txt] + [app.main.data.workspace :as udw] [app.main.data.workspace.changes :as dwc] [app.main.store :as st] [app.plugins.utils :refer [get-data get-data-fn]] @@ -18,24 +22,51 @@ (defn- make-fills [fills] - ;; TODO: Transform explicitly? - (apply array - (->> fills - (map #(clj->js % {:keyword-fn (fn [k] (str/camel (name k)))}))))) + (.freeze + js/Object + (apply array + (->> fills + ;; TODO: Transform explicitly instead of cljs->js? + (map #(clj->js % {:keyword-fn (fn [k] (str/camel (name k)))})))))) + +(defn- make-strokes + [strokes] + (.freeze + js/Object + (apply array + (->> strokes + ;; TODO: Transform explicitly instead of cljs->js? + (map #(clj->js % {:keyword-fn (fn [k] (str/camel (name k)))})))))) (defn- locate-shape [shape-id] (let [page-id (:current-page-id @st/state)] (dm/get-in @st/state [:workspace-data :pages-index page-id :objects shape-id]))) -(deftype ShapeProxy [#_:clj-kondo/ignore _data] +(defn- get-state + ([self attr] + (let [id (get-data self :id) + page-id (d/nilv (get-data self :page-id) (:current-page-id @st/state))] + (dm/get-in @st/state [:workspace-data :pages-index page-id :objects id attr]))) + ([self attr mapfn] + (-> (get-state self attr) + (mapfn)))) + +(deftype ShapeProxy [^:mutable #_:clj-kondo/ignore _data] Object (getChildren [self] - (apply array (->> (get-data self :shapes) + (apply array (->> (get-state self :shapes) (map locate-shape) (map data->shape-proxy)))) + (resize + [self width height] + + (let [id (get-data self :id)] + (st/emit! (udw/update-dimensions [id] :width width) + (udw/update-dimensions [id] :height height)))) + (clone [_] (.log js/console (clj->js _data))) (delete [_] (.log js/console (clj->js _data))) (appendChild [_] (.log js/console (clj->js _data)))) @@ -48,25 +79,61 @@ (defn data->shape-proxy [data] - (crc/add-properties! - (ShapeProxy. data) - {:name "_data" - :enumerable false} + (-> (ShapeProxy. data) + (crc/add-properties! + {:name "_data" + :enumerable false} - {:name "id" - :get (get-data-fn :id str)} + {:name "id" + :get (get-data-fn :id str)} - {:name "name" - :get (get-data-fn :name) - :set (fn [self value] - (let [id (get-data self :id)] - (st/emit! (dwc/update-shapes [id] #(assoc % :name value)))))} + {:name "type" + :get (get-data-fn :type)} - {:name "children" - :get #(.getChildren ^js %)} + {:name "x" + :get #(get-state % :x) + :set + (fn [self value] + (let [id (get-data self :id)] + (st/emit! (udw/update-position id {:x value}))))} - {:name "fills" - :get (get-data-fn :fills make-fills) - ;;:set (fn [self value] (.log js/console self value)) - })) + {:name "y" + :get #(get-state % :y) + :set + (fn [self value] + (let [id (get-data self :id)] + (st/emit! (udw/update-position id {:y value}))))} + + {:name "width" + :get #(get-state % :width)} + + {:name "height" + :get #(get-state % :height)} + + {:name "name" + :get #(get-state % :name) + :set (fn [self value] + (let [id (get-data self :id)] + (st/emit! (dwc/update-shapes [id] #(assoc % :name value)))))} + + {:name "children" + :get #(.getChildren ^js %)} + + {:name "fills" + :get #(get-state % :fills make-fills) + ;;:set (fn [self value] (.log js/console self value)) + } + + {:name "strokes" + :get #(get-state % :strokes make-strokes) + ;;:set (fn [self value] (.log js/console self value)) + }) + + (cond-> (cfh/text-shape? data) + (crc/add-properties! + {:name "characters" + :get #(get-state % :content txt/content->text) + :set (fn [self value] + (let [id (get-data self :id)] + (st/emit! (dwc/update-shapes [id] #(txt/change-text % value)))))}))))