diff --git a/docs/resources/changelog.md b/docs/resources/changelog.md index 0dfce793..3785663b 100644 --- a/docs/resources/changelog.md +++ b/docs/resources/changelog.md @@ -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 `` and `` [#745](https://github.com/shoelace-style/shoelace/issues/745) - Improved performance of `` 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 `` 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 `` so the cursor is hidden and the preview is larger when dragging the grid - Refactored `` 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 `` [#741](https://github.com/shoelace-style/shoelace/discussions/741) diff --git a/src/components/color-picker/color-picker.styles.ts b/src/components/color-picker/color-picker.styles.ts index 75ad1862..279446e6 100644 --- a/src/components/color-picker/color-picker.styles.ts +++ b/src/components/color-picker/color-picker.styles.ts @@ -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; diff --git a/src/components/color-picker/color-picker.ts b/src/components/color-picker/color-picker.ts index 1dec905e..979760fc 100644 --- a/src/components/color-picker/color-picker.ts +++ b/src/components/color-picker/color-picker.ts @@ -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`
{ + drag(this.base, { + onMove: x => { this.position = parseFloat(clamp((x / width) * 100, 0, 100).toFixed(2)); }, - { - initialEvent: event - } - ); + initialEvent: event + }); } handleKeyDown(event: KeyboardEvent) { diff --git a/src/components/split-panel/split-panel.ts b/src/components/split-panel/split-panel.ts index d19e8657..203c8f01 100644 --- a/src/components/split-panel/split-panel.ts +++ b/src/components/split-panel/split-panel.ts @@ -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) { diff --git a/src/internal/drag.ts b/src/internal/drag.ts index 5e731e1f..5e67b422 100644 --- a/src/internal/drag.ts +++ b/src/internal/drag.ts @@ -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) { +export function drag(container: HTMLElement, options?: Partial) { 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 });