mirror of
https://github.com/penpot/penpot.git
synced 2025-06-04 14:11:38 +02:00
Merge remote-tracking branch 'origin/staging' into develop
This commit is contained in:
commit
416e9e8e1d
52 changed files with 607 additions and 86 deletions
|
@ -204,6 +204,9 @@
|
|||
(rx/filter #(= % ::force-persist))))]
|
||||
|
||||
(rx/merge
|
||||
(->> notifier-s
|
||||
(rx/map #(ptk/data-event ::persistence-notification)))
|
||||
|
||||
(->> local-commits-s
|
||||
(rx/debounce 200)
|
||||
(rx/map (fn [_]
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
[app.main.data.helpers :as dsh]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.notifications :as ntf]
|
||||
[app.main.data.persistence :as-alias dps]
|
||||
[app.main.data.plugins :as dp]
|
||||
[app.main.data.profile :as du]
|
||||
[app.main.data.project :as dpj]
|
||||
|
@ -354,6 +355,11 @@
|
|||
(-> (workspace-initialized file-id)
|
||||
(with-meta {:file-id file-id}))))))
|
||||
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::dps/persistence-notification))
|
||||
(rx/take 1)
|
||||
(rx/map dwc/set-workspace-visited))
|
||||
|
||||
(when-let [component-id (some-> rparams :component-id parse-uuid)]
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::workspace-initialized))
|
||||
|
@ -476,10 +482,13 @@
|
|||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(if-let [page (dsh/lookup-page state file-id page-id)]
|
||||
(rx/of (initialize-page* file-id page-id page)
|
||||
(dwth/watch-state-changes file-id page-id)
|
||||
(dwl/watch-component-changes)
|
||||
(select-frame-tool file-id page-id))
|
||||
(rx/concat (rx/of (initialize-page* file-id page-id page)
|
||||
(dwth/watch-state-changes file-id page-id)
|
||||
(dwl/watch-component-changes))
|
||||
(let [profile (:profile state)
|
||||
props (get profile :props)]
|
||||
(when (not (:workspace-visited props))
|
||||
(rx/of (select-frame-tool file-id page-id)))))
|
||||
(rx/of (dcm/go-to-workspace :file-id file-id ::rt/replace true))))))
|
||||
|
||||
(defn finalize-page
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
(ns app.main.data.workspace.common
|
||||
(:require
|
||||
[app.common.logging :as log]
|
||||
[app.config :as cf]
|
||||
[app.main.data.profile :as du]
|
||||
[app.main.data.workspace.layout :as dwl]
|
||||
[beicon.v2.core :as rx]
|
||||
|
@ -38,7 +37,7 @@
|
|||
(watch [_ state _]
|
||||
(let [profile (:profile state)
|
||||
props (get profile :props)]
|
||||
(when (and (cf/external-feature-flag "boards-03" "test") (not (:workspace-visited props)))
|
||||
(when (not (:workspace-visited props))
|
||||
(rx/of (du/update-profile-props {:workspace-visited true})))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
[app.main.data.changes :as dch]
|
||||
[app.main.data.helpers :as dsh]
|
||||
[app.main.data.persistence :as-alias dps]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.notifications :as-alias wnt]
|
||||
[app.main.rasterizer :as thr]
|
||||
[app.main.refs :as refs]
|
||||
|
@ -293,10 +292,4 @@
|
|||
(rx/mapcat #(into #{} %))
|
||||
(rx/map #(update-thumbnail file-id page-id % "frame" "watch-state-changes"))))
|
||||
|
||||
;; WARNING: This is a workaround for an AB test, in case we consolidate this change we should
|
||||
;; find a better way to handle this.
|
||||
(->> notifier-s
|
||||
(rx/take 1)
|
||||
(rx/map dwc/set-workspace-visited))
|
||||
|
||||
(rx/take-until stopper-s))))))
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
[app.common.data.macros :as dm]
|
||||
[app.common.files.helpers :as cfh]
|
||||
[app.common.geom.align :as gal]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.rect :as gpr]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :as mth]
|
||||
|
@ -83,6 +84,25 @@
|
|||
(fn [local]
|
||||
(setup state local)))))))
|
||||
|
||||
(defn calculate-centered-viewbox
|
||||
"Updates the viewbox coordinates for a given center position"
|
||||
[local position]
|
||||
(let [vbox (:vbox local)
|
||||
nw (/ (:width vbox) 2)
|
||||
nh (/ (:height vbox) 2)
|
||||
nx (- (:x position) nw)
|
||||
ny (- (:y position) nh)]
|
||||
(update local :vbox assoc :x nx :y ny)))
|
||||
|
||||
(defn update-viewport-position-center
|
||||
[position]
|
||||
(assert (gpt/point? position) "expected a point instance for `position` param")
|
||||
|
||||
(ptk/reify ::update-viewport-position-center
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :workspace-local calculate-centered-viewbox position))))
|
||||
|
||||
(defn update-viewport-position
|
||||
[{:keys [x y] :or {x identity y identity}}]
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(defn- impl-update-zoom
|
||||
(defn impl-update-zoom
|
||||
[{:keys [vbox] :as local} center zoom]
|
||||
(let [new-zoom (if (fn? zoom) (zoom (:zoom local)) zoom)
|
||||
old-zoom (:zoom local)
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
[app.main.data.comments :as dcm]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.workspace.comments :as dwcm]
|
||||
[app.main.data.workspace.viewport :as dwv]
|
||||
[app.main.data.workspace.zoom :as dwz]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
|
@ -1097,15 +1098,35 @@
|
|||
groups))
|
||||
(group-bubbles zoom remaining visited (cons [current] groups)))))))
|
||||
|
||||
(defn- calculate-zoom-scale-to-ungroup-bubbles
|
||||
"Calculate the minimum zoom scale needed for a group of bubbles to avoid overlap among them"
|
||||
[zoom threads]
|
||||
(defn- inside-vbox?
|
||||
"Checks if a bubble or a bubble group is inside a viewbox"
|
||||
[thread-group wl]
|
||||
(let [vbox (:vbox wl)
|
||||
positions (mapv :position thread-group)
|
||||
position (gpt/center-points positions)
|
||||
pos-x (:x position)
|
||||
pos-y (:y position)
|
||||
x1 (:x vbox)
|
||||
y1 (:y vbox)
|
||||
x2 (+ x1 (:width vbox))
|
||||
y2 (+ y1 (:height vbox))]
|
||||
(and (> x2 pos-x x1) (> y2 pos-y y1))))
|
||||
|
||||
(defn- calculate-zoom-scale
|
||||
"Calculates the zoom level needed to ungroup the largest number of bubbles while
|
||||
keeping them all visible in the viewbox."
|
||||
[position zoom threads wl]
|
||||
(let [num-threads (count threads)
|
||||
num-grouped-threads (count (group-bubbles zoom threads))
|
||||
zoom-scale-step 1.75]
|
||||
(if (= num-threads num-grouped-threads)
|
||||
grouped-threads (group-bubbles zoom threads)
|
||||
num-grouped-threads (count grouped-threads)
|
||||
zoom-scale-step 1.75
|
||||
scaled-zoom (* zoom zoom-scale-step)
|
||||
zoomed-wl (dwz/impl-update-zoom wl position scaled-zoom)
|
||||
outside-vbox? (complement inside-vbox?)]
|
||||
(if (or (= num-threads num-grouped-threads)
|
||||
(some #(outside-vbox? % zoomed-wl) grouped-threads))
|
||||
zoom
|
||||
(calculate-zoom-scale-to-ungroup-bubbles (* zoom zoom-scale-step) threads))))
|
||||
(calculate-zoom-scale position scaled-zoom threads zoomed-wl))))
|
||||
|
||||
(mf/defc comment-floating-group*
|
||||
{::mf/wrap [mf/memo]}
|
||||
|
@ -1126,11 +1147,14 @@
|
|||
|
||||
on-click
|
||||
(mf/use-fn
|
||||
(mf/deps thread-group position)
|
||||
(mf/deps thread-group position zoom)
|
||||
(fn []
|
||||
(let [updated-zoom (calculate-zoom-scale-to-ungroup-bubbles zoom thread-group)
|
||||
(let [wl (deref refs/workspace-local)
|
||||
centered-wl (dwv/calculate-centered-viewbox wl position)
|
||||
updated-zoom (calculate-zoom-scale position zoom thread-group centered-wl)
|
||||
scale-zoom (/ updated-zoom zoom)]
|
||||
(st/emit! (dwz/set-zoom position scale-zoom)))))]
|
||||
(st/emit! (dwv/update-viewport-position-center position)
|
||||
(dwz/set-zoom position scale-zoom)))))]
|
||||
|
||||
[:div {:style {:top (dm/str pos-y "px")
|
||||
:left (dm/str pos-x "px")}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
margin-right: $s-16;
|
||||
border-top: $s-1 solid var(--panel-border-color);
|
||||
overflow-y: auto;
|
||||
padding-bottom: $s-32;
|
||||
}
|
||||
|
||||
.dashboard-projects {
|
||||
|
|
|
@ -11,9 +11,10 @@
|
|||
bottom: 0;
|
||||
border-bottom-left-radius: $br-8;
|
||||
border-bottom-right-radius: $br-8;
|
||||
border-top-right-radius: $br-8;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: $s-228;
|
||||
height: $s-244;
|
||||
justify-content: flex-end;
|
||||
margin-left: $s-6;
|
||||
margin-right: $s-6;
|
||||
|
@ -39,8 +40,8 @@
|
|||
|
||||
.title {
|
||||
pointer-events: all;
|
||||
width: 100%;
|
||||
top: calc(-1 * $s-56);
|
||||
width: $s-420;
|
||||
top: calc(-1 * $s-40);
|
||||
text-align: right;
|
||||
height: $s-56;
|
||||
position: absolute;
|
||||
|
@ -102,8 +103,8 @@
|
|||
|
||||
.move-button {
|
||||
position: absolute;
|
||||
top: $s-80;
|
||||
border: $s-2 solid var(--button-secondary-background-color-rest);
|
||||
top: $s-96;
|
||||
border: $s-2 solid var(--color-foreground-secondary);
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
width: $s-36;
|
||||
|
@ -139,7 +140,7 @@
|
|||
font-size: $fs-14;
|
||||
color: var(--color-foreground-primary);
|
||||
margin-bottom: -8px;
|
||||
margin-top: -4px;
|
||||
margin-top: $s-16;
|
||||
margin-left: $s-16;
|
||||
visibility: visible;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
[app.main.ui.context :as ctx]
|
||||
[app.main.ui.hooks :as hooks]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.globals :as glob]
|
||||
[app.util.storage :as storage]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
|
@ -34,6 +35,28 @@
|
|||
(set! last-resize-type type))
|
||||
|
||||
(defn use-resize-hook
|
||||
"Allows a node to be resized by dragging, and calculates the new size based on the drag setting a maximum and minimum value.
|
||||
|
||||
Parameters:
|
||||
- `key` - A unique key to identify the resize hook.
|
||||
- `initial` - The initial size of the node.
|
||||
- `min-val` - The minimum value the size can be.
|
||||
- `max-val` - The maximum value the size can be. It can be a number or a string representing either a fixed value or a percentage (0 to 1).
|
||||
- `axis` - The axis to resize on, either `:x` or `:y`.
|
||||
- `negate?` - If `true`, the axis is negated.
|
||||
- `resize-type` - The type of resize, either `:width` or `:height`.
|
||||
- `on-change-size` - A function to call when the size changes.
|
||||
|
||||
Returns:
|
||||
- An object with the following:
|
||||
|
||||
- `:on-pointer-down` - A function to call when the pointer is pressed down.
|
||||
- `:on-lost-pointer-capture` - A function to call when the pointer is released.
|
||||
- `:on-pointer-move` - A function to call when the pointer is moved.
|
||||
- `:parent-ref` - A reference to the node.
|
||||
- `:set-size` - A function to set the size.
|
||||
- `:size` - The current size."
|
||||
|
||||
([key initial min-val max-val axis negate? resize-type]
|
||||
(use-resize-hook key initial min-val max-val axis negate? resize-type nil))
|
||||
|
||||
|
@ -48,14 +71,26 @@
|
|||
start-size-ref (mf/use-ref nil)
|
||||
start-ref (mf/use-ref nil)
|
||||
|
||||
window-height (dom/get-window-height)
|
||||
;; Since Penpot is not responsive designed, this value will only refer to vertical axis.
|
||||
window-height* (mf/use-state #(dom/get-window-height))
|
||||
window-height (deref window-height*)
|
||||
|
||||
;; In case max-val is a string, we need to parse it as a double.
|
||||
max-val (mf/with-memo [max-val window-height]
|
||||
(let [parsed-max-val (when (string? max-val) (d/parse-double max-val))]
|
||||
(if parsed-max-val
|
||||
(* window-height parsed-max-val)
|
||||
max-val)))
|
||||
|
||||
set-size
|
||||
(mf/use-fn
|
||||
(mf/deps file-id key min-val max-val window-height)
|
||||
(fn [new-size]
|
||||
(let [new-size (mth/clamp new-size min-val max-val)]
|
||||
(reset! current-size* new-size)
|
||||
;; Save the new size in the local storage for this file and this specific node.
|
||||
(swap! storage/user update-persistent-state file-id key new-size))))
|
||||
|
||||
on-pointer-down
|
||||
(mf/use-fn
|
||||
(mf/deps current-size)
|
||||
|
@ -89,21 +124,28 @@
|
|||
start-size (mf/ref-val start-size-ref)
|
||||
|
||||
new-size (-> (+ start-size delta) (max min-val) (min max-val))]
|
||||
(reset! current-size* new-size)
|
||||
(swap! storage/user update-persistent-state file-id key new-size)))))
|
||||
|
||||
set-size
|
||||
(set-size new-size)))))
|
||||
|
||||
on-resize-window
|
||||
(mf/use-fn
|
||||
(mf/deps on-change-size file-id key)
|
||||
(fn [new-size]
|
||||
(let [new-size (mth/clamp new-size min-val max-val)]
|
||||
(reset! current-size* new-size)
|
||||
(swap! storage/user update-persistent-state file-id key new-size))))]
|
||||
(fn []
|
||||
(let [new-window-height (dom/get-window-height)]
|
||||
(reset! window-height* new-window-height))))]
|
||||
|
||||
(mf/with-effect [on-change-size current-size]
|
||||
(when on-change-size
|
||||
(on-change-size current-size)))
|
||||
|
||||
(mf/with-effect []
|
||||
(.addEventListener glob/window "resize" on-resize-window)
|
||||
(fn []
|
||||
(.removeEventListener glob/window "resize" on-resize-window)))
|
||||
|
||||
(mf/with-effect [window-height]
|
||||
(let [new-size (mth/clamp current-size min-val max-val)]
|
||||
(set-size new-size)))
|
||||
|
||||
{:on-pointer-down on-pointer-down
|
||||
:on-lost-pointer-capture on-lost-pointer-capture
|
||||
:on-pointer-move on-pointer-move
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
Caution: This will allow a trailing dot like `token-name.`,
|
||||
But we will trim that in the `finalize-name`,
|
||||
to not throw too many errors while the user is editing."
|
||||
#"(?!\$)([a-zA-Z0-9-$]+\.?)*")
|
||||
#"(?!\$)([a-zA-Z0-9-$_]+\.?)*")
|
||||
|
||||
(def valid-token-name-schema
|
||||
(m/-simple-schema
|
||||
|
@ -235,7 +235,14 @@
|
|||
token-properties (wtch/get-token-properties token)
|
||||
color? (wtt/color-token? token)
|
||||
selected-set-tokens (mf/deref refs/workspace-selected-token-set-tokens)
|
||||
active-theme-tokens (mf/deref refs/workspace-active-theme-sets-tokens)
|
||||
|
||||
active-theme-tokens (cond-> (mf/deref refs/workspace-active-theme-sets-tokens)
|
||||
;; Ensure that the resolved value uses the currently editing token
|
||||
;; even if the name has been overriden by a token with the same name
|
||||
;; in another set below.
|
||||
(and (:name token) (:value token))
|
||||
(assoc (:name token) token))
|
||||
|
||||
resolved-tokens (sd/use-resolved-tokens active-theme-tokens {:cache-atom form-token-cache-atom
|
||||
:interactive? true})
|
||||
token-path (mf/use-memo
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
(ns app.main.ui.workspace.tokens.modals
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
|
@ -20,30 +21,55 @@
|
|||
|
||||
(defn calculate-position
|
||||
"Calculates the style properties for the given coordinates and position"
|
||||
[{vh :height} position x y]
|
||||
(let [;; picker height in pixels
|
||||
h 510
|
||||
[{vh :height} position x y color?]
|
||||
(let [;; picker height in pixels
|
||||
;; TODO: Revisit these harcoded values
|
||||
h (if color? 610 510)
|
||||
;; Checks for overflow outside the viewport height
|
||||
overflow-fix (max 0 (+ y (- 50) h (- vh)))
|
||||
|
||||
x-pos 325]
|
||||
max-y (- vh h)
|
||||
overflow-fix (max 0 (+ y (- 50) h (- vh)))
|
||||
bottom-offset "1rem"
|
||||
top-offset (dm/str (- y 70) "px")
|
||||
max-height-top (str "calc(100vh - " top-offset)
|
||||
max-height-bottom (str "calc(100vh -" bottom-offset)
|
||||
x-pos 325
|
||||
rulers? (mf/deref refs/rulers?)
|
||||
left-offset (if rulers? 80 58)
|
||||
left-position (dm/str (- x x-pos) "px")]
|
||||
(cond
|
||||
(or (nil? x) (nil? y)) {:left "auto" :right "16rem" :top "4rem"}
|
||||
(= position :left) {:left (str (- x x-pos) "px")
|
||||
:top (str (- y 50 overflow-fix) "px")}
|
||||
:else {:left (str (+ x 80) "px")
|
||||
:top (str (- y 70 overflow-fix) "px")})))
|
||||
(or (nil? x) (nil? y))
|
||||
{:left "auto" :right "16rem" :top "4rem"}
|
||||
|
||||
(defn use-viewport-position-style [x y position]
|
||||
(= position :left)
|
||||
(if (> y max-y)
|
||||
{:left left-position
|
||||
:bottom bottom-offset
|
||||
:maxHeight max-height-bottom}
|
||||
|
||||
{:left left-position
|
||||
:maxHeight max-height-top
|
||||
:top (dm/str (- y 50 overflow-fix) "px")})
|
||||
|
||||
:else
|
||||
(if (> y max-y)
|
||||
{:left (dm/str (+ x left-offset) "px")
|
||||
:bottom bottom-offset
|
||||
:maxHeight max-height-bottom}
|
||||
|
||||
{:left (dm/str (+ x left-offset) "px")
|
||||
:top (dm/str (- y 70 overflow-fix) "px")
|
||||
:maxHeight max-height-top}))))
|
||||
|
||||
(defn use-viewport-position-style [x y position color?]
|
||||
(let [vport (-> (l/derived :vport refs/workspace-local)
|
||||
(mf/deref))]
|
||||
(-> (calculate-position vport position x y)
|
||||
(-> (calculate-position vport position x y color?)
|
||||
(clj->js))))
|
||||
|
||||
(mf/defc token-update-create-modal
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [x y position token token-type action selected-token-set-name] :as _args}]
|
||||
(let [wrapper-style (use-viewport-position-style x y position)
|
||||
(let [wrapper-style (use-viewport-position-style x y position (= token-type :color))
|
||||
modal-size-large* (mf/use-state false)
|
||||
modal-size-large? (deref modal-size-large*)
|
||||
close-modal (mf/use-fn
|
||||
|
|
|
@ -160,7 +160,7 @@
|
|||
(fn [event]
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(when-not is-editing
|
||||
(when (and can-edit? (not is-editing))
|
||||
(st/emit! (dt/assign-token-set-context-menu
|
||||
{:position (dom/get-client-position event)
|
||||
:is-group true
|
||||
|
|
|
@ -233,6 +233,7 @@
|
|||
(mf/defc token-sets-section*
|
||||
{::mf/private true}
|
||||
[{:keys [resize-height] :as props}]
|
||||
|
||||
(let [can-edit?
|
||||
(mf/use-ctx ctx/can-edit?)]
|
||||
|
||||
|
|
|
@ -336,7 +336,7 @@
|
|||
[tokens & {:keys [interactive?]}]
|
||||
(let [state* (mf/use-state tokens)]
|
||||
(mf/with-effect [tokens interactive?]
|
||||
(when (seq tokens)
|
||||
(if (seq tokens)
|
||||
(let [tpoint (dt/tpoint-ms)
|
||||
promise (if interactive?
|
||||
(resolve-tokens-interactive+ tokens)
|
||||
|
@ -346,5 +346,6 @@
|
|||
(p/fmap (fn [resolved-tokens]
|
||||
(let [elapsed (tpoint)]
|
||||
(l/dbg :hint "use-resolved-tokens*" :elapsed elapsed)
|
||||
(reset! state* resolved-tokens))))))))
|
||||
(reset! state* resolved-tokens))))))
|
||||
(reset! state* tokens)))
|
||||
@state*))
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.media :as cm]
|
||||
[app.config :as cf]
|
||||
[app.main.data.event :as ev]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.workspace :as dw]
|
||||
|
@ -126,8 +125,7 @@
|
|||
profile (mf/deref refs/profile)
|
||||
props (get profile :props)
|
||||
test-tooltip-board-text
|
||||
(if (and (cf/external-feature-flag "boards-03" "test")
|
||||
(not (:workspace-visited props)))
|
||||
(if (not (:workspace-visited props))
|
||||
(tr "workspace.toolbar.frame-first-time" (sc/get-tooltip :draw-frame))
|
||||
(tr "workspace.toolbar.frame" (sc/get-tooltip :draw-frame)))]
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue