diff --git a/frontend/resources/images/cap-circle-marker.svg b/frontend/resources/images/cap-circle-marker.svg
new file mode 100644
index 0000000000..b41388e506
--- /dev/null
+++ b/frontend/resources/images/cap-circle-marker.svg
@@ -0,0 +1 @@
+
diff --git a/frontend/resources/images/cap-diamond-marker.svg b/frontend/resources/images/cap-diamond-marker.svg
new file mode 100644
index 0000000000..0e23183406
--- /dev/null
+++ b/frontend/resources/images/cap-diamond-marker.svg
@@ -0,0 +1 @@
+
diff --git a/frontend/resources/images/cap-line-arrow.svg b/frontend/resources/images/cap-line-arrow.svg
new file mode 100644
index 0000000000..0df0673ba7
--- /dev/null
+++ b/frontend/resources/images/cap-line-arrow.svg
@@ -0,0 +1 @@
+
diff --git a/frontend/resources/images/cap-round.svg b/frontend/resources/images/cap-round.svg
new file mode 100644
index 0000000000..594e02575f
--- /dev/null
+++ b/frontend/resources/images/cap-round.svg
@@ -0,0 +1 @@
+
diff --git a/frontend/resources/images/cap-square-marker.svg b/frontend/resources/images/cap-square-marker.svg
new file mode 100644
index 0000000000..2340ce571b
--- /dev/null
+++ b/frontend/resources/images/cap-square-marker.svg
@@ -0,0 +1 @@
+
diff --git a/frontend/resources/images/cap-square.svg b/frontend/resources/images/cap-square.svg
new file mode 100644
index 0000000000..a2e3b6260b
--- /dev/null
+++ b/frontend/resources/images/cap-square.svg
@@ -0,0 +1 @@
+
diff --git a/frontend/resources/images/cap-triangle-arrow.svg b/frontend/resources/images/cap-triangle-arrow.svg
new file mode 100644
index 0000000000..f294d01cf5
--- /dev/null
+++ b/frontend/resources/images/cap-triangle-arrow.svg
@@ -0,0 +1 @@
+
diff --git a/frontend/resources/styles/main/partials/sidebar-element-options.scss b/frontend/resources/styles/main/partials/sidebar-element-options.scss
index 5ddf325c03..0380d9303f 100644
--- a/frontend/resources/styles/main/partials/sidebar-element-options.scss
+++ b/frontend/resources/styles/main/partials/sidebar-element-options.scss
@@ -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;
+ }
}
diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs
index 39c996bc17..543d55f526 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs
+++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs
@@ -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
diff --git a/frontend/src/app/util/dom.cljs b/frontend/src/app/util/dom.cljs
index 9cf48d13de..91ff3ab2e2 100644
--- a/frontend/src/app/util/dom.cljs
+++ b/frontend/src/app/util/dom.cljs
@@ -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))