-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(module:color-picker): built-in color-picker package (#8428)
- Loading branch information
1 parent
987a799
commit 534fe62
Showing
22 changed files
with
1,158 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
57 changes: 57 additions & 0 deletions
57
components/color-picker/src/components/gradient.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/** | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE | ||
*/ | ||
|
||
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; | ||
|
||
import { Color } from '../interfaces/color'; | ||
import { HsbaColorType } from '../interfaces/type'; | ||
import { generateColor } from '../util/util'; | ||
|
||
@Component({ | ||
// eslint-disable-next-line @angular-eslint/component-selector | ||
selector: 'color-gradient', | ||
standalone: true, | ||
template: ` | ||
<div | ||
class="ant-color-picker-gradient" | ||
style="position: absolute; inset: 0" | ||
[style.background]="'linear-gradient(' + direction + ', ' + gradientColors + ')'" | ||
> | ||
<ng-content></ng-content> | ||
</div> | ||
` | ||
}) | ||
export class GradientComponent implements OnInit, OnChanges { | ||
@Input() colors: Color[] | string[] = []; | ||
@Input() direction: string = 'to right'; | ||
@Input() type: HsbaColorType = 'hue'; | ||
|
||
gradientColors: string = ''; | ||
|
||
constructor() {} | ||
|
||
ngOnInit(): void { | ||
this.useMemo(); | ||
} | ||
|
||
ngOnChanges(changes: SimpleChanges): void { | ||
const { colors, type } = changes; | ||
if (colors || type) { | ||
this.useMemo(); | ||
} | ||
} | ||
|
||
useMemo(): void { | ||
this.gradientColors = this.colors | ||
.map((color, idx) => { | ||
const result = generateColor(color); | ||
if (this.type === 'alpha' && idx === this.colors.length - 1) { | ||
result.setAlpha(1); | ||
} | ||
return result.toRgbString(); | ||
}) | ||
.join(','); | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
components/color-picker/src/components/handler.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/** | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE | ||
*/ | ||
|
||
import { Component, Input } from '@angular/core'; | ||
|
||
type HandlerSize = 'default' | 'small'; | ||
|
||
@Component({ | ||
// eslint-disable-next-line @angular-eslint/component-selector | ||
selector: 'color-handler', | ||
standalone: true, | ||
template: ` | ||
<div | ||
class="ant-color-picker-handler" | ||
[style.background-color]="color" | ||
[class.ant-color-picker-handler-sm]="size === 'small'" | ||
></div> | ||
` | ||
}) | ||
export class HandlerComponent { | ||
@Input() color: string | null = null; | ||
@Input() size: HandlerSize = 'default'; | ||
} |
18 changes: 18 additions & 0 deletions
18
components/color-picker/src/components/palette.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/** | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE | ||
*/ | ||
|
||
import { Component } from '@angular/core'; | ||
|
||
@Component({ | ||
// eslint-disable-next-line @angular-eslint/component-selector | ||
selector: 'color-palette', | ||
standalone: true, | ||
template: ` | ||
<div class="ant-color-picker-palette" style="position: relative"> | ||
<ng-content></ng-content> | ||
</div> | ||
` | ||
}) | ||
export class PaletteComponent {} |
205 changes: 205 additions & 0 deletions
205
components/color-picker/src/components/picker.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
/** | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE | ||
*/ | ||
|
||
import { DOCUMENT } from '@angular/common'; | ||
import { | ||
AfterViewInit, | ||
ChangeDetectorRef, | ||
Component, | ||
ElementRef, | ||
EventEmitter, | ||
Inject, | ||
Input, | ||
OnChanges, | ||
OnInit, | ||
Output, | ||
SimpleChanges, | ||
ViewChild | ||
} from '@angular/core'; | ||
|
||
import { Color } from '../interfaces/color'; | ||
import { HsbaColorType, TransformOffset } from '../interfaces/type'; | ||
import { calculateColor, calculateOffset } from '../util/util'; | ||
import { HandlerComponent } from './handler.component'; | ||
import { PaletteComponent } from './palette.component'; | ||
|
||
type EventType = MouseEvent | TouchEvent; | ||
|
||
type EventHandle = (e: EventType) => void; | ||
|
||
function getPosition(e: EventType): { pageX: number; pageY: number } { | ||
const obj = 'touches' in e ? e.touches[0] : e; | ||
const scrollXOffset = document.documentElement.scrollLeft || document.body.scrollLeft || window.pageXOffset; | ||
const scrollYOffset = document.documentElement.scrollTop || document.body.scrollTop || window.pageYOffset; | ||
return { pageX: obj.pageX - scrollXOffset, pageY: obj.pageY - scrollYOffset }; | ||
} | ||
|
||
@Component({ | ||
// eslint-disable-next-line @angular-eslint/component-selector | ||
selector: 'color-picker', | ||
standalone: true, | ||
imports: [HandlerComponent, PaletteComponent], | ||
template: ` | ||
<div | ||
#slider | ||
class="ant-color-picker-select" | ||
(mousedown)="dragStartHandle($event)" | ||
(touchstart)="dragStartHandle($event)" | ||
> | ||
<color-palette> | ||
<div | ||
#transform | ||
style="position: absolute; z-index: 1;" | ||
[style.left]="offsetValue.x + 'px'" | ||
[style.top]="offsetValue.y + 'px'" | ||
> | ||
<color-handler [color]="toRgbString()" /> | ||
</div> | ||
<div | ||
class="ant-color-picker-saturation" | ||
style=" | ||
background-image: linear-gradient(0deg, #000, transparent), | ||
linear-gradient(90deg, #fff, hsla(0, 0%, 100%, 0)); | ||
" | ||
[style.background-color]="toHsb()" | ||
></div> | ||
</color-palette> | ||
</div> | ||
` | ||
}) | ||
export class PickerComponent implements OnInit, AfterViewInit, OnChanges { | ||
@ViewChild('slider', { static: false }) containerRef!: ElementRef<HTMLDivElement>; | ||
@ViewChild('transform', { static: false }) transformRef!: ElementRef<HTMLDivElement>; | ||
|
||
@Input() color: Color | null = null; | ||
@Output() readonly nzOnChange = new EventEmitter<Color>(); | ||
@Output() readonly nzOnChangeComplete = new EventEmitter<HsbaColorType>(); | ||
@Input() disabled: boolean = false; | ||
|
||
offsetValue: TransformOffset = { x: 0, y: 0 }; | ||
dragRef: boolean = false; | ||
mouseMoveRef: (e: MouseEvent | TouchEvent) => void = () => null; | ||
mouseUpRef: (e: MouseEvent | TouchEvent) => void = () => null; | ||
|
||
toRgbString(): string { | ||
return this.color?.toRgbString() as string; | ||
} | ||
|
||
toHsb(): string { | ||
return `hsl(${this.color?.toHsb().h},100%, 50%)`; | ||
} | ||
|
||
constructor( | ||
private cdr: ChangeDetectorRef, | ||
@Inject(DOCUMENT) private document: Document | ||
) {} | ||
|
||
ngOnInit(): void { | ||
this.document.removeEventListener('mousemove', this.mouseMoveRef); | ||
this.document.removeEventListener('mouseup', this.mouseUpRef); | ||
this.document.removeEventListener('touchmove', this.mouseMoveRef); | ||
this.document.removeEventListener('touchend', this.mouseUpRef); | ||
this.mouseMoveRef = () => null; | ||
this.mouseUpRef = () => null; | ||
} | ||
|
||
ngOnChanges(changes: SimpleChanges): void { | ||
const { color } = changes; | ||
|
||
if (color) { | ||
if (!this.dragRef && this.containerRef && this.transformRef) { | ||
const calcOffset = calculateOffset( | ||
this.containerRef.nativeElement, | ||
this.transformRef.nativeElement, | ||
this.color | ||
); | ||
if (calcOffset) { | ||
this.offsetValue = calcOffset; | ||
this.cdr.detectChanges(); | ||
} | ||
} | ||
} | ||
} | ||
|
||
ngAfterViewInit(): void { | ||
if (!this.dragRef && this.containerRef && this.transformRef) { | ||
const calcOffset = calculateOffset(this.containerRef.nativeElement, this.transformRef.nativeElement, this.color); | ||
if (calcOffset) { | ||
this.offsetValue = calcOffset; | ||
this.cdr.detectChanges(); | ||
} | ||
} | ||
} | ||
|
||
dragStartHandle(e: MouseEvent | TouchEvent): void { | ||
this.onDragStart(e); | ||
} | ||
|
||
updateOffset: EventHandle = (e: EventType, direction: 'x' | 'y' = 'y') => { | ||
const { pageX, pageY } = getPosition(e); | ||
const { | ||
x: rectX, | ||
y: rectY, | ||
width, | ||
height | ||
} = this.containerRef?.nativeElement?.getBoundingClientRect() || { x: 0, y: 0, width: 0, height: 0 }; | ||
const { width: targetWidth, height: targetHeight } = this.transformRef?.nativeElement?.getBoundingClientRect() || { | ||
width: 0, | ||
height: 0 | ||
}; | ||
|
||
const centerOffsetX = targetWidth / 2; | ||
const centerOffsetY = targetHeight / 2; | ||
|
||
const offsetX = Math.max(0, Math.min(pageX - rectX, width)) - centerOffsetX; | ||
const offsetY = Math.max(0, Math.min(pageY - rectY, height)) - centerOffsetY; | ||
|
||
const calcOffset = { | ||
x: offsetX, | ||
y: direction === 'x' ? this.offsetValue.y : offsetY | ||
}; | ||
// Exclusion of boundary cases | ||
if ((targetWidth === 0 && targetHeight === 0) || targetWidth !== targetHeight) { | ||
return; | ||
} | ||
this.offsetValue = calcOffset; | ||
this.nzOnChange.emit( | ||
calculateColor(calcOffset, this.containerRef.nativeElement, this.transformRef.nativeElement, this.color) | ||
); | ||
this.cdr.detectChanges(); | ||
}; | ||
|
||
onDragMove: EventHandle = (e: EventType) => { | ||
e.preventDefault(); | ||
this.updateOffset(e); | ||
}; | ||
|
||
onDragStop: EventHandle = (e: EventType) => { | ||
e.preventDefault(); | ||
this.dragRef = false; | ||
this.document.removeEventListener('mousemove', this.onDragMove); | ||
this.document.removeEventListener('mouseup', this.mouseUpRef); | ||
this.document.removeEventListener('touchmove', this.mouseMoveRef); | ||
this.document.removeEventListener('touchend', this.mouseUpRef); | ||
this.mouseMoveRef = () => null; | ||
this.mouseUpRef = () => null; | ||
this.nzOnChangeComplete?.emit(); | ||
}; | ||
|
||
onDragStart: EventHandle = (e: EventType) => { | ||
if (this.disabled) { | ||
return; | ||
} | ||
this.updateOffset(e); | ||
this.dragRef = true; | ||
this.document.addEventListener('mousemove', this.onDragMove); | ||
this.document.addEventListener('mouseup', this.onDragStop); | ||
this.document.addEventListener('touchmove', this.onDragMove); | ||
this.document.addEventListener('touchend', this.onDragStop); | ||
this.mouseMoveRef = this.onDragMove; | ||
this.mouseUpRef = this.onDragStop; | ||
this.cdr.markForCheck(); | ||
}; | ||
} |
Oops, something went wrong.