use HSB grid for color picker; fixes #762

pull/768/head
Cory LaViska 2022-05-26 17:01:29 -04:00
rodzic 139073dc3e
commit 79306e0618
6 zmienionych plików z 76 dodań i 51 usunięć

Wyświetl plik

@ -20,6 +20,8 @@ _During the beta period, these restrictions may be relaxed in the event of a mis
- Improved behavior of clearable and password toggle buttons in `<sl-input>` and `<sl-select>` [#745](https://github.com/shoelace-style/shoelace/issues/745)
- Improved performance of `<sl-select>` by caching menu items instead of traversing for them each time
- Improved drag utility so initial click/touch events can be accepted [#758](https://github.com/shoelace-style/shoelace/issues/758)
- Improved `<sl-color-picker>` to use an HSB grid instead of HSL to be more consistent with existing color picker implementations [#762](https://github.com/shoelace-style/shoelace/issues/762)
- Improved `<sl-color-picker>` so the cursor is hidden and the preview is larger when dragging the grid
- Refactored `<sl-menu>` to be more performant by caching menu items on slot change
- Reverted form submit logic [#718](https://github.com/shoelace-style/shoelace/issues/718)
- Updated the `disabled` attribute so it reflects in `<sl-dropdown>` [#741](https://github.com/shoelace-style/shoelace/discussions/741)

Wyświetl plik

@ -39,14 +39,8 @@ export default css`
.color-picker__grid {
position: relative;
height: var(--grid-height);
background-image: linear-gradient(
to bottom,
hsl(0, 0%, 100%) 0%,
hsla(0, 0%, 100%, 0) 50%,
hsla(0, 0%, 0%, 0) 50%,
hsl(0, 0%, 0%) 100%
),
linear-gradient(to right, hsl(0, 0%, 50%) 0%, hsla(0, 0%, 50%, 0) 100%);
background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 100%),
linear-gradient(to right, #fff 0%, rgba(255, 255, 255, 0) 100%);
border-top-left-radius: var(--sl-border-radius-medium);
border-top-right-radius: var(--sl-border-radius-medium);
cursor: crosshair;
@ -61,6 +55,12 @@ export default css`
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) transform;
}
.color-picker__grid-handle--dragging {
cursor: none;
transform: scale(1.5);
}
.color-picker__grid-handle${focusVisibleSelector} {
@ -133,10 +133,10 @@ export default css`
align-items: center;
justify-content: center;
position: relative;
width: 3.25rem;
width: 2.25rem;
height: 2.25rem;
border: none;
border-radius: var(--sl-input-border-radius-medium);
border-radius: var(--sl-border-radius-circle);
background: none;
margin-left: var(--sl-spacing-small);
cursor: copy;

Wyświetl plik

@ -93,10 +93,12 @@ export default class SlColorPicker extends LitElement {
private lastValueEmitted: string;
private readonly localize = new LocalizeController(this);
@state() private isDraggingGridHandle = false;
@state() private inputValue = '';
@state() private hue = 0;
@state() private saturation = 100;
@state() private lightness = 100;
@state() private brightness = 100;
@state() private alpha = 100;
/** The current color. */
@ -211,6 +213,14 @@ export default class SlColorPicker extends LitElement {
}
}
getBrightness(lightness: number) {
return clamp(-1 * ((200 * lightness) / (this.saturation - 200)), 0, 100);
}
getLightness(brightness: number) {
return clamp(((((200 - this.saturation) * brightness) / 100) * 5) / 10, 0, 100);
}
/** Checks for validity and shows the browser's validation message if the control is invalid. */
reportValidity() {
// If the input is invalid, show the dropdown so the browser can focus on it
@ -262,14 +272,13 @@ export default class SlColorPicker extends LitElement {
handle.focus();
event.preventDefault();
drag(
container,
x => {
drag(container, {
onMove: x => {
this.alpha = clamp((x / width) * 100, 0, 100);
this.syncValues();
},
{ initialEvent: event }
);
initialEvent: event
});
}
handleHueDrag(event: PointerEvent) {
@ -280,14 +289,13 @@ export default class SlColorPicker extends LitElement {
handle.focus();
event.preventDefault();
drag(
container,
x => {
drag(container, {
onMove: x => {
this.hue = clamp((x / width) * 360, 0, 360);
this.syncValues();
},
{ initialEvent: event }
);
initialEvent: event
});
}
handleGridDrag(event: PointerEvent) {
@ -298,17 +306,19 @@ export default class SlColorPicker extends LitElement {
handle.focus();
event.preventDefault();
drag(
grid,
(x, y) => {
this.isDraggingGridHandle = true;
drag(grid, {
onMove: (x, y) => {
this.saturation = clamp((x / width) * 100, 0, 100);
this.lightness = clamp(100 - (y / height) * 100, 0, 100);
this.brightness = clamp(100 - (y / height) * 100, 0, 100);
this.lightness = this.getLightness(this.brightness);
this.syncValues();
},
{
initialEvent: event
}
);
onStop: () => (this.isDraggingGridHandle = false),
initialEvent: event
});
}
handleAlphaKeyDown(event: KeyboardEvent) {
@ -384,13 +394,17 @@ export default class SlColorPicker extends LitElement {
if (event.key === 'ArrowUp') {
event.preventDefault();
this.lightness = clamp(this.lightness + increment, 0, 100);
this.brightness = clamp(this.brightness + increment, 0, 100);
this.lightness = this.getLightness(this.brightness);
console.log(this.lightness, this.brightness);
this.syncValues();
}
if (event.key === 'ArrowDown') {
event.preventDefault();
this.lightness = clamp(this.lightness - increment, 0, 100);
this.brightness = clamp(this.brightness - increment, 0, 100);
this.lightness = this.getLightness(this.brightness);
console.log(this.lightness, this.brightness);
this.syncValues();
}
}
@ -543,6 +557,7 @@ export default class SlColorPicker extends LitElement {
this.hue = newColor.hsla.h;
this.saturation = newColor.hsla.s;
this.lightness = newColor.hsla.l;
this.brightness = this.getBrightness(newColor.hsla.l);
this.alpha = this.opacity ? newColor.hsla.a * 100 : 100;
this.syncValues();
@ -623,6 +638,7 @@ export default class SlColorPicker extends LitElement {
this.hue = newColor.hsla.h;
this.saturation = newColor.hsla.s;
this.lightness = newColor.hsla.l;
this.brightness = this.getBrightness(newColor.hsla.l);
this.alpha = newColor.hsla.a * 100;
} else {
this.inputValue = oldValue;
@ -636,8 +652,8 @@ export default class SlColorPicker extends LitElement {
}
render() {
const x = this.saturation;
const y = 100 - this.lightness;
const gridHandleX = this.saturation;
const gridHandleY = 100 - this.brightness;
const colorPicker = html`
<div
@ -668,10 +684,13 @@ export default class SlColorPicker extends LitElement {
>
<span
part="grid-handle"
class="color-picker__grid-handle"
class=${classMap({
'color-picker__grid-handle': true,
'color-picker__grid-handle--dragging': this.isDraggingGridHandle
})}
style=${styleMap({
top: `${y}%`,
left: `${x}%`,
top: `${gridHandleY}%`,
left: `${gridHandleX}%`,
backgroundColor: `hsla(${this.hue}deg, ${this.saturation}%, ${this.lightness}%)`
})}
role="application"

Wyświetl plik

@ -44,15 +44,12 @@ export default class SlImageComparer extends LitElement {
event.preventDefault();
drag(
this.base,
x => {
drag(this.base, {
onMove: x => {
this.position = parseFloat(clamp((x / width) * 100, 0, 100).toFixed(2));
},
{
initialEvent: event
}
);
initialEvent: event
});
}
handleKeyDown(event: KeyboardEvent) {

Wyświetl plik

@ -108,9 +108,8 @@ export default class SlSplitPanel extends LitElement {
// Prevent text selection when dragging
event.preventDefault();
drag(
this,
(x, y) => {
drag(this, {
onMove: (x, y) => {
let newPositionInPixels = this.vertical ? y : x;
// Flip for end panels
@ -142,10 +141,8 @@ export default class SlSplitPanel extends LitElement {
this.position = clamp(this.pixelsToPercentage(newPositionInPixels), 0, 100);
},
{
initialEvent: event
}
);
initialEvent: event
});
}
handleKeyDown(event: KeyboardEvent) {

Wyświetl plik

@ -1,4 +1,8 @@
interface DragOptions {
/** Callback that runs as dragging occurs. */
onMove: (x: number, y: number) => void;
/** Callback that runs when dragging stops. */
onStop: () => void;
/**
* When an initial event is passed, the first drag will be triggered immediately using the coordinates therein. This
* is useful when the drag is initiated by a mousedown/touchstart event but you want the initial "click" to activate
@ -7,7 +11,7 @@ interface DragOptions {
initialEvent: PointerEvent;
}
export function drag(container: HTMLElement, onMove: (x: number, y: number) => void, options?: Partial<DragOptions>) {
export function drag(container: HTMLElement, options?: Partial<DragOptions>) {
function move(pointerEvent: PointerEvent) {
const dims = container.getBoundingClientRect();
const defaultView = container.ownerDocument.defaultView!;
@ -16,12 +20,18 @@ export function drag(container: HTMLElement, onMove: (x: number, y: number) => v
const x = pointerEvent.pageX - offsetX;
const y = pointerEvent.pageY - offsetY;
onMove(x, y);
if (options?.onMove) {
options.onMove(x, y);
}
}
function stop() {
document.removeEventListener('pointermove', move);
document.removeEventListener('pointerup', stop);
if (options?.onStop) {
options.onStop();
}
}
document.addEventListener('pointermove', move, { passive: true });