mirror of
https://github.com/penpot/penpot.git
synced 2025-03-23 05:01:23 -05:00
commit
8298d460e6
10 changed files with 117 additions and 85 deletions
|
@ -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)
|
||||
|
|
|
@ -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])]])))
|
||||
|
||||
|
|
|
@ -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)]]]))]]))
|
||||
|
|
|
@ -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)}]))]))
|
||||
|
|
|
@ -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]]))))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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}))))))))))
|
||||
|
|
Loading…
Add table
Reference in a new issue