0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-11 23:31:21 -05:00

Merge pull request #1263 from penpot/bugfixes

Bugfixes
This commit is contained in:
Andrey Antukh 2021-10-07 13:50:52 +02:00 committed by GitHub
commit 59d44c41e4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 343 additions and 148 deletions

View file

@ -9,8 +9,30 @@
- Add advanced prototyping [#244](https://tree.taiga.io/project/penpot/us/244).
### :bug: Bugs fixed
- Fix problem with overflow dropdown on stroke-cap [#1216](https://github.com/penpot/penpot/issues/1216)
- Fix menu context for single element nested in components [#1186](https://github.com/penpot/penpot/issues/1186)
- Fix error screen when operations over comments fail [#1219](https://github.com/penpot/penpot/issues/1219)
- Fix undo problem when changing typography/color from library [#1230](https://github.com/penpot/penpot/issues/1230)
- Fix problem with text margin while rendering [#1231](https://github.com/penpot/penpot/issues/1231)
- Fix problem with masked texts on exporting [Taiga #2116](https://tree.taiga.io/project/penpot/issue/2116)
- Fix text editor enter behaviour with centered texts [Taiga #2126](https://tree.taiga.io/project/penpot/issue/2126)
- Fix residual stroke on imported svg [Taiga #2125](https://tree.taiga.io/project/penpot/issue/2125)
- Add links for terms of service and privacy policy in register checkbox [Taiga #2020](https://tree.taiga.io/project/penpot/issue/2020)
- Allow three character hex and web colors in color picker hex input [#1184](https://github.com/penpot/penpot/issues/1184)
- Allow lowercase search for fonts [#1180](https://github.com/penpot/penpot/issues/1180)
- Fix group renaming problem [Taiga #1969](https://tree.taiga.io/project/penpot/issue/1969)
- Fix export group with shadows on children [Taiga #2036](https://tree.taiga.io/project/penpot/issue/2036)
- Fix zoom context menu in viewer [Taiga #2041](https://tree.taiga.io/project/penpot/issue/2041)
- Fix stroke caps adjustments in relation with stroke size [Taiga #2123](https://tree.taiga.io/project/penpot/issue/2123)
- Fix problem duplicating paths [Taiga #2147](https://tree.taiga.io/project/penpot/issue/2147)
### :arrow_up: Deps updates
### :boom: Breaking changes
- Some stroke-caps can change behaviour
- Text display bug fix could potentialy make some texts jump a line
### :heart: Community contributions by (Thank you!)
## 1.8.3-alpha

View file

@ -141,6 +141,7 @@
(d/export gpr/points->selrect)
(d/export gpr/points->rect)
(d/export gpr/center->rect)
(d/export gpr/join-rects)
(d/export gtr/move)
(d/export gtr/absolute-move)

View file

@ -7,6 +7,8 @@
(ns app.common.geom.shapes.bool
(:require
[app.common.geom.shapes.path :as gsp]
[app.common.geom.shapes.rect :as gpr]
[app.common.geom.shapes.transforms :as gtr]
[app.common.path.bool :as pb]
[app.common.path.shapes-to-path :as stp]))
@ -19,7 +21,12 @@
(mapv :content)
(pb/content-bool (:bool-type shape)))
[points selrect] (gsp/content->points+selrect shape content)]
[points selrect]
(if (empty? content)
(let [selrect (gtr/selection-rect children)
points (gpr/rect->points selrect)]
[points selrect])
(gsp/content->points+selrect shape content))]
(-> shape
(assoc :selrect selrect)
(assoc :points points))))

View file

@ -12,7 +12,8 @@
[app.common.geom.shapes.common :as gsc]
[app.common.geom.shapes.rect :as gpr]
[app.common.math :as mth]
[app.common.path.commands :as upc]))
[app.common.path.commands :as upc]
[app.common.path.subpaths :as sp]))
(def ^:const curve-curve-precision 0.1)
(def ^:const curve-range-precision 2)
@ -818,19 +819,33 @@
(defn is-point-in-content?
[point content]
(let [selrect (content->selrect content)
ray-line [point (gpt/point (inc (:x point)) (:y point))]
(letfn [(cast-ray [cmd]
(let [ray-line [point (gpt/point (inc (:x point)) (:y point))]]
(case (:command cmd)
:line-to (ray-line-intersect point (command->line cmd))
:curve-to (ray-curve-intersect ray-line (command->bezier cmd))
#_:else [])))]
closed-subpaths
(->> content
(sp/close-subpaths)
(sp/get-subpaths)
(filterv sp/is-closed?))
(->> content
(mapcat cast-ray)
(map second)
(reduce +)
(not= 0))))
cast-ray
(fn [cmd]
(case (:command cmd)
:line-to (ray-line-intersect point (command->line cmd))
:curve-to (ray-curve-intersect ray-line (command->bezier cmd))
#_:else []))
is-point-in-subpath?
(fn [subpath]
(and (gpr/contains-point? (content->selrect (:data subpath)) point)
(->> (:data subpath)
(mapcat cast-ray)
(map second)
(reduce +)
(not= 0))))]
(and (gpr/contains-point? selrect point)
(some is-point-in-subpath? closed-subpaths))))
(defn split-line-to
"Given a point and a line-to command will create a two new line-to commands

View file

@ -48,6 +48,16 @@
(defn rect->selrect [rect]
(-> rect rect->points points->selrect))
(defn join-rects [rects]
(let [minx (transduce (comp (map :x) (remove nil?)) min ##Inf rects)
miny (transduce (comp (map :y) (remove nil?)) min ##Inf rects)
maxx (transduce (comp (map #(+ (:x %) (:width %))) (remove nil?)) max ##-Inf rects)
maxy (transduce (comp (map #(+ (:y %) (:height %))) (remove nil?)) max ##-Inf rects)]
{:x minx
:y miny
:width (- maxx minx)
:height (- maxy miny)}))
(defn join-selrects [selrects]
(let [minx (transduce (comp (map :x1) (remove nil?)) min ##Inf selrects)
miny (transduce (comp (map :y1) (remove nil?)) min ##Inf selrects)

View file

@ -18,7 +18,6 @@
[app.common.spec :as us]
[app.common.text :as txt]))
;; --- Relative Movement
(defn- move-selrect [selrect {dx :x dy :y}]
@ -681,3 +680,12 @@
(assoc :resize-transform (:resize-transform parent-modifiers)
:resize-transform-inverse (:resize-transform-inverse parent-modifiers)))))
(defn selection-rect
"Returns a rect that contains all the shapes and is aware of the
rotation of each shape. Mainly used for multiple selection."
[shapes]
(->> shapes
(transform-shape)
(map (comp gpr/points->selrect :points))
(gpr/join-selrects)))

View file

@ -116,6 +116,9 @@
(->> subpaths
(reduce merge-with-candidate [candidate []]))))
(defn is-closed? [subpath]
(pt= (:from subpath) (:to subpath)))
(defn close-subpaths
"Searches a path for posible supaths that can create closed loops and merge them"
[content]
@ -127,7 +130,7 @@
(if (some? current)
(let [[new-current new-subpaths]
(if (pt= (:from current) (:to current))
(if (is-closed? current)
[current subpaths]
(merge-paths current subpaths))]

View file

@ -210,11 +210,10 @@
:height (.. attrs -height -value)
:colors (.split colors ",")}))
(extract-single-node [node]
(extract-single-node [[shot node]]
(log/trace :fn :extract-single-node)
(p/let [attrs (bw/eval! node extract-element-attrs)
shot (bw/screenshot node {:omit-background? true :type "png"})]
(p/let [attrs (bw/eval! node extract-element-attrs)]
{:id (unchecked-get attrs "id")
:x (unchecked-get attrs "x")
:y (unchecked-get attrs "y")
@ -223,13 +222,21 @@
:colors (vec (unchecked-get attrs "colors"))
:data shot}))
(resolve-text-node [page node]
(p/let [attrs (bw/eval! node extract-element-attrs)
id (unchecked-get attrs "id")
text-node (bw/select page (str "#screenshot-text-" id " foreignObject"))
shot (bw/screenshot text-node {:omit-background? true :type "png"})]
[shot node]))
(clean-temp-data [{:keys [tempdir] :as node}]
(p/do!
(sh/rmdir! tempdir)
(dissoc node :tempdir)))
(process-text-node [item]
(process-text-node [page item]
(-> (p/resolved item)
(p/then (partial resolve-text-node page))
(p/then extract-single-node)
(p/then trace-node)
(p/then clean-temp-data)))
@ -237,7 +244,7 @@
(process-text-nodes [page]
(log/trace :fn :process-text-nodes)
(-> (bw/select-all page "#screenshot foreignObject")
(p/then (fn [nodes] (p/all (map process-text-node nodes))))))
(p/then (fn [nodes] (p/all (map (partial process-text-node page) nodes))))))
(extract-svg [page]
(p/let [dom (bw/select page "#screenshot")
@ -271,7 +278,7 @@
(p/let [page (render-in-page page rctx)]
(extract-svg page)))]
(let [path (str "/render-object/" file-id "/" page-id "/" object-id)
(let [path (str "/render-object/" file-id "/" page-id "/" object-id "?render-texts=true")
uri (-> (u/uri (cf/get :public-uri))
(assoc :path "/")
(assoc :fragment path))

View file

@ -874,7 +874,6 @@
position: relative;
top: 2px;
width: 100%;
z-index: 20;
}
.btn-options {
@ -1539,7 +1538,8 @@
right: 5px;
top: 30px;
z-index: 12;
min-width: 200px;
width: 200px;
height: 320px;
position: fixed;
& li.separator {

View file

@ -57,13 +57,6 @@
flex-shrink: 0;
}
.zoom-widget {
.dropdown {
top: 45px;
left: 25px;
}
}
.view-options {
align-items: center;
cursor: pointer;

View file

@ -77,7 +77,8 @@
(watch [_ _ _]
(->> (rp/mutation :create-comment-thread params)
(rx/mapcat #(rp/query :comment-thread {:file-id (:file-id %) :id (:id %)}))
(rx/map #(partial created %)))))))
(rx/map #(partial created %))
(rx/catch #(rx/throw {:type :comment-error})))))))
(defn update-comment-thread-status
[{:keys [id] :as thread}]
@ -87,7 +88,8 @@
(watch [_ _ _]
(let [done #(d/update-in-when % [:comment-threads id] assoc :count-unread-comments 0)]
(->> (rp/mutation :update-comment-thread-status {:id id})
(rx/map (constantly done)))))))
(rx/map (constantly done))
(rx/catch #(rx/throw {:type :comment-error})))))))
(defn update-comment-thread
@ -104,6 +106,7 @@
ptk/WatchEvent
(watch [_ _ _]
(->> (rp/mutation :update-comment-thread {:id id :is-resolved is-resolved})
(rx/catch #(rx/throw {:type :comment-error}))
(rx/ignore)))))
@ -118,7 +121,8 @@
(watch [_ _ _]
(rx/concat
(->> (rp/mutation :add-comment {:thread-id (:id thread) :content content})
(rx/map #(partial created %)))
(rx/map #(partial created %))
(rx/catch #(rx/throw {:type :comment-error})))
(rx/of (refresh-comment-thread thread)))))))
(defn update-comment
@ -132,6 +136,7 @@
ptk/WatchEvent
(watch [_ _ _]
(->> (rp/mutation :update-comment {:id id :content content})
(rx/catch #(rx/throw {:type :comment-error}))
(rx/ignore)))))
(defn delete-comment-thread
@ -147,6 +152,7 @@
ptk/WatchEvent
(watch [_ _ _]
(->> (rp/mutation :delete-comment-thread {:id id})
(rx/catch #(rx/throw {:type :comment-error}))
(rx/ignore)))))
(defn delete-comment
@ -160,6 +166,7 @@
ptk/WatchEvent
(watch [_ _ _]
(->> (rp/mutation :delete-comment {:id id})
(rx/catch #(rx/throw {:type :comment-error}))
(rx/ignore)))))
(defn refresh-comment-thread
@ -171,7 +178,8 @@
ptk/WatchEvent
(watch [_ _ _]
(->> (rp/query :comment-thread {:file-id file-id :id id})
(rx/map #(partial fetched %)))))))
(rx/map #(partial fetched %))
(rx/catch #(rx/throw {:type :comment-error})))))))
(defn retrieve-comment-threads
[file-id]
@ -182,7 +190,8 @@
ptk/WatchEvent
(watch [_ _ _]
(->> (rp/query :comment-threads {:file-id file-id})
(rx/map #(partial fetched %)))))))
(rx/map #(partial fetched %))
(rx/catch #(rx/throw {:type :comment-error})))))))
(defn retrieve-comments
[thread-id]
@ -193,7 +202,8 @@
ptk/WatchEvent
(watch [_ _ _]
(->> (rp/query :comments {:thread-id thread-id})
(rx/map #(partial fetched %)))))))
(rx/map #(partial fetched %))
(rx/catch #(rx/throw {:type :comment-error})))))))
(defn retrieve-unread-comment-threads
"A event used mainly in dashboard for retrieve all unread threads of a team."
@ -204,7 +214,8 @@
(watch [_ _ _]
(let [fetched #(assoc %2 :comment-threads (d/index-by :id %1))]
(->> (rp/query :unread-comment-threads {:team-id team-id})
(rx/map #(partial fetched %)))))))
(rx/map #(partial fetched %))
(rx/catch #(rx/throw {:type :comment-error})))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View file

@ -1324,10 +1324,33 @@
(ptk/reify ::show-context-menu
ptk/UpdateEvent
(update [_ state]
(let [mdata (cond-> params
(some? shape)
(assoc :selected
(wsh/lookup-selected state)))]
(let [selected (wsh/lookup-selected state)
objects (wsh/lookup-page-objects state)
selected-with-children
(into []
(mapcat #(cp/get-object-with-children % objects))
selected)
head (get objects (first selected))
first-not-group-like?
(and (= (count selected) 1)
(not (contains? #{:group :bool} (:type head))))
has-invalid-shapes? (->> selected-with-children
(some (comp #{:frame :text} :type)))
disable-booleans? (or (empty? selected) has-invalid-shapes? first-not-group-like?)
disable-flatten? (or (empty? selected) has-invalid-shapes?)
mdata
(-> params
(assoc :disable-booleans? disable-booleans?)
(assoc :disable-flatten? disable-flatten?)
(cond-> (some? shape)
(assoc :selected selected)))]
(assoc-in state [:workspace-local :context-menu] mdata)))))
(defn show-shape-context-menu

View file

@ -19,6 +19,7 @@
[app.main.data.workspace.groups :as dwg]
[app.main.data.workspace.libraries-helpers :as dwlh]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.undo :as dwu]
[app.main.repo :as rp]
[app.main.store :as st]
[app.util.i18n :refer [tr]]
@ -134,10 +135,12 @@
:color color}
uchg {:type :mod-color
:color prev}]
(rx/of (dch/commit-changes {:redo-changes [rchg]
(rx/of (dwu/start-undo-transaction)
(dch/commit-changes {:redo-changes [rchg]
:undo-changes [uchg]
:origin it})
(sync-file (:current-file-id state) file-id))))))
(sync-file (:current-file-id state) file-id)
(dwu/commit-undo-transaction))))))
(defn delete-color
[{:keys [id] :as params}]
@ -244,10 +247,12 @@
:typography typography}
uchg {:type :mod-typography
:typography prev}]
(rx/of (dch/commit-changes {:redo-changes [rchg]
(rx/of (dwu/start-undo-transaction)
(dch/commit-changes {:redo-changes [rchg]
:undo-changes [uchg]
:origin it})
(sync-file (:current-file-id state) file-id))))))
(sync-file (:current-file-id state) file-id)
(dwu/commit-undo-transaction))))))
(defn delete-typography
[id]

View file

@ -420,13 +420,13 @@
(gpt/point (+ (:width obj) 50) 0)
(gpt/point 0 0))
(let [obj-original (get objects id-original)
obj-duplicated (get objects id-duplicated)
distance (gpt/subtract (gpt/point obj-duplicated)
(gpt/point obj-original))
new-pos (gpt/add (gpt/point obj-duplicated) distance)
delta (gpt/subtract new-pos (gpt/point obj))]
delta))))
(let [pt-original (-> (get objects id-original) :selrect gpt/point)
pt-duplicated (-> (get objects id-duplicated) :selrect gpt/point)
pt-obj (-> obj :selrect gpt/point)
distance (gpt/subtract pt-duplicated pt-original)
new-pos (gpt/add pt-duplicated distance)]
(gpt/subtract new-pos pt-obj)))))
(defn duplicate-selected [move-delta?]
(ptk/reify ::duplicate-selected

View file

@ -180,12 +180,10 @@
shape (get objects id)
merge-fn (fn [node attrs]
(reduce-kv (fn [node k v]
(if (= (get node k) v)
(dissoc node k)
(assoc node k v)))
node
attrs))
(reduce-kv
(fn [node k v] (assoc node k v))
node
attrs))
update-fn #(update-shape % txt/is-paragraph-node? merge-fn attrs)
shape-ids (cond (= (:type shape) :text) [id]

View file

@ -94,6 +94,14 @@
:type :error
:timeout 3000}))))
(defmethod ptk/handle-error :comment-error
[_]
(ts/schedule
(st/emitf
(dm/show {:content "There was an error with the comment"
:type :error
:timeout 3000}))))
;; This is a pure frontend error that can be caused by an active
;; assertion (assertion that is preserved on production builds). From
;; the user perspective this should be treated as internal error.

View file

@ -152,12 +152,14 @@
:render-object
(do
(let [file-id (uuid (get-in route [:path-params :file-id]))
page-id (uuid (get-in route [:path-params :page-id]))
object-id (uuid (get-in route [:path-params :object-id]))]
(let [file-id (uuid (get-in route [:path-params :file-id]))
page-id (uuid (get-in route [:path-params :page-id]))
object-id (uuid (get-in route [:path-params :object-id]))
render-texts (get-in route [:query-params :render-texts])]
[:& render/render-object {:file-id file-id
:page-id page-id
:object-id object-id}]))
:object-id object-id
:render-texts? (and (some? render-texts) (= render-texts "true"))}]))
:render-sprite
(do

View file

@ -210,8 +210,13 @@
[:div.fields-row
[:& fm/input {:name :accept-terms-and-privacy
:class "check-primary"
:label (tr "auth.terms-privacy-agreement")
:type "checkbox"}]]
:type "checkbox"}
[:span
(tr "auth.terms-privacy-agreement")
[:div
[:a {:href "https://penpot.app/terms.html" :target "_blank"} (tr "auth.terms-of-service")]
[:span ",\u00A0"]
[:a {:href "https://penpot.app/privacy.html" :target "_blank"} (tr "auth.privacy-policy")]]]]]
;; (when (contains? @cf/flags :newsletter-registration-check)
;; [:div.fields-row

View file

@ -302,21 +302,22 @@
(when-let [node (mf/ref-val ref)]
(.scrollIntoViewIfNeeded ^js node))))
[:div.thread-content
{:style {:top (str pos-y "px")
:left (str pos-x "px")}
:on-click dom/stop-propagation}
(when (some? comment)
[:div.thread-content
{:style {:top (str pos-y "px")
:left (str pos-x "px")}
:on-click dom/stop-propagation}
[:div.comments
[:& comment-item {:comment comment
:users users
:thread thread}]
(for [item (rest comments)]
[:*
[:hr]
[:& comment-item {:comment item :users users}]])
[:div {:ref ref}]]
[:& reply-form {:thread thread}]]))
[:div.comments
[:& comment-item {:comment comment
:users users
:thread thread}]
(for [item (rest comments)]
[:*
[:hr]
[:& comment-item {:comment item :users users}]])
[:div {:ref ref}]]
[:& reply-form {:thread thread}]])))
(mf/defc thread-bubble
{::mf/wrap [mf/memo]}

View file

@ -19,7 +19,7 @@
(def use-form fm/use-form)
(mf/defc input
[{:keys [label help-icon disabled form hint trim] :as props}]
[{:keys [label help-icon disabled form hint trim children] :as props}]
(let [input-type (get props :type "text")
input-name (get props :name)
more-classes (get props :class)
@ -82,7 +82,7 @@
(swap! form assoc-in [:touched input-name] true)))
props (-> props
(dissoc :help-icon :form :trim)
(dissoc :help-icon :form :trim :children)
(assoc :id (name input-name)
:value value
:auto-focus auto-focus?
@ -97,7 +97,13 @@
{:class klass}
[:*
[:> :input props]
[:label {:for (name input-name)} label]
(cond
(some? label)
[:label {:for (name input-name)} label]
(some? children)
[:label {:for (name input-name)} children])
(when help-icon'
[:div.help-icon
{:style {:cursor "pointer"}

View file

@ -25,9 +25,26 @@
[cuerdas.core :as str]
[rumext.alpha :as mf]))
(defn bounds
[object objects]
(if (= :group (:type object))
(let [children-bounds
(into []
(comp (map #(get objects %))
(map #(bounds % objects)))
(:shapes object))]
(gsh/join-rects children-bounds))
(let [padding (filters/calculate-padding object)]
(-> (filters/get-filters-bounds object)
(update :x - padding)
(update :y - padding)
(update :width + (* 2 padding))
(update :height + (* 2 padding))))))
(mf/defc object-svg
{::mf/wrap [mf/memo]}
[{:keys [objects object-id zoom] :or {zoom 1} :as props}]
[{:keys [objects object-id zoom render-texts?] :or {zoom 1} :as props}]
(let [object (get objects object-id)
frame-id (if (= :frame (:type object))
(:id object)
@ -47,20 +64,10 @@
objects (reduce updt-fn objects mod-ids)
object (get objects object-id)
;; We need to get the shadows/blurs paddings to create the viewbox properly
{:keys [x y width height]} (filters/get-filters-bounds object)
{:keys [x y width height] :as bs} (bounds object objects)
[_ _ width height :as coords] (->> [x y width height] (map #(* % zoom)))
x (* x zoom)
y (* y zoom)
width (* width zoom)
height (* height zoom)
padding (* (filters/calculate-padding object) zoom)
vbox (str/join " " [(- x padding)
(- y padding)
(+ width padding padding)
(+ height padding padding)])
vbox (str/join " " coords)
frame-wrapper
(mf/use-memo
@ -76,18 +83,22 @@
(mf/use-memo
(mf/deps objects)
#(exports/shape-wrapper-factory objects))
]
text-shapes
(->> objects
(filter (fn [[_ shape]] (= :text (:type shape))))
(mapv second))]
(mf/use-effect
(mf/deps width height)
#(dom/set-page-style {:size (str (mth/ceil (+ width padding padding)) "px "
(mth/ceil (+ height padding padding)) "px")}))
#(dom/set-page-style {:size (str (mth/ceil width) "px "
(mth/ceil height) "px")}))
[:& (mf/provider embed/context) {:value true}
[:svg {:id "screenshot"
:view-box vbox
:width (+ width padding padding)
:height (+ height padding padding)
:width width
:height height
:version "1.1"
:xmlns "http://www.w3.org/2000/svg"
:xmlnsXlink "http://www.w3.org/1999/xlink"
@ -100,7 +111,19 @@
:frame [:& frame-wrapper {:shape object :view-box vbox}]
:group [:> shape-container {:shape object}
[:& group-wrapper {:shape object}]]
[:& shape-wrapper {:shape object}])]]))
[:& shape-wrapper {:shape object}])]
;; Auxiliary SVG for rendering text-shapes
(when render-texts?
(for [object text-shapes]
[:svg {:id (str "screenshot-text-" (:id object))
:view-box (str "0 0 " (:width object) " " (:height object))
:width (:width object)
:height (:height object)
:version "1.1"
:xmlns "http://www.w3.org/2000/svg"
:xmlnsXlink "http://www.w3.org/1999/xlink"}
[:& shape-wrapper {:shape (-> object (assoc :x 0 :y 0))}]]))]))
(defn- adapt-root-frame
[objects object-id]
@ -120,7 +143,7 @@
;; backend entry point for download only the data of single page.
(mf/defc render-object
[{:keys [file-id page-id object-id] :as props}]
[{:keys [file-id page-id object-id render-texts?] :as props}]
(let [objects (mf/use-state nil)]
(mf/use-effect
(mf/deps file-id page-id object-id)
@ -140,6 +163,7 @@
(when @objects
[:& object-svg {:objects @objects
:object-id object-id
:render-texts? render-texts?
:zoom 1}])))
(mf/defc render-sprite

View file

@ -60,8 +60,8 @@
:viewBox "0 0 3 6"
:refX "2"
:refY "3"
:markerWidth "3"
:markerHeight "6"
:markerWidth "8.5"
:markerHeight "8.5"
:orient "auto-start-reverse"
:fill stroke-color
:fillOpacity stroke-opacity}
@ -72,8 +72,8 @@
:viewBox "0 0 3 6"
:refX "2"
:refY "3"
:markerWidth "3"
:markerHeight "6"
:markerWidth "8.5"
:markerHeight "8.5"
:orient "auto-start-reverse"
:fill stroke-color
:fillOpacity stroke-opacity}
@ -82,10 +82,10 @@
(when (or (= cap-start :square-marker) (= cap-end :square-marker))
[:marker {:id (str marker-id-prefix "-square-marker")
:viewBox "0 0 6 6"
:refX "5"
:refX "3"
:refY "3"
:markerWidth "6"
:markerHeight "6"
:markerWidth "4.2426" ;; diagonal length of a 3x3 square
:markerHeight "4.2426"
:orient "auto-start-reverse"
:fill stroke-color
:fillOpacity stroke-opacity}
@ -94,10 +94,10 @@
(when (or (= cap-start :circle-marker) (= cap-end :circle-marker))
[:marker {:id (str marker-id-prefix "-circle-marker")
:viewBox "0 0 6 6"
:refX "5"
:refX "3"
:refY "3"
:markerWidth "6"
:markerHeight "6"
:markerWidth "4"
:markerHeight "4"
:orient "auto-start-reverse"
:fill stroke-color
:fillOpacity stroke-opacity}
@ -106,7 +106,7 @@
(when (or (= cap-start :diamond-marker) (= cap-end :diamond-marker))
[:marker {:id (str marker-id-prefix "-diamond-marker")
:viewBox "0 0 6 6"
:refX "5"
:refX "3"
:refY "3"
:markerWidth "6"
:markerHeight "6"

View file

@ -18,7 +18,8 @@
(let [valign (:vertical-align node "top")
width (some-> (:width shape) (+ 1))
base #js {:height (or (:height shape) "100%")
:width (or width "100%")}]
:width (or width "100%")
:fontFamily "sourcesanspro"}]
(cond-> base
(= valign "top") (obj/set! "justifyContent" "flex-start")
(= valign "center") (obj/set! "justifyContent" "center")
@ -40,6 +41,7 @@
:justifyContent "inherit"
:minHeight (when-not (or auto-width? auto-height?) "100%")
:minWidth (when-not auto-width? "100%")
:marginRight "1px"
:verticalAlign "top"}))
(defn generate-paragraph-styles

View file

@ -27,15 +27,27 @@
:v (mf/use-ref nil)
:alpha (mf/use-ref nil)}
setup-hex-color
(fn [hex]
(let [[r g b] (uc/hex->rgb hex)
[h s v] (uc/hex->hsv hex)]
(on-change {:hex hex
:h h :s s :v v
:r r :g g :b b})))
on-change-hex
(fn [e]
(let [val (-> e dom/get-target-val parse-hex)]
(when (uc/hex? val)
(let [[r g b] (uc/hex->rgb val)
[h s v] (uc/hex->hsv hex)]
(on-change {:hex val
:h h :s s :v v
:r r :g g :b b})))))
(setup-hex-color val))))
on-blur-hex
(fn [e]
(let [val (-> e dom/get-target-val)
val (cond
(uc/color? val) (uc/parse-color val)
(uc/hex? (parse-hex val)) (parse-hex val))]
(when (some? val)
(setup-hex-color val))))
on-change-property
(fn [property max-value]
@ -81,9 +93,10 @@
[:div.color-values
{:class (when disable-opacity "disable-opacity")}
[:input {:id "hex-value"
:ref (:hex refs)
:default-value hex
:on-change on-change-hex}]
:ref (:hex refs)
:default-value hex
:on-change on-change-hex
:on-blur on-blur-hex}]
(if (= type :rgb)
[:*

View file

@ -87,15 +87,15 @@
(mf/defc shape-context-menu
[{:keys [mdata] :as props}]
(let [{:keys [id] :as shape} (:shape mdata)
selected (:selected mdata)
(let [{:keys [shape selected disable-booleans? disable-flatten?]} mdata
{:keys [id type]} shape
single? (= (count selected) 1)
multiple? (> (count selected) 1)
editable-shape? (#{:group :text :path} (:type shape))
editable-shape? (#{:group :text :path} type)
is-group? (and (some? shape) (= :group (:type shape)))
is-bool? (and (some? shape) (= :bool (:type shape)))
is-group? (and (some? shape) (= :group type))
is-bool? (and (some? shape) (= :bool type))
options (mf/deref refs/workspace-page-options)
flows (:flows options)
@ -235,10 +235,12 @@
:shortcut (sc/get-tooltip :start-editing)
:on-click do-start-editing}])
[:& menu-entry {:title (tr "workspace.shape.menu.transform-to-path")
:on-click do-transform-to-path}]
(when-not disable-flatten?
[:& menu-entry {:title (tr "workspace.shape.menu.transform-to-path")
:on-click do-transform-to-path}])
(when (or multiple? (and single? (or is-group? is-bool?)))
(when (and (not disable-booleans?)
(or multiple? (and single? (or is-group? is-bool?))))
[:& menu-entry {:title (tr "workspace.shape.menu.path")}
[:& menu-entry {:title (tr "workspace.shape.menu.union")
:shortcut (sc/get-tooltip :boolean-union)
@ -253,7 +255,7 @@
:shortcut (sc/get-tooltip :boolean-exclude)
:on-click (set-bool :exclude)}]
(when (and single? is-bool?)
(when (and single? is-bool? (not disable-flatten?))
[:*
[:& menu-separator]
[:& menu-entry {:title (tr "workspace.shape.menu.flatten")
@ -279,9 +281,7 @@
[:& menu-entry {:title (tr "workspace.shape.menu.delete-flow-start")
:on-click (do-remove-flow flow)}])))
(when (and (or (nil? (:shape-ref shape))
(> (count selected) 1))
(not= (:type shape) :frame))
(when (not= (:type shape) :frame)
[:*
[:& menu-separator]
[:& menu-entry {:title (tr "workspace.shape.menu.create-component")

View file

@ -174,7 +174,7 @@
handle-return
(mf/use-callback
(fn [_ state]
(let [style (ted/get-editor-current-inline-styles state)
(let [style (ted/get-editor-current-block-data state)
state (-> (ted/insert-text state "\n" style)
(handle-change))]
(st/emit! (dwt/update-editor-state shape state)))

View file

@ -230,7 +230,7 @@
on-fold-group
(mf/use-callback
(mf/deps group-open?)
(mf/deps file-id box path group-open?)
(fn [event]
(dom/stop-propagation event)
(st/emit! (dwl/set-assets-group-open file-id

View file

@ -17,19 +17,22 @@
(mf/defc booleans-options
[]
(let [selected (mf/deref refs/selected-objects)
selected-with-children (mf/deref refs/selected-objects-with-children)
disabled-bool-btns
(or (empty? selected)
(and (<= (count selected) 1)
(not (contains? #{:group :bool} (:type (first selected))))))
has-invalid-shapes? (->> selected-with-children
(some (comp #{:frame :text} :type)))
disabled-flatten
(empty? selected)
first-not-group-like?
(and (= (count selected) 1)
(not (contains? #{:group :bool} (:type (first selected)))))
disabled-bool-btns (or (empty? selected) has-invalid-shapes? first-not-group-like?)
disabled-flatten (or (empty? selected) has-invalid-shapes?)
head (first selected)
is-group? (and (some? head) (= :group (:type head)))
is-bool? (and (some? head) (= :bool (:type head)))
head-bool-type (and (some? head) (:bool-type head))
head-bool-type (and (some? head) is-bool? (:bool-type head))
set-bool
(fn [bool-type]

View file

@ -73,7 +73,7 @@
:group (tr "workspace.options.group-stroke")
(tr "workspace.options.stroke"))
show-options (not= (:stroke-style values :none) :none)
show-options (not= (or (:stroke-style values) :none) :none)
show-caps (and show-caps
(not (#{:inner :outer} (:stroke-alignment values))))
@ -141,7 +141,10 @@
target (dom/get-current-target event)
rect (dom/get-bounding-rect target)
top (+ (:bottom rect) 5)
top (if (< (+ (:bottom rect) 320) (:height window-size))
(+ (:bottom rect) 5)
(- (:height window-size) 325))
left (if (< (+ (:left rect) 200) (:width window-size))
(:left rect)
(- (:width window-size) 205))]

View file

@ -74,7 +74,8 @@
(defn filter-fonts
[{:keys [term backends]} fonts]
(let [xform (cond-> (map identity)
(let [term (str/lower term)
xform (cond-> (map identity)
(seq term)
(comp (filter #(str/includes? (str/lower (:name %)) term)))
@ -175,7 +176,7 @@
[:div.font-selector
[:div.font-selector-dropdown
[:header
[:input {:placeholder "Search font"
[:input {:placeholder (tr "workspace.options.search-font")
:value (:term @state)
:ref input
:spell-check false

View file

@ -197,7 +197,7 @@
[:& use/export-page {:options options}]
[:& (mf/provider use/include-metadata-ctx) {:value true}
[:& (mf/provider use/include-metadata-ctx) {:value false}
[:& (mf/provider embed/context) {:value true}
;; Render root shape
[:& shapes/root-shape {:key page-id

View file

@ -163,6 +163,12 @@ msgstr ""
"When creating a new account, you agree to our terms of service and privacy "
"policy."
msgid "auth.terms-of-service"
msgstr "Terms of service"
msgid "auth.privacy-policy"
msgstr "Privacy policy"
#: src/app/main/ui/auth/register.cljs
msgid "auth.verification-email-sent"
msgstr "We've sent a verification email to"
@ -3177,3 +3183,6 @@ msgstr "Flatten"
msgid "workspace.shape.menu.transform-to-path"
msgstr "Transform to path"
msgid "workspace.options.search-font"
msgstr "Search font"

View file

@ -167,6 +167,12 @@ msgstr ""
"Al crear una nueva cuenta, aceptas nuestros términos de servicio y política "
"de privacidad."
msgid "auth.terms-of-service"
msgstr "Terminos de servicio"
msgid "auth.privacy-policy"
msgstr "Política de privacidad"
#: src/app/main/ui/auth/register.cljs
msgid "auth.verification-email-sent"
msgstr "Hemos enviado un email de verificación a"
@ -3065,3 +3071,6 @@ msgstr "Aplanar"
msgid "workspace.shape.menu.transform-to-path"
msgstr "Convertir en vector"
msgid "workspace.options.search-font"
msgstr "Buscar fuente"

View file

@ -161,6 +161,12 @@ msgstr ""
"En créant un compte, vous acceptez nos conditions générales d'utilisation "
"et notre politique de confidentialité."
msgid "auth.terms-of-service"
msgstr "Conditions générales d'utilisation"
msgid "auth.privacy-policy"
msgstr "Politique de confidentialité"
#: src/app/main/ui/auth/register.cljs
msgid "auth.verification-email-sent"
msgstr "Nous avons envoyé un e-mail de vérification à"