Moar progress

This commit is contained in:
Cory LaViska
2020-05-07 16:40:49 -04:00
parent a1c3118be8
commit a935332d44
6 changed files with 189 additions and 42 deletions

6
package-lock.json generated
View File

@@ -130,9 +130,9 @@
"integrity": "sha512-sLqWxCzC5/QHLhziXSCAksBxHfOnQlhPRVgPK0egEw+ktWvG75T2k+aYWVjVh9+WKeT3tlG3ZNbZQvZLmfuOIw=="
},
"@stencil/core": {
"version": "1.12.6",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-1.12.6.tgz",
"integrity": "sha512-fOHG9x92wxlQOlp9YRouLlejFShtfvZEx4LFjNi7bqTX/nHTetdv3Ir+J+SanebjBuJgKiAmLIwXviQ0Geq23w==",
"version": "1.12.7",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-1.12.7.tgz",
"integrity": "sha512-Ob+xncjuHebjSHqOGh9acXx0Jw2rSTzGP1XiYNKYQnoFtcvKpGqLhr2ibWqS1Z40aEwgI/OvYYldfWsp/C+0mw==",
"requires": {
"typescript": "3.8.3"
}

View File

@@ -41,7 +41,7 @@
"license": "MIT",
"dependencies": {
"@popperjs/core": "^2.1.1",
"@stencil/core": "^1.12.6",
"@stencil/core": "^1.12.7",
"@stencil/sass": "^1.1.1",
"color-convert": "^2.0.1",
"feather-icons": "^4.28.0",

8
src/components.d.ts vendored
View File

@@ -97,6 +97,10 @@ export namespace Components {
"value": string;
}
interface SlColorPicker {
/**
* The format to use for the generated color `value`. If opacity is enabled, these will translate to HEXA, RGBA, and HSLA respectively. Note that browser support for HEXA doesn't include pre-Chromium Edge, so it's usually safer to use RGBA or HSLA when using opacity.
*/
"format": "hex" | "rgb" | "hsl";
/**
* Whether to show the opacity slider.
*/
@@ -836,6 +840,10 @@ declare namespace LocalJSX {
"value"?: string;
}
interface SlColorPicker {
/**
* The format to use for the generated color `value`. If opacity is enabled, these will translate to HEXA, RGBA, and HSLA respectively. Note that browser support for HEXA doesn't include pre-Chromium Edge, so it's usually safer to use RGBA or HSLA when using opacity.
*/
"format"?: "hex" | "rgb" | "hsl";
/**
* Whether to show the opacity slider.
*/

View File

@@ -59,7 +59,8 @@
border: solid 2px white;
margin-top: calc(var(--grid-handle-size) / -2);
margin-left: calc(var(--grid-handle-size) / -2);
transition: var(--sl-transition-fast) box-shadow;
transition: var(--sl-transition-fast) box-shadow, var(--sl-transition-x-fast) top ease,
var(--sl-transition-x-fast) left ease;
&:focus {
outline: none;
@@ -99,7 +100,8 @@
border-radius: 50%;
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25);
margin-left: calc(var(--slider-handle-size) / -2);
transition: var(--sl-transition-fast) box-shadow;
transition: var(--sl-transition-fast) box-shadow, var(--sl-transition-x-fast) top ease,
var(--sl-transition-x-fast) left ease;
cursor: pointer;
&:focus {
@@ -123,11 +125,6 @@
}
.sl-color-picker__alpha {
background-image: linear-gradient(45deg, #eee 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #eee 75%),
linear-gradient(45deg, transparent 75%, #eee 75%), linear-gradient(45deg, #eee 25%, transparent 25%);
background-size: 10px 10px;
background-position: 0 0, 0 0, -5px -5px, 5px 5px;
.sl-color-picker__alpha-gradient {
position: absolute;
top: 0;
@@ -143,10 +140,6 @@
position: relative;
width: 2rem;
height: 2rem;
background-image: linear-gradient(45deg, #eee 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #eee 75%),
linear-gradient(45deg, transparent 75%, #eee 75%), linear-gradient(45deg, #eee 25%, transparent 25%);
background-size: 10px 10px;
background-position: 0 0, 0 0, -5px -5px, 5px 5px;
border-radius: 50%;
margin-left: var(--sl-spacing-medium);
@@ -188,14 +181,31 @@
.sl-color-picker__swatch {
flex: 0 0 auto;
position: relative;
width: 20px;
height: 20px;
border-radius: 2px;
border: solid 1px rgba(0, 0, 0, 0.125);
cursor: pointer;
.sl-color-picker__swatch-color {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: solid 1px rgba(0, 0, 0, 0.125);
border-radius: inherit;
cursor: pointer;
}
&:focus {
outline: none;
box-shadow: var(--sl-focus-ring-box-shadow);
}
}
.sl-color-picker__transparent-bg {
background-image: linear-gradient(45deg, #eee 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #eee 75%),
linear-gradient(45deg, transparent 75%, #eee 75%), linear-gradient(45deg, #eee 25%, transparent 25%);
background-size: 10px 10px;
background-position: 0 0, 0 0, -5px -5px, 5px 5px;
}

View File

@@ -29,16 +29,24 @@ export class ColorPicker {
this.handleGridKeyDown = this.handleGridKeyDown.bind(this);
this.handleHueDrag = this.handleHueDrag.bind(this);
this.handleHueKeyDown = this.handleHueKeyDown.bind(this);
this.handleUserInput = this.handleUserInput.bind(this);
}
@State() hue = 0;
@State() saturation = 100;
@State() lightness = 50;
@State() lightness = 100;
@State() alpha = 100;
/** The current color. */
@Prop({ mutable: true, reflect: true }) value = '';
/**
* The format to use for the generated color `value`. If opacity is enabled, these will translate to HEXA, RGBA, and
* HSLA respectively. Note that browser support for HEXA doesn't include pre-Chromium Edge, so it's usually safer to
* use RGBA or HSLA when using opacity.
*/
@Prop() format: 'hex' | 'rgb' | 'hsl' = 'hex';
/** Whether to show the opacity slider. */
@Prop() opacity = false;
@@ -62,6 +70,46 @@ export class ColorPicker {
'#fff'
];
componentDidLoad() {
this.syncValue();
}
getHex() {
const hsl = [this.hue, this.saturation, this.lightness];
const hex = convert.hsl.hex(hsl);
const alpha = Math.ceil((this.alpha * 255) / 100 + 0x10000)
.toString(16)
.substr(-2)
.toUpperCase();
if (this.opacity) {
return `#${hex}${alpha}`;
} else {
return `#${hex}`;
}
}
getRGB() {
const hsl = [this.hue, this.saturation, this.lightness];
const rgb = convert.hsl.rgb(hsl);
if (this.opacity) {
return `rgba(${rgb[0]}, ${rgb[1]}, ${rgb[2]}, ${this.alpha}%)`;
} else {
return `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`;
}
}
getHSL() {
const hsl = [this.hue, this.saturation, this.lightness];
if (this.opacity) {
return `hsla(${hsl[0]}, ${hsl[1]}%, ${hsl[2]}%, ${this.alpha}%)`;
} else {
return `hsl(${hsl[0]}, ${hsl[1]}%, ${hsl[2]}%)`;
}
}
handleHueInput(event: Event) {
const target = event.target as HTMLInputElement;
this.hue = clamp(Number(target.value), 0, 360);
@@ -91,6 +139,7 @@ export class ColorPicker {
this.handleDrag(event, container, x => {
this.alpha = clamp(Math.round((x / width) * 100), 0, 100);
this.syncValue();
});
}
@@ -103,6 +152,7 @@ export class ColorPicker {
this.handleDrag(event, container, x => {
this.hue = clamp(Math.round((x / width) * 360), 0, 360);
this.syncValue();
});
}
@@ -116,6 +166,7 @@ export class ColorPicker {
this.handleDrag(event, container, (x, y) => {
this.saturation = clamp(Math.round((x / width) * 100), 0, 100);
this.lightness = clamp(Math.round(100 - (y / height) * 100), 0, 100);
this.syncValue();
});
}
@@ -215,26 +266,92 @@ export class ColorPicker {
}
}
handleUserInput(event: KeyboardEvent) {
const target = event.target as HTMLInputElement;
// this.setColor(target.value);
}
parseColor(color: string) {
const hexPattern = /#?([a-f0-9]{1,2})([a-f0-9]{1,2})([a-f0-9]{1,2})([a-f0-9]{1,2})?/i;
let hue = 0;
let saturation = 0;
let lightness = 0;
let alpha = 100;
color = color.trim().toLowerCase();
// Parse RGB
if (/^rgba?/i.test(color)) {
const rgb = color.replace(/[^\d,.%]/g, '').split(',');
[hue, saturation, lightness] = convert.rgb.hsl(rgb);
if (rgb[3] && rgb[3].indexOf('%') > -1) {
alpha = Number(rgb[3].replace('%', ''));
} else if (rgb[3]) {
alpha = Number(rgb[3]) * 100;
}
}
// Parse HSL
if (/^hsla?/i.test(color)) {
const hsl = color.replace(/[^\d,.%]/g, '').split(',');
hue = Number(hsl[0]);
saturation = Number(hsl[0]);
lightness = Number(hsl[0]);
if (hsl[3] && hsl[3].indexOf('%') > -1) {
alpha = Number(hsl[3].replace('%', ''));
} else if (hsl[3]) {
alpha = Number(hsl[3]) * 100;
}
}
// Parse hex
if (hexPattern.test(color)) {
const hex = color.match(hexPattern).slice(1, 5);
[hue, saturation, lightness] = convert.hex.hsl(hex.join(''));
if (hex[3]) {
alpha = Math.round((parseInt(hex[3], 16) / 255) * 100);
console.log(hex[3], alpha);
// const alpha = (Math.round((this.alpha * 255) / 100) + 0x10000).toString(16).substr(-2).toUpperCase();
}
}
return {
hue,
saturation,
lightness,
alpha
};
}
setColor(color: string) {
//
// TODO:
//
// - detect what format the color is in
// - parse it
// - convert to HSL and update HSLA
//
const hsla = this.parseColor(color);
this.hue = hsla.hue;
this.saturation = hsla.saturation;
this.lightness = hsla.lightness;
this.alpha = this.opacity ? hsla.alpha : 100;
this.syncValue();
}
syncValue() {
if (this.format === 'hsl') {
this.value = this.getHSL();
} else if (this.format === 'rgb') {
this.value = this.getRGB();
} else {
this.value = this.getHex();
}
}
render() {
const hsl = [this.hue, this.saturation, this.lightness];
// const rgb = convert.hsl.rgb(hsl);
const hex = convert.hsl.hex(hsl);
const x = this.saturation;
const y = 100 - this.lightness;
return (
<div ref={el => (this.trigger = el)} class="sl-color-picker">
<div class="sl-color-picker__trigger">Trigger</div>
<div ref={el => (this.menu = el)} class="sl-color-picker__menu">
<div
ref={el => (this.grid = el)}
@@ -286,7 +403,7 @@ export class ColorPicker {
{this.opacity && (
<div
ref={el => (this.alphaSlider = el)}
class="sl-color-picker__alpha sl-color-picker__slider"
class="sl-color-picker__alpha sl-color-picker__slider sl-color-picker__transparent-bg"
onMouseDown={this.handleAlphaDrag}
onTouchStart={this.handleAlphaDrag}
>
@@ -319,7 +436,7 @@ export class ColorPicker {
</div>
<div
class="sl-color-picker__preview"
class="sl-color-picker__preview sl-color-picker__transparent-bg"
style={{
color: `hsla(${this.hue}deg, ${this.saturation}%, ${this.lightness}%, ${this.alpha}%)`
}}
@@ -327,17 +444,28 @@ export class ColorPicker {
</div>
<div class="sl-color-picker__inputs">
<div class="sl-color-picker__input sl-color-picker__input--hex">
<sl-input size="small" type="text" pattern="[a-fA-F\d]+" value={hex}>
<span slot="prefix">#</span>
</sl-input>
<div class="sl-color-picker__input">
<sl-input
size="small"
type="text"
pattern="[a-fA-F\d]+"
value={this.value}
onInput={this.handleUserInput}
/>
</div>
</div>
{this.swatches && (
<div class="sl-color-picker__swatches">
{this.swatches.map(swatch => (
<div class="sl-color-picker__swatch" style={{ backgroundColor: swatch }} tabIndex={0}></div>
// @ts-ignore
<div
class="sl-color-picker__swatch sl-color-picker__transparent-bg"
tabIndex={0}
onClick={() => this.setColor(swatch)} // eslint-disable-line
>
<div class="sl-color-picker__swatch-color" style={{ backgroundColor: swatch }} />
</div>
))}
</div>
)}

View File

@@ -1,7 +1,7 @@
# Color Picker
```html preview
<sl-color-picker opacity></sl-color-picker>
<sl-color-picker opacity format="hex"></sl-color-picker>
```
@@ -10,11 +10,12 @@
## Properties
| Property | Attribute | Description | Type | Default |
| ---------- | --------- | ------------------------------------------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `opacity` | `opacity` | Whether to show the opacity slider. | `boolean` | `false` |
| `swatches` | -- | An array of predefined color swatches to display. | `string[]` | `[ '#d0021b', '#f5a623', '#f8e71c', '#8b572a', '#7ed321', '#417505', '#bd10e0', '#9013fe', '#4a90e2', '#50e3c2', '#b8e986', '#000', '#444', '#888', '#ccc', '#fff' ]` |
| `value` | `value` | The current color. | `string` | `''` |
| Property | Attribute | Description | Type | Default |
| ---------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `format` | `format` | The format to use for the generated color `value`. If opacity is enabled, these will translate to HEXA, RGBA, and HSLA respectively. Note that browser support for HEXA doesn't include pre-Chromium Edge, so it's usually safer to use RGBA or HSLA when using opacity. | `"hex" \| "hsl" \| "rgb"` | `'hex'` |
| `opacity` | `opacity` | Whether to show the opacity slider. | `boolean` | `false` |
| `swatches` | -- | An array of predefined color swatches to display. | `string[]` | `[ '#d0021b', '#f5a623', '#f8e71c', '#8b572a', '#7ed321', '#417505', '#bd10e0', '#9013fe', '#4a90e2', '#50e3c2', '#b8e986', '#000', '#444', '#888', '#ccc', '#fff' ]` |
| `value` | `value` | The current color. | `string` | `''` |
## Dependencies