Changes to text editor

This commit is contained in:
alonso.torres 2022-02-17 14:53:46 +01:00
parent d83459f674
commit 618d22d214
9 changed files with 115 additions and 149 deletions

View file

@ -1,62 +1,61 @@
foreignObject {
.text-editor,
.rich-text {
color: $color-black;
height: 100%;
white-space: pre-wrap;
font-family: sourcesanspro;
div { .text-editor,
line-height: inherit; .rich-text {
user-select: text; color: $color-black;
} height: 100%;
white-space: pre-wrap;
font-family: sourcesanspro;
span { div {
line-height: inherit; line-height: inherit;
} user-select: text;
} }
.text-editor { span {
.DraftEditor-root { line-height: inherit;
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;
}
}
} }
}
.rich-text .paragraphs { .text-editor {
.DraftEditor-root {
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
}
&.align-top { &.align-top {
.DraftEditor-root {
justify-content: flex-start; justify-content: flex-start;
} }
}
&.align-center { &.align-center {
.DraftEditor-root {
justify-content: center; justify-content: center;
} }
}
&.align-bottom { &.align-bottom {
.DraftEditor-root {
justify-content: flex-end; 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;
}
}

View file

@ -29,7 +29,6 @@
(def pointer-move (cursor-ref :pointer-move 0 0 10 42)) (def pointer-move (cursor-ref :pointer-move 0 0 10 42))
(def pointer-node (cursor-ref :pointer-node 0 0 10 32)) (def pointer-node (cursor-ref :pointer-node 0 0 10 32))
(def resize-alt (cursor-ref :resize-alt)) (def resize-alt (cursor-ref :resize-alt))
(def text (cursor-ref :text))
(def zoom (cursor-ref :zoom)) (def zoom (cursor-ref :zoom))
(def zoom-in (cursor-ref :zoom-in)) (def zoom-in (cursor-ref :zoom-in))
(def zoom-out (cursor-ref :zoom-out)) (def zoom-out (cursor-ref :zoom-out))
@ -40,6 +39,7 @@
(def resize-ns (cursor-fn :resize-h 90)) (def resize-ns (cursor-fn :resize-h 90))
(def resize-nwse (cursor-fn :resize-h 135)) (def resize-nwse (cursor-fn :resize-h 135))
(def rotate (cursor-fn :rotate 90)) (def rotate (cursor-fn :rotate 90))
(def text (cursor-fn :text 0))
;; ;;
(def resize-ew-2 (cursor-fn :resize-h-2 0)) (def resize-ew-2 (cursor-fn :resize-h-2 0))

View file

@ -80,34 +80,38 @@
"z")})) "z")}))
attrs)) attrs))
(defn add-fill [attrs shape render-id index] (defn add-fill
(let [ ([attrs shape render-id]
fill-attrs (cond (add-fill attrs shape render-id 0))
(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-gradient) ([attrs shape render-id index]
(let [fill-color-gradient-id (str "fill-color-gradient_" render-id "_" index)] (let [fill-attrs
{:fill (str/format "url(#%s)" fill-color-gradient-id)}) (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) (contains? shape :fill-color-gradient)
{:fill (:fill-color shape)} (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 (contains? shape :fill-color)
;; we setup the default fill as transparent (instead of black) {:fill (:fill-color shape)}
(and (not (contains? shape :svg-attrs))
(not (#{:svg-raw :group} (:type shape))))
{:fill "none"}
: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 :else
(contains? shape :fill-opacity) {})
(assoc :fillOpacity (:fill-opacity shape)))]
(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] (defn add-stroke [attrs shape render-id]
(let [stroke-style (:stroke-style shape :none) (let [stroke-style (:stroke-style shape :none)

View file

@ -38,19 +38,8 @@
stroke-width (case (:stroke-alignment shape :center) stroke-width (case (:stroke-alignment shape :center)
:center (/ (:stroke-width shape 0) 2) :center (/ (:stroke-width shape 0) 2)
:outer (:stroke-width shape 0) :outer (:stroke-width shape 0)
0) 0)]
margin (gsh/shape-stroke-margin shape stroke-width) [:mask {:id stroke-mask-id}
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"}
[:use {:xlinkHref (str "#" shape-id) [:use {:xlinkHref (str "#" shape-id)
:style #js {:fill "none" :stroke "white" :strokeWidth (* stroke-width 2)}}] :style #js {:fill "none" :stroke "white" :strokeWidth (* stroke-width 2)}}]

View file

@ -19,11 +19,12 @@
(let [valign (:vertical-align node "top") (let [valign (:vertical-align node "top")
base #js {:height "100%" base #js {:height "100%"
:width "100%" :width "100%"
:fontFamily "sourcesanspro"}] :fontFamily "sourcesanspro"
:display "flex"}]
(cond-> base (cond-> base
(= valign "top") (obj/set! "justifyContent" "flex-start") (= valign "top") (obj/set! "alignItems" "flex-start")
(= valign "center") (obj/set! "justifyContent" "center") (= valign "center") (obj/set! "alignItems" "center")
(= valign "bottom") (obj/set! "justifyContent" "flex-end")))) (= valign "bottom") (obj/set! "alignItems" "flex-end"))))
(defn generate-paragraph-set-styles (defn generate-paragraph-set-styles
[{:keys [grow-type] :as shape}] [{:keys [grow-type] :as shape}]
@ -39,7 +40,6 @@
#js {:display "inline-flex" #js {:display "inline-flex"
:flexDirection "column" :flexDirection "column"
:justifyContent "inherit" :justifyContent "inherit"
:minHeight (when-not (or auto-width? auto-height?) "100%")
:minWidth (when-not auto-width? "100%") :minWidth (when-not auto-width? "100%")
:marginRight "1px" :marginRight "1px"
:verticalAlign "top"})) :verticalAlign "top"}))

View file

@ -24,9 +24,8 @@
(let [render-id (mf/use-ctx muc/render-ctx) (let [render-id (mf/use-ctx muc/render-ctx)
{:keys [id x y width height position-data] :as shape} (obj/get props "shape") {:keys [id x y width height position-data] :as shape} (obj/get props "shape")
clip-id (str "clip-text" id "_" render-id) transform (str (gsh/transform-matrix shape))
group-props (-> #js {:transform (gsh/transform-matrix shape) group-props (-> #js {:transform transform}
:clipPath (str "url(#" clip-id ")")}
(attrs/add-style-attrs shape render-id)) (attrs/add-style-attrs shape render-id))
get-gradient-id get-gradient-id
(fn [index] (fn [index]
@ -43,9 +42,6 @@
[:& shape-custom-stroke {:shape shape} [:& shape-custom-stroke {:shape shape}
[:> :g group-props [:> :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)] (for [[index data] (d/enumerate position-data)]
(let [props (-> #js {:x (:x data) (let [props (-> #js {:x (:x data)
:y (:y data) :y (:y data)

View file

@ -131,7 +131,6 @@
handle-change-foreign-object handle-change-foreign-object
(fn [node] (fn [node]
(when (some? node) (when (some? node)
(prn "change!")
(let [position-data (utp/calc-position-data node) (let [position-data (utp/calc-position-data node)
parent (dom/get-parent node) parent (dom/get-parent node)
parent-transform (dom/get-attribute parent "transform") parent-transform (dom/get-attribute parent "transform")
@ -174,7 +173,7 @@
;; and updates the selrect accordingly ;; and updates the selrect accordingly
[:* [:*
[:g.text-shape {:ref on-change-node [: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"} :pointer-events "none"}
;; The `:key` prop here is mandatory because the ;; The `:key` prop here is mandatory because the

View file

@ -7,6 +7,7 @@
(ns app.main.ui.workspace.shapes.text.editor (ns app.main.ui.workspace.shapes.text.editor
(:require (:require
["draft-js" :as draft] ["draft-js" :as draft]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh] [app.common.geom.shapes :as gsh]
[app.common.text :as txt] [app.common.text :as txt]
[app.main.data.workspace :as dw] [app.main.data.workspace :as dw]
@ -211,7 +212,7 @@
[:div.text-editor [:div.text-editor
{:ref self-ref {:ref self-ref
:style {:cursor cur/text :style {:cursor (cur/text (:rotation shape))
:width (:width shape) :width (:width shape)
:height (:height shape)} :height (:height shape)}
:on-click on-click :on-click on-click
@ -231,61 +232,34 @@
:ref on-editor :ref on-editor
:editor-state state}]])) :editor-state state}]]))
(mf/defc text-shape-edit (defn translate-point-from-viewport
{::mf/wrap [mf/memo] "Translate a point in the viewport into client coordinates"
::mf/wrap-props false [pt viewport zoom]
::mf/forward-ref true} (let [vbox (.. ^js viewport -viewBox -baseVal)
[props _] box (gpt/point (.-x vbox) (.-y vbox))
(let [{:keys [id x y width height grow-type] :as shape} (obj/get props "shape") zoom (gpt/point zoom)]
transform (str (gsh/transform-matrix shape)) (-> (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 [:div {:style {:position "absolute"
;;(mf/use-callback :left (str (:x position) "px")
;; (fn [node] :top (str (:y position) "px")
;; (when node :pointer-events "all"
;; (let [position-data (utp/calc-position-data node)] :transform (str (gsh/transform-matrix shape nil (gpt/point 0 0)))
;; (reset! local-position-data position-data))))) :transform-origin "center center"}}
;;
;;[shape-ref on-change-node] (use-mutable-observer handle-change-foreign-object)
;;handle-interaction [:div {:style {:transform (str "scale(" zoom ")")
;;(mf/use-callback :transform-origin "top left"}}
;; (fn [] [:& text-shape-edit-html {:shape shape :key (str (:id shape))}]]]))
;; (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)}]]]]))

View file

@ -110,6 +110,8 @@
;; Only when we have all the selected shapes in one frame ;; Only when we have all the selected shapes in one frame
selected-frame (when (= (count selected-frames) 1) (get base-objects (first selected-frames))) 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) create-comment? (= :comments drawing-tool)
drawing-path? (or (and edition (= :draw (get-in edit-path [edition :edit-mode]))) drawing-path? (or (and edition (= :draw (get-in edit-path [edition :edit-mode])))
(and (some? drawing-obj) (= :path (:type drawing-obj)))) (and (some? drawing-obj) (= :path (:type drawing-obj))))
@ -159,6 +161,8 @@
show-artboard-names? (contains? layout :display-artboard-names) show-artboard-names? (contains? layout :display-artboard-names)
show-rules? (and (contains? layout :rules) (not (contains? layout :hide-ui))) 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)] disabled-guides? (or drawing-tool transform)]
(hooks/setup-dom-events viewport-ref zoom disable-paste in-viewport?) (hooks/setup-dom-events viewport-ref zoom disable-paste in-viewport?)
@ -176,6 +180,10 @@
[:& wtr/frame-renderer {:objects base-objects [:& wtr/frame-renderer {:objects base-objects
:background background}] :background background}]
(when show-text-editor?
[:& editor/text-editor-viewport {:shape editing-shape
:viewport-ref viewport-ref
:zoom zoom}])
(when show-comments? (when show-comments?
[:& comments/comments-layer {:vbox vbox [:& comments/comments-layer {:vbox vbox
:vport vport :vport vport
@ -268,9 +276,6 @@
:hover-shape @hover :hover-shape @hover
:zoom zoom}]) :zoom zoom}])
(when text-editing?
[:& editor/text-shape-edit {:shape (get base-objects edition)}])
[:& widgets/frame-titles [:& widgets/frame-titles
{:objects objects-modified {:objects objects-modified
:selected selected :selected selected