diff --git a/frontend/src/app/main/ui/ds/utilities/swatch.cljs b/frontend/src/app/main/ui/ds/utilities/swatch.cljs
index bfce203c8..d8cbd9657 100644
--- a/frontend/src/app/main/ui/ds/utilities/swatch.cljs
+++ b/frontend/src/app/main/ui/ds/utilities/swatch.cljs
@@ -7,70 +7,77 @@
(ns app.main.ui.ds.utilities.swatch
(:require-macros
+
[app.main.style :as stl])
+
(:require
+
[app.common.data.macros :as dm]
+ [app.common.json :as json]
+ [app.common.schema :as sm]
+ [app.common.types.color :as ct]
+ [app.config :as cfg]
+ [app.util.color :as uc]
+ [app.util.i18n :refer [tr]]
[cuerdas.core :as str]
[rumext.v2 :as mf]))
(def ^:private schema:swatch
- [:map
- [:background :string]
+ [:map {:title "SchemaSwatch"}
+ [:background {:optional true} ct/schema:color]
[:class {:optional true} :string]
- [:format {:optional true} [:enum "square" "rounded"]]
[:size {:optional true} [:enum "small" "medium"]]
[:active {:optional true} :boolean]
[:on-click {:optional true} fn?]])
-(def hex-regex #"^#(?:[0-9a-fA-F]{3}){1,2}$")
-(def rgb-regex #"^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$")
-(def hsl-regex #"^hsl\((\d{1,3}),\s*(\d{1,3})%,\s*(\d{1,3})%\)$")
-(def hsla-regex #"^hsla\((\d{1,3}),\s*(\d{1,3})%,\s*(\d{1,3})%,\s*(0|1|0?\.\d+)\)$")
-(def rgba-regex #"^rgba\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*(0|1|0?\.\d+)\)$")
+(defn- color-title
+ [color-item]
+ (let [name (:name color-item)
+ path (:path color-item)
+ path-and-name (if path (str path " / " name) name)
+ gradient (:gradient color-item)
+ image (:image color-item)
+ color (:color color-item)]
-(defn- gradient? [background]
- (or
- (str/starts-with? background "linear-gradient")
- (str/starts-with? background "radial-gradient")))
+ (if (some? name)
+ (cond
+ (some? color)
+ (str/ffmt "% (%)" path-and-name color)
-(defn- color-solid? [background]
- (boolean
- (or (re-matches hex-regex background)
- (or (re-matches hsl-regex background)
- (re-matches rgb-regex background)))))
+ (some? gradient)
+ (str/ffmt "% (%)" path-and-name (uc/gradient-type->string (:type gradient)))
-(defn- color-opacity? [background]
- (boolean
- (or (re-matches hsla-regex background)
- (re-matches rgba-regex background))))
+ (some? image)
+ (str/ffmt "% (%)" path-and-name (tr "media.image"))
-(defn- extract-color-and-opacity [background]
- (cond
- (re-matches rgba-regex background)
- (let [[_ r g b a] (re-matches rgba-regex background)]
- {:color (dm/str "rgb(" r ", " g ", " b ")")
- :opacity (js/parseFloat a)})
+ :else
+ path-and-name)
- (re-matches hsla-regex background)
- (let [[_ h s l a] (re-matches hsla-regex background)]
- {:color (dm/str "hsl(" h ", " s "%, " l "%)")
- :opacity (js/parseFloat a)})
+ (cond
+ (some? color)
+ color
- :else
- {:color background
- :opacity 1.0}))
+ (some? gradient)
+ (uc/gradient-type->string (:type gradient))
+
+ (some? image)
+ (tr "media.image")))))
(mf/defc swatch*
{::mf/props :obj
- ::mf/schema schema:swatch}
- [{:keys [background on-click format size active class]
+ ::mf/schema (sm/schema schema:swatch)}
+ [{:keys [background on-click size active class]
:rest props}]
- (let [element-type (if on-click "button" "div")
- button-type (if on-click "button" nil)
- format (or format "square")
+ (let [background (if (object? background) (json/->clj background) background)
+ read-only? (nil? on-click)
+ id? (some? (:id background))
+ element-type (if read-only? "div" "button")
+ button-type (if (not read-only?) "button" nil)
size (or size "small")
active (or active false)
- {:keys [color opacity]} (extract-color-and-opacity background)
+ gradient (:gradient background)
+ image (:image background)
+ format (if id? "rounded" "square")
class (dm/str class " " (stl/css-case
:swatch true
:small (= size "small")
@@ -79,25 +86,26 @@
:active (= active true)
:interactive (= element-type "button")
:rounded (= format "rounded")))
- props (mf/spread-props props {:class class :on-click on-click :type button-type})]
+ props (mf/spread-props props {:class class
+ :on-click on-click
+ :type button-type
+ :title (color-title background)})]
[:> element-type props
(cond
- (color-solid? background)
- [:span {:class (stl/css :swatch-solid)
- :style {:background background}}]
- (color-opacity? background)
- [:span {:class (stl/css :swatch-opacity)}
- [:span {:class (stl/css :swatch-solid-side)
- :style {:background color}}]
- [:span {:class (stl/css :swatch-opacity-side)
- :style {:background color :opacity opacity}}]]
-
- (gradient? background)
+ (some? gradient)
[:span {:class (stl/css :swatch-gradient)
- :style {:background-image (str background ", repeating-conic-gradient(lightgray 0% 25%, white 0% 50%)")}}]
+ :style {:background-image (str gradient ", repeating-conic-gradient(lightgray 0% 25%, white 0% 50%)")}}]
+
+ (some? image)
+ (let [uri (cfg/resolve-file-media image)]
+ [:span {:class (stl/css :swatch-image)
+ :style {:background-image (str/ffmt "url(%)" uri)}}])
:else
- [:span {:class (stl/css :swatch-image)
- :style {:background-image (str "url('" background "'), repeating-conic-gradient(lightgray 0% 25%, white 0% 50%)")}}])]))
+ [:span {:class (stl/css :swatch-opacity)}
+ [:span {:class (stl/css :swatch-solid-side)
+ :style {:background (uc/color->background (assoc background :opacity 1))}}]
+ [:span {:class (stl/css :swatch-opacity-side)
+ :style {:background (uc/color->background background)}}]])]))
diff --git a/frontend/src/app/main/ui/ds/utilities/swatch.mdx b/frontend/src/app/main/ui/ds/utilities/swatch.mdx
index a091a4e32..bef929f07 100644
--- a/frontend/src/app/main/ui/ds/utilities/swatch.mdx
+++ b/frontend/src/app/main/ui/ds/utilities/swatch.mdx
@@ -7,56 +7,47 @@ import * as SwatchStories from "./swatch.stories";
Swatches are elements that display a color, gradient or image. They can sometimes trigger an action.
+## Background Property
+
+A swatch component can receive several props. The `background` prop is the most important and must be an object. Depending on the value of the background property we will get different variants of the component.
+
## Variants
-**Color** (`"color"`), displays a solid color. It can take a hexadecimal, an rgb or an rgba.
+If the background prop has a hex `color` value it will display a full swatch with a solid color
-**WithOpacity** (`"color"`), displays a solid color on one side and the same color with its opacity applied on the other side. It can take a hexadecimal, an rgb or an rgba.
+If the background prop has a hex `color` value and an opacity value it will display a full swatch with a solid color on one side and the same color with the opacity applied on the other side. (default opacity: 1)
-**Gradient** (`"gradient"`), displays a gradient. A gradient should be a `linear-gradient` or a `conic-gradient`.
-
-
-
-**Image** (`"image"`) the swatch could display any image.
-
-
-
-**Active** (`"active"`) displays the swatch as active while an interface related action is happening.
-
-
-
-**Size** (`"size"`) shows a bigger or smaller swatch. Accepts `small` and `medium` (_default_) sizes.
+This component can take a size property to set the size of the swatch. In this case we can set it to `small` (default size: `medium`)
-**Format** (`"format"`) displays a square or rounded swatch. Accepts `square` (_default_) and `rounded` sizes.
+With the `active` property, we can display the element as being active
-
+
+
+The element can also be interactive, and execute an external function. Typically, it launches the color picker. To make it an interactive button, it accepts an onClick function.
+
+
+
+> Due to technical issues regarding the transformation between Clojurescript and Javascript, we are unable to display:
+
+ - Swatches with gradients
+ - Library Swatches
+ - Swatches with images
## Technical Notes
-### Background
-
-The `swatch*` component accepts a `background` prop, which must be:
-
-- An hexadecimal (e.g. `#996633`)
-- An RGB (e.g. `rgb(125, 125, 0)`)
-- An RGBA (e.g. `rgba(125, 125, 0, 0.3)`)
-- A linear gradient (e.g. `linear-gradient(to right, blue, pink)`)
-- A conic gradient (e.g. `conic-gradient(red, orange, yellow, green, blue)`)
-- An image (e.g. `url(https://placecats.com/100/100)`)
-
### onClick
-> Note: If the swatch is interactive, an `aria-label` is required. More on the `Accessibility` section.
+> Note: If the swatch is interactive, an `aria-label` is required. See the `Accessibility` section for more information.
-The swatch button accepts an onClick prop that expect a function on the parent context.
+The swatch button accepts an onClick prop that expects a function on the parent context.
It should be useful for launching other tools as a color picker.
-It runs when the user clics on the swatch, or presses enter or space while focusing it.
+It is executed when the user clicks on the swatch, or presses Enter or Spacebar while focused.
### Accessibility
diff --git a/frontend/src/app/main/ui/ds/utilities/swatch.stories.jsx b/frontend/src/app/main/ui/ds/utilities/swatch.stories.jsx
index 165b7c599..08f0e3d07 100644
--- a/frontend/src/app/main/ui/ds/utilities/swatch.stories.jsx
+++ b/frontend/src/app/main/ui/ds/utilities/swatch.stories.jsx
@@ -15,11 +15,7 @@ export default {
component: Swatch,
argTypes: {
background: {
- control: { type: "text" },
- },
- format: {
- control: "select",
- options: ["square", "rounded"],
+ control: "object",
},
size: {
control: "select",
@@ -30,8 +26,7 @@ export default {
},
},
args: {
- background: "#663399",
- format: "square",
+ background: { color: "#7efff5" },
size: "medium",
active: false,
},
@@ -42,28 +37,52 @@ export const Default = {};
export const WithOpacity = {
args: {
- background: "rgba(255, 0, 0, 0.5)",
+ background: {
+ color: "#7efff5",
+ opacity: 0.5,
+ },
},
};
-export const LinearGradient = {
- args: {
- background: "linear-gradient(to right, transparent, mistyrose)",
- },
-};
+// These stories are disabled because the gradient and the UUID variants cannot be translated from cljs into JS
+// When the repo is updated to use the new version of rumext, these stories should be re-enabled and tested
+//
+// export const LinearGradient = {
+// args: {
+// background: {
+// gradient: {
+// type: "linear",
+// startX: 0,
+// startY: 0,
+// endX: 1,
+// endY: 0,
+// width: 1,
+// stops: [
+// {
+// color: "#fabada",
+// opacity: 1,
+// offset: 0,
+// },
+// {
+// color: "#cc0000",
+// opacity: 0.5,
+// offset: 1,
+// },
+// ],
+// },
+// },
+// },
+// };
-export const Image = {
- args: {
- background: "images/form/never-used.png",
- size: "medium",
- },
-};
-
-export const Rounded = {
- args: {
- format: "rounded",
- },
-};
+// export const Rounded = {
+// args: {
+// background: {
+// id: crypto.randomUUID(),
+// color: "#7efff5",
+// opacity: 0.5,
+// },
+// },
+// };
export const Small = {
args: {
@@ -74,7 +93,6 @@ export const Small = {
export const Active = {
args: {
active: true,
- background: "#CC00CC",
},
};