kopia lustrzana https://github.com/shoelace-style/shoelace
				
				
				
			use HSB grid for color picker; fixes #762
							rodzic
							
								
									139073dc3e
								
							
						
					
					
						commit
						79306e0618
					
				|  | @ -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 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 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 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 | - 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) | - 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) | - Updated the `disabled` attribute so it reflects in `<sl-dropdown>` [#741](https://github.com/shoelace-style/shoelace/discussions/741) | ||||||
|  |  | ||||||
|  | @ -39,14 +39,8 @@ export default css` | ||||||
|   .color-picker__grid { |   .color-picker__grid { | ||||||
|     position: relative; |     position: relative; | ||||||
|     height: var(--grid-height); |     height: var(--grid-height); | ||||||
|     background-image: linear-gradient( |     background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 100%), | ||||||
|         to bottom, |       linear-gradient(to right, #fff 0%, rgba(255, 255, 255, 0) 100%); | ||||||
|         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%); |  | ||||||
|     border-top-left-radius: var(--sl-border-radius-medium); |     border-top-left-radius: var(--sl-border-radius-medium); | ||||||
|     border-top-right-radius: var(--sl-border-radius-medium); |     border-top-right-radius: var(--sl-border-radius-medium); | ||||||
|     cursor: crosshair; |     cursor: crosshair; | ||||||
|  | @ -61,6 +55,12 @@ export default css` | ||||||
|     border: solid 2px white; |     border: solid 2px white; | ||||||
|     margin-top: calc(var(--grid-handle-size) / -2); |     margin-top: calc(var(--grid-handle-size) / -2); | ||||||
|     margin-left: 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} { |   .color-picker__grid-handle${focusVisibleSelector} { | ||||||
|  | @ -133,10 +133,10 @@ export default css` | ||||||
|     align-items: center; |     align-items: center; | ||||||
|     justify-content: center; |     justify-content: center; | ||||||
|     position: relative; |     position: relative; | ||||||
|     width: 3.25rem; |     width: 2.25rem; | ||||||
|     height: 2.25rem; |     height: 2.25rem; | ||||||
|     border: none; |     border: none; | ||||||
|     border-radius: var(--sl-input-border-radius-medium); |     border-radius: var(--sl-border-radius-circle); | ||||||
|     background: none; |     background: none; | ||||||
|     margin-left: var(--sl-spacing-small); |     margin-left: var(--sl-spacing-small); | ||||||
|     cursor: copy; |     cursor: copy; | ||||||
|  |  | ||||||
|  | @ -93,10 +93,12 @@ export default class SlColorPicker extends LitElement { | ||||||
|   private lastValueEmitted: string; |   private lastValueEmitted: string; | ||||||
|   private readonly localize = new LocalizeController(this); |   private readonly localize = new LocalizeController(this); | ||||||
| 
 | 
 | ||||||
|  |   @state() private isDraggingGridHandle = false; | ||||||
|   @state() private inputValue = ''; |   @state() private inputValue = ''; | ||||||
|   @state() private hue = 0; |   @state() private hue = 0; | ||||||
|   @state() private saturation = 100; |   @state() private saturation = 100; | ||||||
|   @state() private lightness = 100; |   @state() private lightness = 100; | ||||||
|  |   @state() private brightness = 100; | ||||||
|   @state() private alpha = 100; |   @state() private alpha = 100; | ||||||
| 
 | 
 | ||||||
|   /** The current color. */ |   /** 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. */ |   /** Checks for validity and shows the browser's validation message if the control is invalid. */ | ||||||
|   reportValidity() { |   reportValidity() { | ||||||
|     // If the input is invalid, show the dropdown so the browser can focus on it
 |     // 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(); |     handle.focus(); | ||||||
|     event.preventDefault(); |     event.preventDefault(); | ||||||
| 
 | 
 | ||||||
|     drag( |     drag(container, { | ||||||
|       container, |       onMove: x => { | ||||||
|       x => { |  | ||||||
|         this.alpha = clamp((x / width) * 100, 0, 100); |         this.alpha = clamp((x / width) * 100, 0, 100); | ||||||
|         this.syncValues(); |         this.syncValues(); | ||||||
|       }, |       }, | ||||||
|       { initialEvent: event } |       initialEvent: event | ||||||
|     ); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   handleHueDrag(event: PointerEvent) { |   handleHueDrag(event: PointerEvent) { | ||||||
|  | @ -280,14 +289,13 @@ export default class SlColorPicker extends LitElement { | ||||||
|     handle.focus(); |     handle.focus(); | ||||||
|     event.preventDefault(); |     event.preventDefault(); | ||||||
| 
 | 
 | ||||||
|     drag( |     drag(container, { | ||||||
|       container, |       onMove: x => { | ||||||
|       x => { |  | ||||||
|         this.hue = clamp((x / width) * 360, 0, 360); |         this.hue = clamp((x / width) * 360, 0, 360); | ||||||
|         this.syncValues(); |         this.syncValues(); | ||||||
|       }, |       }, | ||||||
|       { initialEvent: event } |       initialEvent: event | ||||||
|     ); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   handleGridDrag(event: PointerEvent) { |   handleGridDrag(event: PointerEvent) { | ||||||
|  | @ -298,17 +306,19 @@ export default class SlColorPicker extends LitElement { | ||||||
|     handle.focus(); |     handle.focus(); | ||||||
|     event.preventDefault(); |     event.preventDefault(); | ||||||
| 
 | 
 | ||||||
|     drag( |     this.isDraggingGridHandle = true; | ||||||
|       grid, | 
 | ||||||
|       (x, y) => { |     drag(grid, { | ||||||
|  |       onMove: (x, y) => { | ||||||
|         this.saturation = clamp((x / width) * 100, 0, 100); |         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(); |         this.syncValues(); | ||||||
|       }, |       }, | ||||||
|       { |       onStop: () => (this.isDraggingGridHandle = false), | ||||||
|         initialEvent: event |       initialEvent: event | ||||||
|       } |     }); | ||||||
|     ); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   handleAlphaKeyDown(event: KeyboardEvent) { |   handleAlphaKeyDown(event: KeyboardEvent) { | ||||||
|  | @ -384,13 +394,17 @@ export default class SlColorPicker extends LitElement { | ||||||
| 
 | 
 | ||||||
|     if (event.key === 'ArrowUp') { |     if (event.key === 'ArrowUp') { | ||||||
|       event.preventDefault(); |       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(); |       this.syncValues(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (event.key === 'ArrowDown') { |     if (event.key === 'ArrowDown') { | ||||||
|       event.preventDefault(); |       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(); |       this.syncValues(); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | @ -543,6 +557,7 @@ export default class SlColorPicker extends LitElement { | ||||||
|     this.hue = newColor.hsla.h; |     this.hue = newColor.hsla.h; | ||||||
|     this.saturation = newColor.hsla.s; |     this.saturation = newColor.hsla.s; | ||||||
|     this.lightness = newColor.hsla.l; |     this.lightness = newColor.hsla.l; | ||||||
|  |     this.brightness = this.getBrightness(newColor.hsla.l); | ||||||
|     this.alpha = this.opacity ? newColor.hsla.a * 100 : 100; |     this.alpha = this.opacity ? newColor.hsla.a * 100 : 100; | ||||||
| 
 | 
 | ||||||
|     this.syncValues(); |     this.syncValues(); | ||||||
|  | @ -623,6 +638,7 @@ export default class SlColorPicker extends LitElement { | ||||||
|         this.hue = newColor.hsla.h; |         this.hue = newColor.hsla.h; | ||||||
|         this.saturation = newColor.hsla.s; |         this.saturation = newColor.hsla.s; | ||||||
|         this.lightness = newColor.hsla.l; |         this.lightness = newColor.hsla.l; | ||||||
|  |         this.brightness = this.getBrightness(newColor.hsla.l); | ||||||
|         this.alpha = newColor.hsla.a * 100; |         this.alpha = newColor.hsla.a * 100; | ||||||
|       } else { |       } else { | ||||||
|         this.inputValue = oldValue; |         this.inputValue = oldValue; | ||||||
|  | @ -636,8 +652,8 @@ export default class SlColorPicker extends LitElement { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   render() { |   render() { | ||||||
|     const x = this.saturation; |     const gridHandleX = this.saturation; | ||||||
|     const y = 100 - this.lightness; |     const gridHandleY = 100 - this.brightness; | ||||||
| 
 | 
 | ||||||
|     const colorPicker = html` |     const colorPicker = html` | ||||||
|       <div |       <div | ||||||
|  | @ -668,10 +684,13 @@ export default class SlColorPicker extends LitElement { | ||||||
|         > |         > | ||||||
|           <span |           <span | ||||||
|             part="grid-handle" |             part="grid-handle" | ||||||
|             class="color-picker__grid-handle" |             class=${classMap({ | ||||||
|  |               'color-picker__grid-handle': true, | ||||||
|  |               'color-picker__grid-handle--dragging': this.isDraggingGridHandle | ||||||
|  |             })} | ||||||
|             style=${styleMap({ |             style=${styleMap({ | ||||||
|               top: `${y}%`, |               top: `${gridHandleY}%`, | ||||||
|               left: `${x}%`, |               left: `${gridHandleX}%`, | ||||||
|               backgroundColor: `hsla(${this.hue}deg, ${this.saturation}%, ${this.lightness}%)` |               backgroundColor: `hsla(${this.hue}deg, ${this.saturation}%, ${this.lightness}%)` | ||||||
|             })} |             })} | ||||||
|             role="application" |             role="application" | ||||||
|  |  | ||||||
|  | @ -44,15 +44,12 @@ export default class SlImageComparer extends LitElement { | ||||||
| 
 | 
 | ||||||
|     event.preventDefault(); |     event.preventDefault(); | ||||||
| 
 | 
 | ||||||
|     drag( |     drag(this.base, { | ||||||
|       this.base, |       onMove: x => { | ||||||
|       x => { |  | ||||||
|         this.position = parseFloat(clamp((x / width) * 100, 0, 100).toFixed(2)); |         this.position = parseFloat(clamp((x / width) * 100, 0, 100).toFixed(2)); | ||||||
|       }, |       }, | ||||||
|       { |       initialEvent: event | ||||||
|         initialEvent: event |     }); | ||||||
|       } |  | ||||||
|     ); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   handleKeyDown(event: KeyboardEvent) { |   handleKeyDown(event: KeyboardEvent) { | ||||||
|  |  | ||||||
|  | @ -108,9 +108,8 @@ export default class SlSplitPanel extends LitElement { | ||||||
|     // Prevent text selection when dragging
 |     // Prevent text selection when dragging
 | ||||||
|     event.preventDefault(); |     event.preventDefault(); | ||||||
| 
 | 
 | ||||||
|     drag( |     drag(this, { | ||||||
|       this, |       onMove: (x, y) => { | ||||||
|       (x, y) => { |  | ||||||
|         let newPositionInPixels = this.vertical ? y : x; |         let newPositionInPixels = this.vertical ? y : x; | ||||||
| 
 | 
 | ||||||
|         // Flip for end panels
 |         // Flip for end panels
 | ||||||
|  | @ -142,10 +141,8 @@ export default class SlSplitPanel extends LitElement { | ||||||
| 
 | 
 | ||||||
|         this.position = clamp(this.pixelsToPercentage(newPositionInPixels), 0, 100); |         this.position = clamp(this.pixelsToPercentage(newPositionInPixels), 0, 100); | ||||||
|       }, |       }, | ||||||
|       { |       initialEvent: event | ||||||
|         initialEvent: event |     }); | ||||||
|       } |  | ||||||
|     ); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   handleKeyDown(event: KeyboardEvent) { |   handleKeyDown(event: KeyboardEvent) { | ||||||
|  |  | ||||||
|  | @ -1,4 +1,8 @@ | ||||||
| interface DragOptions { | 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 |    * 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 |    * 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; |   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) { |   function move(pointerEvent: PointerEvent) { | ||||||
|     const dims = container.getBoundingClientRect(); |     const dims = container.getBoundingClientRect(); | ||||||
|     const defaultView = container.ownerDocument.defaultView!; |     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 x = pointerEvent.pageX - offsetX; | ||||||
|     const y = pointerEvent.pageY - offsetY; |     const y = pointerEvent.pageY - offsetY; | ||||||
| 
 | 
 | ||||||
|     onMove(x, y); |     if (options?.onMove) { | ||||||
|  |       options.onMove(x, y); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function stop() { |   function stop() { | ||||||
|     document.removeEventListener('pointermove', move); |     document.removeEventListener('pointermove', move); | ||||||
|     document.removeEventListener('pointerup', stop); |     document.removeEventListener('pointerup', stop); | ||||||
|  | 
 | ||||||
|  |     if (options?.onStop) { | ||||||
|  |       options.onStop(); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   document.addEventListener('pointermove', move, { passive: true }); |   document.addEventListener('pointermove', move, { passive: true }); | ||||||
|  |  | ||||||
		Ładowanie…
	
		Reference in New Issue
	
	 Cory LaViska
						Cory LaViska