-
Notifications
You must be signed in to change notification settings - Fork 536
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(color-picker): add color-picker component (#2958)
* feat(color-picker): add color-picker component * feat(color-picker): add logic for disabledAlpha
- Loading branch information
Showing
53 changed files
with
2,329 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,215 @@ | ||
// https://github.com/scttcper/tinycolor | ||
export const hsvToRgb = (h: number, s: number, v: number) => { | ||
const i = Math.floor(h * 6); | ||
const f = h * 6 - i; | ||
const p = v * (1 - s); | ||
const q = v * (1 - f * s); | ||
const t = v * (1 - (1 - f) * s); | ||
const mod = i % 6; | ||
const r = [v, q, p, p, t, v][mod]; | ||
const g = [t, v, v, q, p, p][mod]; | ||
const b = [p, p, t, v, v, q][mod]; | ||
|
||
return { | ||
r: Math.round(r * 255), | ||
g: Math.round(g * 255), | ||
b: Math.round(b * 255), | ||
}; | ||
}; | ||
|
||
export const rgbToHsv = (r: number, g: number, b: number) => { | ||
r /= 255; | ||
g /= 255; | ||
b /= 255; | ||
|
||
const max = Math.max(r, g, b); | ||
const min = Math.min(r, g, b); | ||
let h = 0; | ||
const v = max; | ||
const d = max - min; | ||
const s = max === 0 ? 0 : d / max; | ||
|
||
if (max === min) { | ||
h = 0; | ||
} else { | ||
switch (max) { | ||
case r: | ||
h = (g - b) / d + (g < b ? 6 : 0); | ||
break; | ||
case g: | ||
h = (b - r) / d + 2; | ||
break; | ||
case b: | ||
h = (r - g) / d + 4; | ||
break; | ||
default: | ||
break; | ||
} | ||
|
||
h /= 6; | ||
} | ||
|
||
return { h, s, v }; | ||
}; | ||
|
||
// <http://www.w3.org/TR/css3-values/#integers> | ||
const CSS_INTEGER = '[-\\+]?\\d+%?'; | ||
|
||
// <http://www.w3.org/TR/css3-values/#number-value> | ||
const CSS_NUMBER = '[-\\+]?\\d*\\.\\d+%?'; | ||
|
||
// Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome. | ||
const CSS_UNIT = `(?:${CSS_NUMBER})|(?:${CSS_INTEGER})`; | ||
|
||
// Actual matching. | ||
// Parentheses and commas are optional, but not required. | ||
// Whitespace can take the place of commas or opening paren | ||
const PERMISSIVE_MATCH3 = `[\\s|\\(]+(${CSS_UNIT})[,|\\s]+(${CSS_UNIT})[,|\\s]+(${CSS_UNIT})\\s*\\)?`; | ||
const PERMISSIVE_MATCH4 = `[\\s|\\(]+(${CSS_UNIT})[,|\\s]+(${CSS_UNIT})[,|\\s]+(${CSS_UNIT})[,|\\s]+(${CSS_UNIT})\\s*\\)?`; | ||
|
||
const matchers = { | ||
rgb: new RegExp(`rgb${PERMISSIVE_MATCH3}`), | ||
rgba: new RegExp(`rgba${PERMISSIVE_MATCH4}`), | ||
hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, | ||
hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/, | ||
hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, | ||
hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/, | ||
}; | ||
|
||
export const parseIntFromHex = (val: string): number => { | ||
return parseInt(val, 16); | ||
}; | ||
|
||
export const convertHexToDecimal = (h: string): number => { | ||
return parseIntFromHex(h) / 255; | ||
}; | ||
|
||
export const formatInputToRgb = ( | ||
color: string | ||
): { r: number; g: number; b: number; a?: number } | false => { | ||
let match = matchers.rgb.exec(color); | ||
if (match) { | ||
return { | ||
r: parseInt(match[1], 10), | ||
g: parseInt(match[2], 10), | ||
b: parseInt(match[3], 10), | ||
}; | ||
} | ||
|
||
match = matchers.rgba.exec(color); | ||
if (match) { | ||
return { | ||
r: parseInt(match[1], 10), | ||
g: parseInt(match[2], 10), | ||
b: parseInt(match[3], 10), | ||
a: parseFloat(match[4]), | ||
}; | ||
} | ||
|
||
match = matchers.hex8.exec(color); | ||
if (match) { | ||
return { | ||
r: parseIntFromHex(match[1]), | ||
g: parseIntFromHex(match[2]), | ||
b: parseIntFromHex(match[3]), | ||
a: convertHexToDecimal(match[4]), | ||
}; | ||
} | ||
|
||
match = matchers.hex6.exec(color); | ||
if (match) { | ||
return { | ||
r: parseIntFromHex(match[1]), | ||
g: parseIntFromHex(match[2]), | ||
b: parseIntFromHex(match[3]), | ||
}; | ||
} | ||
|
||
match = matchers.hex4.exec(color); | ||
if (match) { | ||
return { | ||
r: parseIntFromHex(match[1] + match[1]), | ||
g: parseIntFromHex(match[2] + match[2]), | ||
b: parseIntFromHex(match[3] + match[3]), | ||
a: convertHexToDecimal(match[4] + match[4]), | ||
}; | ||
} | ||
|
||
match = matchers.hex3.exec(color); | ||
if (match) { | ||
return { | ||
r: parseIntFromHex(match[1] + match[1]), | ||
g: parseIntFromHex(match[2] + match[2]), | ||
b: parseIntFromHex(match[3] + match[3]), | ||
}; | ||
} | ||
|
||
return false; | ||
}; | ||
|
||
export const formatInputToHSVA = (color: string) => { | ||
const rgba = formatInputToRgb(color); | ||
if (rgba) { | ||
const hsv = rgbToHsv(rgba.r, rgba.g, rgba.b); | ||
return { | ||
...hsv, | ||
a: rgba.a ?? 1, | ||
}; | ||
} | ||
return { | ||
h: 0, | ||
s: 1, | ||
v: 1, | ||
a: 1, | ||
}; | ||
}; | ||
|
||
export const hexToRgb = (color: string): any => { | ||
color = color.trim().toLowerCase(); | ||
if (color.length === 0) { | ||
return false; | ||
} | ||
|
||
let match = matchers.hex6.exec(color); | ||
if (match) { | ||
return { | ||
r: parseIntFromHex(match[1]), | ||
g: parseIntFromHex(match[2]), | ||
b: parseIntFromHex(match[3]), | ||
}; | ||
} | ||
|
||
match = matchers.hex3.exec(color); | ||
if (match) { | ||
return { | ||
r: parseIntFromHex(match[1] + match[1]), | ||
g: parseIntFromHex(match[2] + match[2]), | ||
b: parseIntFromHex(match[3] + match[3]), | ||
}; | ||
} | ||
|
||
return false; | ||
}; | ||
|
||
export const rgbToHex = (r: number, g: number, b: number) => { | ||
const hex = [ | ||
Math.round(r).toString(16).padStart(2, '0'), | ||
Math.round(g).toString(16).padStart(2, '0'), | ||
Math.round(b).toString(16).padStart(2, '0'), | ||
]; | ||
|
||
return hex.join('').toUpperCase(); | ||
}; | ||
|
||
export const rgbaToHex = (r: number, g: number, b: number, a: number) => { | ||
const hex = [ | ||
Math.round(r).toString(16).padStart(2, '0'), | ||
Math.round(g).toString(16).padStart(2, '0'), | ||
Math.round(b).toString(16).padStart(2, '0'), | ||
Math.round(a * 255) | ||
.toString(16) | ||
.padStart(2, '0'), | ||
]; | ||
|
||
return hex.join('').toUpperCase(); | ||
}; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
```yaml | ||
meta: | ||
type: Component | ||
category: Data Entry | ||
title: ColorPicker | ||
description: Used for select and display colors. | ||
``` | ||
*Auto translate by google.* | ||
@import ./__demo__/basic.md | ||
@import ./__demo__/size.md | ||
@import ./__demo__/disabled.md | ||
@import ./__demo__/format.md | ||
@import ./__demo__/colors.md | ||
@import ./__demo__/trigger.md | ||
@import ./__demo__/trigger-element.md | ||
@import ./__demo__/only-panel.md | ||
## API | ||
### `<color-picker>` Props | ||
|
||
|Attribute|Description|Type|Default| | ||
|---|---|---|:---:| | ||
|model-value **(v-model)**|Value|`string`|`-`| | ||
|default-value|Default value (uncontrolled state)|`string`|`-`| | ||
|format|Color value format|`'hex' \| 'rgb'`|`-`| | ||
|size|Size|`'mini' \| 'small' \| 'medium' \| 'large'`|`'medium'`| | ||
|show-text|Show color value|`boolean`|`false`| | ||
|show-history|Show history colors|`boolean`|`false`| | ||
|show-preset|Show preset colors|`boolean`|`false`| | ||
|disabled|disabled|`boolean`|`false`| | ||
|disabled-alpha|Disable transparency channel|`boolean`|`false`| | ||
|hide-trigger|There is no trigger element, only the color panel is displayed|`boolean`|`false`| | ||
|trigger-props|Can accept Props of all [Trigger](/vue/component/trigger) components|`Partial<TriggerProps>`|`-`| | ||
|history-colors|Color array of historical colors|`string[]`|`-`| | ||
|preset-colors|Color array of preset colors|`string[]`|`() => colors`| | ||
### `<color-picker>` Events | ||
|
||
|Event Name|Description|Parameters| | ||
|---|---|---| | ||
|change|Triggered when the color value changes|value: `string`| | ||
|popup-visible-change|Triggered when the color panel is expanded and collapsed|visible: `boolean`<br>value: `string`| | ||
|
||
|
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,45 @@ | ||
```yaml | ||
meta: | ||
type: 组件 | ||
category: 数据输入 | ||
title: 颜色选择器 ColorPicker | ||
description: 用于选择和展示颜色 | ||
``` | ||
@import ./__demo__/basic.md | ||
@import ./__demo__/size.md | ||
@import ./__demo__/disabled.md | ||
@import ./__demo__/format.md | ||
@import ./__demo__/colors.md | ||
@import ./__demo__/trigger.md | ||
@import ./__demo__/trigger-element.md | ||
@import ./__demo__/only-panel.md | ||
## API | ||
### `<color-picker>` Props | ||
|
||
|参数名|描述|类型|默认值| | ||
|---|---|---|:---:| | ||
|model-value **(v-model)**|绑定值|`string`|`-`| | ||
|default-value|默认值(非受控状态)|`string`|`-`| | ||
|format|颜色值的格式|`'hex' \| 'rgb'`|`-`| | ||
|size|尺寸|`'mini' \| 'small' \| 'medium' \| 'large'`|`'medium'`| | ||
|show-text|显示颜色值|`boolean`|`false`| | ||
|show-history|显示历史颜色|`boolean`|`false`| | ||
|show-preset|显示预设颜色|`boolean`|`false`| | ||
|disabled|禁用|`boolean`|`false`| | ||
|disabled-alpha|禁用透明通道|`boolean`|`false`| | ||
|hide-trigger|没有触发元素,只显示颜色面板|`boolean`|`false`| | ||
|trigger-props|接受所有 [Trigger](/vue/component/trigger) 组件的Props|`Partial<TriggerProps>`|`-`| | ||
|history-colors|历史颜色的颜色数组|`string[]`|`-`| | ||
|preset-colors|预设颜色的颜色数组|`string[]`|`() => colors`| | ||
### `<color-picker>` Events | ||
|
||
|事件名|描述|参数| | ||
|---|---|---| | ||
|change|颜色值改变时触发|value: `string`| | ||
|popup-visible-change|颜色面板展开和收起时触发|visible: `boolean`<br>value: `string`| | ||
|
||
|
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,31 @@ | ||
## zh-CN | ||
```yaml | ||
meta: | ||
type: 组件 | ||
category: 数据输入 | ||
title: 颜色选择器 ColorPicker | ||
description: 用于选择和展示颜色 | ||
``` | ||
--- | ||
## en-US | ||
```yaml | ||
meta: | ||
type: Component | ||
category: Data Entry | ||
title: ColorPicker | ||
description: Used for select and display colors. | ||
``` | ||
--- | ||
@import ./__demo__/basic.md | ||
@import ./__demo__/size.md | ||
@import ./__demo__/disabled.md | ||
@import ./__demo__/format.md | ||
@import ./__demo__/colors.md | ||
@import ./__demo__/trigger.md | ||
@import ./__demo__/trigger-element.md | ||
@import ./__demo__/only-panel.md | ||
## API | ||
%%API(color-picker.tsx)%% |
Oops, something went wrong.