Skip to content

Commit 534fe62

Browse files
authored
feat(module:color-picker): built-in color-picker package (#8428)
1 parent 987a799 commit 534fe62

22 files changed

+1158
-37
lines changed

components/color-picker/color-block.component.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55

66
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
77

8-
import { defaultColor, NgAntdColorPickerModule } from 'ng-antd-color-picker';
9-
108
import { NzSizeLDSType } from 'ng-zorro-antd/core/types';
119

10+
import { NgAntdColorPickerModule } from './src/ng-antd-color-picker.module';
11+
import { defaultColor } from './src/util/util';
12+
1213
@Component({
1314
selector: 'nz-color-block',
1415
exportAs: 'NzColorBlock',

components/color-picker/color-format.component.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,12 @@ import {
2626
import { Subject } from 'rxjs';
2727
import { debounceTime, filter, takeUntil } from 'rxjs/operators';
2828

29-
import { generateColor } from 'ng-antd-color-picker';
30-
3129
import { InputBoolean } from 'ng-zorro-antd/core/util';
3230
import { NzInputDirective, NzInputGroupComponent } from 'ng-zorro-antd/input';
3331
import { NzInputNumberComponent } from 'ng-zorro-antd/input-number';
3432
import { NzSelectModule } from 'ng-zorro-antd/select';
3533

34+
import { generateColor } from './src/util/util';
3635
import { NzColorPickerFormatType } from './typings';
3736

3837
@Component({

components/color-picker/color-picker.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@ import { ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR } from '@angular/f
2222
import { Subject } from 'rxjs';
2323
import { takeUntil } from 'rxjs/operators';
2424

25-
import { defaultColor, generateColor, NgAntdColorPickerModule } from 'ng-antd-color-picker';
26-
2725
import { BooleanInput, NzSafeAny, NzSizeLDSType } from 'ng-zorro-antd/core/types';
2826
import { InputBoolean, isNonEmptyString, isTemplateRef } from 'ng-zorro-antd/core/util';
2927
import { NzPopoverDirective } from 'ng-zorro-antd/popover';
3028

3129
import { NzColorBlockComponent } from './color-block.component';
3230
import { NzColorFormatComponent } from './color-format.component';
31+
import { NgAntdColorPickerModule } from './src/ng-antd-color-picker.module';
32+
import { defaultColor, generateColor } from './src/util/util';
3333
import { NzColor, NzColorPickerFormatType, NzColorPickerTriggerType } from './typings';
3434

3535
@Component({

components/color-picker/doc/index.en-US.md

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,6 @@ import { NzColorPickerModule } from 'ng-zorro-antd/color-picker';
1616

1717
## API
1818

19-
Install `ng-antd-color-picker` in your project first:
20-
21-
```sh
22-
npm install ng-antd-color-picker
23-
```
24-
2519
### nz-color-picker:standalone
2620

2721
| Parameter | Description | Type | Default |

components/color-picker/doc/index.zh-CN.md

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,6 @@ import { NzColorPickerModule } from 'ng-zorro-antd/color-picker';
1717

1818
## API
1919

20-
别忘记先安装 ng-antd-color-picker:
21-
22-
```sh
23-
npm install ng-antd-color-picker
24-
```
25-
2620
### nz-color-picker:standalone
2721

2822
| 参数 | 说明 | 类型 | 默认值 |
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* Use of this source code is governed by an MIT-style license that can be
3+
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
4+
*/
5+
6+
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
7+
8+
import { Color } from '../interfaces/color';
9+
import { HsbaColorType } from '../interfaces/type';
10+
import { generateColor } from '../util/util';
11+
12+
@Component({
13+
// eslint-disable-next-line @angular-eslint/component-selector
14+
selector: 'color-gradient',
15+
standalone: true,
16+
template: `
17+
<div
18+
class="ant-color-picker-gradient"
19+
style="position: absolute; inset: 0"
20+
[style.background]="'linear-gradient(' + direction + ', ' + gradientColors + ')'"
21+
>
22+
<ng-content></ng-content>
23+
</div>
24+
`
25+
})
26+
export class GradientComponent implements OnInit, OnChanges {
27+
@Input() colors: Color[] | string[] = [];
28+
@Input() direction: string = 'to right';
29+
@Input() type: HsbaColorType = 'hue';
30+
31+
gradientColors: string = '';
32+
33+
constructor() {}
34+
35+
ngOnInit(): void {
36+
this.useMemo();
37+
}
38+
39+
ngOnChanges(changes: SimpleChanges): void {
40+
const { colors, type } = changes;
41+
if (colors || type) {
42+
this.useMemo();
43+
}
44+
}
45+
46+
useMemo(): void {
47+
this.gradientColors = this.colors
48+
.map((color, idx) => {
49+
const result = generateColor(color);
50+
if (this.type === 'alpha' && idx === this.colors.length - 1) {
51+
result.setAlpha(1);
52+
}
53+
return result.toRgbString();
54+
})
55+
.join(',');
56+
}
57+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* Use of this source code is governed by an MIT-style license that can be
3+
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
4+
*/
5+
6+
import { Component, Input } from '@angular/core';
7+
8+
type HandlerSize = 'default' | 'small';
9+
10+
@Component({
11+
// eslint-disable-next-line @angular-eslint/component-selector
12+
selector: 'color-handler',
13+
standalone: true,
14+
template: `
15+
<div
16+
class="ant-color-picker-handler"
17+
[style.background-color]="color"
18+
[class.ant-color-picker-handler-sm]="size === 'small'"
19+
></div>
20+
`
21+
})
22+
export class HandlerComponent {
23+
@Input() color: string | null = null;
24+
@Input() size: HandlerSize = 'default';
25+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* Use of this source code is governed by an MIT-style license that can be
3+
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
4+
*/
5+
6+
import { Component } from '@angular/core';
7+
8+
@Component({
9+
// eslint-disable-next-line @angular-eslint/component-selector
10+
selector: 'color-palette',
11+
standalone: true,
12+
template: `
13+
<div class="ant-color-picker-palette" style="position: relative">
14+
<ng-content></ng-content>
15+
</div>
16+
`
17+
})
18+
export class PaletteComponent {}
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
/**
2+
* Use of this source code is governed by an MIT-style license that can be
3+
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
4+
*/
5+
6+
import { DOCUMENT } from '@angular/common';
7+
import {
8+
AfterViewInit,
9+
ChangeDetectorRef,
10+
Component,
11+
ElementRef,
12+
EventEmitter,
13+
Inject,
14+
Input,
15+
OnChanges,
16+
OnInit,
17+
Output,
18+
SimpleChanges,
19+
ViewChild
20+
} from '@angular/core';
21+
22+
import { Color } from '../interfaces/color';
23+
import { HsbaColorType, TransformOffset } from '../interfaces/type';
24+
import { calculateColor, calculateOffset } from '../util/util';
25+
import { HandlerComponent } from './handler.component';
26+
import { PaletteComponent } from './palette.component';
27+
28+
type EventType = MouseEvent | TouchEvent;
29+
30+
type EventHandle = (e: EventType) => void;
31+
32+
function getPosition(e: EventType): { pageX: number; pageY: number } {
33+
const obj = 'touches' in e ? e.touches[0] : e;
34+
const scrollXOffset = document.documentElement.scrollLeft || document.body.scrollLeft || window.pageXOffset;
35+
const scrollYOffset = document.documentElement.scrollTop || document.body.scrollTop || window.pageYOffset;
36+
return { pageX: obj.pageX - scrollXOffset, pageY: obj.pageY - scrollYOffset };
37+
}
38+
39+
@Component({
40+
// eslint-disable-next-line @angular-eslint/component-selector
41+
selector: 'color-picker',
42+
standalone: true,
43+
imports: [HandlerComponent, PaletteComponent],
44+
template: `
45+
<div
46+
#slider
47+
class="ant-color-picker-select"
48+
(mousedown)="dragStartHandle($event)"
49+
(touchstart)="dragStartHandle($event)"
50+
>
51+
<color-palette>
52+
<div
53+
#transform
54+
style="position: absolute; z-index: 1;"
55+
[style.left]="offsetValue.x + 'px'"
56+
[style.top]="offsetValue.y + 'px'"
57+
>
58+
<color-handler [color]="toRgbString()" />
59+
</div>
60+
<div
61+
class="ant-color-picker-saturation"
62+
style="
63+
background-image: linear-gradient(0deg, #000, transparent),
64+
linear-gradient(90deg, #fff, hsla(0, 0%, 100%, 0));
65+
"
66+
[style.background-color]="toHsb()"
67+
></div>
68+
</color-palette>
69+
</div>
70+
`
71+
})
72+
export class PickerComponent implements OnInit, AfterViewInit, OnChanges {
73+
@ViewChild('slider', { static: false }) containerRef!: ElementRef<HTMLDivElement>;
74+
@ViewChild('transform', { static: false }) transformRef!: ElementRef<HTMLDivElement>;
75+
76+
@Input() color: Color | null = null;
77+
@Output() readonly nzOnChange = new EventEmitter<Color>();
78+
@Output() readonly nzOnChangeComplete = new EventEmitter<HsbaColorType>();
79+
@Input() disabled: boolean = false;
80+
81+
offsetValue: TransformOffset = { x: 0, y: 0 };
82+
dragRef: boolean = false;
83+
mouseMoveRef: (e: MouseEvent | TouchEvent) => void = () => null;
84+
mouseUpRef: (e: MouseEvent | TouchEvent) => void = () => null;
85+
86+
toRgbString(): string {
87+
return this.color?.toRgbString() as string;
88+
}
89+
90+
toHsb(): string {
91+
return `hsl(${this.color?.toHsb().h},100%, 50%)`;
92+
}
93+
94+
constructor(
95+
private cdr: ChangeDetectorRef,
96+
@Inject(DOCUMENT) private document: Document
97+
) {}
98+
99+
ngOnInit(): void {
100+
this.document.removeEventListener('mousemove', this.mouseMoveRef);
101+
this.document.removeEventListener('mouseup', this.mouseUpRef);
102+
this.document.removeEventListener('touchmove', this.mouseMoveRef);
103+
this.document.removeEventListener('touchend', this.mouseUpRef);
104+
this.mouseMoveRef = () => null;
105+
this.mouseUpRef = () => null;
106+
}
107+
108+
ngOnChanges(changes: SimpleChanges): void {
109+
const { color } = changes;
110+
111+
if (color) {
112+
if (!this.dragRef && this.containerRef && this.transformRef) {
113+
const calcOffset = calculateOffset(
114+
this.containerRef.nativeElement,
115+
this.transformRef.nativeElement,
116+
this.color
117+
);
118+
if (calcOffset) {
119+
this.offsetValue = calcOffset;
120+
this.cdr.detectChanges();
121+
}
122+
}
123+
}
124+
}
125+
126+
ngAfterViewInit(): void {
127+
if (!this.dragRef && this.containerRef && this.transformRef) {
128+
const calcOffset = calculateOffset(this.containerRef.nativeElement, this.transformRef.nativeElement, this.color);
129+
if (calcOffset) {
130+
this.offsetValue = calcOffset;
131+
this.cdr.detectChanges();
132+
}
133+
}
134+
}
135+
136+
dragStartHandle(e: MouseEvent | TouchEvent): void {
137+
this.onDragStart(e);
138+
}
139+
140+
updateOffset: EventHandle = (e: EventType, direction: 'x' | 'y' = 'y') => {
141+
const { pageX, pageY } = getPosition(e);
142+
const {
143+
x: rectX,
144+
y: rectY,
145+
width,
146+
height
147+
} = this.containerRef?.nativeElement?.getBoundingClientRect() || { x: 0, y: 0, width: 0, height: 0 };
148+
const { width: targetWidth, height: targetHeight } = this.transformRef?.nativeElement?.getBoundingClientRect() || {
149+
width: 0,
150+
height: 0
151+
};
152+
153+
const centerOffsetX = targetWidth / 2;
154+
const centerOffsetY = targetHeight / 2;
155+
156+
const offsetX = Math.max(0, Math.min(pageX - rectX, width)) - centerOffsetX;
157+
const offsetY = Math.max(0, Math.min(pageY - rectY, height)) - centerOffsetY;
158+
159+
const calcOffset = {
160+
x: offsetX,
161+
y: direction === 'x' ? this.offsetValue.y : offsetY
162+
};
163+
// Exclusion of boundary cases
164+
if ((targetWidth === 0 && targetHeight === 0) || targetWidth !== targetHeight) {
165+
return;
166+
}
167+
this.offsetValue = calcOffset;
168+
this.nzOnChange.emit(
169+
calculateColor(calcOffset, this.containerRef.nativeElement, this.transformRef.nativeElement, this.color)
170+
);
171+
this.cdr.detectChanges();
172+
};
173+
174+
onDragMove: EventHandle = (e: EventType) => {
175+
e.preventDefault();
176+
this.updateOffset(e);
177+
};
178+
179+
onDragStop: EventHandle = (e: EventType) => {
180+
e.preventDefault();
181+
this.dragRef = false;
182+
this.document.removeEventListener('mousemove', this.onDragMove);
183+
this.document.removeEventListener('mouseup', this.mouseUpRef);
184+
this.document.removeEventListener('touchmove', this.mouseMoveRef);
185+
this.document.removeEventListener('touchend', this.mouseUpRef);
186+
this.mouseMoveRef = () => null;
187+
this.mouseUpRef = () => null;
188+
this.nzOnChangeComplete?.emit();
189+
};
190+
191+
onDragStart: EventHandle = (e: EventType) => {
192+
if (this.disabled) {
193+
return;
194+
}
195+
this.updateOffset(e);
196+
this.dragRef = true;
197+
this.document.addEventListener('mousemove', this.onDragMove);
198+
this.document.addEventListener('mouseup', this.onDragStop);
199+
this.document.addEventListener('touchmove', this.onDragMove);
200+
this.document.addEventListener('touchend', this.onDragStop);
201+
this.mouseMoveRef = this.onDragMove;
202+
this.mouseUpRef = this.onDragStop;
203+
this.cdr.markForCheck();
204+
};
205+
}

0 commit comments

Comments
 (0)