diff --git a/frontend/src/app/main/ui/components/editable_select.cljs b/frontend/src/app/main/ui/components/editable_select.cljs index 9d4101bead..ebc5e5589d 100644 --- a/frontend/src/app/main/ui/components/editable_select.cljs +++ b/frontend/src/app/main/ui/components/editable_select.cljs @@ -10,6 +10,7 @@ [app.common.math :as mth] [app.common.uuid :as uuid] [app.main.ui.components.dropdown :refer [dropdown]] + [app.main.ui.components.numeric-input :refer [numeric-input]] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.keyboard :as kbd] @@ -141,13 +142,19 @@ [:div.editable-select {:class class :ref on-node-load} - [:input.input-text {:value (or (some-> @state :current-value value->label) "") - :on-change handle-change-input - :on-key-down handle-key-down - :on-focus handle-focus - :on-blur handle-blur - :placeholder placeholder - :type type}] + (if (= type "number") + [:> numeric-input {:value (or (some-> @state :current-value value->label) "") + :on-change set-value + :on-focus handle-focus + :on-blur handle-blur + :placeholder placeholder}] + [:input.input-text {:value (or (some-> @state :current-value value->label) "") + :on-change handle-change-input + :on-key-down handle-key-down + :on-focus handle-focus + :on-blur handle-blur + :placeholder placeholder + :type type}]) [:span.dropdown-button {:on-click open-dropdown} i/arrow-down] [:& dropdown {:show (get @state :is-open? false) diff --git a/frontend/src/app/main/ui/shapes/frame.cljs b/frontend/src/app/main/ui/shapes/frame.cljs index 0d371ce496..91425cd797 100644 --- a/frontend/src/app/main/ui/shapes/frame.cljs +++ b/frontend/src/app/main/ui/shapes/frame.cljs @@ -45,15 +45,32 @@ [props] (let [shape (obj/get props "shape")] (when (:thumbnail shape) - [:image.frame-thumbnail - {:id (dm/str "thumbnail-" (:id shape)) - :xlinkHref (:thumbnail shape) - :x (:x shape) - :y (:y shape) - :width (:width shape) - :height (:height shape) - ;; DEBUG - :style {:filter (when (debug? :thumbnails) "sepia(1)")}}]))) + (let [{:keys [x y width height]} shape + transform (gsh/transform-matrix shape) + props (-> (attrs/extract-style-attrs shape) + (obj/merge! + #js {:x x + :y y + :transform (str transform) + :width width + :height height + :className "frame-background"})) + path? (some? (.-d props))] + [:* + [:image.frame-thumbnail + {:id (dm/str "thumbnail-" (:id shape)) + :xlinkHref (:thumbnail shape) + :x (:x shape) + :y (:y shape) + :width (:width shape) + :height (:height shape) + ;; DEBUG + :style {:filter (when (debug? :thumbnails) "sepia(1)")}}] + + [:& shape-strokes {:shape shape} + (if path? + [:> :path props] + [:> :rect props])]])))) (defn frame-shape [shape-wrapper] @@ -88,8 +105,9 @@ (for [item childs] [:& shape-wrapper {:shape item :key (dm/str (:id item))}]) - [:& shape-strokes {:shape shape} - (if path? - [:> :path props] - [:> :rect props])]]]]))) + ]] + [:& shape-strokes {:shape shape} + (if path? + [:> :path props] + [:> :rect props])]]))) 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 03cfd7235d..c13f048e6f 100644 --- a/frontend/src/app/main/ui/shapes/text/svg_text.cljs +++ b/frontend/src/app/main/ui/shapes/text/svg_text.cljs @@ -82,11 +82,13 @@ :fontWeight (:font-weight data) :textTransform (:text-transform data) :textDecoration (:text-decoration data) + :letterSpacing (:letter-spacing data) :fontStyle (:font-style data) :direction (if (:rtl data) "rtl" "ltr") :whiteSpace "pre"} (obj/set! "fill" (str "url(#fill-" index "-" render-id ")")))}) shape (assoc shape :fills (:fills data))] - [:& shape-custom-strokes {:shape shape :key index} - [:> :text props (:text data)]]))]])) + [:& (mf/provider muc/render-ctx) {:value (str render-id "_" (:id shape) "_" index)} + [:& shape-custom-strokes {:shape shape :key index} + [:> :text props (:text data)]]]))]])) diff --git a/frontend/src/app/main/ui/workspace/shapes.cljs b/frontend/src/app/main/ui/workspace/shapes.cljs index 78cb6ab8b8..7905df9518 100644 --- a/frontend/src/app/main/ui/workspace/shapes.cljs +++ b/frontend/src/app/main/ui/workspace/shapes.cljs @@ -65,7 +65,7 @@ [:& frame-wrapper {:shape item :key (:id item) :objects (get frame-objects (:id item)) - :thumbnail? (not (get active-frames (:id item) false))}] + :thumbnail? (not (contains? active-frames (:id item)))}] [:& shape-wrapper {:shape item :key (:id item)}]))])) diff --git a/frontend/src/app/main/ui/workspace/shapes/frame.cljs b/frontend/src/app/main/ui/workspace/shapes/frame.cljs index 22991ac905..e74c3b56ab 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame.cljs @@ -114,10 +114,10 @@ (when (not @rendered?) (reset! rendered? true))))) [:g.frame-container {:key "frame-container" :ref on-frame-load} - thumb-renderer - - [:g.frame-thumbnail + [:g.frame-thumbnail {:id (dm/str "thumbnail-container-" (:id shape))} [:> frame/frame-thumbnail {:key (dm/str (:id shape)) :shape (cond-> shape (some? thumbnail-data) - (assoc :thumbnail thumbnail-data))}]]])))) + (assoc :thumbnail thumbnail-data))}] + + thumb-renderer]])))) diff --git a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs index d0b9870332..5aa7489b68 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs @@ -14,16 +14,22 @@ [app.util.dom :as dom] [app.util.object :as obj] [app.util.timers :as ts] + [beicon.core :as rx] [rumext.alpha :as mf])) (defn- draw-thumbnail-canvas [canvas-node img-node] - (let [canvas-context (.getContext canvas-node "2d") - canvas-width (.-width canvas-node) - canvas-height (.-height canvas-node)] - (.clearRect canvas-context 0 0 canvas-width canvas-height) - (.drawImage canvas-context img-node 0 0 canvas-width canvas-height) - (.toDataURL canvas-node "image/jpeg" 0.8))) + (try + (when (and (some? canvas-node) (some? img-node)) + (let [canvas-context (.getContext canvas-node "2d") + canvas-width (.-width canvas-node) + canvas-height (.-height canvas-node)] + (.clearRect canvas-context 0 0 canvas-width canvas-height) + (.drawImage canvas-context img-node 0 0 canvas-width canvas-height) + (.toDataURL canvas-node "image/png"))) + (catch :default err + (.error js/console err) + nil))) (defn use-render-thumbnail "Hook that will create the thumbnail thata" @@ -44,43 +50,52 @@ thumbnail-ref? (mf/use-var thumbnail?) + updates-str (mf/use-memo #(rx/subject)) + on-image-load (mf/use-callback (fn [] - (let [canvas-node (mf/ref-val frame-canvas-ref) - img-node (mf/ref-val frame-image-ref) - thumb-data (draw-thumbnail-canvas canvas-node img-node)] - (st/emit! (dw/update-thumbnail id thumb-data)) - (reset! image-url nil)))) + (ts/raf + #(let [canvas-node (mf/ref-val frame-canvas-ref) + img-node (mf/ref-val frame-image-ref) + thumb-data (draw-thumbnail-canvas canvas-node img-node)] + (when (some? thumb-data) + (st/emit! (dw/update-thumbnail id thumb-data)) + (reset! image-url nil)))))) - on-change - (mf/use-callback - (fn [] - (when (and (some? @node-ref) (not @disable-ref?)) - (let [node @node-ref] - (ts/schedule-on-idle - #(let [frame-html (dom/node->xml node) - {:keys [x y width height]} @shape-ref - svg-node - (-> (dom/make-node "http://www.w3.org/2000/svg" "svg") - (dom/set-property! "version" "1.1") - (dom/set-property! "viewBox" (dm/str x " " y " " width " " height)) - (dom/set-property! "width" width) - (dom/set-property! "height" height) - (dom/set-property! "fill" "none") - (obj/set! "innerHTML" frame-html)) - img-src (-> svg-node dom/node->xml dom/svg->data-uri)] - (reset! image-url img-src))))))) + on-update-frame + (fn [] + (when (and (some? @node-ref) (not @disable-ref?)) + (let [node @node-ref + frame-html (dom/node->xml node) + {:keys [x y width height]} @shape-ref + svg-node + (-> (dom/make-node "http://www.w3.org/2000/svg" "svg") + (dom/set-property! "version" "1.1") + (dom/set-property! "viewBox" (dm/str x " " y " " width " " height)) + (dom/set-property! "width" width) + (dom/set-property! "height" height) + (dom/set-property! "fill" "none") + (obj/set! "innerHTML" frame-html)) + img-src (-> svg-node dom/node->xml dom/svg->data-uri)] + (reset! image-url img-src)))) on-load-frame-dom (mf/use-callback (fn [node] (when (and (some? node) (nil? @observer-ref)) - (on-change []) - (let [observer (js/MutationObserver. on-change)] + (rx/push! updates-str :update) + (let [observer (js/MutationObserver. (partial rx/push! updates-str))] (.observe observer node #js {:childList true :attributes true :characterData true :subtree true}) (reset! observer-ref observer)))))] + (mf/use-effect + (fn [] + (let [subid (->> updates-str + (rx/debounce 200) + (rx/subs on-update-frame))] + #(rx/dispose! subid)))) + (mf/use-effect (mf/deps disable?) (fn [] @@ -104,7 +119,7 @@ [on-load-frame-dom (when (some? @image-url) (mf/html - [:g.thumbnail-rendering {:opacity 0} + [:g.thumbnail-rendering [:foreignObject {:x x :y y :width width :height height} [:canvas {:ref frame-canvas-ref :width fixed-width diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index 0e5c2ed638..715543c848 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -90,7 +90,7 @@ hover (mf/use-state nil) hover-disabled? (mf/use-state false) frame-hover (mf/use-state nil) - active-frames (mf/use-state {}) + active-frames (mf/use-state #{}) ;; REFS viewport-ref (mf/use-ref nil) @@ -183,7 +183,7 @@ (hooks/setup-hover-shapes page-id move-stream base-objects transform selected mod? hover hover-ids @hover-disabled? focus 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 zoom) + (hooks/setup-active-frames base-objects hover-ids selected active-frames zoom) [:div.viewport [:div.viewport-overlays {:ref overlays-ref} diff --git a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs index a8890c0b44..9f656d1de8 100644 --- a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs @@ -219,32 +219,21 @@ (gsh/overlaps? frame vbox)))) (defn setup-active-frames - [objects vbox hover active-frames zoom] + [objects hover-ids selected active-frames zoom] (mf/use-effect - (mf/deps vbox) - + (mf/deps objects @hover-ids selected zoom) (fn [] - (swap! active-frames - (fn [active-frames] - (let [set-active-frames - (fn [active-frames id active?] - (cond-> active-frames - (and active? (inside-vbox vbox objects id)) - (assoc id true)))] - (reduce-kv set-active-frames {} active-frames)))))) - - (mf/use-effect - (mf/deps @hover @active-frames zoom) - (fn [] - (let [frame-id (if (= :frame (:type @hover)) - (:id @hover) - (:frame-id @hover))] - (if (< zoom 0.25) - (when (some? @active-frames) - (reset! active-frames nil)) - (when (and (some? frame-id)(not (contains? @active-frames frame-id))) - (reset! active-frames {frame-id true}))))))) + (when (some? @hover-ids) + (let [hover-frame (when (> zoom 0.25) (last @hover-ids)) + new-active-frames (if (some? hover-frame) #{hover-frame} #{}) + new-active-frames + (into new-active-frames + (comp + (filter #(not= :frame (get-in objects [% :type]))) + (map #(get-in objects [% :frame-id]))) + selected) ] + (reset! active-frames new-active-frames)))))) ;; NOTE: this is executed on each page change, maybe we need to move ;; this shortcuts outside the viewport? diff --git a/frontend/src/app/main/ui/workspace/viewport/utils.cljs b/frontend/src/app/main/ui/workspace/viewport/utils.cljs index 69a53474fc..fe5c4586e0 100644 --- a/frontend/src/app/main/ui/workspace/viewport/utils.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/utils.cljs @@ -86,7 +86,7 @@ mask? (and group? masked-group?) ;; When the shape is a frame we maybe need to move its thumbnail - thumb-node (when frame? (dom/query (str "#thumbnail-" id)))] + thumb-node (when frame? (dom/query (str "#thumbnail-container-" id)))] (cond frame? diff --git a/frontend/src/app/util/text_svg_position.cljs b/frontend/src/app/util/text_svg_position.cljs index b8a8a4f946..d880391967 100644 --- a/frontend/src/app/util/text_svg_position.cljs +++ b/frontend/src/app/util/text_svg_position.cljs @@ -119,6 +119,7 @@ :font-weight (str (get "font-weight")) :text-transform (str (get "text-transform")) :text-decoration (str (get "text-decoration")) + :letter-spacing (str (get "letter-spacing")) :font-style (str (get "font-style")) :fills (transit/decode-str (get "--fills")) :text text}))))))))))