From 618d22d2144ccbb9e52e7ea28a49da3b8b8ff13e Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 17 Feb 2022 14:53:46 +0100 Subject: [PATCH] :sparkles: Changes to text editor --- .../resources/styles/main/partials/texts.scss | 83 +++++++++--------- frontend/src/app/main/ui/cursors.cljs | 2 +- frontend/src/app/main/ui/shapes/attrs.cljs | 48 ++++++----- .../src/app/main/ui/shapes/custom_stroke.cljs | 15 +--- .../src/app/main/ui/shapes/text/styles.cljs | 10 +-- .../src/app/main/ui/shapes/text/svg_text.cljs | 8 +- .../app/main/ui/workspace/shapes/text.cljs | 3 +- .../main/ui/workspace/shapes/text/editor.cljs | 84 +++++++------------ .../src/app/main/ui/workspace/viewport.cljs | 11 ++- 9 files changed, 115 insertions(+), 149 deletions(-) diff --git a/frontend/resources/styles/main/partials/texts.scss b/frontend/resources/styles/main/partials/texts.scss index 4b4a2f711..d84442eb1 100644 --- a/frontend/resources/styles/main/partials/texts.scss +++ b/frontend/resources/styles/main/partials/texts.scss @@ -1,62 +1,61 @@ -foreignObject { - .text-editor, - .rich-text { - color: $color-black; - height: 100%; - white-space: pre-wrap; - font-family: sourcesanspro; - div { - line-height: inherit; - user-select: text; - } +.text-editor, +.rich-text { + color: $color-black; + height: 100%; + white-space: pre-wrap; + font-family: sourcesanspro; - span { - line-height: inherit; - } + div { + line-height: inherit; + user-select: text; } - .text-editor { - .DraftEditor-root { - height: 100%; - display: flex; - flex-direction: column; - } - - &.align-top { - .DraftEditor-root { - justify-content: flex-start; - } - } - - &.align-center { - .DraftEditor-root { - justify-content: center; - } - } - - &.align-bottom { - .DraftEditor-root { - justify-content: flex-end; - } - } + span { + line-height: inherit; } +} - .rich-text .paragraphs { +.text-editor { + .DraftEditor-root { height: 100%; display: flex; flex-direction: column; + } - &.align-top { + &.align-top { + .DraftEditor-root { justify-content: flex-start; } + } - &.align-center { + &.align-center { + .DraftEditor-root { justify-content: center; } + } - &.align-bottom { + &.align-bottom { + .DraftEditor-root { justify-content: flex-end; } } } + +.rich-text .paragraphs { + height: 100%; + display: flex; + flex-direction: column; + + &.align-top { + justify-content: flex-start; + } + + &.align-center { + justify-content: center; + } + + &.align-bottom { + justify-content: flex-end; + } +} diff --git a/frontend/src/app/main/ui/cursors.cljs b/frontend/src/app/main/ui/cursors.cljs index 2d571ed06..144c0e853 100644 --- a/frontend/src/app/main/ui/cursors.cljs +++ b/frontend/src/app/main/ui/cursors.cljs @@ -29,7 +29,6 @@ (def pointer-move (cursor-ref :pointer-move 0 0 10 42)) (def pointer-node (cursor-ref :pointer-node 0 0 10 32)) (def resize-alt (cursor-ref :resize-alt)) -(def text (cursor-ref :text)) (def zoom (cursor-ref :zoom)) (def zoom-in (cursor-ref :zoom-in)) (def zoom-out (cursor-ref :zoom-out)) @@ -40,6 +39,7 @@ (def resize-ns (cursor-fn :resize-h 90)) (def resize-nwse (cursor-fn :resize-h 135)) (def rotate (cursor-fn :rotate 90)) +(def text (cursor-fn :text 0)) ;; (def resize-ew-2 (cursor-fn :resize-h-2 0)) diff --git a/frontend/src/app/main/ui/shapes/attrs.cljs b/frontend/src/app/main/ui/shapes/attrs.cljs index 51e84ab26..3a30cf7f3 100644 --- a/frontend/src/app/main/ui/shapes/attrs.cljs +++ b/frontend/src/app/main/ui/shapes/attrs.cljs @@ -80,34 +80,38 @@ "z")})) attrs)) -(defn add-fill [attrs shape render-id index] - (let [ - fill-attrs (cond - (contains? shape :fill-image) - (let [fill-image-id (str "fill-image-" render-id)] - {:fill (str/format "url(#%s)" fill-image-id)}) +(defn add-fill + ([attrs shape render-id] + (add-fill attrs shape render-id 0)) - (contains? shape :fill-color-gradient) - (let [fill-color-gradient-id (str "fill-color-gradient_" render-id "_" index)] - {:fill (str/format "url(#%s)" fill-color-gradient-id)}) + ([attrs shape render-id index] + (let [fill-attrs + (cond + (contains? shape :fill-image) + (let [fill-image-id (str "fill-image-" render-id)] + {:fill (str/format "url(#%s)" fill-image-id)}) - (contains? shape :fill-color) - {:fill (:fill-color shape)} + (contains? shape :fill-color-gradient) + (let [fill-color-gradient-id (str "fill-color-gradient_" render-id "_" index)] + {:fill (str/format "url(#%s)" fill-color-gradient-id)}) - ;; If contains svg-attrs the origin is svg. If it's not svg origin - ;; we setup the default fill as transparent (instead of black) - (and (not (contains? shape :svg-attrs)) - (not (#{:svg-raw :group} (:type shape)))) - {:fill "none"} + (contains? shape :fill-color) + {:fill (:fill-color shape)} - :else - {}) + ;; If contains svg-attrs the origin is svg. If it's not svg origin + ;; we setup the default fill as transparent (instead of black) + (and (not (contains? shape :svg-attrs)) + (not (#{:svg-raw :group} (:type shape)))) + {:fill "none"} - fill-attrs (cond-> fill-attrs - (contains? shape :fill-opacity) - (assoc :fillOpacity (:fill-opacity shape)))] + :else + {}) - (obj/merge! attrs (clj->js fill-attrs)))) + fill-attrs (cond-> fill-attrs + (contains? shape :fill-opacity) + (assoc :fillOpacity (:fill-opacity shape)))] + + (obj/merge! attrs (clj->js fill-attrs))))) (defn add-stroke [attrs shape render-id] (let [stroke-style (:stroke-style shape :none) diff --git a/frontend/src/app/main/ui/shapes/custom_stroke.cljs b/frontend/src/app/main/ui/shapes/custom_stroke.cljs index b88e72722..04e688a6f 100644 --- a/frontend/src/app/main/ui/shapes/custom_stroke.cljs +++ b/frontend/src/app/main/ui/shapes/custom_stroke.cljs @@ -38,19 +38,8 @@ stroke-width (case (:stroke-alignment shape :center) :center (/ (:stroke-width shape 0) 2) :outer (:stroke-width shape 0) - 0) - margin (gsh/shape-stroke-margin shape stroke-width) - bounding-box (-> (gsh/points->selrect (:points shape)) - (update :x - (+ stroke-width margin)) - (update :y - (+ stroke-width margin)) - (update :width + (* 2 (+ stroke-width margin))) - (update :height + (* 2 (+ stroke-width margin))))] - [:mask {:id stroke-mask-id - :x (:x bounding-box) - :y (:y bounding-box) - :width (:width bounding-box) - :height (:height bounding-box) - :maskUnits "userSpaceOnUse"} + 0)] + [:mask {:id stroke-mask-id} [:use {:xlinkHref (str "#" shape-id) :style #js {:fill "none" :stroke "white" :strokeWidth (* stroke-width 2)}}] diff --git a/frontend/src/app/main/ui/shapes/text/styles.cljs b/frontend/src/app/main/ui/shapes/text/styles.cljs index 0d5e98d8d..d223087d2 100644 --- a/frontend/src/app/main/ui/shapes/text/styles.cljs +++ b/frontend/src/app/main/ui/shapes/text/styles.cljs @@ -19,11 +19,12 @@ (let [valign (:vertical-align node "top") base #js {:height "100%" :width "100%" - :fontFamily "sourcesanspro"}] + :fontFamily "sourcesanspro" + :display "flex"}] (cond-> base - (= valign "top") (obj/set! "justifyContent" "flex-start") - (= valign "center") (obj/set! "justifyContent" "center") - (= valign "bottom") (obj/set! "justifyContent" "flex-end")))) + (= valign "top") (obj/set! "alignItems" "flex-start") + (= valign "center") (obj/set! "alignItems" "center") + (= valign "bottom") (obj/set! "alignItems" "flex-end")))) (defn generate-paragraph-set-styles [{:keys [grow-type] :as shape}] @@ -39,7 +40,6 @@ #js {:display "inline-flex" :flexDirection "column" :justifyContent "inherit" - :minHeight (when-not (or auto-width? auto-height?) "100%") :minWidth (when-not auto-width? "100%") :marginRight "1px" :verticalAlign "top"})) 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 145723ec2..468ffbe08 100644 --- a/frontend/src/app/main/ui/shapes/text/svg_text.cljs +++ b/frontend/src/app/main/ui/shapes/text/svg_text.cljs @@ -24,9 +24,8 @@ (let [render-id (mf/use-ctx muc/render-ctx) {:keys [id x y width height position-data] :as shape} (obj/get props "shape") - clip-id (str "clip-text" id "_" render-id) - group-props (-> #js {:transform (gsh/transform-matrix shape) - :clipPath (str "url(#" clip-id ")")} + transform (str (gsh/transform-matrix shape)) + group-props (-> #js {:transform transform} (attrs/add-style-attrs shape render-id)) get-gradient-id (fn [index] @@ -43,9 +42,6 @@ [:& shape-custom-stroke {:shape shape} [:> :g group-props - [:defs - [:clipPath {:id clip-id} - [:rect.text-clip {:x x :y y :width width :height height}]]] (for [[index data] (d/enumerate position-data)] (let [props (-> #js {:x (:x data) :y (:y data) diff --git a/frontend/src/app/main/ui/workspace/shapes/text.cljs b/frontend/src/app/main/ui/workspace/shapes/text.cljs index b2589c91e..e6ee44b87 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text.cljs @@ -131,7 +131,6 @@ handle-change-foreign-object (fn [node] (when (some? node) - (prn "change!") (let [position-data (utp/calc-position-data node) parent (dom/get-parent node) parent-transform (dom/get-attribute parent "transform") @@ -174,7 +173,7 @@ ;; and updates the selrect accordingly [:* [:g.text-shape {:ref on-change-node - :opacity (when (or edition? (some? (:position-data shape))) 0.2) + :opacity (when (or edition? (some? (:position-data shape))) 0) :pointer-events "none"} ;; The `:key` prop here is mandatory because the 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 4278a005c..e665329c1 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/editor.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/editor.cljs @@ -7,6 +7,7 @@ (ns app.main.ui.workspace.shapes.text.editor (:require ["draft-js" :as draft] + [app.common.geom.point :as gpt] [app.common.geom.shapes :as gsh] [app.common.text :as txt] [app.main.data.workspace :as dw] @@ -211,7 +212,7 @@ [:div.text-editor {:ref self-ref - :style {:cursor cur/text + :style {:cursor (cur/text (:rotation shape)) :width (:width shape) :height (:height shape)} :on-click on-click @@ -231,61 +232,34 @@ :ref on-editor :editor-state state}]])) -(mf/defc text-shape-edit - {::mf/wrap [mf/memo] - ::mf/wrap-props false - ::mf/forward-ref true} - [props _] - (let [{:keys [id x y width height grow-type] :as shape} (obj/get props "shape") - transform (str (gsh/transform-matrix shape)) +(defn translate-point-from-viewport + "Translate a point in the viewport into client coordinates" + [pt viewport zoom] + (let [vbox (.. ^js viewport -viewBox -baseVal) + box (gpt/point (.-x vbox) (.-y vbox)) + zoom (gpt/point zoom)] + (-> (gpt/subtract pt box) + (gpt/multiply zoom)))) - clip-id (str "clip-" id) +(mf/defc text-editor-viewport + {::mf/wrap-props false} + [props] + (let [shape (obj/get props "shape") + viewport-ref (obj/get props "viewport-ref") + zoom (obj/get props "zoom") - ;; local-position-data (mf/use-state nil) + position + (-> (gpt/point (-> shape :selrect :x) + (-> shape :selrect :y)) + (translate-point-from-viewport (mf/ref-val viewport-ref) zoom))] - ;;handle-change-foreign-object - ;;(mf/use-callback - ;; (fn [node] - ;; (when node - ;; (let [position-data (utp/calc-position-data node)] - ;; (reset! local-position-data position-data))))) - ;; - ;;[shape-ref on-change-node] (use-mutable-observer handle-change-foreign-object) + [:div {:style {:position "absolute" + :left (str (:x position) "px") + :top (str (:y position) "px") + :pointer-events "all" + :transform (str (gsh/transform-matrix shape nil (gpt/point 0 0))) + :transform-origin "center center"}} - ;;handle-interaction - ;;(mf/use-callback - ;; (fn [] - ;; (handle-change-foreign-object (mf/ref-val shape-ref)))) - ] - - #_(mf/use-effect - (mf/use-callback handle-interaction) - (fn [] - (let [keys [(events/listen js/document EventType.KEYUP handle-interaction) - (events/listen js/document EventType.KEYDOWN handle-interaction) - (events/listen js/document EventType.MOUSEDOWN handle-interaction)]] - #(doseq [key keys] - (events/unlistenByKey key))))) - [:* - #_[:> shape-container {:shape shape - :pointer-events "none"} - [:& svg/text-shape {:shape (cond-> shape - (some? @local-position-data) - (assoc :position-data @local-position-data))}]] - - [:g.text-editor {:clip-path (str "url(#" clip-id ")") - ;; :ref on-change-node - :key (str "editor-" id)} - [:defs - ;; This clippath will cut the huge foreign object we use to calculate the automatic resize - [:clipPath {:id clip-id} - [:rect {:x x :y y - :width (+ width 8) :height (+ height 8) - :transform transform}]]] - - [:foreignObject {:transform transform - :x x :y y - :width (if (#{:auto-width} grow-type) 100000 width) - :height (if (#{:auto-height :auto-width} grow-type) 100000 height)} - - [:& text-shape-edit-html {:shape shape :key (str id)}]]]])) + [:div {:style {:transform (str "scale(" zoom ")") + :transform-origin "top left"}} + [:& text-shape-edit-html {:shape shape :key (str (:id shape))}]]])) diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index fd6fc1afd..81286e698 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -110,6 +110,8 @@ ;; Only when we have all the selected shapes in one frame selected-frame (when (= (count selected-frames) 1) (get base-objects (first selected-frames))) + editing-shape (when edition (get base-objects edition)) + create-comment? (= :comments drawing-tool) drawing-path? (or (and edition (= :draw (get-in edit-path [edition :edit-mode]))) (and (some? drawing-obj) (= :path (:type drawing-obj)))) @@ -159,6 +161,8 @@ show-artboard-names? (contains? layout :display-artboard-names) show-rules? (and (contains? layout :rules) (not (contains? layout :hide-ui))) + show-text-editor? (and editing-shape (= :text (:type editing-shape))) + disabled-guides? (or drawing-tool transform)] (hooks/setup-dom-events viewport-ref zoom disable-paste in-viewport?) @@ -176,6 +180,10 @@ [:& wtr/frame-renderer {:objects base-objects :background background}] + (when show-text-editor? + [:& editor/text-editor-viewport {:shape editing-shape + :viewport-ref viewport-ref + :zoom zoom}]) (when show-comments? [:& comments/comments-layer {:vbox vbox :vport vport @@ -268,9 +276,6 @@ :hover-shape @hover :zoom zoom}]) - (when text-editing? - [:& editor/text-shape-edit {:shape (get base-objects edition)}]) - [:& widgets/frame-titles {:objects objects-modified :selected selected