🎉 Enhance line caps selectors
1
frontend/resources/images/cap-circle-marker.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg width="16" height="6" xmlns="http://www.w3.org/2000/svg"><rect rx="6" ry="6" x="10" width="6" height="6"/><path d="M0 3h14.5" fill="none" stroke="#000"/></svg>
|
After Width: | Height: | Size: 165 B |
1
frontend/resources/images/cap-diamond-marker.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg width="16" height="6" xmlns="http://www.w3.org/2000/svg"><rect rx="0" ry="0" x="11" y="1" transform="rotate(45 13 3)" width="4" height="4"/><path d="M0 3h14.5" fill="none" stroke="#000"/></svg>
|
After Width: | Height: | Size: 199 B |
1
frontend/resources/images/cap-line-arrow.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg width="16" height="6" xmlns="http://www.w3.org/2000/svg"><path d="M0 3h14.5M11.7 0l1 1 1.6 2-2.6 3" fill="none" stroke="#000"/></svg>
|
After Width: | Height: | Size: 139 B |
1
frontend/resources/images/cap-round.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg viewBox="1863 1374 16 8" width="16" height="8" xmlns="http://www.w3.org/2000/svg"><path d="M1879 1374h-12s-4 0-4 4 4 4 4 4h12" fill="none" stroke="#000"/></svg>
|
After Width: | Height: | Size: 166 B |
1
frontend/resources/images/cap-square-marker.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg width="16" height="6" xmlns="http://www.w3.org/2000/svg"><rect rx="0" ry="0" x="10" width="6" height="6" fill="#070707"/><path d="M0 3h14.5" fill="none" stroke="#000"/></svg>
|
After Width: | Height: | Size: 180 B |
1
frontend/resources/images/cap-square.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg viewBox="1863 1407 16 8" width="16" height="8" xmlns="http://www.w3.org/2000/svg"><path d="M1879 1407h-16v8h16" fill="none" stroke="#000"/></svg>
|
After Width: | Height: | Size: 151 B |
1
frontend/resources/images/cap-triangle-arrow.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg width="16" height="6" xmlns="http://www.w3.org/2000/svg"><path d="M0 3h14.5" fill="none" stroke="#000"/><path d="M13 0l2.9 3L13 6V0z"/></svg>
|
After Width: | Height: | Size: 147 B |
|
@ -1436,5 +1436,57 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.cap-select {
|
||||
background-color: transparent;
|
||||
border: 1px solid transparent;
|
||||
border-bottom-color: $color-gray-40;
|
||||
color: $color-gray-10;
|
||||
cursor: pointer;
|
||||
font-size: $fs11;
|
||||
margin: $x-small;
|
||||
overflow: hidden;
|
||||
padding: $x-small;
|
||||
padding-right: 20px;
|
||||
position: relative;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
|
||||
& .cap-select-button {
|
||||
svg {
|
||||
fill: $color-gray-10;
|
||||
height: 11px;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 6px;
|
||||
width: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: $color-gray-40;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-color: $color-primary;
|
||||
}
|
||||
}
|
||||
|
||||
.cap-select-dropdown {
|
||||
right: 5px;
|
||||
top: 30px;
|
||||
z-index: 12;
|
||||
min-width: 200px;
|
||||
position: fixed;
|
||||
|
||||
& li.separator {
|
||||
border-top: 1px solid $color-gray-10;
|
||||
}
|
||||
|
||||
& li img {
|
||||
width: 16px;
|
||||
margin-right: $small;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
[app.main.data.workspace.colors :as dc]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]]
|
||||
[app.util.dom :as dom]
|
||||
|
@ -44,6 +45,26 @@
|
|||
""
|
||||
(pr-str value)))
|
||||
|
||||
(defn- stroke-cap-names []
|
||||
[[nil (tr "workspace.options.stroke-cap.none") false]
|
||||
[:line-arrow (tr "workspace.options.stroke-cap.line-arrow") true]
|
||||
[:triangle-arrow (tr "workspace.options.stroke-cap.triangle-arrow") false]
|
||||
[:square-marker (tr "workspace.options.stroke-cap.square-marker") false]
|
||||
[:circle-marker (tr "workspace.options.stroke-cap.circle-marker") false]
|
||||
[:diamond-marker (tr "workspace.options.stroke-cap.diamond-marker") false]
|
||||
[:round (tr "workspace.options.stroke-cap.round") true]
|
||||
[:square (tr "workspace.options.stroke-cap.square") false]])
|
||||
|
||||
(defn- value->name [value]
|
||||
(if (= value :multiple)
|
||||
"--"
|
||||
(-> (d/seek #(= (first %) value) (stroke-cap-names))
|
||||
(second))))
|
||||
|
||||
(defn- value->img [value]
|
||||
(when (and value (not= value :multiple))
|
||||
(str "images/cap-" (name value) ".svg")))
|
||||
|
||||
(mf/defc stroke-menu
|
||||
{::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values" "type" "show-caps"]))]}
|
||||
[{:keys [ids type values show-caps] :as props}]
|
||||
|
@ -53,8 +74,14 @@
|
|||
(tr "workspace.options.stroke"))
|
||||
|
||||
show-options (not= (:stroke-style values :none) :none)
|
||||
show-caps (and show-caps (= (:stroke-alignment values) :center))
|
||||
|
||||
show-caps (and show-caps (= (:stroke-alignment values) :center))
|
||||
start-caps-state (mf/use-state {:open? false
|
||||
:top 0
|
||||
:left 0})
|
||||
end-caps-state (mf/use-state {:open? false
|
||||
:top 0
|
||||
:left 0})
|
||||
|
||||
current-stroke-color {:color (:stroke-color values)
|
||||
:opacity (:stroke-opacity values)
|
||||
|
@ -105,21 +132,34 @@
|
|||
(apply (partial assoc %) kvs)
|
||||
%))
|
||||
|
||||
open-caps-select
|
||||
(fn [caps-state]
|
||||
(fn [event]
|
||||
(let [window-size (dom/get-window-size)
|
||||
|
||||
target (dom/get-current-target event)
|
||||
rect (dom/get-bounding-rect target)
|
||||
|
||||
top (+ (:bottom rect) 5)
|
||||
left (if (< (+ (:left rect) 200) (:width window-size))
|
||||
(:left rect)
|
||||
(- (:width window-size) 205))]
|
||||
(swap! caps-state assoc :open? true
|
||||
:left left
|
||||
:top top))))
|
||||
|
||||
close-caps-select
|
||||
(fn [caps-state]
|
||||
(fn [_]
|
||||
(swap! caps-state assoc :open? false)))
|
||||
|
||||
on-stroke-cap-start-change
|
||||
(fn [event]
|
||||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
(d/read-string))]
|
||||
(when-not (str/empty? value)
|
||||
(st/emit! (dch/update-shapes ids (update-cap-attr :stroke-cap-start value))))))
|
||||
(fn [value]
|
||||
(st/emit! (dch/update-shapes ids (update-cap-attr :stroke-cap-start value))))
|
||||
|
||||
on-stroke-cap-end-change
|
||||
(fn [event]
|
||||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
(d/read-string))]
|
||||
(when-not (str/empty? value)
|
||||
(st/emit! (dch/update-shapes ids (update-cap-attr :stroke-cap-end value))))))
|
||||
(fn [value]
|
||||
(st/emit! (dch/update-shapes ids (update-cap-attr :stroke-cap-end value))))
|
||||
|
||||
on-stroke-cap-switch
|
||||
(fn [_]
|
||||
|
@ -199,34 +239,40 @@
|
|||
;; Stroke Caps
|
||||
(when show-caps
|
||||
[:div.row-flex
|
||||
[:select#style.input-select {:value (enum->string (:stroke-cap-start values))
|
||||
:on-change on-stroke-cap-start-change}
|
||||
(when (= (:stroke-cap-start values) :multiple)
|
||||
[:option {:value ""} "--"])
|
||||
[:option {:value ""} (tr "workspace.options.stroke-cap.none")]
|
||||
[:option {:value ":line-arrow"} (tr "workspace.options.stroke-cap.line-arrow")]
|
||||
[:option {:value ":triangle-arrow"} (tr "workspace.options.stroke-cap.triangle-arrow")]
|
||||
[:option {:value ":square-marker"} (tr "workspace.options.stroke-cap.square-marker")]
|
||||
[:option {:value ":circle-marker"} (tr "workspace.options.stroke-cap.circle-marker")]
|
||||
[:option {:value ":diamond-marker"} (tr "workspace.options.stroke-cap.diamond-marker")]
|
||||
[:option {:value ":round"} (tr "workspace.options.stroke-cap.round")]
|
||||
[:option {:value ":square"} (tr "workspace.options.stroke-cap.square")]]
|
||||
[:div.cap-select {:tab-index 0 ;; tab-index to make the element focusable
|
||||
:on-click (open-caps-select start-caps-state)}
|
||||
(value->name (:stroke-cap-start values))
|
||||
[:span.cap-select-button
|
||||
i/arrow-down]]
|
||||
[:& dropdown {:show (:open? @start-caps-state)
|
||||
:on-close (close-caps-select start-caps-state)}
|
||||
[:ul.dropdown.cap-select-dropdown {:style {:top (:top @start-caps-state)
|
||||
:left (:left @start-caps-state)}}
|
||||
(for [[value label separator] (stroke-cap-names)]
|
||||
(let [img (value->img value)]
|
||||
[:li {:class (dom/classnames :separator separator)
|
||||
:on-click #(on-stroke-cap-start-change value)}
|
||||
(when img [:img {:src (value->img value)}])
|
||||
label]))]]
|
||||
|
||||
[:div.element-set-actions-button {:on-click on-stroke-cap-switch}
|
||||
i/switch]
|
||||
|
||||
[:select#style.input-select {:value (enum->string (:stroke-cap-end values))
|
||||
:on-change on-stroke-cap-end-change}
|
||||
(when (= (:stroke-cap-end values) :multiple)
|
||||
[:option {:value ""} "--"])
|
||||
[:option {:value ""} (tr "workspace.options.stroke-cap.none")]
|
||||
[:option {:value ":line-arrow"} (tr "workspace.options.stroke-cap.line-arrow")]
|
||||
[:option {:value ":triangle-arrow"} (tr "workspace.options.stroke-cap.triangle-arrow")]
|
||||
[:option {:value ":square-marker"} (tr "workspace.options.stroke-cap.square-marker")]
|
||||
[:option {:value ":circle-marker"} (tr "workspace.options.stroke-cap.circle-marker")]
|
||||
[:option {:value ":diamond-marker"} (tr "workspace.options.stroke-cap.diamond-marker")]
|
||||
[:option {:value ":round"} (tr "workspace.options.stroke-cap.round")]
|
||||
[:option {:value ":square"} (tr "workspace.options.stroke-cap.square")]]])]]
|
||||
[:div.cap-select {:tab-index 0
|
||||
:on-click (open-caps-select end-caps-state)}
|
||||
(value->name (:stroke-cap-end values))
|
||||
[:span.cap-select-button
|
||||
i/arrow-down]]
|
||||
[:& dropdown {:show (:open? @end-caps-state)
|
||||
:on-close (close-caps-select end-caps-state)}
|
||||
[:ul.dropdown.cap-select-dropdown {:style {:top (:top @end-caps-state)
|
||||
:left (:left @end-caps-state)}}
|
||||
(for [[value label separator] (stroke-cap-names)]
|
||||
(let [img (value->img value)]
|
||||
[:li {:class (dom/classnames :separator separator)
|
||||
:on-click #(on-stroke-cap-end-change value)}
|
||||
(when img [:img {:src (value->img value)}])
|
||||
label]))]]])]]
|
||||
|
||||
;; NO STROKE
|
||||
[:div.element-set
|
||||
|
|
|
@ -86,6 +86,12 @@
|
|||
[event]
|
||||
(.-target event))
|
||||
|
||||
(defn get-current-target
|
||||
"Extract the current target from event instance (different from target
|
||||
when event triggered in a child of the suscribing element)."
|
||||
[event]
|
||||
(.-currentTarget event))
|
||||
|
||||
(defn get-parent
|
||||
[dom]
|
||||
(.-parentElement ^js dom))
|
||||
|
|