0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-28 07:31:25 -05:00

🎉 Overlay positions buttons

This commit is contained in:
Andrés Moya 2021-09-17 19:48:18 +02:00
parent 0516cfa296
commit f208731746
17 changed files with 328 additions and 48 deletions

View file

@ -45,6 +45,14 @@
:open-url})
(s/def ::destination (s/nilable ::us/uuid))
(s/def ::overlay-pos-type #{:manual
:center
:top-left
:top-right
:top-center
:bottom-left
:bottom-right
:bottom-center})
(s/def ::overlay-position ::point)
(s/def ::url ::us/string)
@ -55,7 +63,8 @@
(defmethod action-opts-spec :open-overlay [_]
(s/keys :req-un [::destination
::overlay-position]))
::overlay-position
::overlay-pos-type]))
(defmethod action-opts-spec :close-overlay [_]
(s/keys :req-un [::destination]))
@ -92,6 +101,8 @@
;; -- Helpers
(declare calc-overlay-position)
(defn set-event-type
[interaction event-type]
(us/verify ::interaction interaction)
@ -110,7 +121,7 @@
(defn set-action-type
[interaction action-type]
[interaction action-type shape objects]
(us/verify ::interaction interaction)
(us/verify ::action-type action-type)
(if (= (:action-type interaction) action-type)
@ -123,10 +134,21 @@
:destination (get interaction :destination))
:open-overlay
(assoc interaction
:action-type action-type
:destination (get interaction :destination)
:overlay-position (get interaction :overlay-position (gpt/point 0 0)))
(let [destination (get interaction :destination)
overlay-pos-type (get interaction :overlay-pos-type :center)
overlay-position (get interaction
:overlay-position
(calc-overlay-position
destination
interaction
shape
objects
overlay-pos-type))]
(assoc interaction
:action-type action-type
:destination destination
:overlay-pos-type overlay-pos-type
:overlay-position overlay-position))
:close-overlay
(assoc interaction
@ -142,7 +164,6 @@
:action-type action-type
:url (get interaction :url "")))))
(defn set-destination
[interaction destination shape objects]
(us/verify ::interaction interaction)
@ -150,33 +171,95 @@
(assert (or (nil? destination)
(some? (get objects destination))))
(assert #(:navigate :open-overlay :close-overlay) (:action-type interaction))
(let [calc-overlay-position
(fn []
(if (nil? destination)
(gpt/point 0 0)
(let [dest-frame (get objects destination)
overlay-size (:selrect dest-frame)
(cond-> interaction
:always
(assoc :destination destination)
orig-frame (if (= (:type shape) :frame)
shape
(get objects (:frame-id shape)))
frame-size (:selrect orig-frame)
(= (:action-type interaction) :open-overlay)
(assoc :overlay-pos-type :center
:overlay-position (calc-overlay-position destination
interaction
shape
objects
:center))))
x (/ (- (:width frame-size) (:width overlay-size)) 2)
y (/ (- (:height frame-size) (:height overlay-size)) 2)]
(gpt/point x y))))]
(cond-> interaction
:always
(assoc :destination destination)
(= (:action-type interaction) :open-overlay)
(assoc :overlay-position (calc-overlay-position)))))
(defn set-overlay-pos-type
[interaction overlay-pos-type shape objects]
(us/verify ::interaction interaction)
(us/verify ::overlay-pos-type overlay-pos-type)
(assert #(= :open-overlay (:action-type interaction)))
(assoc interaction
:overlay-pos-type overlay-pos-type
:overlay-position (calc-overlay-position (:destination interaction)
interaction
shape
objects
overlay-pos-type)))
(defn toggle-overlay-pos-type
[interaction overlay-pos-type shape objects]
(us/verify ::interaction interaction)
(us/verify ::overlay-pos-type overlay-pos-type)
(assert #(= :open-overlay (:action-type interaction)))
(let [new-pos-type (if (= (:overlay-pos-type interaction) overlay-pos-type)
:manual
overlay-pos-type)]
(assoc interaction
:overlay-pos-type new-pos-type
:overlay-position (calc-overlay-position (:destination interaction)
interaction
shape
objects
new-pos-type))))
(defn set-overlay-position
[interaction overlay-position]
(us/verify ::interaction interaction)
(us/verify ::overlay-position overlay-position)
(assoc interaction :overlay-position overlay-position))
(assert #(= :open-overlay (:action-type interaction)))
(assoc interaction
:overlay-pos-type :manual
:overlay-position overlay-position))
(defn- calc-overlay-position
[destination interaction shape objects overlay-pos-type]
(if (nil? destination)
(gpt/point 0 0)
(let [dest-frame (get objects destination)
overlay-size (:selrect dest-frame)
orig-frame (if (= (:type shape) :frame)
shape
(get objects (:frame-id shape)))
frame-size (:selrect orig-frame)]
(case overlay-pos-type
:center
(gpt/point (/ (- (:width frame-size) (:width overlay-size)) 2)
(/ (- (:height frame-size) (:height overlay-size)) 2))
:top-left
(gpt/point 0 0)
:top-right
(gpt/point (- (:width frame-size) (:width overlay-size))
0)
:top-center
(gpt/point (/ (- (:width frame-size) (:width overlay-size)) 2)
0)
:bottom-left
(gpt/point 0
(- (:height frame-size) (:height overlay-size)))
:bottom-right
(gpt/point (- (:width frame-size) (:width overlay-size))
(- (:height frame-size) (:height overlay-size)))
:bottom-center
(gpt/point (/ (- (:width frame-size) (:width overlay-size)) 2)
(- (:height frame-size) (:height overlay-size)))
:manual
(:overlay-position interaction)))))

View file

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18">
<g>
<rect rx="0" ry="0" x="0" y="0" width="18" height="18" id="a" fill="none" stroke-width="2" stroke-opacity="1"/>
<rect rx="0" ry="0" x="5" y="10" width="8" height="8"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 260 B

View file

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18">
<g>
<rect rx="0" ry="0" x="0" y="0" width="18" height="18" id="a" fill="none" stroke-width="2" stroke-opacity="1"/>
<rect rx="0" ry="0" x="0" y="10" width="8" height="8"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 260 B

View file

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18">
<g>
<rect rx="0" ry="0" x="0" y="0" width="18" height="18" id="a" fill="none" stroke-width="2" stroke-opacity="1"/>
<rect rx="0" ry="0" x="10" y="10" width="8" height="8"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 261 B

View file

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18">
<g>
<rect rx="0" ry="0" x="0" y="0" width="18" height="18" id="a" fill="none" stroke-width="2" stroke-opacity="1"/>
<rect rx="0" ry="0" x="5" y="5" width="8" height="8"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 259 B

View file

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18">
<g>
<rect rx="0" ry="0" x="0" y="0" width="18" height="18" id="a" fill="none" stroke-width="2" stroke-opacity="1"/>
<rect rx="0" ry="0" x="5" width="8" height="8"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 253 B

View file

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18">
<g>
<rect rx="0" ry="0" x="0" y="0" width="18" height="18" id="a" fill="none" stroke-width="2" stroke-opacity="1"/>
<rect rx="0" ry="0" x="0" width="8" height="8"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 254 B

View file

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18">
<g>
<rect rx="0" ry="0" x="0" y="0" width="18" height="18" id="a" fill="none" stroke-width="2" stroke-opacity="1"/>
<rect rx="0" ry="0" x="10" width="8" height="8"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 254 B

View file

@ -945,11 +945,15 @@
width: 12px;
height: 12px;
fill: $color-gray-20;
stroke: $color-gray-20;
}
&:hover svg {
&:hover svg,
&.active svg {
fill: $color-primary;
stroke: $color-primary;
}
&.actions-inside {
position: absolute;
right: 0;

View file

@ -47,3 +47,13 @@
width: 64px;
}
}
.interactions-pos-buttons {
margin-top: $small;
justify-content: space-around;
svg {
width: 18px;
height: 18px;
}
}

View file

@ -1827,8 +1827,8 @@
frame)]
;; Update or create interaction
(if index
(update-in interactions [index]
#(cti/set-destination % (:id frame) shape objects))
(update interactions index
#(cti/set-destination % (:id frame) shape objects))
(conj (or interactions [])
(cti/set-destination cti/default-interaction
(:id frame)
@ -1870,11 +1870,11 @@
(rx/concat
(->> ms/mouse-position
(rx/take-until stopper)
(rx/map #(move-overlay-pos % overlay-pos frame-pos offset)))
(rx/of (finish-move-overlay-pos index overlay-pos frame-pos offset)))))))))
(rx/map #(move-overlay-pos % frame-pos offset)))
(rx/of (finish-move-overlay-pos index frame-pos offset)))))))))
(defn move-overlay-pos
[pos overlay-pos frame-pos offset]
[pos frame-pos offset]
(ptk/reify ::move-overlay-pos
ptk/UpdateEvent
(update [_ state]
@ -1884,7 +1884,7 @@
(assoc-in state [:workspace-local :move-overlay-to] pos)))))
(defn finish-move-overlay-pos
[index overlay-pos frame-pos offset]
[index frame-pos offset]
(ptk/reify ::finish-move-overlay-pos
ptk/UpdateEvent
(update [_ state]

View file

@ -101,6 +101,13 @@
(def play (icon-xref :play))
(def plus (icon-xref :plus))
(def pointer-inner (icon-xref :pointer-inner))
(def position-bottom-center (icon-xref :position-bottom-center))
(def position-bottom-left (icon-xref :position-bottom-left))
(def position-bottom-right (icon-xref :position-bottom-right))
(def position-center (icon-xref :position-center))
(def position-top-center (icon-xref :position-top-center))
(def position-top-left (icon-xref :position-top-left))
(def position-top-right (icon-xref :position-top-right))
(def radius (icon-xref :radius))
(def radius-1 (icon-xref :radius-1))
(def radius-4 (icon-xref :radius-4))

View file

@ -146,8 +146,8 @@
{:style {:width (:width size-over)
:height (:height size-over)
:position "absolute"
:left (:x (:position overlay))
:top (:y (:position overlay))}}
:left (* (:x (:position overlay)) zoom)
:top (* (:y (:position overlay)) zoom)}}
[:& interactions/viewport
{:frame (:frame overlay)
:size size-over

View file

@ -31,7 +31,8 @@
[]
{:navigate (tr "workspace.options.interaction-navigate-to")
:open-overlay (tr "workspace.options.interaction-open-overlay")
:close-overlay (tr "workspace.options.interaction-close-overlay")})
:close-overlay (tr "workspace.options.interaction-close-overlay")
:prev-screen (tr "workspace.options.interaction-prev-screen")})
(defn- action-summary
[interaction destination]
@ -44,6 +45,17 @@
(get destination :name (tr "workspace.options.interaction-self")))
"--"))
(defn- overlay-pos-type-names
[]
{:manual (tr "workspace.options.interaction-pos-manual")
:center (tr "workspace.options.interaction-pos-center")
:top-left (tr "workspace.options.interaction-pos-top-left")
:top-right (tr "workspace.options.interaction-pos-top-right")
:top-center (tr "workspace.options.interaction-pos-top-center")
:bottom-left (tr "workspace.options.interaction-pos-bottom-left")
:bottom-right (tr "workspace.options.interaction-pos-bottom-right")
:bottom-center (tr "workspace.options.interaction-pos-bottom-center")})
(mf/defc interaction-entry
[{:keys [index shape interaction update-interaction remove-interaction]}]
(let [objects (deref refs/workspace-page-objects)
@ -51,6 +63,9 @@
frames (mf/use-memo (mf/deps objects)
#(cp/select-frames objects))
action-type (:action-type interaction)
overlay-pos-type (:overlay-pos-type interaction)
extended-open? (mf/use-state false)
change-event-type
@ -61,13 +76,22 @@
change-action-type
(fn [event]
(let [value (-> event dom/get-target dom/get-value d/read-string)]
(update-interaction index #(cti/set-action-type % value))))
(update-interaction index #(cti/set-action-type % value shape objects))))
change-destination
(fn [event]
(let [value (-> event dom/get-target dom/get-value)
value (when (not= value "") (uuid/uuid value))]
(update-interaction index #(cti/set-destination % value shape objects))))]
(update-interaction index #(cti/set-destination % value shape objects))))
change-overlay-pos-type
(fn [event]
(let [value (-> event dom/get-target dom/get-value d/read-string)]
(update-interaction index #(cti/set-overlay-pos-type % value shape objects))))
toggle-overlay-pos-type
(fn [pos-type]
(update-interaction index #(cti/toggle-overlay-pos-type % pos-type shape objects)))]
[:*
[:div.element-set-options-group
@ -95,7 +119,8 @@
:on-change change-action-type}
(for [[value name] (action-type-names)]
[:option {:value (str value)} name])]]
[:div.interactions-element
(when (#{:navigate :open-overlay :close-overlay} action-type)
[:div.interactions-element
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-destination")]
[:select.input-select
{:value (str (:destination interaction))
@ -104,7 +129,45 @@
(for [frame frames]
(when (and (not= (:id frame) (:id shape)) ; A frame cannot navigate to itself
(not= (:id frame) (:frame-id shape))) ; nor a shape to its container frame
[:option {:value (str (:id frame))} (:name frame)]))]]]])]))
[:option {:value (str (:id frame))} (:name frame)]))]])
(when (= action-type :open-overlay)
[:*
[:div.interactions-element
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-position")]
[:select.input-select
{:value (str (:overlay-pos-type interaction))
:on-change change-overlay-pos-type}
(for [[value name] (overlay-pos-type-names)]
[:option {:value (str value)} name])]]
[:div.interactions-element.interactions-pos-buttons
[:div.element-set-actions-button
{:class (dom/classnames :active (= overlay-pos-type :center))
:on-click #(toggle-overlay-pos-type :center)}
i/position-center]
[:div.element-set-actions-button
{:class (dom/classnames :active (= overlay-pos-type :top-left))
:on-click #(toggle-overlay-pos-type :top-left)}
i/position-top-left]
[:div.element-set-actions-button
{:class (dom/classnames :active (= overlay-pos-type :top-right))
:on-click #(toggle-overlay-pos-type :top-right)}
i/position-top-right]
[:div.element-set-actions-button
{:class (dom/classnames :active (= overlay-pos-type :top-center))
:on-click #(toggle-overlay-pos-type :top-center)}
i/position-top-center]
[:div.element-set-actions-button
{:class (dom/classnames :active (= overlay-pos-type :bottom-left))
:on-click #(toggle-overlay-pos-type :bottom-left)}
i/position-bottom-left]
[:div.element-set-actions-button
{:class (dom/classnames :active (= overlay-pos-type :bottom-right))
:on-click #(toggle-overlay-pos-type :bottom-right)}
i/position-bottom-right]
[:div.element-set-actions-button
{:class (dom/classnames :active (= overlay-pos-type :bottom-center))
:on-click #(toggle-overlay-pos-type :bottom-center)}
i/position-bottom-center]]])]])]))
(mf/defc interactions-menu
[{:keys [shape] :as props}]

View file

@ -194,9 +194,9 @@
(mf/defc overlay-marker
[{:keys [index orig-shape dest-shape position objects zoom] :as props}]
[{:keys [index orig-shape dest-shape position objects] :as props}]
(let [start-move-position
(fn [event]
(fn [_]
(st/emit! (dw/start-move-overlay-pos index)))]
(when dest-shape
@ -287,15 +287,13 @@
:orig-shape shape
:dest-shape dest-shape
:position move-overlay-to
:objects objects
:zoom zoom}]
:objects objects}]
[:& overlay-marker {:key (str "pos" (:id shape) "-" index)
:index index
:orig-shape shape
:dest-shape dest-shape
:position (:overlay-position interaction)
:objects objects
:zoom zoom}]))])))
:objects objects}]))])))
(when (not (#{:move :rotate} current-transform))
[:& interaction-handle {:key (:id shape)
:index nil

View file

@ -2441,6 +2441,42 @@ msgstr "Open overlay"
msgid "workspace.options.interaction-open-overlay-dest"
msgstr "Open overlay: %s"
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
msgid "workspace.options.interaction-pos-manual"
msgstr "Manual"
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
msgid "workspace.options.interaction-pos-center"
msgstr "Center"
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
msgid "workspace.options.interaction-pos-top-left"
msgstr "Top left"
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
msgid "workspace.options.interaction-pos-top-right"
msgstr "Top right"
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
msgid "workspace.options.interaction-pos-top-center"
msgstr "Top center"
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
msgid "workspace.options.interaction-pos-bottom-left"
msgstr "Bottom left"
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
msgid "workspace.options.interaction-pos-bottom-right"
msgstr "Bottom right"
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
msgid "workspace.options.interaction-pos-bottom-center"
msgstr "Bottom center"
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
msgid "workspace.options.interaction-position"
msgstr "Position"
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
msgid "workspace.options.interaction-self"
msgstr "self"

View file

@ -2324,6 +2324,42 @@ msgstr "Open overlay"
msgid "workspace.options.interaction-open-overlay-dest"
msgstr "Open overlay: %s"
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
msgid "workspace.options.interaction-pos-manual"
msgstr "Manual"
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
msgid "workspace.options.interaction-pos-center"
msgstr "Centro"
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
msgid "workspace.options.interaction-pos-top-left"
msgstr "Arriba izquierda"
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
msgid "workspace.options.interaction-pos-top-right"
msgstr "Arriba derecha"
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
msgid "workspace.options.interaction-pos-top-center"
msgstr "Arriba centro"
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
msgid "workspace.options.interaction-pos-bottom-left"
msgstr "Abajo izquierda"
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
msgid "workspace.options.interaction-pos-bottom-right"
msgstr "Abajo derecha"
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
msgid "workspace.options.interaction-pos-bottom-center"
msgstr "Abajo centro"
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
msgid "workspace.options.interaction-position"
msgstr "Posición"
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
msgid "workspace.options.interaction-self"
msgstr "self"