From 6dffc7dfcdc0c4f67f5526c822a8acae8fafc200 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 8 Oct 2026 09:54:15 +0200 Subject: [PATCH] :sparkles: Change iconography to reflect orientation --- .../images/icons/text-align-bottom.svg | 5 + .../images/icons/text-align-middle.svg | 5 + .../resources/images/icons/text-align-top.svg | 5 + .../icons/text-auto-height-vertical.svg | 5 + .../images/icons/text-auto-width-vertical.svg | 3 + .../images/icons/text-horizontal-center.svg | 5 + .../images/icons/text-justify-vertical.svg | 5 + frontend/resources/images/icons/text-left.svg | 5 + .../resources/images/icons/text-right.svg | 5 + .../main/ui/ds/foundations/assets/icon.cljs | 9 ++ .../workspace/sidebar/options/menus/text.cljs | 87 ++++++++----- .../options/menus/text_japanese_layout.cljs | 1 + .../frontend_tests/ui/text_options_test.cljs | 114 ++++++++++++++++++ 13 files changed, 222 insertions(+), 32 deletions(-) create mode 100644 frontend/resources/images/icons/text-align-bottom.svg create mode 100644 frontend/resources/images/icons/text-align-middle.svg create mode 100644 frontend/resources/images/icons/text-align-top.svg create mode 100644 frontend/resources/images/icons/text-auto-height-vertical.svg create mode 100644 frontend/resources/images/icons/text-auto-width-vertical.svg create mode 100644 frontend/resources/images/icons/text-horizontal-center.svg create mode 100644 frontend/resources/images/icons/text-justify-vertical.svg create mode 100644 frontend/resources/images/icons/text-left.svg create mode 100644 frontend/resources/images/icons/text-right.svg diff --git a/frontend/resources/images/icons/text-align-bottom.svg b/frontend/resources/images/icons/text-align-bottom.svg new file mode 100644 index 0000000000..590e7a053f --- /dev/null +++ b/frontend/resources/images/icons/text-align-bottom.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/resources/images/icons/text-align-middle.svg b/frontend/resources/images/icons/text-align-middle.svg new file mode 100644 index 0000000000..9480d4a6b0 --- /dev/null +++ b/frontend/resources/images/icons/text-align-middle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/resources/images/icons/text-align-top.svg b/frontend/resources/images/icons/text-align-top.svg new file mode 100644 index 0000000000..c3179a781a --- /dev/null +++ b/frontend/resources/images/icons/text-align-top.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/resources/images/icons/text-auto-height-vertical.svg b/frontend/resources/images/icons/text-auto-height-vertical.svg new file mode 100644 index 0000000000..8de6bd7048 --- /dev/null +++ b/frontend/resources/images/icons/text-auto-height-vertical.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/resources/images/icons/text-auto-width-vertical.svg b/frontend/resources/images/icons/text-auto-width-vertical.svg new file mode 100644 index 0000000000..51a0e12e80 --- /dev/null +++ b/frontend/resources/images/icons/text-auto-width-vertical.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/images/icons/text-horizontal-center.svg b/frontend/resources/images/icons/text-horizontal-center.svg new file mode 100644 index 0000000000..782b8d6592 --- /dev/null +++ b/frontend/resources/images/icons/text-horizontal-center.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/resources/images/icons/text-justify-vertical.svg b/frontend/resources/images/icons/text-justify-vertical.svg new file mode 100644 index 0000000000..2ddbbef9c3 --- /dev/null +++ b/frontend/resources/images/icons/text-justify-vertical.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/resources/images/icons/text-left.svg b/frontend/resources/images/icons/text-left.svg new file mode 100644 index 0000000000..f489023b88 --- /dev/null +++ b/frontend/resources/images/icons/text-left.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/resources/images/icons/text-right.svg b/frontend/resources/images/icons/text-right.svg new file mode 100644 index 0000000000..a7f77b7b31 --- /dev/null +++ b/frontend/resources/images/icons/text-right.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/app/main/ui/ds/foundations/assets/icon.cljs b/frontend/src/app/main/ui/ds/foundations/assets/icon.cljs index 1278e80fd3..4c140b6df9 100644 --- a/frontend/src/app/main/ui/ds/foundations/assets/icon.cljs +++ b/frontend/src/app/main/ui/ds/foundations/assets/icon.cljs @@ -270,11 +270,16 @@ (def ^:icon-id swatches "swatches") (def ^:icon-id switch "switch") (def ^:icon-id text "text") +(def ^:icon-id text-align-bottom "text-align-bottom") (def ^:icon-id text-align-center "text-align-center") (def ^:icon-id text-align-left "text-align-left") +(def ^:icon-id text-align-middle "text-align-middle") (def ^:icon-id text-align-right "text-align-right") +(def ^:icon-id text-align-top "text-align-top") (def ^:icon-id text-auto-height "text-auto-height") +(def ^:icon-id text-auto-height-vertical "text-auto-height-vertical") (def ^:icon-id text-auto-width "text-auto-width") +(def ^:icon-id text-auto-width-vertical "text-auto-width-vertical") (def ^:icon-id text-bottom "text-bottom") (def ^:icon-id text-combine-upright-all "text-combine-upright-all") (def ^:icon-id text-combine-upright-digits "text-combine-upright-digits") @@ -283,7 +288,10 @@ (def ^:icon-id text-font-family "text-font-family") (def ^:icon-id text-font-size "text-font-size") (def ^:icon-id text-font-weight "text-font-weight") +(def ^:icon-id text-horizontal-center "text-horizontal-center") (def ^:icon-id text-justify "text-justify") +(def ^:icon-id text-justify-vertical "text-justify-vertical") +(def ^:icon-id text-left "text-left") (def ^:icon-id text-letterspacing "text-letterspacing") (def ^:icon-id text-lineheight "text-lineheight") (def ^:icon-id text-lowercase "text-lowercase") @@ -294,6 +302,7 @@ (def ^:icon-id text-orientation-upright "text-orientation-upright") (def ^:icon-id text-palette "text-palette") (def ^:icon-id text-paragraph "text-paragraph") +(def ^:icon-id text-right "text-right") (def ^:icon-id text-rtl "text-rtl") (def ^:icon-id text-stroked "text-stroked") (def ^:icon-id text-top "text-top") diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs index b571baed15..ffbf56784d 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs @@ -40,6 +40,7 @@ [app.util.object :as obj] [app.util.text.content :as content] [app.util.text.ui :as txu] + [app.util.text.writing-mode :as wm] [app.util.timers :as ts] [beicon.v2.core :as rx] [potok.v2.core :as ptk] @@ -56,24 +57,32 @@ (mf/defc text-align-options* [{:keys [values on-change on-blur]}] - (let [options - (mf/with-memo [] + (let [vertical? (and (wm/vertical-layout-active?) + (tjl/vertical-japanese-layout? values)) + options + (mf/with-memo [vertical?] [{:value "left" :id "text-align-left" - :label (tr "workspace.options.text-options.text-align-left") - :icon i/text-align-left} + :label (if vertical? + (tr "workspace.options.text-options.align-top") + (tr "workspace.options.text-options.text-align-left")) + :icon (if vertical? i/text-align-top i/text-align-left)} {:value "center" :id "text-align-center" - :label (tr "workspace.options.text-options.text-align-center") - :icon i/text-align-center} + :label (if vertical? + (tr "workspace.options.text-options.align-middle") + (tr "workspace.options.text-options.text-align-center")) + :icon (if vertical? i/text-align-middle i/text-align-center)} {:value "right" :id "text-align-right" - :label (tr "workspace.options.text-options.text-align-right") - :icon i/text-align-right} + :label (if vertical? + (tr "workspace.options.text-options.align-bottom") + (tr "workspace.options.text-options.text-align-right")) + :icon (if vertical? i/text-align-bottom i/text-align-right)} {:value "justify" :id "text-align-justify" :label (tr "workspace.options.text-options.text-align-justify") - :icon i/text-justify}]) + :icon (if vertical? i/text-justify-vertical i/text-justify)}]) handle-change (mf/use-fn @@ -90,7 +99,9 @@ (mf/defc text-direction-options* [{:keys [values on-change on-blur]}] - (let [direction (radio-selected (:text-direction values)) + (let [vertical? (and (wm/vertical-layout-active?) + (tjl/vertical-japanese-layout? values)) + direction (radio-selected (:text-direction values)) options (mf/with-memo [] [{:value "ltr" @@ -109,29 +120,38 @@ (on-change {:text-direction (if (= value direction) "none" value)}) (when (some? on-blur) (on-blur))))] - [:div {:class (stl/css :text-direction-options)} - [:> radio-buttons* {:selected direction - :on-change handle-change - :name "text-direction-options" - :options options}]])) + (when-not vertical? + [:div {:class (stl/css :text-direction-options)} + [:> radio-buttons* {:selected direction + :on-change handle-change + :name "text-direction-options" + :options options}]]))) (mf/defc vertical-align* [{:keys [values on-change on-blur]}] - (let [vertical-align (radio-selected (:vertical-align values) "top") + (let [vertical? (and (wm/vertical-layout-active?) + (tjl/vertical-japanese-layout? values)) + vertical-align (radio-selected (:vertical-align values) "top") options - (mf/with-memo [] + (mf/with-memo [vertical?] [{:value "top" :id "vertical-text-align-top" - :label (tr "workspace.options.text-options.align-top") - :icon i/text-top} + :label (if vertical? + (tr "workspace.options.text-options.text-align-right") + (tr "workspace.options.text-options.align-top")) + :icon (if vertical? i/text-right i/text-top)} {:value "center" :id "vertical-text-align-center" - :label (tr "workspace.options.text-options.align-middle") - :icon i/text-middle} + :label (if vertical? + (tr "workspace.options.text-options.text-align-center") + (tr "workspace.options.text-options.align-middle")) + :icon (if vertical? i/text-horizontal-center i/text-middle)} {:value "bottom" :id "vertical-text-align-bottom" - :label (tr "workspace.options.text-options.align-bottom") - :icon i/text-bottom}]) + :label (if vertical? + (tr "workspace.options.text-options.text-align-left") + (tr "workspace.options.text-options.align-bottom")) + :icon (if vertical? i/text-left i/text-bottom)}]) handle-change (mf/use-fn @@ -148,23 +168,29 @@ (mf/defc grow-options* [{:keys [ids values on-blur]}] - (let [grow-type (:grow-type values) + (let [vertical? (and (wm/vertical-layout-active?) + (tjl/vertical-japanese-layout? values)) + grow-type (:grow-type values) selected (radio-selected grow-type) editor-instance (mf/deref refs/workspace-editor) options - (mf/with-memo [] + (mf/with-memo [vertical?] [{:value "fixed" :id "text-fixed-grow" :label (tr "workspace.options.text-options.grow-fixed") :icon i/text-fixed} {:value "auto-width" :id "text-auto-width-grow" - :label (tr "workspace.options.text-options.grow-auto-width") - :icon i/text-auto-width} + :label (if vertical? + (tr "workspace.options.text-options.grow-auto-height") + (tr "workspace.options.text-options.grow-auto-width")) + :icon (if vertical? i/text-auto-width-vertical i/text-auto-width)} {:value "auto-height" :id "text-auto-height-grow" - :label (tr "workspace.options.text-options.grow-auto-height") - :icon i/text-auto-height}]) + :label (if vertical? + (tr "workspace.options.text-options.grow-auto-width") + (tr "workspace.options.text-options.grow-auto-height")) + :icon (if vertical? i/text-auto-height-vertical i/text-auto-height)}]) handle-change (mf/use-fn @@ -651,9 +677,6 @@ :on-click toggle-more-options :icon i/menu}]] - (when japanese-layout-config-enabled? - [:> tjl/line-adjustment-option* common-props]) - (when more-options-open? [:div {:class (stl/css :text-decoration-options)} [:> vertical-align* common-props] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/text_japanese_layout.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/text_japanese_layout.cljs index 8714f09158..441fa3a41e 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/text_japanese_layout.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/text_japanese_layout.cljs @@ -506,6 +506,7 @@ (when ^boolean vertical? [:> text-combine-upright-count-options* common-props]) [:> font-features-options* common-props] + [:> line-adjustment-option* common-props] ;; Emphasis marks (圏点 / bouten) apply to the selected characters. (when ^boolean text-selection-active [:> span-select-option* (mf/spread-props common-props diff --git a/frontend/test/frontend_tests/ui/text_options_test.cljs b/frontend/test/frontend_tests/ui/text_options_test.cljs index e0999e870b..e94f233f85 100644 --- a/frontend/test/frontend_tests/ui/text_options_test.cljs +++ b/frontend/test/frontend_tests/ui/text_options_test.cljs @@ -10,9 +10,11 @@ [app.common.types.text.cjk-layout :as cjk] [app.main.data.workspace.texts :as dwt] [app.main.ui.ds.controls.select :as select] + [app.main.ui.hooks :as hooks] [app.main.ui.shapes.text.html-text :as html-text] [app.main.ui.shapes.text.styles :as text-styles] [app.main.ui.workspace.sidebar.options.common :refer [radio-selected]] + [app.main.ui.workspace.sidebar.options.menus.text :as text-menu] [app.main.ui.workspace.sidebar.options.menus.text-japanese-layout :as tjl] [app.util.text.writing-mode :as wm] [cljs.test :as t :include-macros true] @@ -246,3 +248,115 @@ (t/is (not (str/includes? (markup) "vertical-rl")))) (with-redefs [wm/vertical-layout-active? (constantly true)] (t/is (str/includes? (markup) "writing-mode:vertical-rl"))))) + +(defn- alignment-markup + ([component writing-mode wasm-enabled] + (alignment-markup component writing-mode wasm-enabled {})) + ([component writing-mode wasm-enabled values] + ;; Server rendering has no DOM container for tooltip portals. + (with-redefs [wm/vertical-layout-active? (constantly wasm-enabled) + hooks/use-portal-container (fn ([] nil) ([_] nil))] + (rds/renderToStaticMarkup + (mf/element component #js {:values (assoc values :writing-mode writing-mode) + :on-change identity}))))) + +(defn- alignment-labels + [markup] + (mapv second (re-seq #"aria-label=\"([^\"]+)\"" markup))) + +(defn- alignment-icons + [markup] + (mapv second (re-seq #"href=\"#icon-([^\"]+)\"" markup))) + +(t/deftest vertical-paragraph-alignment-shows-top-middle-bottom-and-vertical-justify + (let [markup (alignment-markup text-menu/text-align-options* "vertical-rl" true)] + (t/is (= ["text-align-top" "text-align-middle" "text-align-bottom" "text-justify-vertical"] + (alignment-icons markup))) + (t/is (= ["workspace.options.text-options.align-top" + "workspace.options.text-options.align-middle" + "workspace.options.text-options.align-bottom" + "workspace.options.text-options.text-align-justify"] + (alignment-labels markup))) + (t/is (= ["left" "center" "right" "justify"] + (mapv second (re-seq #"value=\"([^\"]+)\"" markup)))))) + +(t/deftest vertical-column-block-alignment-shows-right-center-and-left + (let [markup (alignment-markup text-menu/vertical-align* "vertical-rl" true)] + (t/is (= ["text-right" "text-horizontal-center" "text-left"] + (alignment-icons markup))) + (t/is (= ["workspace.options.text-options.text-align-right" + "workspace.options.text-options.text-align-center" + "workspace.options.text-options.text-align-left"] + (alignment-labels markup))) + (t/is (= ["top" "center" "bottom"] + (mapv second (re-seq #"value=\"([^\"]+)\"" markup)))))) + +(t/deftest horizontal-and-mixed-writing-modes-use-horizontal-alignment-controls + (doseq [writing-mode [nil "horizontal-tb" :multiple]] + (let [paragraph (alignment-markup text-menu/text-align-options* writing-mode true) + block (alignment-markup text-menu/vertical-align* writing-mode true)] + (t/is (= ["text-align-left" "text-align-center" "text-align-right" "text-justify"] + (alignment-icons paragraph))) + (t/is (= ["text-top" "text-middle" "text-bottom"] (alignment-icons block)))))) + +(t/deftest svg-renderer-keeps-horizontal-alignment-for-stored-vertical-text + (let [paragraph (alignment-markup text-menu/text-align-options* "vertical-rl" false) + block (alignment-markup text-menu/vertical-align* "vertical-rl" false)] + (t/is (= ["text-align-left" "text-align-center" "text-align-right" "text-justify"] + (alignment-icons paragraph))) + (t/is (= ["text-top" "text-middle" "text-bottom"] (alignment-icons block))) + (t/is (= "workspace.options.text-options.text-align-left" (first (alignment-labels paragraph)))) + (t/is (= "workspace.options.text-options.align-top" (first (alignment-labels block)))))) + + +(t/deftest vertical-text-growth-controls-show-the-physical-growth-direction + (let [markup (alignment-markup text-menu/grow-options* "vertical-rl" true)] + (t/is (= ["text-fixed" "text-auto-width-vertical" "text-auto-height-vertical"] + (alignment-icons markup))) + (t/is (= ["workspace.options.text-options.grow-fixed" + "workspace.options.text-options.grow-auto-height" + "workspace.options.text-options.grow-auto-width"] + (alignment-labels markup))) + (t/is (= ["fixed" "auto-width" "auto-height"] + (mapv second (re-seq #"value=\"([^\"]+)\"" markup)))))) + +(t/deftest horizontal-and-svg-text-growth-controls-keep-horizontal-directions + (doseq [[writing-mode wasm-enabled] [[nil true] + ["horizontal-tb" true] + [:multiple true] + ["vertical-rl" false]]] + (let [markup (alignment-markup text-menu/grow-options* writing-mode wasm-enabled)] + (t/is (= ["text-fixed" "text-auto-width" "text-auto-height"] + (alignment-icons markup))) + (t/is (= ["workspace.options.text-options.grow-fixed" + "workspace.options.text-options.grow-auto-width" + "workspace.options.text-options.grow-auto-height"] + (alignment-labels markup)))))) + + +(t/deftest japanese-layout-controls-include-line-adjustment + (let [markup (alignment-markup tjl/japanese-layout-options* "vertical-rl" true)] + (t/is (str/includes? markup "workspace.options.text-options.line-adjustment")))) + + +(t/deftest vertical-text-hides-ltr-and-rtl-controls + (t/is (empty? (alignment-markup text-menu/text-direction-options* "vertical-rl" true)))) + +(t/deftest horizontal-and-svg-text-show-ltr-and-rtl-controls + (doseq [[writing-mode wasm-enabled] [[nil true] + ["horizontal-tb" true] + [:multiple true] + ["vertical-rl" false]]] + (let [markup (alignment-markup text-menu/text-direction-options* writing-mode wasm-enabled)] + (t/is (= ["text-ltr" "text-rtl"] (alignment-icons markup)))))) + +(t/deftest hiding-direction-controls-preserves-the-stored-direction + (let [values {:text-direction "rtl"} + render #(alignment-markup text-menu/text-direction-options* % true values)] + (t/is (str/includes? (re-find #"]*id=\"rtl-text-direction\"[^>]*>" + (render "horizontal-tb")) + "checked=\"\"")) + (t/is (empty? (render "vertical-rl"))) + (t/is (str/includes? (re-find #"]*id=\"rtl-text-direction\"[^>]*>" + (render "horizontal-tb")) + "checked=\"\""))))