mirror of
https://github.com/penpot/penpot.git
synced 2025-04-04 19:11:20 -05:00
commit
c2c01831fb
11 changed files with 117 additions and 157 deletions
|
@ -8,9 +8,9 @@
|
|||
metosin/reitit-core {:mvn/version "0.5.17"}
|
||||
|
||||
funcool/beicon {:mvn/version "2021.07.05-1"}
|
||||
funcool/okulary {:mvn/version "2020.04.14-0"}
|
||||
funcool/okulary {:mvn/version "2022.04.01-10"}
|
||||
funcool/potok {:mvn/version "2021.09.20-0"}
|
||||
funcool/rumext {:mvn/version "2022.03.28-131"}
|
||||
funcool/rumext {:mvn/version "2022.03.31-133"}
|
||||
funcool/tubax {:mvn/version "2021.05.20-0"}
|
||||
|
||||
instaparse/instaparse {:mvn/version "1.4.10"}
|
||||
|
|
|
@ -20,39 +20,45 @@
|
|||
([state]
|
||||
(lookup-page-objects state (:current-page-id state)))
|
||||
([state page-id]
|
||||
(get-in state [:workspace-data :pages-index page-id :objects])))
|
||||
(dm/get-in state [:workspace-data :pages-index page-id :objects])))
|
||||
|
||||
(defn lookup-page-options
|
||||
([state]
|
||||
(lookup-page-options state (:current-page-id state)))
|
||||
([state page-id]
|
||||
(get-in state [:workspace-data :pages-index page-id :options])))
|
||||
(dm/get-in state [:workspace-data :pages-index page-id :options])))
|
||||
|
||||
(defn lookup-component-objects
|
||||
([state component-id]
|
||||
(get-in state [:workspace-data :components component-id :objects])))
|
||||
(dm/get-in state [:workspace-data :components component-id :objects])))
|
||||
|
||||
(defn lookup-local-components
|
||||
([state]
|
||||
(get-in state [:workspace-data :components])))
|
||||
(dm/get-in state [:workspace-data :components])))
|
||||
|
||||
(defn process-selected-shapes
|
||||
([objects selected]
|
||||
(process-selected-shapes objects selected nil))
|
||||
|
||||
([objects selected {:keys [omit-blocked?] :or {omit-blocked? false}}]
|
||||
(letfn [(selectable? [id]
|
||||
(and (contains? objects id)
|
||||
(or (not omit-blocked?)
|
||||
(not (get-in objects [id :blocked] false)))))]
|
||||
(let [selected (->> selected (cph/clean-loops objects))]
|
||||
(into (d/ordered-set)
|
||||
(filter selectable?)
|
||||
selected)))))
|
||||
|
||||
;; TODO: improve performance of this
|
||||
(defn lookup-selected
|
||||
([state]
|
||||
(lookup-selected state nil))
|
||||
([state options]
|
||||
(lookup-selected state (:current-page-id state) options))
|
||||
([state page-id {:keys [omit-blocked?] :or {omit-blocked? false}}]
|
||||
([state page-id options]
|
||||
(let [objects (lookup-page-objects state page-id)
|
||||
selected (->> (dm/get-in state [:workspace-local :selected])
|
||||
(cph/clean-loops objects))
|
||||
selectable? (fn [id]
|
||||
(and (contains? objects id)
|
||||
(or (not omit-blocked?)
|
||||
(not (get-in objects [id :blocked] false)))))]
|
||||
(into (d/ordered-set)
|
||||
(filter selectable?)
|
||||
selected))))
|
||||
selected (dm/get-in state [:workspace-local :selected])]
|
||||
(process-selected-shapes objects selected options))))
|
||||
|
||||
(defn lookup-shapes
|
||||
([state ids]
|
||||
|
@ -79,7 +85,7 @@
|
|||
[state file-id]
|
||||
(if (= file-id (:current-file-id state))
|
||||
(get state :workspace-data)
|
||||
(get-in state [:workspace-libraries file-id :data])))
|
||||
(dm/get-in state [:workspace-libraries file-id :data])))
|
||||
|
||||
(defn get-libraries
|
||||
"Retrieve all libraries, including the local file."
|
||||
|
|
|
@ -102,8 +102,22 @@
|
|||
(l/derived :workspace-drawing st/state))
|
||||
|
||||
;; TODO: rename to workspace-selected (?)
|
||||
;; Don't use directly from components, this is a proxy to improve performance of selected-shapes
|
||||
(def ^:private selected-shapes-data
|
||||
(l/derived
|
||||
(fn [state]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
selected (dm/get-in state [:workspace-local :selected])]
|
||||
{:objects objects :selected selected}))
|
||||
st/state (fn [v1 v2]
|
||||
(and (identical? (:objects v1) (:objects v2))
|
||||
(= (:selected v1) (:selected v2))))))
|
||||
|
||||
(def selected-shapes
|
||||
(l/derived wsh/lookup-selected st/state =))
|
||||
(l/derived
|
||||
(fn [{:keys [objects selected]}]
|
||||
(wsh/process-selected-shapes objects selected))
|
||||
selected-shapes-data))
|
||||
|
||||
(defn make-selected-ref
|
||||
[id]
|
||||
|
@ -258,7 +272,7 @@
|
|||
|
||||
(defn objects-by-id
|
||||
[ids]
|
||||
(l/derived #(wsh/lookup-shapes % ids) st/state =))
|
||||
(l/derived #(into [] (keep (d/getf %)) ids) workspace-page-objects))
|
||||
|
||||
(defn- set-content-modifiers [state]
|
||||
(fn [id shape]
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :as mth]
|
||||
[app.config :as cfg]
|
||||
[app.main.ui.context :as muc]
|
||||
[app.main.ui.shapes.attrs :as attrs]
|
||||
[app.main.ui.shapes.custom-stroke :refer [shape-custom-strokes]]
|
||||
|
@ -50,9 +51,16 @@
|
|||
|
||||
[:> :g group-props
|
||||
(for [[index data] (d/enumerate position-data)]
|
||||
(let [props (-> #js {:x (mth/round (:x data))
|
||||
:y (mth/round (- (:y data) (:height data)))
|
||||
:alignmentBaseline "text-before-edge"
|
||||
(let [y (if (cfg/check-browser? :safari)
|
||||
(mth/round (- (:y data) (:height data)))
|
||||
(mth/round (:y data)))
|
||||
|
||||
alignment-bl (when (cfg/check-browser? :safari) "text-before-edge")
|
||||
dominant-bl (when-not (cfg/check-browser? :safari) "ideographic")
|
||||
props (-> #js {:x (mth/round (:x data))
|
||||
:y y
|
||||
:alignmentBaseline alignment-bl
|
||||
:dominantBaseline dominant-bl
|
||||
:style (-> #js {:fontFamily (:font-family data)
|
||||
:fontSize (:font-size data)
|
||||
:fontWeight (:font-weight data)
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
[app.main.ui.shapes.rect :as rect]
|
||||
[app.main.ui.shapes.text.fontfaces :as ff]
|
||||
[app.main.ui.workspace.shapes.bool :as bool]
|
||||
[app.main.ui.workspace.shapes.bounding-box :refer [bounding-box]]
|
||||
[app.main.ui.workspace.shapes.common :as common]
|
||||
[app.main.ui.workspace.shapes.frame :as frame]
|
||||
[app.main.ui.workspace.shapes.group :as group]
|
||||
|
@ -26,7 +25,6 @@
|
|||
[app.main.ui.workspace.shapes.svg-raw :as svg-raw]
|
||||
[app.main.ui.workspace.shapes.text :as text]
|
||||
[app.util.object :as obj]
|
||||
[debug :refer [debug?]]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
(declare shape-wrapper)
|
||||
|
@ -87,10 +85,7 @@
|
|||
;; Only used when drawing a new frame.
|
||||
:frame [:> frame-wrapper opts]
|
||||
|
||||
nil)
|
||||
|
||||
(when (debug? :bounding-boxes)
|
||||
[:> bounding-box opts])])))
|
||||
nil)])))
|
||||
|
||||
(def group-wrapper (group/group-wrapper-factory shape-wrapper))
|
||||
(def svg-raw-wrapper (svg-raw/svg-raw-wrapper-factory shape-wrapper))
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) UXBOX Labs SL
|
||||
|
||||
(ns app.main.ui.workspace.shapes.bounding-box
|
||||
(:require
|
||||
["randomcolor" :as rdcolor]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.main.refs :as refs]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
(defn fixed
|
||||
[num]
|
||||
(when num (.toFixed num 2)))
|
||||
|
||||
(mf/defc cross-point [{:keys [point zoom color]}]
|
||||
(let [width (/ 5 zoom)]
|
||||
[:g.point
|
||||
[:line {:x1 (- (:x point) width) :y1 (- (:y point) width)
|
||||
:x2 (+ (:x point) width) :y2 (+ (:y point) width)
|
||||
:stroke color
|
||||
:stroke-width "1px"
|
||||
:stroke-opacity 0.5}]
|
||||
|
||||
[:line {:x1 (+ (:x point) width) :y1 (- (:y point) width)
|
||||
:x2 (- (:x point) width) :y2 (+ (:y point) width)
|
||||
:stroke color
|
||||
:stroke-width "1px"
|
||||
:stroke-opacity 0.5}]]))
|
||||
|
||||
(mf/defc render-rect [{{:keys [x y width height]} :rect :keys [color transform]}]
|
||||
[:rect {:x x
|
||||
:y y
|
||||
:width width
|
||||
:height height
|
||||
:transform (or transform "none")
|
||||
:style {:stroke color
|
||||
:fill "none"
|
||||
:stroke-width "1px"
|
||||
:pointer-events "none"}}])
|
||||
|
||||
(mf/defc render-rect-points [{:keys [points color]}]
|
||||
(for [[p1 p2] (map vector points (concat (rest points) [(first points)]))]
|
||||
[:line {:x1 (:x p1)
|
||||
:y1 (:y p1)
|
||||
:x2 (:x p2)
|
||||
:y2 (:y p2)
|
||||
:style {:stroke color
|
||||
:stroke-width "1px"}}]))
|
||||
|
||||
(mf/defc bounding-box
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [shape (unchecked-get props "shape")
|
||||
bounding-box (gsh/points->selrect (-> shape :points))
|
||||
shape-center (gsh/center-shape shape)
|
||||
line-color (rdcolor #js {:seed (str (:id shape))})
|
||||
zoom (mf/deref refs/selected-zoom)]
|
||||
|
||||
[:g.bounding-box
|
||||
[:text {:x (:x bounding-box)
|
||||
:y (- (:y bounding-box) 5)
|
||||
:font-size 10
|
||||
:fill line-color
|
||||
:stroke "var(--color-white)"
|
||||
:stroke-width 0.1}
|
||||
(str/format "%s - (%s, %s)" (str/slice (str (:id shape)) 0 8) (fixed (:x bounding-box)) (fixed (:y bounding-box)))]
|
||||
|
||||
[:g.center
|
||||
[:& cross-point {:point shape-center
|
||||
:zoom zoom
|
||||
:color line-color}]]
|
||||
|
||||
[:g.points
|
||||
(for [point (:points shape)]
|
||||
[:& cross-point {:point point
|
||||
:zoom zoom
|
||||
:color line-color}])
|
||||
#_[:& render-rect-points {:points (:points shape)
|
||||
:color line-color}]]
|
||||
|
||||
[:g.selrect
|
||||
[:& render-rect {:rect (:selrect shape)
|
||||
;; :transform (gsh/transform-matrix shape)
|
||||
:color line-color}]
|
||||
#_[:& render-rect {:rect bounding-box
|
||||
:color line-color}]]]))
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
(ns app.util.dom
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.logging :as log]
|
||||
|
@ -231,20 +232,20 @@
|
|||
(.-innerText el)))
|
||||
|
||||
(defn query
|
||||
([^string query]
|
||||
(query globals/document query))
|
||||
([^string selector]
|
||||
(query globals/document selector))
|
||||
|
||||
([^js el ^string query]
|
||||
([^js el ^string selector]
|
||||
(when (some? el)
|
||||
(.querySelector el query))))
|
||||
(.querySelector el selector))))
|
||||
|
||||
(defn query-all
|
||||
([^string query]
|
||||
(query-all globals/document query))
|
||||
([^string selector]
|
||||
(query-all globals/document selector))
|
||||
|
||||
([^js el ^string query]
|
||||
([^js el ^string selector]
|
||||
(when (some? el)
|
||||
(.querySelectorAll el query))))
|
||||
(.querySelectorAll el selector))))
|
||||
|
||||
(defn get-client-position
|
||||
[^js event]
|
||||
|
@ -535,3 +536,13 @@
|
|||
(and (some? node)
|
||||
(some? candidate)
|
||||
(.contains node candidate)))
|
||||
|
||||
(defn seq-nodes
|
||||
[root-node]
|
||||
(letfn [(branch? [node]
|
||||
(d/not-empty? (get-children node)))
|
||||
|
||||
(get-children [node]
|
||||
(seq (.-children node)))]
|
||||
(->> root-node
|
||||
(tree-seq branch? get-children))))
|
||||
|
|
|
@ -214,11 +214,13 @@
|
|||
|
||||
(= type :frame)
|
||||
(let [;; The nodes with the "frame-background" class can have some anidation depending on the strokes they have
|
||||
g-nodes (find-all-nodes node :g)
|
||||
g-nodes (find-all-nodes node :g)
|
||||
defs-nodes (flatten (map #(find-all-nodes % :defs) g-nodes))
|
||||
gg-nodes (flatten (map #(find-all-nodes % :g) g-nodes))
|
||||
rect-nodes (flatten [[(find-all-nodes node :rect)]
|
||||
(map #(find-all-nodes % #{:rect :path}) defs-nodes)
|
||||
(map #(find-all-nodes % #{:rect :path}) g-nodes)])
|
||||
(map #(find-all-nodes % #{:rect :path}) g-nodes)
|
||||
(map #(find-all-nodes % #{:rect :path}) gg-nodes)])
|
||||
svg-node (d/seek #(= "frame-background" (get-in % [:attrs :class])) rect-nodes)]
|
||||
(merge (add-attrs {} (:attrs svg-node)) node-attrs))
|
||||
|
||||
|
|
|
@ -302,16 +302,17 @@
|
|||
(reduce simplify-command [[start] start-pos start-pos start-pos start-pos])
|
||||
(first))))
|
||||
|
||||
|
||||
(defn parse-path [path-str]
|
||||
(let [clean-path-str
|
||||
(-> path-str
|
||||
(str/trim)
|
||||
;; Change "commas" for spaces
|
||||
(str/replace #"," " ")
|
||||
;; Remove all consecutive spaces
|
||||
(str/replace #"\s+" " "))
|
||||
commands (re-seq commands-regex clean-path-str)]
|
||||
(-> (mapcat parse-command commands)
|
||||
(simplify-commands))))
|
||||
(if (empty? path-str)
|
||||
path-str
|
||||
(let [clean-path-str
|
||||
(-> path-str
|
||||
(str/trim)
|
||||
;; Change "commas" for spaces
|
||||
(str/replace #"," " ")
|
||||
;; Remove all consecutive spaces
|
||||
(str/replace #"\s+" " "))
|
||||
commands (re-seq commands-regex clean-path-str)]
|
||||
(-> (mapcat parse-command commands)
|
||||
(simplify-commands)))))
|
||||
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
;; Upload changes batches size
|
||||
(def ^:const change-batch-size 100)
|
||||
|
||||
(def conjv (fnil conj []))
|
||||
|
||||
(defn get-file
|
||||
"Resolves the file inside the context given its id and the data"
|
||||
([context type]
|
||||
|
@ -261,25 +263,29 @@
|
|||
(cond-> (some? old-id)
|
||||
(assoc :id (resolve old-id)))
|
||||
(cond-> (< (:version context 1) 2)
|
||||
(translate-frame type file)))
|
||||
(translate-frame type file)))]
|
||||
(try
|
||||
(let [file (case type
|
||||
:frame (fb/add-artboard file data)
|
||||
:group (fb/add-group file data)
|
||||
:bool (fb/add-bool file data)
|
||||
:rect (fb/create-rect file data)
|
||||
:circle (fb/create-circle file data)
|
||||
:path (fb/create-path file data)
|
||||
:text (fb/create-text file data)
|
||||
:image (fb/create-image file data)
|
||||
:svg-raw (fb/create-svg-raw file data)
|
||||
#_default file)]
|
||||
|
||||
file (case type
|
||||
:frame (fb/add-artboard file data)
|
||||
:group (fb/add-group file data)
|
||||
:bool (fb/add-bool file data)
|
||||
:rect (fb/create-rect file data)
|
||||
:circle (fb/create-circle file data)
|
||||
:path (fb/create-path file data)
|
||||
:text (fb/create-text file data)
|
||||
:image (fb/create-image file data)
|
||||
:svg-raw (fb/create-svg-raw file data)
|
||||
#_default file)]
|
||||
;; We store this data for post-processing after every shape has been
|
||||
;; added
|
||||
(cond-> file
|
||||
(d/not-empty? interactions)
|
||||
(assoc-in [:interactions (:id data)] interactions)))
|
||||
|
||||
;; We store this data for post-processing after every shape has been
|
||||
;; added
|
||||
(cond-> file
|
||||
(d/not-empty? interactions)
|
||||
(assoc-in [:interactions (:id data)] interactions))))))
|
||||
(catch :default err
|
||||
(log/error :hint (ex-message err) :cause err :js/data data)
|
||||
(update file :errors conjv data)))))))
|
||||
|
||||
(defn setup-interactions
|
||||
[file]
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
[app.main.data.workspace.path.shortcuts]
|
||||
[app.main.data.workspace.shortcuts]
|
||||
[app.main.store :as st]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.object :as obj]
|
||||
[app.util.timers :as timers]
|
||||
[beicon.core :as rx]
|
||||
|
@ -340,3 +341,9 @@
|
|||
(.log js/console "%c Viewer" style)
|
||||
(print-shortcuts app.main.data.viewer.shortcuts/shortcuts)))
|
||||
nil)
|
||||
|
||||
(defn ^:export nodeStats
|
||||
[]
|
||||
(let [root-node (dom/query ".viewport .render-shapes")
|
||||
num-nodes (->> (dom/seq-nodes root-node) count)]
|
||||
#js {:number num-nodes}))
|
||||
|
|
Loading…
Add table
Reference in a new issue