Skip to content

Commit

Permalink
feat(module:color-picker): built-in color-picker package (#8428)
Browse files Browse the repository at this point in the history
  • Loading branch information
OriginRing committed Mar 11, 2024
1 parent 987a799 commit 534fe62
Show file tree
Hide file tree
Showing 22 changed files with 1,158 additions and 37 deletions.
5 changes: 3 additions & 2 deletions components/color-picker/color-block.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@

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

import { defaultColor, NgAntdColorPickerModule } from 'ng-antd-color-picker';

import { NzSizeLDSType } from 'ng-zorro-antd/core/types';

import { NgAntdColorPickerModule } from './src/ng-antd-color-picker.module';
import { defaultColor } from './src/util/util';

@Component({
selector: 'nz-color-block',
exportAs: 'NzColorBlock',
Expand Down
3 changes: 1 addition & 2 deletions components/color-picker/color-format.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,12 @@ import {
import { Subject } from 'rxjs';
import { debounceTime, filter, takeUntil } from 'rxjs/operators';

import { generateColor } from 'ng-antd-color-picker';

import { InputBoolean } from 'ng-zorro-antd/core/util';
import { NzInputDirective, NzInputGroupComponent } from 'ng-zorro-antd/input';
import { NzInputNumberComponent } from 'ng-zorro-antd/input-number';
import { NzSelectModule } from 'ng-zorro-antd/select';

import { generateColor } from './src/util/util';
import { NzColorPickerFormatType } from './typings';

@Component({
Expand Down
4 changes: 2 additions & 2 deletions components/color-picker/color-picker.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ import { ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR } from '@angular/f
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

import { defaultColor, generateColor, NgAntdColorPickerModule } from 'ng-antd-color-picker';

import { BooleanInput, NzSafeAny, NzSizeLDSType } from 'ng-zorro-antd/core/types';
import { InputBoolean, isNonEmptyString, isTemplateRef } from 'ng-zorro-antd/core/util';
import { NzPopoverDirective } from 'ng-zorro-antd/popover';

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

@Component({
Expand Down
6 changes: 0 additions & 6 deletions components/color-picker/doc/index.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,6 @@ import { NzColorPickerModule } from 'ng-zorro-antd/color-picker';

## API

Install `ng-antd-color-picker` in your project first:

```sh
npm install ng-antd-color-picker
```

### nz-color-picker:standalone

| Parameter | Description | Type | Default |
Expand Down
6 changes: 0 additions & 6 deletions components/color-picker/doc/index.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,6 @@ import { NzColorPickerModule } from 'ng-zorro-antd/color-picker';

## API

别忘记先安装 ng-antd-color-picker:

```sh
npm install ng-antd-color-picker
```

### nz-color-picker:standalone

| 参数 | 说明 | 类型 | 默认值 |
Expand Down
57 changes: 57 additions & 0 deletions components/color-picker/src/components/gradient.component.ts
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 components/color-picker/src/components/handler.component.ts
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 components/color-picker/src/components/palette.component.ts
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 components/color-picker/src/components/picker.component.ts
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();
};
}

0 comments on commit 534fe62

Please sign in to comment.