0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-04-08 13:01:24 -05:00

Merge remote-tracking branch 'origin/staging' into develop

This commit is contained in:
alonso.torres 2022-05-03 10:29:19 +02:00
commit 4363e32aae
32 changed files with 391 additions and 225 deletions

View file

@ -18,39 +18,44 @@
### :boom: Breaking changes
- We've changed the behaviour of the border-radius so it works as CSS that [has some limits](https://www.w3.org/TR/css-backgrounds-3/#corner-overlap).
- Now exported text are SVG's native `text` tag instead of paths. This could break when opening the file depending on your engine. Some SVG's may require fonts to be installed at system level.
### :sparkles: New features
- Group assets by drag and drop [Taiga #2831](https://tree.taiga.io/project/penpot/us/2831)
- Search and filter layers [Taiga #2564](https://tree.taiga.io/project/penpot/us/2564)
- Constraints are not well assigned when default and multiselection [Taiga #3069](https://tree.taiga.io/project/penpot/issue/3069)
- Exporting big files flow [Taiga #2218](https://tree.taiga.io/project/penpot/us/2218)
- Multiexport from main menu [Taiga #520](https://tree.taiga.io/project/penpot/us/28541)
- Multiexport assets (aka bulk export) [Taiga #520](https://tree.taiga.io/project/penpot/us/520)
- Set the artboard layer fixed at the top side of the layers [Taiga #2636](https://tree.taiga.io/project/penpot/us/2636)
- Set an artboard as the file thumbnail [Taiga #1526](https://tree.taiga.io/project/penpot/us/1526)
- Social login redesign [Taiga #2974](https://tree.taiga.io/project/penpot/task/2974)
- Add border radius to our artboards [Taiga #2056](https://tree.taiga.io/project/penpot/us/2056)
- Add border radius to artboards [Taiga #2056](https://tree.taiga.io/project/penpot/us/2056)
- Allow send multiple team invitations at once [Taiga #2798](https://tree.taiga.io/project/penpot/us/2798)
- Persist color palette and color picker across refresh [Taiga #1660](https://tree.taiga.io/project/penpot/issue/1660)
- Ability to add multiple strokes to a shape [Taiga #2778](https://tree.taiga.io/project/penpot/us/2778)
- Scroll to selected size in font size selector [Taiga #2825](https://tree.taiga.io/project/penpot/us/2825)
- Duplicate artboards create new flows if needed [Taiga #2221](https://tree.taiga.io/project/penpot/issue/2221)
- Add new invitations section [Taiga #2797](https://tree.taiga.io/project/penpot/us/2797)
- Ability to add multiple fills to a shape [Taiga #1394](https://tree.taiga.io/project/penpot/us/1394)
- Team members redesign [Taiga #2283](https://tree.taiga.io/project/penpot/us/2283)
- New focus mode in workspace [Taiga #2748](https://tree.taiga.io/project/penpot/us/2748)
- Changed text shapes to be displayed as natives SVG text elements [Taiga #2759](https://tree.taiga.io/project/penpot/us/2759)
- Texts now can have strokes, multiple fills and can be used as masks
- Add the ability to specify the attr for retrieve the email on OIDC integration [#1460](https://github.com/penpot/penpot/issues/1460)
- Add the ability to specify the attribute for retrieve the email on OIDC integration [#1460](https://github.com/penpot/penpot/issues/1460)
- Allow registration with invitation token when registration is disabled
- Add the ability to disable standard, password login [Taiga #2999](https://tree.taiga.io/project/penpot/us/2999)
- Don't stop SVG import when an image cannot be imported [#1531](https://github.com/penpot/penpot/issues/1531)
- Fix paste shapes while editing text [Taiga #2396](https://tree.taiga.io/project/penpot/issue/2396)
- Show Penpot color in Safari tab bar [#1803](https://github.com/penpot/penpot/issues/1803)
- Added option to disable snap to pixel and improved behaviour for sub-pixel drawing [#2552](https://tree.taiga.io/project/penpot/us/2552)
- Delete guides while supr on hover [#2823](https://tree.taiga.io/project/penpot/us/2823)
- Opt-in subscription on on-premise instances [#2772](https://tree.taiga.io/project/penpot/us/2772)
- Optimizations in frame thumbnails [#3147](https://tree.taiga.io/project/penpot/us/3147)
### :bug: Bugs fixed
- Constraints are not well assigned when default and multiselection [Taiga #3069](https://tree.taiga.io/project/penpot/issue/3069)
- Duplicate artboards create new flows if needed [Taiga #2221](https://tree.taiga.io/project/penpot/issue/2221)
- Fix paste shapes while editing text [Taiga #2396](https://tree.taiga.io/project/penpot/issue/2396)
- Round the size values on handoff to two decimals [Taiga #3227](https://tree.taiga.io/project/penpot/issue/3227)
- Fix blend modes ignored in component updates [Taiga #2626](https://tree.taiga.io/project/penpot/issue/2626)
- Fix internal error when hoverin over shape [Taiga #3237](https://tree.taiga.io/project/penpot/issue/3237)
@ -99,6 +104,11 @@
- Fix resize rotated shape with top&down constraints [Taiga #3167](https://tree.taiga.io/project/penpot/issue/3167)
- Fix multi user not working [Taiga #3195](https://tree.taiga.io/project/penpot/issue/3195)
- Fix guides are not duplicated with the artboard [Taiga #3072](https://tree.taiga.io/project/penpot/issue/3072)
- Fix problem when changing group size with decimal values [Taiga #3203](https://tree.taiga.io/project/penpot/issue/3203)
- Fix error when drawing curves with only one point [Taiga #3282](https://tree.taiga.io/project/penpot/issue/3282)
- Fix issue with paste ordering sometimes not being respected [Taiga #3268](https://tree.taiga.io/project/penpot/issue/3268)
- Fix problem when export/importing guides attached to frame [#1838](https://github.com/penpot/penpot/issues/1838)
- Fix problem when resizing a group with texts with auto-width/height [#3171](https://tree.taiga.io/project/penpot/issue/3171)
### :arrow_up: Deps updates
### :heart: Community contributions by (Thank you!)

View file

@ -103,25 +103,25 @@
(defmethod constraint-modifier :scale
[_ axis _ _ modifiers _]
(let [{:keys [resize-vector resize-vector-2 displacement]} modifiers]
(cond-> {}
(and (some? resize-vector)
(not (mth/close? (axis resize-vector) 1)))
(assoc :resize-origin (:resize-origin modifiers)
:resize-vector (if (= :x axis)
(gpt/point (:x resize-vector) 1)
(gpt/point 1 (:y resize-vector))))
(cond-> {}
(and (some? resize-vector)
(not= (axis resize-vector) 1))
(assoc :resize-origin (:resize-origin modifiers)
:resize-vector (if (= :x axis)
(gpt/point (:x resize-vector) 1)
(gpt/point 1 (:y resize-vector))))
(and (= :y axis) (some? resize-vector-2)
(not (mth/close? (:y resize-vector-2) 1)))
(assoc :resize-origin (:resize-origin-2 modifiers)
:resize-vector (gpt/point 1 (:y resize-vector-2)))
(and (= :y axis) (some? resize-vector-2)
(not (mth/close? (:y resize-vector-2) 1)))
(assoc :resize-origin (:resize-origin-2 modifiers)
:resize-vector (gpt/point 1 (:y resize-vector-2)))
(some? displacement)
(assoc :displacement
(get-displacement axis (-> (gpt/point 0 0)
(gpt/transform displacement)
(gpt/transform (:resize-transform-inverse modifiers (gmt/matrix)))
axis))))))
(some? displacement)
(assoc :displacement
(get-displacement axis (-> (gpt/point 0 0)
(gpt/transform displacement)
(gpt/transform (:resize-transform-inverse modifiers (gmt/matrix)))
axis))))))
(defmethod constraint-modifier :default [_ _ _ _ _]
{})

View file

@ -9,7 +9,7 @@
funcool/beicon {:mvn/version "2021.07.05-1"}
funcool/okulary {:mvn/version "2022.04.11-16"}
funcool/potok {:mvn/version "2021.09.20-0"}
funcool/potok {:mvn/version "2022.04.28-67"}
funcool/rumext {:mvn/version "2022.04.19-148"}
funcool/tubax {:mvn/version "2021.05.20-0"}

Binary file not shown.

After

Width:  |  Height:  |  Size: 889 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 726 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 KiB

View file

@ -1482,12 +1482,11 @@
(let [set-index (fn [[result index] id]
[(assoc result id index) (inc index)])
map-ids (when index
(->> (vals paste-objects)
(filter #(not (selected (:parent-id %))))
(map :id)
(reduce set-index [{} (inc index)])
first))]
map-ids
(->> selected
(map #(get-in paste-objects [% :id]))
(reduce set-index [{} (inc index)])
first)]
(if (and (= :add-obj (:type change))
(contains? map-ids (:old-id change)))
(assoc change :index (get map-ids (:old-id change)))

View file

@ -107,7 +107,8 @@
(update [_ state]
(assoc-in state [:workspace-local :expanded id] true))))
(def collapse-all
(defn collapse-all
[]
(ptk/reify ::collapse-all
ptk/UpdateEvent
(update [_ state]

View file

@ -37,29 +37,29 @@
ptk/WatchEvent
(watch [_ _ stream]
(let [stoper (rx/filter (ptk/type? ::clear-drawing) stream)]
(rx/merge
(when (= tool :path)
(rx/of (start-drawing :path)))
(rx/merge
(when (= tool :path)
(rx/of (start-drawing :path)))
(when (= tool :curve)
(let [stopper (->> stream (rx/filter dwc/interrupt?))]
(->> stream
(rx/filter (ptk/type? ::common/handle-finish-drawing))
(rx/take 1)
(rx/observe-on :async)
(rx/map #(select-for-drawing tool data))
(rx/take-until stopper))))
(when (= tool :curve)
(let [stopper (->> stream (rx/filter dwc/interrupt?))]
(->> stream
(rx/filter (ptk/type? ::common/handle-finish-drawing))
(rx/take 1)
(rx/observe-on :async)
(rx/map #(select-for-drawing tool data))
(rx/take-until stopper))))
;; NOTE: comments are a special case and they manage they
;; own interrupt cycle.q
(when (and (not= tool :comments)
(not= tool :path))
;; NOTE: comments are a special case and they manage they
;; own interrupt cycle.q
(when (and (not= tool :comments)
(not= tool :path))
(let [stopper (rx/filter (ptk/type? ::clear-drawing) stream)]
(->> stream
(rx/filter dwc/interrupt?)
(rx/take 1)
(rx/map (constantly common/clear-drawing))
(rx/take-until stoper)))))))))
(rx/map common/clear-drawing)
(rx/take-until stopper)))))))))
;; NOTE/TODO: when an exception is raised in some point of drawing the

View file

@ -101,4 +101,4 @@
#(update-drawing % (cond-> point snap-pixel? gpt/round) shift?)))
(rx/take-until stoper))
(rx/of common/handle-finish-drawing))))))
(rx/of (common/handle-finish-drawing)))))))

View file

@ -16,13 +16,15 @@
[beicon.core :as rx]
[potok.core :as ptk]))
(def clear-drawing
(defn clear-drawing
[]
(ptk/reify ::clear-drawing
ptk/UpdateEvent
(update [_ state]
(update state :workspace-drawing dissoc :tool :object))))
(def handle-finish-drawing
(defn handle-finish-drawing
[]
(ptk/reify ::handle-finish-drawing
ptk/WatchEvent
(watch [_ state _]
@ -71,5 +73,6 @@
(rx/empty)))))
;; Delay so the mouse event can read the drawing state
(->> (rx/of clear-drawing)
(->> (rx/of (clear-drawing))
(rx/delay 0)))))))

View file

@ -59,15 +59,22 @@
(dissoc :segments)
(assoc :content content)
(assoc :selrect selrect)
(assoc :points points))))
(assoc :points points)
(defn finish-drawing-curve [state]
(update-in
state [:workspace-drawing :object]
(fn [shape]
(-> shape
(update :segments #(ups/simplify % simplify-tolerance))
(curve-to-path)))))
(cond-> (or (empty? points) (nil? selrect) (<= (count content) 1))
(assoc :initialized? false)))))
(defn finish-drawing-curve
[]
(ptk/reify ::finish-drawing-curve
ptk/UpdateEvent
(update [_ state]
(letfn [(update-curve [shape]
(-> shape
(update :segments #(ups/simplify % simplify-tolerance))
(curve-to-path)))]
(-> state
(update-in [:workspace-drawing :object] update-curve))))))
(defn handle-drawing-curve []
(ptk/reify ::handle-drawing-curve
@ -81,6 +88,6 @@
(rx/map (fn [pt] #(insert-point-segment % pt)))
(rx/take-until stoper))
(rx/of (setup-frame-curve)
finish-drawing-curve
common/handle-finish-drawing))))))
(finish-drawing-curve)
(common/handle-finish-drawing)))))))

View file

@ -275,7 +275,7 @@
ptk/WatchEvent
(watch [_ _ _]
(rx/of (setup-frame-path)
dwdc/handle-finish-drawing
(dwdc/handle-finish-drawing)
(dwc/start-edition-mode shape-id)
(change-edit-mode :draw)))))

View file

@ -246,7 +246,7 @@
(ptk/reify ::fetch-bundle
ptk/WatchEvent
(watch [_ _ _]
(->> (rx/zip (rp/query :file {:id file-id})
(->> (rx/zip (rp/query :file-raw {:id file-id})
(rp/query :team-users {:file-id file-id})
(rp/query :project {:id project-id})
(rp/query :file-libraries {:file-id file-id}))

View file

@ -124,7 +124,7 @@
(get-in shape [:svg-attrs :style :stroke-opacity])
(-> (update-in [:svg-attrs :style] dissoc :stroke-opacity)
(assoc-in [:fills 0 :stroke-opacity] (-> (get-in shape [:svg-attrs :style :stroke-opacity])
(assoc-in [:strokes 0 :stroke-opacity] (-> (get-in shape [:svg-attrs :style :stroke-opacity])
(d/parse-double))))
(get-in shape [:svg-attrs :stroke-width])
@ -395,14 +395,12 @@
:image (create-image-shape name frame-id svg-data element-data)
#_other (create-raw-svg name frame-id svg-data element-data)))]
(when (some? shape)
(let [shape (assoc shape :fills [])
shape (assoc shape :strokes [])
shape (when (some? shape)
(-> shape
(assoc :svg-defs (select-keys (:defs svg-data) references))
(setup-fill)
(setup-stroke)))
(let [shape (-> shape
(assoc :fills [])
(assoc :strokes [])
(assoc :svg-defs (select-keys (:defs svg-data) references))
(setup-fill)
(setup-stroke))
children (cond->> (:content element-data)
(or (= tag :g) (= tag :svg))

View file

@ -384,6 +384,11 @@
(defn commit-position-data
[]
(ptk/reify ::commit-position-data
ptk/UpdateEvent
(update [_ state]
(let [ids (keys (::update-position-data state))]
(update state :workspace-text-modifiers #(apply dissoc % ids))))
ptk/WatchEvent
(watch [_ state _]
(let [position-data (::update-position-data state)]
@ -404,9 +409,10 @@
(ptk/reify ::update-position-data
ptk/UpdateEvent
(update [_ state]
(if (nil? (::update-position-data-debounce state))
(assoc state ::update-position-data-debounce start)
(assoc-in state [::update-position-data id] position-data)))
(let [state (assoc-in state [:workspace-text-modifier id :position-data] position-data)]
(if (nil? (::update-position-data-debounce state))
(assoc state ::update-position-data-debounce start)
(assoc-in state [::update-position-data id] position-data))))
ptk/WatchEvent
(watch [_ state stream]

View file

@ -110,7 +110,7 @@
;; geometric attributes of the shapes.
(declare clear-local-transform)
(declare set-modifiers-recursive)
(declare set-objects-modifiers)
(declare get-ignore-tree)
(defn- set-modifiers
@ -139,7 +139,7 @@
(fn [state id]
(let [shape (get objects id)]
(update state :workspace-modifiers
#(set-modifiers-recursive % objects shape modifiers ignore-constraints snap-pixel?))))]
#(set-objects-modifiers % objects shape modifiers ignore-constraints snap-pixel?))))]
(reduce setup-modifiers state ids))))))
@ -166,6 +166,20 @@
(update state :workspace-modifiers #(reduce update-shape % shapes)))))))
(defn- update-grow-type
[shape old-shape]
(let [auto-width? (= :auto-width (:grow-type shape))
auto-height? (= :auto-height (:grow-type shape))
changed-width? (not (mth/close? (:width shape) (:width old-shape)))
changed-height? (not (mth/close? (:height shape) (:height old-shape)))
change-to-fixed? (or (and auto-width? (or changed-height? changed-width?))
(and auto-height? changed-height?))]
(cond-> shape
change-to-fixed?
(assoc :grow-type :fixed))))
(defn- apply-modifiers
[ids]
(us/verify (s/coll-of uuid?) ids)
@ -182,27 +196,33 @@
(rx/of (dwu/start-undo-transaction)
(dwg/move-frame-guides ids-with-children)
(dch/update-shapes
ids-with-children
(fn [shape]
(let [modif (get object-modifiers (:id shape))]
(gsh/transform-shape (merge shape modif))))
{:reg-objects? true
:ignore-tree ignore-tree
;; Attributes that can change in the transform. This way we don't have to check
;; all the attributes
:attrs [:selrect
:points
:x
:y
:width
:height
:content
:transform
:transform-inverse
:rotation
:position-data
:flip-x
:flip-y]})
ids-with-children
(fn [shape]
(let [modif (get object-modifiers (:id shape))
text-shape? (cph/text-shape? shape)]
(-> shape
(merge modif)
(gsh/transform-shape)
(cond-> text-shape?
(update-grow-type shape)))))
{:reg-objects? true
:ignore-tree ignore-tree
;; Attributes that can change in the transform. This way we don't have to check
;; all the attributes
:attrs [:selrect
:points
:x
:y
:width
:height
:content
:transform
:transform-inverse
:rotation
:position-data
:flip-x
:flip-y
:grow-type]})
(clear-local-transform)
(dwu/commit-undo-transaction))))))
@ -330,25 +350,28 @@
(assoc :displacement (gmt/translate-matrix delta-v))))]
modifiers)))
(defn- set-modifiers-recursive
(defn- set-objects-modifiers
[modif-tree objects shape modifiers ignore-constraints snap-pixel?]
(letfn [(set-modifiers-rec
[modif-tree shape modifiers]
(let [children (map (d/getf objects) (:shapes shape))
modifiers (cond-> modifiers snap-pixel? (set-pixel-precision shape))
transformed-rect (gsh/transform-selrect (:selrect shape) modifiers)
(let [children (map (d/getf objects) (:shapes shape))
modifiers (cond-> modifiers snap-pixel? (set-pixel-precision shape))
transformed-rect (gsh/transform-selrect (:selrect shape) modifiers)
set-child
(fn [modif-tree child]
(let [child-modifiers (gsh/calc-child-modifiers shape child modifiers ignore-constraints transformed-rect)]
(cond-> modif-tree
(not (gsh/empty-modifiers? child-modifiers))
(set-modifiers-recursive objects child child-modifiers ignore-constraints snap-pixel?))))
set-child
(fn [modif-tree child]
(let [child-modifiers (gsh/calc-child-modifiers shape child modifiers ignore-constraints transformed-rect)]
(cond-> modif-tree
(not (gsh/empty-modifiers? child-modifiers))
(set-modifiers-rec child child-modifiers))))
modif-tree
(-> modif-tree
(assoc-in [(:id shape) :modifiers] modifiers))]
modif-tree
(-> modif-tree
(assoc-in [(:id shape) :modifiers] modifiers))]
(reduce set-child modif-tree children)))
(reduce set-child modif-tree children)))]
(set-modifiers-rec modif-tree shape modifiers)))
(defn- get-ignore-tree
"Retrieves a map with the flag `ignore-geometry?` given a tree of modifiers"
@ -480,12 +503,8 @@
focus (:workspace-focus-selected state)
zoom (get-in state [:workspace-local :zoom] 1)
objects (wsh/lookup-page-objects state page-id)
resizing-shapes (map #(get objects %) ids)
text-shapes-ids (->> resizing-shapes
(filter #(= :text (:type %)))
(map :id))]
resizing-shapes (map #(get objects %) ids)]
(rx/concat
(rx/of (dch/update-shapes text-shapes-ids #(assoc % :grow-type :fixed)))
(->> ms/mouse-position
(rx/with-latest-from ms/mouse-position-shift ms/mouse-position-alt)
(rx/map normalize-proportion-lock)
@ -507,15 +526,17 @@
(ptk/reify ::update-dimensions
ptk/UpdateEvent
(update [_ state]
(let [page-id (:current-page-id state)
objects (get-in state [:workspace-data :pages-index page-id :objects])
(let [objects (wsh/lookup-page-objects state)
layout (get state :workspace-layout)
snap-pixel? (contains? layout :snap-pixel-grid)
update-modifiers
(fn [state id]
(let [shape (get objects id)
(let [shape (get objects id)
modifiers (gsh/resize-modifiers shape attr value)]
(update state :workspace-modifiers
#(set-modifiers-recursive % objects shape modifiers false false))))]
(-> state
(update :workspace-modifiers
#(set-objects-modifiers % objects shape modifiers false (and snap-pixel? (int? value)))))))]
(reduce update-modifiers state ids)))
ptk/WatchEvent

View file

@ -82,9 +82,9 @@
[id params]
(send-query! id params))
(defmethod query :file
[id params]
(send-query! id params {:raw-transit? true}))
(defmethod query :file-raw
[_id params]
(send-query! :file params {:raw-transit? true}))
(defmethod mutation :default
[id params]

View file

@ -13,6 +13,7 @@
[app.main.ui.releases.v1-10]
[app.main.ui.releases.v1-11]
[app.main.ui.releases.v1-12]
[app.main.ui.releases.v1-13]
[app.main.ui.releases.v1-4]
[app.main.ui.releases.v1-5]
[app.main.ui.releases.v1-6]
@ -83,4 +84,4 @@
(defmethod rc/render-release-notes "0.0"
[params]
(rc/render-release-notes (assoc params :version "1.12")))
(rc/render-release-notes (assoc params :version "1.13")))

View file

@ -0,0 +1,108 @@
;; 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.releases.v1-13
(:require
[app.main.ui.releases.common :as c]
[rumext.alpha :as mf]))
(defmethod c/render-release-notes "1.13"
[{:keys [slide klass next finish navigate version]}]
(mf/html
(case @slide
:start
[:div.modal-overlay
[:div.animated {:class @klass}
[:div.modal-container.onboarding.feature
[:div.modal-left
[:img {:src "images/login-on.jpg" :border "0" :alt "What's new Beta release 1.13"}]]
[:div.modal-right
[:div.modal-title
[:h2 "What's new?"]]
[:span.release "Beta version " version]
[:div.modal-content
[:p "Penpot continues growing with new features that improve performance, user experience and visual design."]
[:p "We are happy to show you a sneak peak of the most important stuff that the Beta 1.13 version brings."]]
[:div.modal-navigation
[:button.btn-secondary {:on-click next} "Continue"]]]
[:img.deco {:src "images/deco-left.png" :border "0"}]
[:img.deco.right {:src "images/deco-right.png" :border "0"}]]]]
0
[:div.modal-overlay
[:div.animated {:class @klass}
[:div.modal-container.onboarding.feature
[:div.modal-left
[:img {:src "images/features/1.13-multi-export.gif" :border "0" :alt "Multiple exports"}]]
[:div.modal-right
[:div.modal-title
[:h2 "Multiple exports"]]
[:div.modal-content
[:p "Speed your workflow exporting multiple elements simultaneously."]
[:p "Use the export window to manage your multiple exports and be informed about the download progress. Big exports will happen in the background so you can continue designing in the meantime ;)"]]
[:div.modal-navigation
[:button.btn-secondary {:on-click next} "Continue"]
[:& c/navigation-bullets
{:slide @slide
:navigate navigate
:total 4}]]]]]]
1
[:div.modal-overlay
[:div.animated {:class @klass}
[:div.modal-container.onboarding.feature
[:div.modal-left
[:img {:src "images/features/1.13-multiple-fills.gif" :border "0" :alt "Multiple fills and strokes"}]]
[:div.modal-right
[:div.modal-title
[:h2 "Multiple fills and strokes"]]
[:div.modal-content
[:p "Now you can add multiple color fills and strokes to a single element, including shapes and texts."]
[:p "This opens endless graphic possibilities such as combining gradients and blending modes in the same element to create visual effects."]]
[:div.modal-navigation
[:button.btn-secondary {:on-click next} "Continue"]
[:& c/navigation-bullets
{:slide @slide
:navigate navigate
:total 4}]]]]]]
2
[:div.modal-overlay
[:div.animated {:class @klass}
[:div.modal-container.onboarding.feature
[:div.modal-left
[:img {:src "images/features/1.13-members.gif" :border "0" :alt "Members area redesign"}]]
[:div.modal-right
[:div.modal-title
[:h2 "Members area redesign"]]
[:div.modal-content
[:p "Penpot is meant for teams, thats why we decided to give some love to the members area."]
[:p "A refreshed interface and two new features: the Invitations section where you can check the state of the team invites and the ability to invite multiple members at the same time."]]
[:div.modal-navigation
[:button.btn-secondary {:on-click next} "Continue"]
[:& c/navigation-bullets
{:slide @slide
:navigate navigate
:total 4}]]]]]]
3
[:div.modal-overlay
[:div.animated {:class @klass}
[:div.modal-container.onboarding.feature
[:div.modal-left
[:img {:src "images/features/1.13-focus.gif" :border "0" :alt "Focus mode"}]]
[:div.modal-right
[:div.modal-title
[:h2 "Focus mode"]]
[:div.modal-content
[:p "Select the elements of a page you want to work with in a specific moment hiding the rest so they dont get in the way of your attention."]
[:p "This option is also useful to improve the performance in cases where the page has a large number of elements."]]
[:div.modal-navigation
[:button.btn-secondary {:on-click finish} "Start!"]
[:& c/navigation-bullets
{:slide @slide
:navigate navigate
:total 4}]]]]]])))

View file

@ -6,6 +6,7 @@
(ns app.main.ui.shapes.custom-stroke
(:require
[app.common.colors :as clr]
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.geom.shapes :as gsh]
@ -382,6 +383,14 @@
(some? style)
(obj/set! "style" style)))
(some? (:svg-attrs shape))
(let [style
(-> (obj/get props "style")
(obj/clone)
(obj/set! "fill" clr/black))]
(-> props
(obj/set! "style" style)))
(and (= :path (:type shape)) (empty? (:fills shape)))
(let [style
(-> (obj/get props "style")

View file

@ -72,11 +72,13 @@
[:> (name tag) (clj->js attrs)
[:> wrapper wrapper-props
(for [node content] [:& svg-node {:type type
:node node
:prefix-id prefix-id
:transform transform
:bounds bounds}])]])))
(for [[index node] (d/enumerate content)]
[:& svg-node {:key (dm/str "node-" index)
:type type
:node node
:prefix-id prefix-id
:transform transform
:bounds bounds}])]])))
(defn svg-def-bounds [svg-def shape transform]
(let [{:keys [tag]} svg-def]
@ -107,10 +109,10 @@
(cond->> id
(contains? svg-defs id) (str render-id "-")))]
;; TODO: no key?
(when (seq svg-defs)
(for [svg-def (vals svg-defs)]
[:& svg-node {:type (:type shape)
(for [[key svg-def] svg-defs]
[:& svg-node {:key (dm/str key)
:type (:type shape)
:node svg-def
:prefix-id prefix-id
:transform transform

View file

@ -117,6 +117,7 @@
thumb-renderer
[:g.frame-thumbnail
[:> frame/frame-thumbnail {:shape (cond-> shape
[:> frame/frame-thumbnail {:key (dm/str (:id shape))
:shape (cond-> shape
(some? thumbnail-data)
(assoc :thumbnail thumbnail-data))}]]]))))

View file

@ -38,15 +38,18 @@
(mf/use-layout-effect
(mf/deps transforms)
(fn []
(when (and (empty? @prev-modifiers) (d/not-empty? modifiers))
(utils/start-transform! node shapes))
(let [is-prev-val? (d/not-empty? @prev-modifiers)
is-cur-val? (d/not-empty? modifiers)]
(when (d/not-empty? modifiers)
(utils/update-transform! node shapes transforms modifiers))
(when (and (not is-prev-val?) is-cur-val?)
(utils/start-transform! node shapes))
(when (and (d/not-empty? @prev-modifiers) (empty? modifiers))
(utils/remove-transform! node @prev-shapes))
(when is-cur-val?
(utils/update-transform! node shapes transforms modifiers))
(reset! prev-modifiers modifiers)
(reset! prev-transforms transforms)
(reset! prev-shapes shapes)))))
(when (and is-prev-val? (not is-cur-val?))
(utils/remove-transform! node @prev-shapes))
(reset! prev-modifiers modifiers)
(reset! prev-transforms transforms)
(reset! prev-shapes shapes))))))

View file

@ -48,11 +48,10 @@
(mf/use-callback
(fn []
(let [canvas-node (mf/ref-val frame-canvas-ref)
img-node (mf/ref-val frame-image-ref)]
(ts/raf
#(let [thumb-data (draw-thumbnail-canvas canvas-node img-node)]
(st/emit! (dw/update-thumbnail id thumb-data))
(reset! image-url nil))))))
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))))
on-change
(mf/use-callback
@ -70,7 +69,6 @@
(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)))))))

View file

@ -108,7 +108,7 @@
(fn [event]
(dom/stop-propagation event)
(if (and expanded? (kbd/shift? event))
(st/emit! dwc/collapse-all)
(st/emit! (dwc/collapse-all))
(st/emit! (dwc/toggle-collapse id))))
toggle-blocking

View file

@ -158,9 +158,10 @@
show-outlines? (and (nil? transform) (not edition) (not drawing-obj) (not (#{:comments :path :curve} drawing-tool)))
show-pixel-grid? (and (contains? layout :show-pixel-grid)
(>= zoom 8))
show-text-editor? (and editing-shape (= :text (:type editing-shape)))
show-presence? page-id
show-prototypes? (= options-mode :prototype)
show-selection-handlers? (and (seq selected) (not edition))
show-selection-handlers? (and (seq selected) (not show-text-editor?))
show-snap-distance? (and (contains? layout :dynamic-alignment)
(= transform :move)
(seq selected))
@ -172,7 +173,6 @@
show-artboard-names? (contains? layout :display-artboard-names)
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)]

View file

@ -123,81 +123,75 @@
h-scrollbar-x)
on-mouse-move
(mf/use-callback
(mf/deps zoom v-scrolling?)
(fn [event axis]
(when-let [_ (or @v-scrolling? @h-scrolling?)]
(let [viewport (mf/ref-val viewport-ref)
start-pt (mf/ref-val start-ref)
current-pt (dom/get-client-position event)
current-pt-viewport (utils/translate-point-to-viewport viewport zoom current-pt)
y-delta (/ (* (mf/ref-val height-factor-ref) (- (:y current-pt) (:y start-pt))) zoom)
x-delta (/ (* (mf/ref-val width-factor-ref) (- (:x current-pt) (:x start-pt))) zoom)
new-v-scrollbar-y (-> current-pt-viewport
(:y)
(+ (mf/ref-val v-scrollbar-y-padding-ref)))
new-h-scrollbar-x (-> current-pt-viewport
(:x)
(+ (mf/ref-val h-scrollbar-x-padding-ref)))
viewport-update (-> {}
(cond-> (= axis :y) (assoc :y #(+ % y-delta)))
(cond-> (= axis :x) (assoc :x #(+ % x-delta))))]
(mf/set-ref-val! vbox-y-ref vbox-y)
(mf/set-ref-val! vbox-x-ref vbox-x)
(st/emit! (dw/update-viewport-position viewport-update))
(mf/set-ref-val! v-scrollbar-y-ref new-v-scrollbar-y)
(mf/set-ref-val! h-scrollbar-x-ref new-h-scrollbar-x)
(mf/set-ref-val! start-ref current-pt)))))
(fn [event axis]
(when-let [_ (or @v-scrolling? @h-scrolling?)]
(let [viewport (mf/ref-val viewport-ref)
start-pt (mf/ref-val start-ref)
current-pt (dom/get-client-position event)
current-pt-viewport (utils/translate-point-to-viewport viewport zoom current-pt)
y-delta (/ (* (mf/ref-val height-factor-ref) (- (:y current-pt) (:y start-pt))) zoom)
x-delta (/ (* (mf/ref-val width-factor-ref) (- (:x current-pt) (:x start-pt))) zoom)
new-v-scrollbar-y (-> current-pt-viewport
(:y)
(+ (mf/ref-val v-scrollbar-y-padding-ref)))
new-h-scrollbar-x (-> current-pt-viewport
(:x)
(+ (mf/ref-val h-scrollbar-x-padding-ref)))
viewport-update (-> {}
(cond-> (= axis :y) (assoc :y #(+ % y-delta)))
(cond-> (= axis :x) (assoc :x #(+ % x-delta))))]
(mf/set-ref-val! vbox-y-ref vbox-y)
(mf/set-ref-val! vbox-x-ref vbox-x)
(st/emit! (dw/update-viewport-position viewport-update))
(mf/set-ref-val! v-scrollbar-y-ref new-v-scrollbar-y)
(mf/set-ref-val! h-scrollbar-x-ref new-h-scrollbar-x)
(mf/set-ref-val! start-ref current-pt))))
on-mouse-down
(mf/use-callback
(mf/deps v-scrollbar-y scrollbar-height)
(fn [event axis]
(let [viewport (mf/ref-val viewport-ref)
start-pt (dom/get-client-position event)
viewport-point (utils/translate-point-to-viewport viewport zoom start-pt)
new-h-scrollbar-x (:x viewport-point)
new-v-scrollbar-y (:y viewport-point)
v-scrollbar-y-padding (- v-scrollbar-y new-v-scrollbar-y)
h-scrollbar-x-padding (- h-scrollbar-x new-h-scrollbar-x)
vbox-rect {:x vbox-x
:y vbox-y
:x1 vbox-x
:y1 vbox-y
:x2 (+ vbox-x (:width vbox))
:y2 (+ vbox-y (:height vbox))
:width (:width vbox)
:height (:height vbox)}
containing-rect (gsh/join-selrects [base-objects-rect vbox-rect])
height-factor (/ (:height containing-rect) vbox-height)
width-factor (/ (:width containing-rect) vbox-width)]
(mf/set-ref-val! start-ref start-pt)
(mf/set-ref-val! v-scrollbar-y-padding-ref v-scrollbar-y-padding)
(mf/set-ref-val! h-scrollbar-x-padding v-scrollbar-y-padding)
(mf/set-ref-val! v-scrollbar-y-ref (+ new-v-scrollbar-y v-scrollbar-y-padding))
(mf/set-ref-val! h-scrollbar-x-ref (+ new-h-scrollbar-x h-scrollbar-x-padding))
(mf/set-ref-val! vbox-y-ref vbox-y)
(mf/set-ref-val! vbox-x-ref vbox-x)
(mf/set-ref-val! scrollbar-height-ref scrollbar-height)
(mf/set-ref-val! scrollbar-width-ref scrollbar-width)
(mf/set-ref-val! height-factor-ref height-factor)
(mf/set-ref-val! width-factor-ref width-factor)
(reset! v-scrolling? (= axis :y))
(reset! h-scrolling? (= axis :x)))))
(fn [event axis]
(let [viewport (mf/ref-val viewport-ref)
start-pt (dom/get-client-position event)
viewport-point (utils/translate-point-to-viewport viewport zoom start-pt)
new-h-scrollbar-x (:x viewport-point)
new-v-scrollbar-y (:y viewport-point)
v-scrollbar-y-padding (- v-scrollbar-y new-v-scrollbar-y)
h-scrollbar-x-padding (- h-scrollbar-x new-h-scrollbar-x)
vbox-rect {:x vbox-x
:y vbox-y
:x1 vbox-x
:y1 vbox-y
:x2 (+ vbox-x (:width vbox))
:y2 (+ vbox-y (:height vbox))
:width (:width vbox)
:height (:height vbox)}
containing-rect (gsh/join-selrects [base-objects-rect vbox-rect])
height-factor (/ (:height containing-rect) vbox-height)
width-factor (/ (:width containing-rect) vbox-width)]
(mf/set-ref-val! start-ref start-pt)
(mf/set-ref-val! v-scrollbar-y-padding-ref v-scrollbar-y-padding)
(mf/set-ref-val! h-scrollbar-x-padding-ref h-scrollbar-x-padding)
(mf/set-ref-val! v-scrollbar-y-ref (+ new-v-scrollbar-y v-scrollbar-y-padding))
(mf/set-ref-val! h-scrollbar-x-ref (+ new-h-scrollbar-x h-scrollbar-x-padding))
(mf/set-ref-val! vbox-y-ref vbox-y)
(mf/set-ref-val! vbox-x-ref vbox-x)
(mf/set-ref-val! scrollbar-height-ref scrollbar-height)
(mf/set-ref-val! scrollbar-width-ref scrollbar-width)
(mf/set-ref-val! height-factor-ref height-factor)
(mf/set-ref-val! width-factor-ref width-factor)
(reset! v-scrolling? (= axis :y))
(reset! h-scrolling? (= axis :x))))
on-mouse-up
(mf/use-callback
(mf/deps)
(fn [_]
(reset! v-scrolling? false)
(reset! h-scrolling? false)))]
(fn []
(reset! v-scrolling? false)
(reset! h-scrolling? false))]
[:*
(when show-v-scroll?
[:g.v-scroll
[:rect {:on-mouse-move #(on-mouse-move % :y)
:on-mouse-down #(on-mouse-down % :y)
:on-mouse-up #(on-mouse-up % :y)
:on-mouse-up on-mouse-up
:width (* inv-zoom 7)
:rx (* inv-zoom 3)
:ry (* inv-zoom 3)
@ -211,7 +205,7 @@
[:g.h-scroll
[:rect {:on-mouse-move #(on-mouse-move % :x)
:on-mouse-down #(on-mouse-down % :x)
:on-mouse-up #(on-mouse-up % :x)
:on-mouse-up on-mouse-up
:width scrollbar-width
:rx (* inv-zoom 3)
:ry (* inv-zoom 3)

View file

@ -91,7 +91,7 @@
(cond
frame?
[thumb-node
(dom/query shape-node ".frame-background")
(dom/get-parent (dom/query shape-node ".frame-background"))
(dom/query shape-node ".frame-clip")]
;; For groups we don't want to transform the whole group but only
@ -223,8 +223,7 @@
:else
(let [old-transform (dom/get-attribute node "data-old-transform")]
(if (some? old-transform)
(do (dom/remove-attribute! node "data-old-transform")
(dom/set-attribute! node "transform" old-transform))
(dom/remove-attribute! node "data-old-transform")
(dom/remove-attribute! node "transform")))))))))
(defn format-viewbox [vbox]

View file

@ -344,7 +344,13 @@
(assoc :id (resolve page-id)))
flows (->> (get-in page-data [:options :flows])
(mapv #(update % :starting-frame resolve)))
page-data (d/assoc-in-when page-data [:options :flows] flows)
guides (-> (get-in page-data [:options :guides])
(d/update-vals #(update % :frame-id resolve)))
page-data (-> page-data
(d/assoc-in-when [:options :flows] flows)
(d/assoc-in-when [:options :guides] guides))
file (-> file (fb/add-page page-data))
;; Preprocess nodes to parallel upload the images. Store the result in a table

View file

@ -79,7 +79,7 @@
(let [objects (:objects page)
frame (some->> page :thumbnail-frame-id (get objects))
element (if frame
(mf/element render/frame-svg #js {:objects objects :frame frame})
(mf/element render/frame-svg #js {:objects objects :frame frame :show-thumbnails? true})
(mf/element render/page-svg #js {:data page :thumbnails? true}))]
{:data (rds/renderToStaticMarkup element)
:fonts @fonts/loaded