diff --git a/frontend/playwright/ui/specs/colorpicker.spec.js b/frontend/playwright/ui/specs/colorpicker.spec.js index 75dad9c97f..344aa6c18a 100644 --- a/frontend/playwright/ui/specs/colorpicker.spec.js +++ b/frontend/playwright/ui/specs/colorpicker.spec.js @@ -236,7 +236,7 @@ test("Bug 10089 - Cannot change alpha", async ({ page }) => { const swatch = workspacePage.page.getByRole("button", { name: "#B1B2B5" }); await swatch.click(); - const alpha = workspacePage.page.getByLabel("A", { exact: true }); + const alpha = workspacePage.page.getByRole("spinbutton", { name: "Alpha" }); await expect(alpha).toHaveValue("100"); const alphaSlider = workspacePage.page.getByTestId("slider-opacity"); diff --git a/frontend/src/app/main/ui/ds/controls/numeric_input.scss b/frontend/src/app/main/ui/ds/controls/numeric_input.scss index 80b44a94f1..278ae2880b 100644 --- a/frontend/src/app/main/ui/ds/controls/numeric_input.scss +++ b/frontend/src/app/main/ui/ds/controls/numeric_input.scss @@ -43,10 +43,9 @@ } .text-icon { - color: var(--color-foreground-secondary); - @include t.use-typography("body-small"); + color: var(--color-foreground-secondary); inline-size: fit-content; min-inline-size: px2rem(46); padding-inline-start: var(--sp-xs); diff --git a/frontend/src/app/main/ui/ds/controls/utilities/input_field.cljs b/frontend/src/app/main/ui/ds/controls/utilities/input_field.cljs index c0ee6f245e..8a0fd74cc4 100644 --- a/frontend/src/app/main/ui/ds/controls/utilities/input_field.cljs +++ b/frontend/src/app/main/ui/ds/controls/utilities/input_field.cljs @@ -22,6 +22,7 @@ [:id :string] [:icon {:optional true} [:maybe [:and :string [:fn #(contains? icon-list %)]]]] + [:text-icon {:optional true} [:maybe :string]] [:has-hint {:optional true} :boolean] [:hint-type {:optional true} [:maybe [:enum "hint" "error" "warning"]]] [:type {:optional true} :string] @@ -33,7 +34,7 @@ (mf/defc input-field* {::mf/forward-ref true ::mf/schema schema:input-field} - [{:keys [id icon class type + [{:keys [id icon text-icon class type has-hint hint-type max-length variant slot-start slot-end @@ -86,6 +87,9 @@ :class (stl/css :icon) :size "s" :on-click on-icon-click}]) + (when (some? text-icon) + [:span {:class (stl/css :text-icon)} + text-icon]) (if aria-label [:> tooltip* {:content aria-label :trigger-ref (or ref input-ref) diff --git a/frontend/src/app/main/ui/ds/controls/utilities/input_field.scss b/frontend/src/app/main/ui/ds/controls/utilities/input_field.scss index adc4f5a301..e1e870d5c3 100644 --- a/frontend/src/app/main/ui/ds/controls/utilities/input_field.scss +++ b/frontend/src/app/main/ui/ds/controls/utilities/input_field.scss @@ -125,3 +125,12 @@ .tooltip-wrapper { inline-size: 100%; } + +.text-icon { + @include use-typography("body-small"); + + color: var(--color-foreground-secondary); + inline-size: fit-content; + min-inline-size: px2rem(46); + padding-inline-start: var(--sp-xs); +} diff --git a/frontend/src/app/main/ui/workspace/colorpicker/color_inputs.cljs b/frontend/src/app/main/ui/workspace/colorpicker/color_inputs.cljs index 69c466669c..bd9867c82f 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/color_inputs.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker/color_inputs.cljs @@ -10,6 +10,8 @@ [app.common.data :as d] [app.common.math :as mth] [app.common.types.color :as cc] + [app.main.ui.ds.controls.input :refer [input*]] + [app.main.ui.ds.foundations.assets.icon :as i] [app.util.dom :as dom] [app.util.keyboard :as kbd] [rumext.v2 :as mf])) @@ -194,123 +196,139 @@ (cond (= type :rgb) [:* - [:div {:class (stl/css :input-wrapper)} - [:label {:for "red-value" :class (stl/css :input-label)} "R"] - [:input {:id "red-value" - :ref (:r refs) - :type "number" - :min 0 - :max 255 - :default-value red - :on-change (on-change-property :r 255) - :on-key-down (on-key-down-property :r 255)}]] - [:div {:class (stl/css :input-wrapper)} - [:label {:for "green-value" :class (stl/css :input-label)} "G"] - [:input {:id "green-value" - :ref (:g refs) - :type "number" - :min 0 - :max 255 - :default-value green - :on-change (on-change-property :g 255) - :on-key-down (on-key-down-property :g 255)}]] - [:div {:class (stl/css :input-wrapper)} - [:label {:for "blue-value" :class (stl/css :input-label)} "B"] - [:input {:id "blue-value" - :ref (:b refs) - :type "number" - :min 0 - :max 255 - :default-value blue - :on-change (on-change-property :b 255) - :on-key-down (on-key-down-property :b 255)}]]] + [:> input* {:id "red-value" + :ref (:r refs) + :type "number" + :min 0 + :icon i/character-r + :property "Red" + :aria-label "Red" + :max 255 + :default-value red + :on-change (on-change-property :r 255) + :on-key-down (on-key-down-property :r 255)}] + [:> input* {:id "green-value" + :ref (:g refs) + :type "number" + :min 0 + :icon i/character-g + :property "Green" + :aria-label "Green" + :max 255 + :default-value green + :on-change (on-change-property :g 255) + :on-key-down (on-key-down-property :g 255)}] + [:> input* {:id "blue-value" + :ref (:b refs) + :type "number" + :min 0 + :icon i/character-b + :property "Blue" + :aria-label "Blue" + :max 255 + :default-value blue + :on-change (on-change-property :b 255) + :on-key-down (on-key-down-property :b 255)}]] (= hsb-mode :hsl) [:* - [:div {:class (stl/css :input-wrapper)} - [:label {:for "hue-value" :class (stl/css :input-label)} "H"] - [:input {:id "hue-value" - :ref (:h refs) - :type "number" - :min 0 - :max 360 - :default-value hue - :on-change (on-change-property :h 360) - :on-key-down (on-key-down-property :h 360)}]] - [:div {:class (stl/css :input-wrapper)} - [:label {:for "hsl-saturation-value" :class (stl/css :input-label)} "S"] - [:input {:id "hsl-saturation-value" - :ref (:hsl-s refs) - :type "number" - :min 0 - :max 100 - :step 1 - :default-value (mth/precision (* hsl-s 100) 2) - :on-change (on-change-property :hsl-s 100) - :on-key-down (on-key-down-property :hsl-s 100)}]] - [:div {:class (stl/css :input-wrapper)} - [:label {:for "lightness-value" :class (stl/css :input-label)} "L"] - [:input {:id "lightness-value" - :ref (:hsl-l refs) - :type "number" - :min 0 - :max 100 - :step 1 - :default-value (mth/precision (* hsl-l 100) 2) - :on-change (on-change-property :hsl-l 100) - :on-key-down (on-key-down-property :hsl-l 100)}]]] - + [:> input* {:id "hue-value" + :ref (:h refs) + :type "number" + :min 0 + :icon i/character-h + :property "Hue" + :aria-label "Hue" + :max 360 + :default-value hue + :on-change (on-change-property :h 360) + :on-key-down (on-key-down-property :h 360)}] + [:> input* {:id "saturation-value" + :ref (:s refs) + :type "number" + :min 0 + :icon i/character-s + :property "Saturation" + :aria-label "Saturation" + :max 100 + :step 1 + :default-value saturation + :on-change (on-change-property :s 100) + :on-key-down (on-key-down-property :s 100)}] + [:> input* {:id "lightness-value" + :ref (:hsl-l refs) + :type "number" + :min 0 + :icon i/character-l + :property "Lightness" + :aria-label "Lightness" + :max 100 + :step 1 + :default-value (mth/precision (* hsl-l 100) 2) + :on-change (on-change-property :hsl-l 100) + :on-key-down (on-key-down-property :hsl-l 100)}]] :else [:* - [:div {:class (stl/css :input-wrapper)} - [:label {:for "hue-value" :class (stl/css :input-label)} "H"] - [:input {:id "hue-value" - :ref (:h refs) - :type "number" - :min 0 - :max 360 - :default-value hue - :on-change (on-change-property :h 360) - :on-key-down (on-key-down-property :h 360)}]] - [:div {:class (stl/css :input-wrapper)} - [:label {:for "saturation-value" :class (stl/css :input-label)} "S"] - [:input {:id "saturation-value" - :ref (:s refs) - :type "number" - :min 0 - :max 100 - :step 1 - :default-value saturation - :on-change (on-change-property :s 100) - :on-key-down (on-key-down-property :s 100)}]] - [:div {:class (stl/css :input-wrapper)} - [:label {:for "brightness-value" :class (stl/css :input-label)} "B(V)"] - [:input {:id "brightness-value" - :ref (:v refs) - :type "number" - :min 0 - :max 100 - :default-value value - :on-change (on-change-property :v 100) - :on-key-down (on-key-down-property :v 100)}]]])] + [:> input* {:id "hue-value" + :ref (:h refs) + :type "number" + :min 0 + :icon i/character-h + :property "Hue" + :aria-label "Hue" + :max 360 + :default-value hue + :on-change (on-change-property :h 360) + :on-key-down (on-key-down-property :h 360)}] + [:> input* {:id "saturation-value" + :ref (:s refs) + :type "number" + :min 0 + :icon i/character-s + :property "Saturation" + :max 100 + :step 1 + :aria-label "Saturation" + :default-value saturation + :on-change (on-change-property :s 100) + :on-key-down (on-key-down-property :s 100)}] + + [:> input* {:id "brightness-value" + :ref (:v refs) + :type "number" + :min 0 + :text-icon "B(V)" + :property "Brightness" + :aria-label "Brightness (Value)" + :max 100 + :step 1 + :default-value value + :on-change (on-change-property :v 100) + :on-key-down (on-key-down-property :v 100)}]])] [:div {:class (stl/css :hex-alpha-wrapper)} - [:div {:class (stl/css-case :input-wrapper true - :hex true)} - [:label {:for "hex-value" :class (stl/css :input-label)} "HEX"] - [:input {:id "hex-value" - :ref (:hex refs) - :default-value hex - :on-change on-change-hex - :on-blur on-blur-hex}]] + [:> input* {:id "hex-value" + :class (stl/css :hex-input) + :ref (:hex refs) + :type "text" + :text-icon "HEX" + :property "Hex" + :aria-label "Hexadecimal color value" + :max 7 + :default-value hex + :on-change on-change-hex + :on-blur on-blur-hex}] + (when (not disable-opacity) - [:div {:class (stl/css-case :input-wrapper true)} - [:label {:for "alpha-value" :class (stl/css :input-label)} "A"] - [:input {:id "alpha-value" - :ref (:alpha refs) - :type "number" - :min 0 - :step 1 - :max 100 - :default-value (if (= alpha :multiple) "" alpha) - :on-change (on-change-property :alpha 100) - :on-key-down (on-key-down-property :alpha 100)}]])]])) + [:> input* {:id "alpha-value" + :ref (:alpha refs) + :type "number" + :class (stl/css :alpha-input) + :min 0 + :icon i/character-a + :property "Alpha" + :max 100 + :step 1 + :aria-label "Alpha" + :default-value (if (= alpha :multiple) "" (mth/precision (* alpha 100) 2)) + :on-change (on-change-property :alpha 100) + :on-key-down (on-key-down-property :alpha 100)}])]])) diff --git a/frontend/src/app/main/ui/workspace/colorpicker/color_inputs.scss b/frontend/src/app/main/ui/workspace/colorpicker/color_inputs.scss index 3a3034bc95..ed2a357b8f 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/color_inputs.scss +++ b/frontend/src/app/main/ui/workspace/colorpicker/color_inputs.scss @@ -5,74 +5,66 @@ // Copyright (c) KALEIDOS INC @use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/spacing.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/typography.scss" as t; +@use "ds/_utils.scss" as *; .color-values { - @include deprecated.flex-column; - - margin-top: deprecated.$s-8; - - .model-switcher { - display: flex; - gap: deprecated.$s-4; - margin-bottom: deprecated.$s-8; - padding: deprecated.$s-2; - background-color: var(--color-background-tertiary); - border-radius: deprecated.$s-6; - align-self: flex-start; - - .model-pill { - @include deprecated.body-small-typography; - - padding: deprecated.$s-2 deprecated.$s-8; - border: none; - border-radius: deprecated.$s-4; - background: transparent; - color: var(--color-foreground-secondary); - cursor: pointer; - - &:hover { - color: var(--color-foreground-primary); - } - - &.model-pill-active { - background-color: var(--color-background-primary); - color: var(--color-accent-primary); - } - } - } + display: flex; + flex-direction: column; + gap: var(--sp-xs); + padding: 0 1px; + margin-block-start: var(--sp-s); &.disable-opacity { - grid-template-columns: 3.5rem repeat(3, 1fr); - } - - .colors-row { - @include deprecated.flex-row; - - .input-wrapper { - @extend %input-element; - @include deprecated.body-small-typography; - - width: deprecated.$s-84; - display: flex; - align-items: baseline; - } - } - - .hex-alpha-wrapper { - @include deprecated.flex-row; - - .input-wrapper { - @extend %input-element; - @include deprecated.body-small-typography; - - width: deprecated.$s-84; - - &.hex { - width: deprecated.$s-172; - display: flex; - align-items: baseline; - gap: deprecated.$s-8; - } - } + grid-template-columns: px2rem(56) repeat(3, 1fr); } } + +.model-switcher { + display: flex; + align-self: flex-start; + gap: var(--sp-xs); + padding: var(--sp-xxs); + margin-block-end: var(--sp-s); + border-radius: $br-6; + background-color: var(--color-background-tertiary); +} + +.model-pill { + @include t.use-typography("body-small"); + + padding: var(--sp-xxs) var(--sp-s); + border: none; + border-radius: $br-4; + color: var(--color-foreground-secondary); + background: transparent; + cursor: pointer; + + &:hover { + color: var(--color-foreground-primary); + } +} + +.model-pill-active { + background-color: var(--color-background-primary); + color: var(--color-accent-primary); +} + +.colors-row { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + gap: var(--sp-xs); +} + +.hex-alpha-wrapper { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + gap: var(--sp-xs); +} + +.hex-input { + grid-column: span 2; +}