0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-11 07:11:32 -05:00

New options in color picker (harmony & hsv)

This commit is contained in:
alonso.torres 2020-10-07 14:33:34 +02:00
parent 08b537a158
commit 2c31b074c8
11 changed files with 947 additions and 447 deletions

View file

@ -211,6 +211,7 @@
(unit (point y (- x))))
(defn point-line-distance
"Returns the distance from a point to a line defined by two points"
[point line-point1 line-point2]
(let [{x0 :x y0 :y} point
{x1 :x y1 :y} line-point1

View file

@ -12,6 +12,10 @@
#?(:cljs
(:require [goog.math :as math])))
(def PI
#?(:cljs (.-PI js/Math)
:clj Math/PI))
(defn nan?
[v]
#?(:cljs (js/isNaN v)

View file

@ -0,0 +1,2 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500" height="500" width="500"><path d="M330.902 0c-86.375 0-156.677 70.242-156.677 156.617 0 86.376 70.302 156.676 156.677 156.676 86.375 0 156.616-70.3 156.616-156.676C487.518 70.242 417.278 0 330.902 0zm-.562 36.625a120.87 120.87 0 01.342 0 120.87 120.87 0 01120.87 120.87 120.87 120.87 0 01-120.87 120.87 120.87 120.87 0 01-120.87-120.87A120.87 120.87 0 01330.34 36.624zM126.557 271.852c-62.798 0-114.075 51.275-114.075 114.074C12.482 448.724 63.76 500 126.557 500S240.63 448.724 240.63 385.926c0-62.799-51.276-114.074-114.074-114.074zm-.268 38.396a76.236 76.236 0 01.074 0 76.236 76.236 0 0176.237 76.236 76.236 76.236 0 01-76.237 76.237 76.236 76.236 0 01-76.236-76.237 76.236 76.236 0 0176.162-76.236z"/>
</svg>

After

Width:  |  Height:  |  Size: 775 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500" height="500" width="500"><path d="M164.617 17.078c-49.266 0-90.208 37.515-95.681 85.381h-49.47A19.467 19.467 0 000 121.928a19.467 19.467 0 0019.467 19.467h53.025c12.073 39.418 48.894 68.306 92.125 68.306 43.23 0 80.054-28.888 92.127-68.306h223.79A19.467 19.467 0 00500 121.928a19.467 19.467 0 00-19.467-19.469H260.301c-5.474-47.866-46.418-85.38-95.684-85.38zm-.787 37.004a60.924 60.924 0 0160.924 60.924 60.924 60.924 0 01-60.924 60.924 60.924 60.924 0 01-60.924-60.924 60.924 60.924 0 0160.924-60.924zm171.553 236.217c-49.267 0-90.21 37.518-95.684 85.385H19.467A19.467 19.467 0 000 395.148a19.467 19.467 0 0019.467 19.467h223.789c12.073 39.42 48.896 68.307 92.127 68.307 43.23 0 80.052-28.888 92.125-68.307h53.025A19.467 19.467 0 00500 395.148a19.467 19.467 0 00-19.467-19.464h-49.469c-5.472-47.867-46.414-85.385-95.681-85.385zm1.119 36.148a60.924 60.924 0 0160.924 60.924 60.924 60.924 0 01-60.924 60.924 60.924 60.924 0 01-60.924-60.924 60.924 60.924 0 0160.924-60.924z"/></svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500" height="500" width="500"><path d="M24.7-.203C11.421-.203.001 11.217.001 24.494v451.012c0 13.277 11.417 24.695 24.691 24.697h450.608c13.28 0 24.697-11.42 24.697-24.697V24.494c0-13.276-11.42-24.697-24.697-24.697H24.699zm4.142 31.35h442.316v437.707H28.842V31.146z"/></svg>

After

Width:  |  Height:  |  Size: 332 B

View file

@ -1 +1,4 @@
<svg height="500" viewBox="0 0 500.00001 500.00001" width="500" xmlns="http://www.w3.org/2000/svg"><path d="m11.402 498.536c-4.64-1.613-8.317-5.335-9.896-10.02-.732-2.168-.824-7.17-.824-44.7 0-45.853-.04-45.148 2.807-49.542.653-1.01 53.238-53.866 116.855-117.46 63.617-63.59 115.667-115.852 115.667-116.134s-11.486-11.997-25.524-26.033-25.524-25.75-25.524-26.034c0-.283 14.816-15.328 32.924-33.434l32.924-32.922 29.607 29.59 29.607 29.592 44.286-44.17c32.81-32.726 45.278-44.843 48.115-46.76 7.434-5.025 16.28-8.752 24.758-10.432 5.316-1.053 18.308-.91 23.737.26 25.043 5.4 44.058 24.808 49.064 50.08.927 4.68.927 17.78 0 22.46-1.68 8.48-5.407 17.326-10.432 24.76-1.917 2.836-14.034 15.303-46.76 48.114l-44.17 44.286 29.59 29.608 29.592 29.608-32.92 32.923c-18.107 18.108-33.152 32.924-33.435 32.924s-11.998-11.485-26.034-25.523-25.75-25.524-26.033-25.524c-.282 0-52.543 52.05-116.135 115.667-63.593 63.617-116.45 116.202-117.46 116.856-4.4 2.85-3.662 2.81-49.686 2.783-37.102-.023-42.692-.126-44.702-.824zm188.236-144.766c62.962-62.96 114.477-114.7 114.477-114.98 0-.67-52.165-52.838-52.834-52.838-.282 0-52.026 51.515-114.986 114.477l-114.475 114.475v26.057c0 19.588.153 26.21.614 26.67.46.462 7.084.614 26.67.614h26.056l114.48-114.475z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16">
<path fill="#fff" d="M.607 13.076v2.401l2.224.025 7.86-7.405s-.05-.885-.253-1.087c-.202-.202-2.25-1.744-2.25-1.744z"/>
<path fill="#000" d="M.343 15.974a.514.514 0 01-.317-.321c-.023-.07-.026-.23-.026-1.43 0-1.468-.001-1.445.09-1.586.02-.032 1.703-1.724 3.74-3.759a596.805 596.805 0 003.7-3.716c0-.009-.367-.384-.816-.833a29.9 29.9 0 01-.817-.833c0-.01.474-.49 1.054-1.07l1.053-1.053.948.946.947.947 1.417-1.413C12.366.806 12.765.418 12.856.357c.238-.161.52-.28.792-.334.17-.034.586-.03.76.008.801.173 1.41.794 1.57 1.603.03.15.03.569 0 .718a2.227 2.227 0 01-.334.793c-.061.09-.45.49-1.496 1.54L12.734 6.1l.947.948.947.947-1.053 1.054c-.58.58-1.061 1.054-1.07 1.054-.01 0-.384-.368-.833-.817-.45-.45-.824-.817-.834-.817-.009 0-1.68 1.666-3.716 3.701a493.093 493.093 0 01-3.759 3.74c-.14.091-.117.09-1.59.089-1.187 0-1.366-.004-1.43-.027zm6.024-4.633a592.723 592.723 0 003.663-3.68c0-.02-1.67-1.69-1.69-1.69-.01 0-1.666 1.648-3.68 3.663L.996 13.297v.834c0 .627.005.839.02.854.015.014.227.02.854.02h.833l3.664-3.664z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -6,257 +6,487 @@
// Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
.colorpicker {
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
background-color: $color-white;
}
.colorpicker-content {
display: flex;
flex-direction: column;
padding: 0.5rem;
& > * {
width: 200px;
}
.top-actions {
display: flex;
flex-direction: column;
padding: 0.5rem;
background-color: $color-white;
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
margin-bottom: 0.25rem;
justify-content: space-between;
& > * {
width: 200px;
.picker-btn {
background: none;
border: none;
cursor: pointer;
&.active,
&:hover svg {
fill: $color-primary;
}
svg {
width: 14px;
height: 14px;
}
}
}
.gradients-buttons {
.gradient {
cursor: pointer;
width: 15px;
height: 15px;
padding: 0;
margin: 0;
border: 1px solid $color-gray-20;
border-radius: 2px;
margin-left: 0.25rem;
}
.top-actions {
display: flex;
margin-bottom: 0.25rem;
.picker-btn {
background: none;
border: none;
cursor: pointer;
&.active,
&:hover svg {
fill: $color-primary;
}
svg {
width: 14px;
height: 14px;
}
}
.active {
border-color: $color-primary;
}
.picker-detail-wrapper {
position: relative;
.linear-gradient {
background: linear-gradient(180deg, $color-gray-20, transparent);
}
.center-circle {
width: 14px;
height: 14px;
border: 2px solid $color-white;
border-radius: 8px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-7px, -7px);
filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.25));
}
.radial-gradient {
background: radial-gradient(transparent, $color-gray-20);
}
#picker-detail {
border: 1px solid $color-gray-10;
}
.gradient-stops {
height: 10px;
display: flex;
margin-top: 0.5rem;
margin-bottom: 1rem;
.gradient-background {
height: 100%;
width: 100%;
border: 1px solid $color-gray-10;
}
.gradient-stop-wrapper {
position: absolute;
width: calc(100% - 2rem);
margin-left: 0.5rem;
}
.gradient-stop {
position: absolute;
width: 14px;
height: 14px;
border-radius: 2px;
border: 1px solid $color-gray-20;
margin-top: -2px;
margin-left: -7px;
box-shadow: 0 2px 2px rgb(0 0 0 / 15%);
.selected {
border-color: $color-primary;
}
}
}
.picker-detail-wrapper {
position: relative;
.center-circle {
width: 14px;
height: 14px;
border: 2px solid $color-white;
border-radius: 8px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-7px, -7px);
filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.25));
}
}
#picker-detail {
border: 1px solid $color-gray-10;
}
.slider-selector {
--gradient-direction: 90deg;
--background-repeat: left;
&.vertical {
--gradient-direction: 0deg;
--background-repeat: top;
}
border: 1px solid $color-gray-10;
background: linear-gradient(var(--gradient-direction), rgba(var(--color), 0) 0%, rgba(var(--color), 1.0) 100%);
align-self: center;
position: relative;
cursor: pointer;
width: 100%;
height: calc(0.5rem + 1px);
&.vertical {
width: calc(0.5rem + 1px);
height: 100%;
}
&.hue {
background: linear-gradient(
var(--gradient-direction),
#f00 0%, #ff0 17%, #0f0 33%, #0ff 50%,
#00f 67%, #f0f 83%, #f00 100%);
}
&.saturation {
background: linear-gradient(
var(--gradient-direction),
var(--saturation-grad-from) 0%,
var(--saturation-grad-to) 100%
)
}
&.opacity {
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAADFJREFUOE9jZGBgEAFifOANPknGUQMYhkkYEEgG+NMJKAwIAbwJbdQABnBCIgRoG4gAIF8IsXB/Rs4AAAAASUVORK5CYII=") var(--background-repeat) center;
&::after {
content: "";
position: absolute;
width: 100%;
height: 100%;
background: linear-gradient(var(--gradient-direction), rgba(var(--color), 0) 0%, rgba(var(--color), 1.0) 100%);
}
}
&.value {
background: linear-gradient(var(--gradient-direction), #FFF 0%, #000 100%);
}
.handler {
background-color: $color-white;;
box-shadow: rgba(0, 0, 0, 0.37) 0px 1px 4px 0px;
transform: translate(-6px, -2px);
left: 50%;
position: absolute;
width: 12px;
height: 12px;
border-radius: 6px;
z-index: 1;
}
&.vertical .handler {
transform: translate(-6px, 6px);
}
}
.value-saturation-selector {
background-color: rgba(var(--hue-rgb));
position: relative;
height: 6.75rem;
cursor: pointer;
.handler {
position: absolute;
width: 12px;
height: 12px;
border-radius: 6px;
z-index: 1;
border: 1px solid $color-white;
box-shadow: rgb(255, 255, 255) 0px 0px 0px 1px inset, rgb(0 0 0 / 0.25) 0px 4px 4px inset, rgb(0 0 0 / 0.25) 0px 4px 4px;
transform: translate(-6px, -6px);
left: 50%;
top: 50%;
}
&::before {
content: "";
position: absolute;
width: 100%;
height: 100%;
background: linear-gradient(to right, #fff, rgba(255,255,255,0));
}
&::after {
content: "";
position: absolute;
width: 100%;
height: 100%;
background: linear-gradient(to top, #000, rgba(0,0,0,0));
}
}
.color-bullet {
grid-area: color;
width: 20px;
height: 20px;
background-color: rgba(var(--color));
border-radius: 12px;
border: 1px solid $color-gray-10;
}
.shade-selector {
display: grid;
justify-items: center;
align-items: center;
grid-template-areas: "color hue"
"color opacity";
grid-template-columns: 2.5rem 1fr;
height: 3.5rem;
grid-row-gap: 0.5rem;
cursor: pointer;
margin-bottom: 0.25rem;
.slider-selector.hue {
grid-area: "hue";
align-self: end;
}
.slider-selector.opacity {
grid-area: "opacity";
align-self: start;
}
}
.color-values {
display: grid;
grid-template-columns: 3.5rem repeat(4, 1fr);
grid-row-gap: 0.25rem;
justify-items: center;
grid-column-gap: 0.25rem;
input {
width: 100%;
margin: 0;
border: 1px solid $color-gray-10;
border-radius: 2px;
font-size: $fs11;
height: 1.5rem;
padding: 0 $x-small;
color: $color-gray-40;
}
label {
font-size: $fs11;
}
}
.libraries {
border-top: 1px solid $color-gray-10;
padding-top: 0.5rem;
margin-top: 0.25rem;
width: 200px;
select {
background-image: url(/images/icons/arrow-down.svg);
background-repeat: no-repeat;
background-position: 95% 48%;
background-size: 10px;
margin: 0;
margin-bottom: 0.5rem;
width: 100%;
padding: 2px 0.25rem;
font-size: 0.75rem;
color: $color-gray-40;
border-color: $color-gray-10;
border-radius: 2px;
option {
padding: 0;
}
}
.selected-colors {
display: grid;
grid-template-columns: repeat(8, 1fr);
justify-content: space-between;
margin-right: -8px;
overflow-x: hidden;
overflow-y: auto;
max-height: 5.5rem;
}
.selected-colors::after {
content: "";
flex: auto;
}
.selected-colors .color-bullet {
grid-area: auto;
margin-bottom: 0.25rem;
cursor: pointer;
&:hover {
border-color: $color-primary;
}
&.button {
display: flex;
align-items: center;
justify-content: center;
}
&.button svg {
width: 12px;
height: 12px;
fill: $color-gray-30;
}
&.plus-button svg {
width: 8px;
height: 8px;
fill: $color-black;
}
}
}
.actions {
margin-top: 0.5rem;
display: flex;
flex-direction: row;
justify-content: center;
.btn-primary {
height: 1.5rem;
padding: 0 2.5rem;
font-size: $fs12;
}
}
.harmony-selector {
display: flex;
flex-direction: row;
margin-bottom: 0.5rem;
.hue-wheel-wrapper {
position: relative;
.hue-wheel {
width: 152px;
height: 152px;
}
.handler {
position: absolute;
width: 12px;
height: 12px;
border-radius: 6px;
z-index: 1;
}
.value-selector {
background-color: rgba(var(--hue));
position: relative;
height: 6.75rem;
cursor: pointer;
.handler {
box-shadow: rgb(255, 255, 255) 0px 0px 0px 1px inset;
transform: translate(-6px, -6px);
left: 50%;
top: 50%;
}
}
.value-selector::before {
content: "";
position: absolute;
width: 100%;
height: 100%;
background: linear-gradient(to right, #fff, rgba(255,255,255,0));
}
.value-selector::after {
content: "";
position: absolute;
width: 100%;
height: 100%;
background: linear-gradient(to top, #000, rgba(0,0,0,0));
}
.shade-selector {
display: grid;
justify-items: center;
align-items: center;
grid-template-areas: "color hue" "color opacity";
grid-template-columns: 2.5rem 1fr;
height: 3.5rem;
grid-row-gap: 0.5rem;
cursor: pointer;
}
.color-bullet {
grid-area: color;
width: 20px;
height: 20px;
background-color: rgba(var(--color));
border-radius: 12px;
border: 1px solid $color-gray-10;
}
.hue-selector {
align-self: end;
grid-area: hue;
height: 0.5rem;
width: 100%;
background: linear-gradient(
to right,
#f00 0%, #ff0 17%, #0f0 33%, #0ff 50%,
#00f 67%, #f0f 83%, #f00 100%);
position: relative;
cursor: pointer;
}
.hue-selector .handler,
.opacity-selector .handler {
background-color: rgb(248, 248, 248);
box-shadow: rgba(0, 0, 0, 0.37) 0px 1px 4px 0px;
transform: translate(-6px, -2px);
border: 1px solid $color-white;
box-shadow: rgb(255, 255, 255) 0px 0px 0px 1px inset, rgb(0 0 0 / 0.25) 0px 4px 4px inset, rgb(0 0 0 / 0.25) 0px 4px 4px;
transform: translate(-6px, -6px);
left: 50%;
top: 50%;
}
.handler.complement {
background-color: $color-white;
box-shadow: rgb(0 0 0 / 0.25) 0px 4px 4px;
}
}
.opacity-selector {
align-self: start;
grid-area: opacity;
height: 0.5rem;
width: 100%;
position: relative;
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAADFJREFUOE9jZGBgEAFifOANPknGUQMYhkkYEEgG+NMJKAwIAbwJbdQABnBCIgRoG4gAIF8IsXB/Rs4AAAAASUVORK5CYII=") left center;
}
.opacity-selector::after {
content: "";
background: linear-gradient(to right, rgba(var(--color), 0) 0%, rgba(var(--color), 1.0) 100%);
position: absolute;
width: 100%;
.handlers-wrapper {
height: 152px;
display: flex;
flex-direction: row;
flex-grow: 1;
justify-content: space-around;
padding-top: 0.5rem;
& > * {
height: 100%;
}
}
}
.hsva-selector {
display: grid;
padding: 0.25rem;
grid-template-columns: 20px 1fr;
grid-template-rows: repeat(4, 2rem);
grid-row-gap: 0.5rem;
margin-bottom: 0.5rem;
.hue,
.saturation,
.value,
.opacity {
border-radius: 10px;
}
.color-values {
display: grid;
grid-template-columns: 3.5rem repeat(4, 1fr);
grid-row-gap: 0.25rem;
justify-items: center;
grid-column-gap: 0.25rem;
.hsva-selector-label {
grid-column: 1;
align-self: center;
}
}
}
input {
width: 100%;
margin: 0;
border: 1px solid $color-gray-10;
border-radius: 2px;
font-size: $fs11;
height: 1.5rem;
padding: 0 $x-small;
color: $color-gray-40;
}
.colorpicker-tooltip {
border-radius: $br-small;
display: flex;
flex-direction: column;
left: 1400px;
top: 100px;
position: absolute;
z-index: 11;
width: auto;
label {
font-size: $fs11;
}
span {
color: $color-gray-20;
font-size: $fs12;
}
.inputs-area {
.input-text {
color: $color-gray-60;
font-size: $fs13;
margin: 5px;
padding: 5px;
width: 100%;
}
.libraries {
border-top: 1px solid $color-gray-10;
padding-top: 0.5rem;
margin-top: 0.25rem;
width: 200px;
select {
background-image: url(/images/icons/arrow-down.svg);
background-repeat: no-repeat;
background-position: 95% 48%;
background-size: 10px;
margin: 0;
margin-bottom: 0.5rem;
width: 100%;
padding: 2px 0.25rem;
font-size: 0.75rem;
color: $color-gray-40;
border-color: $color-gray-10;
border-radius: 2px;
}
option {
padding: 0;
}
}
.colorpicker-tabs {
display: flex;
margin-top: 0.25rem;
height: 2rem;
background-color: $color-gray-10;
.selected-colors {
display: grid;
grid-template-columns: repeat(8, 1fr);
justify-content: space-between;
margin-right: -8px;
overflow-x: hidden;
overflow-y: auto;
max-height: 5.5rem;
}
.selected-colors::after {
content: "";
flex: auto;
}
.selected-colors .color-bullet {
grid-area: auto;
margin-bottom: 0.25rem;
cursor: pointer;
&:hover {
border-color: $color-primary;
}
&.button {
display: flex;
align-items: center;
justify-content: center;
}
&.button svg {
width: 12px;
height: 12px;
fill: $color-gray-30;
}
&.plus-button svg {
width: 8px;
height: 8px;
fill: $color-black;
}
}
.active {
background-color: $color-white;
}
.actions {
margin-top: 0.5rem;
display: flex;
flex-direction: row;
justify-content: center;
.colorpicker-tab {
cursor: pointer;
display: flex;
flex-grow: 1;
justify-content: center;
align-items: center;
.btn-primary {
height: 1.5rem;
padding: 0 2.5rem;
font-size: $fs12;
}
svg {
width: 16px;
height: 16px;
fill: $color-gray-30;
}
}
}
}
.color-data {
@ -265,8 +495,8 @@
position: relative;
.color-name {
font-size: $fs13;
margin: 5px 6px 0px 6px;
font-size: $fs13;
margin: 5px 6px 0px 6px;
}
.color-info {
@ -310,30 +540,3 @@
}
}
.colorpicker-tooltip {
border-radius: $br-small;
display: flex;
flex-direction: column;
left: 1400px;
top: 100px;
position: absolute;
z-index: 11;
width: auto;
span {
color: $color-gray-20;
font-size: $fs12;
}
.inputs-area {
.input-text {
color: $color-gray-60;
font-size: $fs13;
margin: 5px;
padding: 5px;
width: 100%;
}
}
}

View file

@ -122,6 +122,9 @@
(def uppercase (icon-xref :uppercase))
(def user (icon-xref :user))
(def tick (icon-xref :tick))
(def picker-harmony (icon-xref :picker-harmony))
(def picker-hsv (icon-xref :picker-hsv))
(def picker-ramp (icon-xref :picker-ramp))
(def loader-pencil
(mf/html

View file

@ -10,18 +10,20 @@
(ns app.main.ui.workspace.colorpicker
(:require
[rumext.alpha :as mf]
[app.main.store :as st]
[okulary.core :as l]
[cuerdas.core :as str]
[app.util.dom :as dom]
[app.util.color :as uc]
[app.main.ui.icons :as i]
[app.common.geom.point :as gpt]
[app.common.math :as math]
[app.common.uuid :refer [uuid]]
[app.util.dom :as dom]
[app.util.color :as uc]
[app.util.object :as obj]
[app.main.store :as st]
[app.main.refs :as refs]
[app.main.data.workspace.libraries :as dwl]
[app.main.data.colors :as dwc]
[app.main.data.modal :as modal]
[okulary.core :as l]
[app.main.refs :as refs]
[app.main.ui.icons :as i]
[app.util.i18n :as i18n :refer [t]]))
;; --- Refs
@ -44,7 +46,7 @@
;; --- Color Picker Modal
(mf/defc value-selector [{:keys [hue saturation value on-change]}]
(mf/defc value-saturation-selector [{:keys [hue saturation value on-change]}]
(let [dragging? (mf/use-state false)
calculate-pos
(fn [ev]
@ -53,7 +55,7 @@
px (math/clamp (/ (- x left) (- right left)) 0 1)
py (* 255 (- 1 (math/clamp (/ (- y top) (- bottom top)) 0 1)))]
(on-change px py)))]
[:div.value-selector
[:div.value-saturation-selector
{:on-mouse-down #(reset! dragging? true)
:on-mouse-up #(reset! dragging? false)
:on-pointer-down (partial dom/capture-pointer)
@ -64,41 +66,389 @@
:left (str (* 100 saturation) "%")
:top (str (* 100 (- 1 (/ value 255))) "%")}}]]))
(mf/defc hue-selector [{:keys [hue on-change]}]
(let [dragging? (mf/use-state false)
calculate-pos
(fn [ev]
(let [{:keys [left right]} (-> ev dom/get-target dom/get-bounding-rect)
{:keys [x]} (-> ev dom/get-client-position)
px (math/clamp (/ (- x left) (- right left)) 0 1)]
(on-change (* px 360))))]
[:div.hue-selector
{:on-mouse-down #(reset! dragging? true)
:on-mouse-up #(reset! dragging? false)
:on-pointer-down (partial dom/capture-pointer)
:on-pointer-up (partial dom/release-pointer)
:on-click calculate-pos
:on-mouse-move #(when @dragging? (calculate-pos %))}
[:div.handler {:style {:pointer-events "none"
:left (str (* (/ hue 360) 100) "%")}}]]))
(mf/defc opacity-selector [{:keys [opacity on-change]}]
(let [dragging? (mf/use-state false)
(mf/defc slider-selector [{:keys [value class min-value max-value vertical? reverse? on-change]}]
(let [min-value (or min-value 0)
max-value (or max-value 1)
dragging? (mf/use-state false)
calculate-pos
(fn [ev]
(let [{:keys [left right]} (-> ev dom/get-target dom/get-bounding-rect)
{:keys [x]} (-> ev dom/get-client-position)
px (math/clamp (/ (- x left) (- right left)) 0 1)]
(on-change px)))]
[:div.opacity-selector
{:on-mouse-down #(reset! dragging? true)
(when on-change
(let [{:keys [left right top bottom]} (-> ev dom/get-target dom/get-bounding-rect)
{:keys [x y]} (-> ev dom/get-client-position)
unit-value (if vertical?
(math/clamp (/ (- bottom y) (- bottom top)) 0 1)
(math/clamp (/ (- x left) (- right left)) 0 1))
unit-value (if reverse?
(math/abs (- unit-value 1.0))
unit-value)
value (+ min-value (* unit-value (- max-value min-value)))]
(on-change value))))]
[:div.slider-selector
{:class (str (if vertical? "vertical " "") class)
:on-mouse-down #(reset! dragging? true)
:on-mouse-up #(reset! dragging? false)
:on-pointer-down (partial dom/capture-pointer)
:on-pointer-up (partial dom/release-pointer)
:on-click calculate-pos
:on-mouse-move #(when @dragging? (calculate-pos %))}
[:div.handler {:style {:pointer-events "none"
:left (str (* opacity 100) "%")}}]]))
(let [value-percent (* (/ (- value min-value)
(- max-value min-value)) 100)
value-percent (if reverse?
(math/abs (- value-percent 100))
value-percent)
value-percent-str (str value-percent "%")
style-common #js {:pointerEvents "none"}
style-horizontal (obj/merge! #js {:left value-percent-str} style-common)
style-vertical (obj/merge! #js {:bottom value-percent-str} style-common)]
[:div.handler {:style (if vertical? style-vertical style-horizontal)}])]))
(defn create-color-wheel
[canvas-node]
(let [ctx (.getContext canvas-node "2d")
width (obj/get canvas-node "width")
height (obj/get canvas-node "height")
radius (/ width 2)
cx (/ width 2)
cy (/ width 2)
step 0.2]
(.clearRect ctx 0 0 width height)
(doseq [degrees (range 0 360 step)]
(let [degrees-rad (math/radians degrees)
x (* radius (math/cos (- degrees-rad)))
y (* radius (math/sin (- degrees-rad)))]
(obj/set! ctx "strokeStyle" (str/format "hsl(%s, 100%, 50%)" degrees))
(.beginPath ctx)
(.moveTo ctx cx cy)
(.lineTo ctx (+ cx x) (+ cy y))
(.stroke ctx)))
(let [grd (.createRadialGradient ctx cx cy 0 cx cx radius)]
(.addColorStop grd 0 "white")
(.addColorStop grd 1 "rgba(255, 255, 255, 0")
(obj/set! ctx "fillStyle" grd)
(.beginPath ctx)
(.arc ctx cx cy radius 0 (* 2 math/PI) true)
(.closePath ctx)
(.fill ctx))))
(mf/defc ramp-selector [{:keys [color on-change]}]
(let [{hue :h saturation :s value :v alpha :alpha} color
on-change-value-saturation
(fn [new-saturation new-value]
(let [hex (uc/hsv->hex [hue new-saturation new-value])
[r g b] (uc/hex->rgb hex)]
(on-change {:hex hex
:r r :g g :b b
:s new-saturation
:v new-value})))
on-change-hue
(fn [new-hue]
(let [hex (uc/hsv->hex [new-hue saturation value])
[r g b] (uc/hex->rgb hex)]
(on-change {:hex hex
:r r :g g :b b
:h new-hue} )))
on-change-opacity
(fn [new-opacity]
(on-change {:alpha new-opacity} ))]
[:*
[:& value-saturation-selector
{:hue hue
:saturation saturation
:value value
:on-change on-change-value-saturation}]
[:div.shade-selector
[:div.color-bullet]
[:& slider-selector {:class "hue"
:max-value 360
:value hue
:on-change on-change-hue}]
[:& slider-selector {:class "opacity"
:max-value 1
:value alpha
:on-change on-change-opacity}]]]))
(defn color->point
[canvas-side hue saturation]
(let [hue-rad (math/radians (- hue))
comp-x (* saturation (math/cos hue-rad))
comp-y (* saturation (math/sin hue-rad))
x (+ (/ canvas-side 2) (* comp-x (/ canvas-side 2)))
y (+ (/ canvas-side 2) (* comp-y (/ canvas-side 2)))]
(gpt/point x y)))
(mf/defc harmony-selector [{:keys [color on-change]}]
(let [canvas-ref (mf/use-ref nil)
{hue :h saturation :s value :v alpha :alpha} color
canvas-side 152
pos-current (color->point canvas-side hue saturation)
pos-complement (color->point canvas-side (mod (+ hue 180) 360) saturation)
dragging? (mf/use-state false)
calculate-pos (fn [ev]
(let [{:keys [left right top bottom]} (-> ev dom/get-target dom/get-bounding-rect)
{:keys [x y]} (-> ev dom/get-client-position)
px (math/clamp (/ (- x left) (- right left)) 0 1)
py (math/clamp (/ (- y top) (- bottom top)) 0 1)
px (- (* 2 px) 1)
py (- (* 2 py) 1)
angle (math/degrees (math/atan2 px py))
new-hue (math/precision (mod (- angle 90 ) 360) 2)
new-saturation (math/clamp (math/distance [px py] [0 0]) 0 1)
hex (uc/hsv->hex [new-hue new-saturation value])
[r g b] (uc/hex->rgb hex)]
(on-change {:hex hex
:r r :g g :b b
:h new-hue
:s new-saturation})))
on-change-value (fn [new-value]
(let [hex (uc/hsv->hex [hue saturation new-value])
[r g b] (uc/hex->rgb hex)]
(on-change {:hex hex
:r r :g g :b b
:v new-value})))
on-complement-click (fn [ev]
(let [new-hue (mod (+ hue 180) 360)
hex (uc/hsv->hex [new-hue saturation value])
[r g b] (uc/hex->rgb hex)]
(on-change {:hex hex
:r r :g g :b b
:h new-hue
:s saturation})))
on-change-opacity (fn [new-alpha] (on-change {:alpha new-alpha}))]
(mf/use-effect
(mf/deps canvas-ref)
(fn [] (when canvas-ref
(create-color-wheel (mf/ref-val canvas-ref)))))
[:div.harmony-selector
[:div.hue-wheel-wrapper
[:canvas.hue-wheel
{:ref canvas-ref
:width canvas-side
:height canvas-side
:on-mouse-down #(reset! dragging? true)
:on-mouse-up #(reset! dragging? false)
:on-pointer-down (partial dom/capture-pointer)
:on-pointer-up (partial dom/release-pointer)
:on-click calculate-pos
:on-mouse-move #(when @dragging? (calculate-pos %))}]
[:div.handler {:style {:pointer-events "none"
:left (:x pos-current)
:top (:y pos-current)}}]
[:div.handler.complement {:style {:left (:x pos-complement)
:top (:y pos-complement)
:cursor "pointer"}
:on-click on-complement-click}]]
[:div.handlers-wrapper
[:& slider-selector {:class "value"
:vertical? true
:reverse? true
:value value
:max-value 255
:vertical true
:on-change on-change-value}]
[:& slider-selector {:class "opacity"
:vertical? true
:value alpha
:max-value 1
:vertical true
:on-change on-change-opacity}]]]))
(mf/defc hsva-selector [{:keys [color on-change]}]
(let [{hue :h saturation :s value :v alpha :alpha} color
handle-change-slider (fn [key]
(fn [new-value]
(let [change (hash-map key new-value)
{:keys [h s v]} (merge color change)
hex (uc/hsv->hex [h s v])
[r g b] (uc/hex->rgb hex)]
(on-change (merge change
{:hex hex
:r r :g g :b b})))))
on-change-opacity (fn [new-alpha] (on-change {:alpha new-alpha}))]
[:div.hsva-selector
[:span.hsva-selector-label "H"]
[:& slider-selector
{:class "hue" :max-value 360 :value hue :on-change (handle-change-slider :h)}]
[:span.hsva-selector-label "S"]
[:& slider-selector
{:class "saturation" :max-value 1 :value saturation :on-change (handle-change-slider :s)}]
[:span.hsva-selector-label "V"]
[:& slider-selector
{:class "value" :reverse? true :max-value 255 :value value :on-change (handle-change-slider :v)}]
[:span.hsva-selector-label "A"]
[:& slider-selector
{:class "opacity" :max-value 1 :value alpha :on-change on-change-opacity}]]))
(mf/defc color-inputs [{:keys [type color on-change]}]
(let [{red :r green :g blue :b
hue :h saturation :s value :v
hex :hex alpha :alpha} color
parse-hex (fn [val] (if (= (first val) \#) val (str \# val)))
refs {:hex (mf/use-ref nil)
:r (mf/use-ref nil)
:g (mf/use-ref nil)
:b (mf/use-ref nil)
:h (mf/use-ref nil)
:s (mf/use-ref nil)
:v (mf/use-ref nil)
:alpha (mf/use-ref nil)}
on-change-hex
(fn [e]
(let [val (-> e dom/get-target-val parse-hex)]
(when (uc/hex? val)
(let [[r g b] (uc/hex->rgb val)
[h s v] (uc/hex->hsv hex)]
(on-change {:hex val
:h h :s s :v v
:r r :g g :b b})))))
on-change-property
(fn [property max-value]
(fn [e]
(let [val (-> e dom/get-target-val (math/clamp 0 max-value))
val (if (#{:s} property) (/ val 100) val)]
(when (not (nil? val))
(if (#{:r :g :b} property)
(let [{:keys [r g b]} (merge color (hash-map property val))
hex (uc/rgb->hex [r g b])
[h s v] (uc/hex->hsv hex)]
(on-change {:hex hex
:h h :s s :v v
:r r :g g :b b}))
(let [{:keys [h s v]} (merge color (hash-map property val))
hex (uc/hsv->hex [h s v])
[r g b] (uc/hex->rgb hex)]
(on-change {:hex hex
:h h :s s :v v
:r r :g g :b b})))))))
on-change-opacity
(fn [e]
(when-let [new-alpha (-> e dom/get-target-val (math/clamp 0 100) (/ 100))]
(on-change {:alpha new-alpha})))]
;; Updates the inputs values when a property is changed in the parent
(mf/use-effect
(mf/deps color type)
(fn []
(doseq [ref-key (keys refs)]
(let [property-val (get color ref-key)
property-ref (get refs ref-key)]
(when (and property-val property-ref)
(when-let [node (mf/ref-val property-ref)]
(case ref-key
(:s :alpha) (dom/set-value! node (math/round (* property-val 100)))
:hex (dom/set-value! node property-val)
(dom/set-value! node (math/round property-val)))))))))
[:div.color-values
[:input {:id "hex-value"
:ref (:hex refs)
:default-value hex
:on-change on-change-hex}]
(if (= type :rgb)
[:*
[:input {:id "red-value"
:ref (:r refs)
:type "number"
:min 0
:max 255
:default-value red
:on-change (on-change-property :r 255)}]
[:input {:id "green-value"
:ref (:g refs)
:type "number"
:min 0
:max 255
:default-value green
:on-change (on-change-property :g 255)}]
[:input {:id "blue-value"
:ref (:b refs)
:type "number"
:min 0
:max 255
:default-value blue
:on-change (on-change-property :b 255)}]]
[:*
[:input {:id "hue-value"
:ref (:h refs)
:type "number"
:min 0
:max 360
:default-value hue
:on-change (on-change-property :h 360)}]
[: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)}]
[:input {:id "value-value"
:ref (:v refs)
:type "number"
:min 0
:max 255
:default-value value
:on-change (on-change-property :v 255)}]])
[:input.alpha-value {:id "alpha-value"
:ref (:alpha refs)
:type "number"
:min 0
:step 1
:max 100
:default-value (if (= alpha :multiple) "" (math/precision alpha 2))
:on-change on-change-opacity}]
[:label.hex-label {:for "hex-value"} "HEX"]
(if (= type :rgb)
[:*
[:label.red-label {:for "red-value"} "R"]
[:label.green-label {:for "green-value"} "G"]
[:label.blue-label {:for "blue-value"} "B"]]
[:*
[:label.red-label {:for "hue-value"} "H"]
[:label.green-label {:for "saturation-value"} "S"]
[:label.blue-label {:for "value-value"} "V"]])
[:label.alpha-label {:for "alpha-value"} "A"]]))
(defn as-color-components [value opacity]
(let [value (if (uc/hex? value) value "#000000")
@ -108,12 +458,13 @@
{:hex (or value "000000")
:alpha (or opacity 1)
:r r :g g :b b
:h h :s s :v v}
))
:h h :s s :v v}))
(mf/defc colorpicker
[{:keys [value opacity on-change on-accept]}]
(let [current-color (mf/use-state (as-color-components value opacity))
active-tab (mf/use-state :ramp #_:harmony #_:hsva)
selected-library (mf/use-state "recent")
current-library-colors (mf/use-state [])
ref-picker (mf/use-ref)
@ -136,7 +487,16 @@
parse-selected (fn [selected]
(if (#{"recent" "file"} selected)
(keyword selected)
(uuid selected)) )]
(uuid selected)) )
change-tab (fn [tab] #(reset! active-tab tab))
handle-change-color (fn [changes]
(swap! current-color merge changes)
(when (:hex changes)
(reset! value-ref (:hex changes)))
(on-change (:hex changes (:hex @current-color))
(:alpha changes (:alpha @current-color))))]
;; Update state when there is a change in the props upstream
(mf/use-effect
@ -149,9 +509,19 @@
(mf/deps @current-color)
(fn [] (let [node (mf/ref-val ref-picker)
rgb [(:r @current-color) (:g @current-color) (:b @current-color)]
hue-rgb (uc/hsv->rgb [(:h @current-color) 1.0 255])]
hue-rgb (uc/hsv->rgb [(:h @current-color) 1.0 255])
hsl-from (uc/hsv->hsl [(:h @current-color) 0 (:v @current-color)])
hsl-to (uc/hsv->hsl [(:h @current-color) 1 (:v @current-color)])
format-hsl (fn [[h s l]]
(str/fmt "hsl(%s, %s, %s)"
h
(str (* s 100) "%")
(str (* l 100) "%")))]
(dom/set-css-property node "--color" (str/join ", " rgb))
(dom/set-css-property node "--hue" (str/join ", " hue-rgb)))))
(dom/set-css-property node "--hue-rgb" (str/join ", " hue-rgb))
(dom/set-css-property node "--saturation-grad-from" (format-hsl hsl-from))
(dom/set-css-property node "--saturation-grad-to" (format-hsl hsl-to)))))
;; Load library colors when the select is changed
(mf/use-effect
@ -204,168 +574,78 @@
(on-change (:hex @current-color) (:alpha @current-color) nil nil picked-shift?))))
[:div.colorpicker {:ref ref-picker}
[:div.top-actions
[:button.picker-btn
{:class (when picking-color? "active")
:on-click (fn []
(modal/allow-click-outside!)
(st/emit! (dwc/start-picker)))}
i/picker]]
[:div.colorpicker-content
[:div.top-actions
[:button.picker-btn
{:class (when picking-color? "active")
:on-click (fn []
(modal/allow-click-outside!)
(st/emit! (dwc/start-picker)))}
i/picker]
(if picking-color?
[:div.picker-detail-wrapper
[:div.center-circle]
[:canvas#picker-detail {:width 200
:height 160}]]
[:& value-selector {:hue (:h @current-color)
:saturation (:s @current-color)
:value (:v @current-color)
:on-change (fn [s v]
(let [hex (uc/hsv->hex [(:h @current-color) s v])
[r g b] (uc/hex->rgb hex)]
(swap! current-color assoc
:hex hex
:r r :g g :b b
:s s :v v)
(reset! value-ref hex)
(on-change hex (:alpha @current-color))))}])
(when (not picking-color?)
[:div.shade-selector
[:div.color-bullet]
[:& hue-selector {:hue (:h @current-color)
:on-change (fn [h]
(let [hex (uc/hsv->hex [h (:s @current-color) (:v @current-color)])
[r g b] (uc/hex->rgb hex)]
(swap! current-color assoc
:hex hex
:r r :g g :b b
:h h )
(reset! value-ref hex)
(on-change hex (:alpha @current-color))))}]
[:& opacity-selector {:opacity (:alpha @current-color)
:on-change (fn [alpha]
(swap! current-color assoc :alpha alpha)
(on-change (:hex @current-color) alpha))}]])
[:div.gradients-buttons
[:button.gradient.linear-gradient #_{:class "active"}]
[:button.gradient.radial-gradient]]]
[:div.color-values
[:input.hex-value {:id "hex-value"
:value (:hex @current-color)
:on-change (fn [e]
(let [val (-> e dom/get-target dom/get-value)
val (if (= (first val) \#) val (str \# val))]
(swap! current-color assoc :hex val)
(when (uc/hex? val)
(reset! value-ref val)
(let [[r g b] (uc/hex->rgb val)
[h s v] (uc/hex->hsv val)]
#_[:div.gradient-stops
[:div.gradient-background {:style {:background "linear-gradient(90deg, #EC0BE5, #CDCDCD)" }}]
[:div.gradient-stop-wrapper
[:div.gradient-stop.start {:style {:background-color "#EC0BE5"}}]
[:div.gradient-stop.end {:style {:background-color "#CDCDCD"
:left "100%"}}]]]
(if picking-color?
[:div.picker-detail-wrapper
[:div.center-circle]
[:canvas#picker-detail {:width 200 :height 160}]]
(case @active-tab
:ramp [:& ramp-selector {:color @current-color :on-change handle-change-color}]
:harmony [:& harmony-selector {:color @current-color :on-change handle-change-color}]
:hsva [:& hsva-selector {:color @current-color :on-change handle-change-color}]
nil))
[:& color-inputs {:type (if (= @active-tab :hsva) :hsv :rgb) :color @current-color :on-change handle-change-color}]
[:div.libraries
[:select {:on-change (fn [e]
(let [val (-> e dom/get-target dom/get-value)]
(reset! selected-library val)))
:value @selected-library}
[:option {:value "recent"} (t locale "workspace.libraries.colors.recent-colors")]
[:option {:value "file"} (t locale "workspace.libraries.colors.file-library")]
(for [[_ {:keys [name id]}] shared-libs]
[:option {:key id
:value id} name])]
[:div.selected-colors
(when (= "file" @selected-library)
[:div.color-bullet.button.plus-button {:style {:background-color "white"}
:on-click #(st/emit! (dwl/add-color (:hex @current-color)))}
i/plus])
[:div.color-bullet.button {:style {:background-color "white"}
:on-click #(st/emit! (dwc/show-palette (parse-selected @selected-library)))}
i/palette]
(for [[idx {:keys [id file-id value]}] (map-indexed vector @current-library-colors)]
[:div.color-bullet {:key (str "color-" idx)
:on-click (fn []
(swap! current-color assoc :hex value)
(reset! value-ref value)
(let [[r g b] (uc/hex->rgb value)
[h s v] (uc/hex->hsv value)]
(swap! current-color assoc
:r r :g g :b b
:h h :s s :v v)
(on-change val (:alpha @current-color))))))}]
[:input.red-value {:id "red-value"
:type "number"
:min 0
:max 255
:value (:r @current-color)
:on-change (fn [e]
(let [val (-> e dom/get-target dom/get-value (math/clamp 0 255))]
(swap! current-color assoc :r val)
(when (not (nil? val))
(let [{:keys [g b]} @current-color
hex (uc/rgb->hex [val g b])
[h s v] (uc/hex->hsv hex)]
(reset! value-ref hex)
(swap! current-color assoc
:hex hex
:h h :s s :v v)
(on-change hex (:alpha @current-color))))))}]
[:input.green-value {:id "green-value"
:type "number"
:min 0
:max 255
:value (:g @current-color)
:on-change (fn [e]
(let [val (-> e dom/get-target dom/get-value (math/clamp 0 255))]
(swap! current-color assoc :g val)
(when (not (nil? val))
(let [{:keys [r b]} @current-color
hex (uc/rgb->hex [r val b])
[h s v] (uc/hex->hsv hex)]
(reset! value-ref hex)
(swap! current-color assoc
:hex hex
:h h :s s :v v)
(on-change hex (:alpha @current-color))))))}]
[:input.blue-value {:id "blue-value"
:type "number"
:min 0
:max 255
:value (:b @current-color)
:on-change (fn [e]
(let [val (-> e dom/get-target dom/get-value (math/clamp 0 255))]
(swap! current-color assoc :b val)
(when (not (nil? val))
(let [{:keys [r g]} @current-color
hex (uc/rgb->hex [r g val])
[h s v] (uc/hex->hsv hex)]
(reset! value-ref hex)
(swap! current-color assoc
:hex hex
:h h :s s :v v)
(on-change hex (:alpha @current-color))))))}]
[:input.alpha-value {:id "alpha-value"
:type "number"
:min 0
:step 0.1
:max 1
:value (if (= (:alpha @current-color) :multiple)
""
(math/precision (:alpha @current-color) 2))
:on-change (fn [e]
(let [val (-> e dom/get-target dom/get-value (math/clamp 0 1))]
(swap! current-color assoc :alpha val)
(on-change (:hex @current-color) val)))}]
[:label.hex-label {:for "hex-value"} "HEX"]
[:label.red-label {:for "red-value"} "R"]
[:label.green-label {:for "green-value"} "G"]
[:label.blue-label {:for "blue-value"} "B"]
[:label.alpha-label {:for "alpha-value"} "A"]]
[:div.libraries
[:select {:on-change (fn [e]
(let [val (-> e dom/get-target dom/get-value)]
(reset! selected-library val)))
:value @selected-library}
[:option {:value "recent"} (t locale "workspace.libraries.colors.recent-colors")]
[:option {:value "file"} (t locale "workspace.libraries.colors.file-library")]
(for [[_ {:keys [name id]}] shared-libs]
[:option {:key id
:value id} name])]
[:div.selected-colors
(when (= "file" @selected-library)
[:div.color-bullet.button.plus-button {:style {:background-color "white"}
:on-click #(st/emit! (dwl/add-color (:hex @current-color)))}
i/plus])
[:div.color-bullet.button {:style {:background-color "white"}
:on-click #(st/emit! (dwc/show-palette (parse-selected @selected-library)))}
i/palette]
(for [[idx {:keys [id file-id value]}] (map-indexed vector @current-library-colors)]
[:div.color-bullet {:key (str "color-" idx)
:on-click (fn []
(swap! current-color assoc :hex value)
(reset! value-ref value)
(let [[r g b] (uc/hex->rgb value)
[h s v] (uc/hex->hsv value)]
(swap! current-color assoc
:r r :g g :b b
:h h :s s :v v)
(on-change value (:alpha @current-color) id file-id)))
:style {:background-color value}}])]
]
(on-change value (:alpha @current-color) id file-id)))
:style {:background-color value}}])]]]
[:div.colorpicker-tabs
[:div.colorpicker-tab {:class (when (= @active-tab :ramp) "active")
:on-click (change-tab :ramp)} i/picker-ramp]
[:div.colorpicker-tab {:class (when (= @active-tab :harmony) "active")
:on-click (change-tab :harmony)} i/picker-harmony]
[:div.colorpicker-tab {:class (when (= @active-tab :hsva) "active")
:on-click (change-tab :hsva)} i/picker-hsv]]
(when on-accept
[:div.actions
[:button.btn-primary.btn-large

View file

@ -91,6 +91,8 @@
(def checkboard "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAIAAAC0tAIdAAACvUlEQVQoFQGyAk39AeLi4gAAAAAAAB0dHQAAAAAAAOPj4wAAAAAAAB0dHQAAAAAAAOPj4wAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB////AAAAAAAA4+PjAAAAAAAAHR0dAAAAAAAA4+PjAAAAAAAAHR0dAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATj4+MAAAAAAAAdHR0AAAAAAADj4+MAAAAAAAAdHR0AAAAAAADj4+MAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjScaa0cU7nIAAAAASUVORK5CYII=")
#_(def checkboard "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAADFJREFUOE9jZGBgEAFifOANPknGUQMYhkkYEEgG+NMJKAwIAbwJbdQABnBCIgRoG4gAIF8IsXB/Rs4AAAAASUVORK5CYII=")
(mf/defc gradient-color-handler
[{:keys [filter-id zoom point color angle on-click on-mouse-down on-mouse-up]}]
[:g {:filter (str/fmt "url(#%s)" filter-id)

View file

@ -212,7 +212,7 @@
:resize-point [:> resize-point-handler props]
:resize-side [:> resize-side-handler props])))
(when (= :rect (:type shape))
#_(when (= :rect (:type shape))
[:& gradient-handlers {:shape tr-shape
:zoom zoom}])])))