diff --git a/site/mobile/mobile.config.js b/site/mobile/mobile.config.js index 67b2c42e3..f5d9055ee 100644 --- a/site/mobile/mobile.config.js +++ b/site/mobile/mobile.config.js @@ -277,5 +277,10 @@ export default { name: 'guide', component: () => import('tdesign-mobile-react/guide/_example/index.tsx'), }, + { + title: 'ColorPicker 颜色选择器', + name: 'color-picker', + component: () => import('tdesign-mobile-react/color-picker/_example/index.tsx'), + }, ], }; diff --git a/site/web/site.config.js b/site/web/site.config.js index 1552bb60b..c2c83076d 100644 --- a/site/web/site.config.js +++ b/site/web/site.config.js @@ -154,6 +154,12 @@ export default { path: '/mobile-react/components/checkbox', component: () => import('tdesign-mobile-react/checkbox/checkbox.md'), }, + { + title: 'ColorPicker 颜色选择器', + name: 'color-picker', + path: '/mobile-react/components/color-picker', + component: () => import('tdesign-mobile-react/color-picker/color-picker.md'), + }, // { // title: 'DateTimePicker 时间选择器', // name: 'date-time-picker', diff --git a/src/_common b/src/_common index b7935cb3e..001b9dc74 160000 --- a/src/_common +++ b/src/_common @@ -1 +1 @@ -Subproject commit b7935cb3e86877dd68091d1d24fd93f55be3f1e7 +Subproject commit 001b9dc746ef42b3ebf738d643a006e3518731b9 diff --git a/src/color-picker/ColorPicker.tsx b/src/color-picker/ColorPicker.tsx new file mode 100644 index 000000000..8812608a2 --- /dev/null +++ b/src/color-picker/ColorPicker.tsx @@ -0,0 +1,433 @@ +import React, { FC, TouchEvent, useCallback, useEffect, useRef, useState } from 'react'; +import { ColorPickerChangeTrigger } from 'tdesign-mobile-react'; +import classNames from 'classnames'; +import { usePrefixClass } from '../hooks/useClass'; +import { Color, Coordinate, getColorObject } from '../_common/js/color-picker'; +import { + DEFAULT_COLOR, + SATURATION_PANEL_DEFAULT_HEIGHT, + SATURATION_PANEL_DEFAULT_WIDTH, + SLIDER_DEFAULT_WIDTH, +} from '../_common/js/color-picker/constants'; +import { PanelRectType } from './types'; +import { genSwatchList, getCoordinate, getFormatList } from './helper/format'; +import type { StyledProps } from '../common'; +import type { TdColorPickerProps } from './type'; +import { colorPickerDefaultProps } from './defaultProps'; +import useDefaultProps from '../hooks/useDefaultProps'; +import { ALPHA_MAX, HUE_MAX } from './constants'; + +export interface ColorPickerProps extends TdColorPickerProps, StyledProps {} + +const ColorPicker: FC = (props) => { + const { format, type, enableAlpha, swatchColors, style, value, defaultValue, fixed, onChange, onPaletteBarChange } = + useDefaultProps(props, colorPickerDefaultProps); + const [formatList, setFormatList] = useState([]); + const [innerSwatchList, setInnerSwatchList] = useState([]); + const [showPrimaryColorPreview] = useState(false); + const [previewColor, setPreviewColor] = useState(''); + const [sliderInfo, setSliderInfo] = useState(0); + const [panelRect, setPanelRect] = useState({ + width: SATURATION_PANEL_DEFAULT_WIDTH, + height: SATURATION_PANEL_DEFAULT_HEIGHT, + left: 0, + top: 0, + }); + const [sliderRect, setSilderRect] = useState({ + width: SLIDER_DEFAULT_WIDTH, + left: 0, + }); + const [saturationThumbStyle, setSaturationThumbStyle] = useState({ + top: '0', + left: '0', + color: '', + }); + const [hueSliderStyle, setHueSliderStyle] = useState({ + color: '', + left: '0%', + }); + const [alphaSliderStyle, setAlphahueSliderStyle] = useState({ + color: '', + left: '0%', + }); + const resizeObserverRef = useRef(null); + const saturationElementRef = useRef(null); + const sliderElementRef = useRef(null); + const hasInit = useRef(false); + const color = useRef(null); + const isMultiple = type === 'multiple'; + const rootClassName = usePrefixClass('color-picker'); + const contentClassName = classNames(`${rootClassName}__body`, `${rootClassName}__body--${type}`); + const getSliderThumbStyle = useCallback( + ({ value, maxValue }) => { + const { width } = sliderRect; + if (!width) { + return; + } + const left = Math.round((value / maxValue) * 100); + return { + left: `${left}%`, + color: color.current.rgb, + }; + }, + [sliderRect], + ); + const getSaturationThumbStyle = useCallback( + ({ saturation, value }) => { + const { width, height } = panelRect; + const top = Math.round((1 - value) * height); + const left = Math.round(saturation * width); + return { + color: color.current.rgb, + left: `${left}px`, + top: `${top}px`, + }; + }, + [panelRect], + ); + + const setCoreStyle = useCallback( + (format: ColorPickerProps['format']) => { + setSliderInfo(color.current.hue); + setHueSliderStyle(getSliderThumbStyle({ value: color.current.hue, maxValue: HUE_MAX })); + setAlphahueSliderStyle(getSliderThumbStyle({ value: color.current.alpha * 100, maxValue: ALPHA_MAX })); + setSaturationThumbStyle( + getSaturationThumbStyle({ + saturation: color.current.saturation, + value: color.current.value, + }), + ); + setPreviewColor(color.current.rgba); + setFormatList(getFormatList(format, color.current)); + }, + [getSaturationThumbStyle, getSliderThumbStyle], + ); + + const getEleRect = useCallback( + (format: ColorPickerProps['format']) => { + if (!saturationElementRef.current || !sliderElementRef.current) { + return; + } + const saturationRect = saturationElementRef.current.getBoundingClientRect(); + const sliderRect = sliderElementRef.current.getBoundingClientRect(); + setPanelRect({ + width: saturationRect.width || SATURATION_PANEL_DEFAULT_WIDTH, + height: saturationRect.height || SATURATION_PANEL_DEFAULT_HEIGHT, + left: saturationRect.left || 0, + top: saturationRect.top || 0, + }); + setSilderRect({ + left: sliderRect.left || 0, + width: sliderRect.width || SLIDER_DEFAULT_WIDTH, + }); + setTimeout(() => { + setCoreStyle(format); + }); + }, + [setCoreStyle], + ); + + useEffect(() => { + if (!saturationElementRef.current) { + return; + } + resizeObserverRef.current = new ResizeObserver((entries) => { + const entry = entries[0]; + if (entry.contentRect.width && entry.contentRect.height) { + getEleRect(format); + } + }); + resizeObserverRef.current.observe(saturationElementRef.current); + return () => { + resizeObserverRef.current?.disconnect?.(); + }; + }, [format, getEleRect]); + + useEffect(() => { + function init() { + const innerValue = value || defaultValue; + const result = innerValue || DEFAULT_COLOR; + color.current = new Color(result); + color.current.update(result); + hasInit.current = true; + getEleRect(format); + } + + if (hasInit.current) { + return; + } + init(); + }, [value, defaultValue, format, getEleRect]); + + useEffect(() => { + color.current = new Color(value || DEFAULT_COLOR); + }, [value]); + + useEffect(() => { + setPreviewColor(value); + }, [value]); + + useEffect(() => { + setCoreStyle(format); + }, [format, setCoreStyle]); + + useEffect(() => { + setInnerSwatchList(genSwatchList(swatchColors)); + }, [swatchColors]); + + function getSaturationAndValueByCoordinate(coordinate: Coordinate) { + const { width, height } = panelRect; + const { x, y } = coordinate; + let saturation = x / width; + let value = 1 - y / height; + saturation = Math.min(1, Math.max(0, saturation)); + value = Math.min(1, Math.max(0, value)); + + return { + saturation, + value, + }; + } + + function handleSaturationDrag(e: TouchEvent) { + const coordinate = getCoordinate(e, panelRect, fixed); + const { saturation, value } = getSaturationAndValueByCoordinate(coordinate); + onChangeSaturation({ saturation, value }); + } + + function handleChangeSlider({ value, isAlpha }) { + if (isAlpha) { + color.current.alpha = value / 100; + } else { + color.current.hue = value; + } + emitColorChange(isAlpha ? 'palette-alpha-bar' : 'palette-hue-bar'); + setCoreStyle(format); + } + + function handleSliderDrag(e: TouchEvent, isAlpha = false) { + const { width } = sliderRect; + const coordinate = getCoordinate(e, sliderRect); + const { x } = coordinate; + const maxValue = isAlpha ? ALPHA_MAX : HUE_MAX; + + let value = Math.round((x / width) * maxValue * 100) / 100; + if (value < 0) { + value = 0; + } + if (value > maxValue) { + value = maxValue; + } + handleChangeSlider({ value, isAlpha }); + } + + function onChangeSaturation({ saturation, value }) { + const { saturation: sat, value: val } = color.current; + if (value !== val && saturation !== sat) { + color.current.saturation = saturation; + color.current.value = value; + } else if (saturation !== sat) { + color.current.saturation = saturation; + } else if (value !== val) { + color.current.value = value; + } else { + return; + } + onPaletteBarChange?.({ + color: getColorObject(color.current), + }); + setCoreStyle(format); + } + + function handleDiffDrag(dragType: string, e: TouchEvent) { + switch (dragType) { + case 'saturation': + handleSaturationDrag(e); + break; + case 'hue-slider': + handleSliderDrag(e); + break; + case 'alpha-slider': + handleSliderDrag(e, true); + break; + default: + break; + } + } + + function formatValue() { + return color.current.getFormatsColorMap()[format] || color.current.css; + } + + function emitColorChange(trigger: ColorPickerChangeTrigger) { + const value = formatValue(); + onChange?.(value, { + trigger, + color: getColorObject(color.current), + }); + } + + const onTouchStart = (e: TouchEvent, dragType: string) => { + handleDiffDrag(dragType, e); + }; + const onTouchMove = (e: TouchEvent, dragType: string) => { + handleDiffDrag(dragType, e); + }; + + const onTouchEnd = (e: TouchEvent, dragType: string) => { + setTimeout(() => { + handleDiffDrag(dragType, e); + }); + }; + + const handleSwatchClicked = (swatch: string) => { + color.current.update(swatch); + setCoreStyle(format); + emitColorChange('preset'); + }; + + const renderPicker = () => { + const renderPreviewColorContent = () => ( +
+
+
+ ); + + const renderAlphaContent = () => ( +
+
+
onTouchStart(e, 'alpha-slider')} + onTouchMove={(e) => onTouchMove(e, 'alpha-slider')} + onTouchEnd={(e) => onTouchEnd(e, 'alpha-slider')} + > +
+
+
+
+ ); + + const renderMultipleContent = () => ( + <> +
onTouchStart(e, 'saturation')} + onTouchMove={(e) => onTouchMove(e, 'saturation')} + onTouchEnd={(e) => onTouchEnd(e, 'saturation')} + > +
{ + e.stopPropagation(); + }} + onTouchMove={(e) => { + e.stopPropagation(); + }} + onTouchEnd={(e) => { + e.stopPropagation(); + }} + /> +
+
+
+
+
onTouchStart(e, 'hue-slider')} + onTouchMove={(e) => onTouchMove(e, 'hue-slider')} + onTouchEnd={(e) => onTouchEnd(e, 'hue-slider')} + > +
+
+
+
+ {enableAlpha ? renderAlphaContent() : null} +
+ {showPrimaryColorPreview ? renderPreviewColorContent() : null} +
+
+
+ {format} +
+
+
+ {formatList.map((item, index) => ( +
+ {item} +
+ ))} +
+
+
+ + ); + + const renderInnerSwatchList = () => ( +
+
+ {isMultiple ?
系统预设色彩
: null} +
+ {innerSwatchList.map((swatch) => ( +
handleSwatchClicked(swatch)} + > +
+
+ ))} +
+
+
+ ); + + const renderColorPicker = () => ( +
+
+ <> + {isMultiple ? renderMultipleContent() : null} + {innerSwatchList.length ? renderInnerSwatchList() : null} + +
+
+ ); + + return renderColorPicker(); + }; + + return renderPicker(); +}; + +export default ColorPicker; diff --git a/src/color-picker/_example/base.tsx b/src/color-picker/_example/base.tsx new file mode 100644 index 000000000..652e7eeab --- /dev/null +++ b/src/color-picker/_example/base.tsx @@ -0,0 +1,6 @@ +import React from 'react'; +import { ColorPicker } from 'tdesign-mobile-react'; + +export default function () { + return ; +} diff --git a/src/color-picker/_example/format.tsx b/src/color-picker/_example/format.tsx new file mode 100644 index 000000000..bf445d81c --- /dev/null +++ b/src/color-picker/_example/format.tsx @@ -0,0 +1,44 @@ +import React, { useState } from 'react'; +import { CheckIcon } from 'tdesign-icons-react'; +import { ColorObject, ColorPicker } from 'tdesign-mobile-react'; + +type ColorPickerFormat = 'CSS' | 'HEX' | 'RGB' | 'HSL' | 'HSV' | 'CMYK'; +const OPTIONS: ColorPickerFormat[] = ['CSS', 'HEX', 'RGB', 'HSL', 'HSV', 'CMYK']; +export default function () { + const [curFormat, setCurFormat] = useState(OPTIONS[0]); + const [color, setColor] = useState('#7bd60b'); + const handleClickFormat = (format: ColorPickerFormat) => { + setCurFormat(format); + }; + const handleColorPickerClick = (value: string) => { + console.log('change', value); + setColor(value); + }; + const handlePaletteBarChange = (e: { color: ColorObject }) => { + console.log('onPaletteBarChange', e.color); + }; + return ( +
+
+ {OPTIONS.map((option) => ( +
handleClickFormat(option)} + > + {curFormat === option ? : null} + {option} +
+ ))} +
+ +
+ ); +} diff --git a/src/color-picker/_example/index.tsx b/src/color-picker/_example/index.tsx new file mode 100644 index 000000000..96e295a2d --- /dev/null +++ b/src/color-picker/_example/index.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import TDemoHeader from '../../../site/mobile/components/DemoHeader'; +import TDemoBlock from '../../../site/mobile/components/DemoBlock'; +import Base from './base'; +import Multiple from './multiple'; +import UsePopup from './usePopup'; +import Format from './format'; +import './style/index.less'; + +export default function ColorPickerDemo() { + return ( +
+ + + + + + + + + + + + + + + + + +
+ ); +} diff --git a/src/color-picker/_example/multiple.tsx b/src/color-picker/_example/multiple.tsx new file mode 100644 index 000000000..2d44eee5e --- /dev/null +++ b/src/color-picker/_example/multiple.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { ColorObject, ColorPicker } from 'tdesign-mobile-react'; + +export default function () { + const onChange = (value: string) => { + console.log('change', value); + }; + const onPaletteBarChange = (value: { color: ColorObject }) => { + console.log('onPaletteBarChange', value); + }; + return ; +} diff --git a/src/color-picker/_example/style/index.less b/src/color-picker/_example/style/index.less new file mode 100644 index 000000000..ee69d1384 --- /dev/null +++ b/src/color-picker/_example/style/index.less @@ -0,0 +1,57 @@ +.row { + display: flex; + padding: 0 16px; +} + +.format-line { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + grid-template-rows: 56px; + grid-column-gap: 12px; + grid-row-gap: 20px; + align-items: center; + justify-content: center; + margin: 0 16px 20px; +} + +.format-item { + border-radius: 6px; + height: 100%; + background-color: #fff; + padding: 16px; + line-height: 100%; + width: 100%; + box-sizing: border-box; + position: relative; + overflow: hidden; + display: flex; + align-items: center; +} + +.check-icon { + position: absolute; + left: 2px; + top: 2px; + z-index: 1; + color: #fff; +} + +.format-item.active { + border: 1.5px solid #0052d9; +} + +.format-item.active::after { + content: ''; + position: absolute; + width: 0; + height: 0; + left: 0; + top: 0; + border-top-left-radius: 3px; + border-top: 28px solid #0052d9; + border-right: 28px solid transparent; +} + +.format-item:not(:last-child) { + margin-right: 12px; +} diff --git a/src/color-picker/_example/usePopup.tsx b/src/color-picker/_example/usePopup.tsx new file mode 100644 index 000000000..3543906a7 --- /dev/null +++ b/src/color-picker/_example/usePopup.tsx @@ -0,0 +1,34 @@ +import React, { useState } from 'react'; +import { ColorObject, ColorPicker, ColorPickerTrigger, Popup } from 'tdesign-mobile-react'; +import Button from 'tdesign-mobile-react/button'; + +export default function () { + const [visible, setVisible] = useState(false); + const onChange = (value: string) => { + console.log('change', value); + }; + const onClose = (visible: boolean, target: ColorPickerTrigger) => { + console.log('close', visible, target); + if (!visible) { + setVisible(false); + } + }; + const onPaletteBarChange = (e: { color: ColorObject }) => { + console.log('onPaletteBarChange', e.color); + }; + const handlePopup = () => { + setVisible(true); + }; + return ( + <> + + + +
+ +
+ + ); +} diff --git a/src/color-picker/color-picker.en-US.md b/src/color-picker/color-picker.en-US.md new file mode 100644 index 000000000..0cf12fa61 --- /dev/null +++ b/src/color-picker/color-picker.en-US.md @@ -0,0 +1,21 @@ +:: BASE_DOC :: + +## API + +### ColorPicker Props + +name | type | default | description | required +-- | -- | -- | -- | -- +className | String | - | className of component | N +style | Object | - | CSS(Cascading Style Sheets),Typescript:`React.CSSProperties` | N +clearable | Boolean | false | \- | N +enableAlpha | Boolean | false | \- | N +fixed | Boolean | false | \- | N +format | String | RGB | options: RGB/RGBA/HSL/HSLA/HSB/HSV/HSVA/HEX/CMYK/CSS | N +swatchColors | Array | - | swatch colors。Typescript:`Array \| null` | N +type | String | base | options: base/multiple。Typescript:`TypeEnum ` `type TypeEnum = 'base' \| 'multiple'`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/color-picker/type.ts) | N +value | String | - | color value | N +defaultValue | String | - | color value。uncontrolled property | N +onChange | Function | | Typescript:`(value: string, context: { color: ColorObject; trigger: ColorPickerChangeTrigger }) => void`
[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/color-picker/type.ts)。
`type ColorPickerChangeTrigger = 'palette-hue-bar' \| 'palette-alpha-bar' \| 'preset' `
| N +onClose | Function | | Typescript:`(trigger: ColorPickerTrigger) => void`
[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/color-picker/type.ts)。
`type ColorPickerTrigger = 'overlay'`
| N +onPaletteBarChange | Function | | Typescript:`(context: { color: ColorObject }) => void`
[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/color-picker/type.ts)。
`interface ColorObject { alpha: number; css: string; hex: string; hex8: string; hsl: string; hsla: string; hsv: string; hsva: string; rgb: string; rgba: string; value: number;}`
| N diff --git a/src/color-picker/color-picker.md b/src/color-picker/color-picker.md new file mode 100644 index 000000000..7bb76e22d --- /dev/null +++ b/src/color-picker/color-picker.md @@ -0,0 +1,21 @@ +:: BASE_DOC :: + +## API + +### ColorPicker Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +className | String | - | 类名 | N +style | Object | - | 样式,TS 类型:`React.CSSProperties` | N +clearable | Boolean | false | 是否可清空 | N +enableAlpha | Boolean | false | 是否开启透明通道 | N +fixed | Boolean | false | 如果 color-picker 是在一个 `position:fixed` 的区域,需要显式指定属性 fixed 为 true | N +format | String | RGB | 格式化色值。`enableAlpha` 为真时,`RGBA/HSLA/HSVA` 等值有效。可选项:RGB/RGBA/HSL/HSLA/HSB/HSV/HSVA/HEX/CMYK/CSS | N +swatchColors | Array | - | 系统预设的颜色样例,值为 `null` 或 `[]` 则不显示系统色,值为 `undefined` 会显示组件内置的系统默认色。TS 类型:`Array \| null` | N +type | String | base | 颜色选择器类型。(base 表示仅展示系统预设内容; multiple 表示展示色板和系统预设内容。可选项:base/multiple。TS 类型:`TypeEnum ` `type TypeEnum = 'base' \| 'multiple'`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/color-picker/type.ts) | N +value | String | - | 色值 | N +defaultValue | String | - | 色值。非受控属性 | N +onChange | Function | | TS 类型:`(value: string, context: { color: ColorObject; trigger: ColorPickerChangeTrigger }) => void`
选中的色值发生变化时触发,第一个参数 `value` 表示新色值,`context.color` 表示当前调色板控制器的色值,`context.trigger` 表示触发颜色变化的来源。[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/color-picker/type.ts)。
`type ColorPickerChangeTrigger = 'palette-hue-bar' \| 'palette-alpha-bar' \| 'preset' `
| N +onClose | Function | | TS 类型:`(trigger: ColorPickerTrigger) => void`
关闭按钮时触发。[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/color-picker/type.ts)。
`type ColorPickerTrigger = 'overlay'`
| N +onPaletteBarChange | Function | | TS 类型:`(context: { color: ColorObject }) => void`
调色板控制器的值变化时触发,`context.color` 指调色板控制器的值。[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/color-picker/type.ts)。
`interface ColorObject { alpha: number; css: string; hex: string; hex8: string; hsl: string; hsla: string; hsv: string; hsva: string; rgb: string; rgba: string; value: number;}`
| N diff --git a/src/color-picker/constants.ts b/src/color-picker/constants.ts new file mode 100644 index 000000000..6d55a2b22 --- /dev/null +++ b/src/color-picker/constants.ts @@ -0,0 +1,2 @@ +export const HUE_MAX = 360; +export const ALPHA_MAX = 100; diff --git a/src/color-picker/defaultProps.ts b/src/color-picker/defaultProps.ts new file mode 100644 index 000000000..344dccec2 --- /dev/null +++ b/src/color-picker/defaultProps.ts @@ -0,0 +1,13 @@ +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import { TdColorPickerProps } from './type'; + +export const colorPickerDefaultProps: TdColorPickerProps = { + clearable: false, + enableAlpha: false, + fixed: false, + format: 'RGB', + type: 'base', +}; diff --git a/src/color-picker/helper/format.ts b/src/color-picker/helper/format.ts new file mode 100644 index 000000000..d4a02781e --- /dev/null +++ b/src/color-picker/helper/format.ts @@ -0,0 +1,48 @@ +import type { TouchEvent } from 'react'; +import { DEFAULT_SYSTEM_SWATCH_COLORS } from '../../_common/js/color-picker/constants'; +import { Color } from '../../_common/js/color-picker'; +import { ColorPickerProps } from '../ColorPicker'; +import { PanelRectType } from '../types'; + +export const getCoordinate = (e: TouchEvent, rect: PanelRectType, isFixed?: boolean) => { + const { pageX, pageY, clientY } = e?.changedTouches?.[0] || {}; + const offsetY = isFixed ? rect.top : (e.target as HTMLElement).offsetTop; + return { + x: Math.min(Math.max(0, pageX - rect.left), rect.width), + y: Math.min(Math.max(0, (isFixed ? clientY : pageY) - offsetY), rect.height), + }; +}; + +export const getFormatList = (format: ColorPickerProps['format'], color: Color) => { + const FORMAT_MAP = { + HSV: Object.values(color.getHsva()), + HSVA: Object.values(color.getHsva()), + + HSL: Object.values(color.getHsla()), + HSLA: Object.values(color.getHsla()), + HSB: Object.values(color.getHsla()), + + RGB: Object.values(color.getRgba()), + RGBA: Object.values(color.getRgba()), + CMYK: [...Object.values(color.getCmyk()), 0], + + CSS: [color.css, 0], + HEX: [color.hex, 0], + }; + + const cur = FORMAT_MAP[format]; + if (cur) { + return [...cur.slice(0, cur.length - 1), `${Math.round(color.alpha * 100)}%`]; + } + return FORMAT_MAP.RGB; +}; + +export const genSwatchList = (prop: ColorPickerProps['swatchColors']) => { + if (prop === undefined) { + return DEFAULT_SYSTEM_SWATCH_COLORS.slice(0, 10); + } + if (!prop || !prop.length) { + return []; + } + return prop; +}; diff --git a/src/color-picker/index.ts b/src/color-picker/index.ts new file mode 100644 index 000000000..8e8fabaf2 --- /dev/null +++ b/src/color-picker/index.ts @@ -0,0 +1,9 @@ +import _ColorPicker from './ColorPicker'; + +import './style/index.js'; + +export type { ColorPickerProps } from './ColorPicker'; + +export * from './type'; + +export const ColorPicker = _ColorPicker; diff --git a/src/color-picker/style/index.js b/src/color-picker/style/index.js new file mode 100644 index 000000000..4977c824b --- /dev/null +++ b/src/color-picker/style/index.js @@ -0,0 +1,2 @@ +// index.js +import '../../_common/style/mobile/components/color-picker/_index.less'; diff --git a/src/color-picker/type.ts b/src/color-picker/type.ts new file mode 100644 index 000000000..9472f31e2 --- /dev/null +++ b/src/color-picker/type.ts @@ -0,0 +1,79 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +export interface TdColorPickerProps { + /** + * 是否可清空 + * @default false + */ + clearable?: boolean; + /** + * 是否开启透明通道 + * @default false + */ + enableAlpha?: boolean; + /** + * 如果 color-picker 是在一个 `position:fixed` 的区域,需要显式指定属性 fixed 为 true + * @default false + */ + fixed?: boolean; + /** + * 格式化色值。`enableAlpha` 为真时,`RGBA/HSLA/HSVA` 等值有效 + * @default RGB + */ + format?: 'RGB' | 'RGBA' | 'HSL' | 'HSLA' | 'HSB' | 'HSV' | 'HSVA' | 'HEX' | 'CMYK' | 'CSS'; + /** + * 系统预设的颜色样例,值为 `null` 或 `[]` 则不显示系统色,值为 `undefined` 会显示组件内置的系统默认色 + */ + swatchColors?: Array | null; + /** + * 颜色选择器类型。(base 表示仅展示系统预设内容; multiple 表示展示色板和系统预设内容 + * @default base + */ + type?: TypeEnum; + /** + * 色值 + * @default '' + */ + value?: string; + /** + * 色值,非受控属性 + * @default '' + */ + defaultValue?: string; + /** + * 选中的色值发生变化时触发,第一个参数 `value` 表示新色值,`context.color` 表示当前调色板控制器的色值,`context.trigger` 表示触发颜色变化的来源 + */ + onChange?: (value: string, context: { color: ColorObject; trigger: ColorPickerChangeTrigger }) => void; + /** + * 关闭按钮时触发 + */ + onClose?: (trigger: ColorPickerTrigger) => void; + /** + * 调色板控制器的值变化时触发,`context.color` 指调色板控制器的值 + */ + onPaletteBarChange?: (context: { color: ColorObject }) => void; +} + +export type TypeEnum = 'base' | 'multiple'; + +export type ColorPickerChangeTrigger = 'palette-hue-bar' | 'palette-alpha-bar' | 'preset'; + +export type ColorPickerTrigger = 'overlay'; + +export interface ColorObject { + alpha: number; + css: string; + hex: string; + hex8: string; + hsl: string; + hsla: string; + hsv: string; + hsva: string; + rgb: string; + rgba: string; + value: number; +} diff --git a/src/color-picker/types.ts b/src/color-picker/types.ts new file mode 100644 index 000000000..203c382bb --- /dev/null +++ b/src/color-picker/types.ts @@ -0,0 +1,6 @@ +export interface PanelRectType { + width?: number; + height?: number; + top?: number; + left?: number; +} diff --git a/src/index.ts b/src/index.ts index 918f0aa46..a2116103f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -31,6 +31,7 @@ export * from './stepper'; export * from './switch'; export * from './textarea'; export * from './upload'; +export * from './color-picker'; /** * 数据展示(11个) diff --git a/test/snap/__snapshots__/csr.test.jsx.snap b/test/snap/__snapshots__/csr.test.jsx.snap index 6c5b933d0..e0bcada2d 100644 --- a/test/snap/__snapshots__/csr.test.jsx.snap +++ b/test/snap/__snapshots__/csr.test.jsx.snap @@ -11198,6 +11198,1574 @@ exports[`csr snapshot test > csr test src/collapse/_example/placement.tsx 1`] =
`; +exports[`csr snapshot test > csr test src/color-picker/_example/base.tsx 1`] = ` +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`; + +exports[`csr snapshot test > csr test src/color-picker/_example/format.tsx 1`] = ` +
+
+
+
+ + + + CSS +
+
+ HEX +
+
+ RGB +
+
+ HSL +
+
+ HSV +
+
+ CMYK +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CSS +
+
+
+
+ rgba(123, 214, 11, 1) +
+
+ 100% +
+
+
+
+
+
+
+ 系统预设色彩 +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`; + +exports[`csr snapshot test > csr test src/color-picker/_example/index.tsx 1`] = ` +
+
+
+

+ ColorPicker 颜色选择器 +

+

+ 用于颜色选择,支持多种格式。 +

+
+
+
+

+ 01 类型 +

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ RGB +
+
+
+
+ 0 +
+
+ 31 +
+
+ 151 +
+
+ 100% +
+
+
+
+
+
+
+ 系统预设色彩 +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+