From e183d67e2a14420fb3ae10c70aceadd68c7d17c5 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 17 Feb 2022 18:10:28 +0100 Subject: [PATCH] :sparkles: Add spec for new text data --- backend/src/app/http/errors.clj | 10 ++-- common/src/app/common/spec/shape.cljc | 43 ++++++++++++++++- .../src/app/main/ui/shapes/text/styles.cljs | 3 +- .../src/app/main/ui/shapes/text/svg_text.cljs | 2 +- .../app/main/ui/workspace/shapes/text.cljs | 26 +++++----- .../main/ui/workspace/shapes/text/editor.cljs | 4 -- .../src/app/main/ui/workspace/viewport.cljs | 5 +- .../main/ui/workspace/viewport/actions.cljs | 3 +- .../app/main/ui/workspace/viewport/hooks.cljs | 3 +- frontend/src/app/util/text_svg_position.cljs | 48 +++++++++---------- 10 files changed, 93 insertions(+), 54 deletions(-) diff --git a/backend/src/app/http/errors.clj b/backend/src/app/http/errors.clj index 0c24a3df7..c7f2a9ba6 100644 --- a/backend/src/app/http/errors.clj +++ b/backend/src/app/http/errors.clj @@ -61,13 +61,13 @@ explain (us/pretty-explain data)] {:status 400 :body (-> data - (dissoc ::s/problems) - (dissoc ::s/value) + (dissoc ::s/problems ::s/value) (cond-> explain (assoc :explain explain)))})) (defmethod handle-exception :assertion [error request] - (let [edata (ex-data error)] + (let [edata (ex-data error) + explain (us/pretty-explain edata)] (l/error ::l/raw (ex-message error) ::l/context (get-error-context request error) :cause error) @@ -75,7 +75,9 @@ {:status 500 :body {:type :server-error :code :assertion - :data (dissoc edata ::s/problems ::s/value ::s/spec)}})) + :data (-> edata + (dissoc ::s/problems ::s/value ::s/spec) + (cond-> explain (assoc :explain explain)))}})) (defmethod handle-exception :not-found [err _] diff --git a/common/src/app/common/spec/shape.cljc b/common/src/app/common/spec/shape.cljc index 08ab1b023..54853d259 100644 --- a/common/src/app/common/spec/shape.cljc +++ b/common/src/app/common/spec/shape.cljc @@ -208,6 +208,46 @@ :text-content (s/keys :req-un [:internal.shape.text/text])))) +(s/def :internal.shape.text/position-data + (s/coll-of :internal.shape.text/position-data-element + :kind vector? + :min-count 1)) + +(s/def :internal.shape.text/position-data-element + (s/keys :req-un [:internal.shape.text.position-data/x + :internal.shape.text.position-data/y + :internal.shape.text.position-data/width + :internal.shape.text.position-data/height] + :opt-un [:internal.shape.text.position-data/fill-color + :internal.shape.text.position-data/fill-opacity + :internal.shape.text.position-data/font-family + :internal.shape.text.position-data/font-size + :internal.shape.text.position-data/font-style + :internal.shape.text.position-data/font-weight + :internal.shape.text.position-data/rtl? + :internal.shape.text.position-data/text + :internal.shape.text.position-data/text-decoration + :internal.shape.text.position-data/text-transform] + )) + +(s/def :internal.shape.text.position-data/x ::us/safe-number) +(s/def :internal.shape.text.position-data/y ::us/safe-number) +(s/def :internal.shape.text.position-data/width ::us/safe-number) +(s/def :internal.shape.text.position-data/height ::us/safe-number) + +(s/def :internal.shape.text.position-data/fill-color ::fill-color) +(s/def :internal.shape.text.position-data/fill-opacity ::fill-opacity) +(s/def :internal.shape.text.position-data/fill-color-gradient ::fill-color-gradient) + +(s/def :internal.shape.text.position-data/font-family string?) +(s/def :internal.shape.text.position-data/font-size string?) +(s/def :internal.shape.text.position-data/font-style string?) +(s/def :internal.shape.text.position-data/font-weight string?) +(s/def :internal.shape.text.position-data/rtl? boolean?) +(s/def :internal.shape.text.position-data/text string?) +(s/def :internal.shape.text.position-data/text-decoration string?) +(s/def :internal.shape.text.position-data/text-transform string?) + (s/def :internal.shape.path/command keyword?) (s/def :internal.shape.path/params (s/nilable (s/map-of keyword? any?))) @@ -226,7 +266,8 @@ (defmethod shape-spec :text [_] (s/and ::shape-attrs - (s/keys :opt-un [:internal.shape.text/content]))) + (s/keys :opt-un [:internal.shape.text/content + :internal.shape.text/position-data]))) (defmethod shape-spec :path [_] (s/and ::shape-attrs diff --git a/frontend/src/app/main/ui/shapes/text/styles.cljs b/frontend/src/app/main/ui/shapes/text/styles.cljs index d223087d2..fc65f836b 100644 --- a/frontend/src/app/main/ui/shapes/text/styles.cljs +++ b/frontend/src/app/main/ui/shapes/text/styles.cljs @@ -35,8 +35,7 @@ ;; the property it's known. ;; `inline-flex` is similar to flex but `overflows` outside the bounds of the ;; parent - (let [auto-width? (= grow-type :auto-width) - auto-height? (= grow-type :auto-height)] + (let [auto-width? (= grow-type :auto-width)] #js {:display "inline-flex" :flexDirection "column" :justifyContent "inherit" diff --git a/frontend/src/app/main/ui/shapes/text/svg_text.cljs b/frontend/src/app/main/ui/shapes/text/svg_text.cljs index 468ffbe08..2be61f6da 100644 --- a/frontend/src/app/main/ui/shapes/text/svg_text.cljs +++ b/frontend/src/app/main/ui/shapes/text/svg_text.cljs @@ -23,7 +23,7 @@ [props] (let [render-id (mf/use-ctx muc/render-ctx) - {:keys [id x y width height position-data] :as shape} (obj/get props "shape") + {:keys [position-data] :as shape} (obj/get props "shape") transform (str (gsh/transform-matrix shape)) group-props (-> #js {:transform transform} (attrs/add-style-attrs shape render-id)) diff --git a/frontend/src/app/main/ui/workspace/shapes/text.cljs b/frontend/src/app/main/ui/workspace/shapes/text.cljs index e6ee44b87..ca7a373c7 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text.cljs @@ -7,6 +7,7 @@ (ns app.main.ui.workspace.shapes.text (:require [app.common.attrs :as attrs] + [app.common.data :as d] [app.common.geom.matrix :as gmt] [app.common.geom.shapes :as gsh] [app.common.logging :as log] @@ -150,7 +151,9 @@ (gsh/transform-rect mtx)))))] (reset! local-position-data position-data)))) - [shape-ref on-change-node] (use-mutable-observer handle-change-foreign-object)] + [shape-ref on-change-node] (use-mutable-observer handle-change-foreign-object) + + show-svg-text? (or (some? (:position-data shape)) (some? @local-position-data))] ;; When the text is "dirty?" we get recalculate the positions (mf/use-layout-effect @@ -159,21 +162,22 @@ (let [node (mf/ref-val shape-ref)] (when (and dirty? (some? node)) (let [position-data (utp/calc-position-data node)] - (reset! local-position-data nil) - (st/emit! (dch/update-shapes - [id] - (fn [shape] - (-> shape - (dissoc :dirty?) - (assoc :position-data position-data))) - {:save-undo? false}))))))) + (when (d/not-empty? position-data) + (reset! local-position-data nil) + (st/emit! (dch/update-shapes + [id] + (fn [shape] + (-> shape + (dissoc :dirty?) + (assoc :position-data position-data))) + {:save-undo? false})))))))) [:> shape-container {:shape shape} ;; We keep hidden the shape when we're editing so it keeps track of the size ;; and updates the selrect accordingly [:* [:g.text-shape {:ref on-change-node - :opacity (when (or edition? (some? (:position-data shape))) 0) + :opacity (when (or edition? show-svg-text?) 0) :pointer-events "none"} ;; The `:key` prop here is mandatory because the @@ -186,7 +190,7 @@ :edition? edition? :key (str id edition?)}]] - (when (and (or (some? (:position-data shape)) (some? local-position-data))) + (when show-svg-text? (let [shape (cond-> shape (some? @local-position-data) diff --git a/frontend/src/app/main/ui/workspace/shapes/text/editor.cljs b/frontend/src/app/main/ui/workspace/shapes/text/editor.cljs index e665329c1..ad6ea6f9b 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/editor.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/editor.cljs @@ -15,15 +15,11 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.cursors :as cur] - [app.main.ui.hooks.mutable-observer :refer [use-mutable-observer]] - [app.main.ui.shapes.shape :refer [shape-container]] [app.main.ui.shapes.text.styles :as sts] - [app.main.ui.shapes.text.svg-text :as svg] [app.util.dom :as dom] [app.util.keyboard :as kbd] [app.util.object :as obj] [app.util.text-editor :as ted] - [app.util.text-svg-position :as utp] [goog.events :as events] [rumext.alpha :as mf]) (:import diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index 81286e698..0430a8dd6 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -84,7 +84,6 @@ ;; REFS viewport-ref (mf/use-ref nil) - raw-position-ref (mf/use-ref nil) ;; Stores the raw position of the cursor ;; VARS disable-paste (mf/use-var false) @@ -130,7 +129,7 @@ on-pointer-down (actions/on-pointer-down) on-pointer-enter (actions/on-pointer-enter in-viewport?) on-pointer-leave (actions/on-pointer-leave in-viewport?) - on-pointer-move (actions/on-pointer-move viewport-ref raw-position-ref zoom move-stream) + on-pointer-move (actions/on-pointer-move viewport-ref zoom move-stream) on-pointer-up (actions/on-pointer-up) on-move-selected (actions/on-move-selected hover hover-ids selected space?) on-menu-selected (actions/on-menu-selected hover hover-ids selected) @@ -169,7 +168,7 @@ (hooks/setup-viewport-size viewport-ref) (hooks/setup-cursor cursor alt? ctrl? space? panning drawing-tool drawing-path? node-editing?) (hooks/setup-keyboard alt? ctrl? space?) - (hooks/setup-hover-shapes page-id move-stream raw-position-ref base-objects transform selected ctrl? hover hover-ids @hover-disabled? zoom) + (hooks/setup-hover-shapes page-id move-stream base-objects transform selected ctrl? hover hover-ids @hover-disabled? zoom) (hooks/setup-viewport-modifiers modifiers base-objects) (hooks/setup-shortcuts node-editing? drawing-path?) (hooks/setup-active-frames base-objects vbox hover active-frames) diff --git a/frontend/src/app/main/ui/workspace/viewport/actions.cljs b/frontend/src/app/main/ui/workspace/viewport/actions.cljs index 0d6ca0ba8..50bfd6b12 100644 --- a/frontend/src/app/main/ui/workspace/viewport/actions.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/actions.cljs @@ -350,14 +350,13 @@ (kbd/shift? event) (kbd/alt? event)))))))) -(defn on-pointer-move [viewport-ref raw-position-ref zoom move-stream] +(defn on-pointer-move [viewport-ref zoom move-stream] (mf/use-callback (mf/deps zoom move-stream) (fn [event] (let [raw-pt (dom/get-client-position event) viewport (mf/ref-val viewport-ref) pt (utils/translate-point-to-viewport viewport zoom raw-pt)] - (mf/set-ref-val! raw-position-ref raw-pt) (rx/push! move-stream pt))))) (defn on-mouse-wheel [viewport-ref zoom] diff --git a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs index 8af6c2aad..0cb64935b 100644 --- a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs @@ -8,7 +8,6 @@ (:require [app.common.data :as d] [app.common.geom.shapes :as gsh] - [app.common.geom.shapes.rect :as gshr] [app.common.pages.helpers :as cph] [app.main.data.shortcuts :as dsc] [app.main.data.workspace :as dw] @@ -98,7 +97,7 @@ (some #(cph/is-parent? objects % group-id)) (not)))) -(defn setup-hover-shapes [page-id move-stream raw-position-ref objects transform selected ctrl? hover hover-ids hover-disabled? zoom] +(defn setup-hover-shapes [page-id move-stream objects transform selected ctrl? hover hover-ids hover-disabled? zoom] (let [;; We use ref so we don't recreate the stream on a change zoom-ref (mf/use-ref zoom) ctrl-ref (mf/use-ref @ctrl?) diff --git a/frontend/src/app/util/text_svg_position.cljs b/frontend/src/app/util/text_svg_position.cljs index 6f30c6864..624aa4327 100644 --- a/frontend/src/app/util/text_svg_position.cljs +++ b/frontend/src/app/util/text_svg_position.cljs @@ -99,27 +99,27 @@ zoom (get-in @st/state [:workspace-local :zoom]) text-data (calc-text-node-positions base-node viewport zoom)] (->> text-data - (map (fn [{:keys [node position text]}] - (let [{:keys [x y width height]} position - rtl? (= "rtl" (.-dir (.-parentElement ^js node))) - styles (js/getComputedStyle ^js node) - get (fn [prop] - (let [value (.getPropertyValue styles prop)] - (when (and value (not= value "")) - value)))] - (d/without-nils - {:rtl? rtl? - :x (if rtl? (+ x width) x) - :y (+ y height) - :width width - :height height - :font-family (str (get "font-family")) - :font-size (str (get "font-size")) - :font-weight (str (get "font-weight")) - :text-transform (str (get "text-transform")) - :text-decoration (str (get "text-decoration")) - :font-style (str (get "font-style")) - :fill-color (or (get "--fill-color") "#000000") - :fill-color-gradient (transit/decode-str (get "--fill-color-gradient")) - :fill-opacity (d/parse-double (or (get "--fill-opacity") "1")) - :text text}))))))) + (mapv (fn [{:keys [node position text]}] + (let [{:keys [x y width height]} position + rtl? (= "rtl" (.-dir (.-parentElement ^js node))) + styles (js/getComputedStyle ^js node) + get (fn [prop] + (let [value (.getPropertyValue styles prop)] + (when (and value (not= value "")) + value)))] + (d/without-nils + {:rtl? rtl? + :x (if rtl? (+ x width) x) + :y (+ y height) + :width width + :height height + :font-family (str (get "font-family")) + :font-size (str (get "font-size")) + :font-weight (str (get "font-weight")) + :text-transform (str (get "text-transform")) + :text-decoration (str (get "text-decoration")) + :font-style (str (get "font-style")) + :fill-color (or (get "--fill-color") "#000000") + :fill-color-gradient (transit/decode-str (get "--fill-color-gradient")) + :fill-opacity (d/parse-double (or (get "--fill-opacity") "1")) + :text text})))))))