Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/common
Submodule common updated 39 files
+5 −5 .github/workflows/component-ci.yml
+1 −1 .github/workflows/pull-request.yml
+0 −1 .github/workflows/typos-config.toml
+6 −0 docs/web/api/color-picker.en-US.md
+6 −0 docs/web/api/color-picker.md
+35 −0 js/color-picker/eyedropper.ts
+1 −0 js/color-picker/index.ts
+1 −0 js/global-config/locale/ar_KW.ts
+1 −0 js/global-config/locale/en_US.ts
+1 −0 js/global-config/locale/it_IT.ts
+1 −0 js/global-config/locale/ja_JP.ts
+1 −0 js/global-config/locale/ko_KR.ts
+1 −0 js/global-config/locale/ru_RU.ts
+1 −0 js/global-config/locale/zh_CN.ts
+1 −0 js/global-config/locale/zh_TW.ts
+5 −31 js/global-config/mobile/locale/ar_KW.ts
+0 −26 js/global-config/mobile/locale/en_US.ts
+4 −29 js/global-config/mobile/locale/it_IT.ts
+0 −25 js/global-config/mobile/locale/ja_JP.ts
+0 −25 js/global-config/mobile/locale/ko_KR.ts
+0 −26 js/global-config/mobile/locale/ru_RU.ts
+0 −25 js/global-config/mobile/locale/zh_CN.ts
+1 −26 js/global-config/mobile/locale/zh_TW.ts
+39 −17 scripts/generate-css-vars.mjs
+2 −54 style/mobile/components/table/_index.less
+0 −21 style/mobile/components/table/_pagination.less
+1 −1 style/mobile/components/table/_var.less
+2 −25 style/mobile/components/upload/_index.less
+0 −1 style/mobile/theme/_dark.less
+0 −1 style/mobile/theme/_light.less
+1 −0 style/web/_reset.less
+1 −1 style/web/components/button/_index.less
+1 −0 style/web/components/button/_mixin.less
+31 −6 style/web/components/color-picker/_index.less
+1 −8 style/web/components/menu/_index.less
+3 −3 style/web/components/select/_index.less
+0 −1 style/web/theme/_dark.less
+0 −1 style/web/theme/_light.less
+101 −0 test/unit/color-picker/eyedropper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ exports[`ColorPickerPanel > :props > :colorModes[monochrome,linear-gradient] 1`]
/>
</div>
</div>
<!---->
</div>
<div
class="t-color-picker__body"
Expand Down Expand Up @@ -1353,6 +1354,7 @@ exports[`ColorPickerPanel > :props > :colorModes[monochrome,linear-gradient] 3`]
/>
</div>
</div>
<!---->
</div>
<div
class="t-color-picker__body"
Expand Down Expand Up @@ -2335,6 +2337,7 @@ exports[`ColorPickerPanel > :ui > :mount 1`] = `
/>
</div>
</div>
<!---->
</div>
<div
class="t-color-picker__body"
Expand Down
8 changes: 8 additions & 0 deletions packages/components/color-picker/_example/eye-dropper.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<template>
<t-color-picker v-model="color" :eye-dropper="true" default-value="#0052d9" />
</template>
<script lang="ts" setup>
import { ref } from 'vue';

const color = ref('#0052d9');
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export default {
type: Boolean,
default: true,
},
/** 是否开启吸色功能,启用后面板顶部出现吸色按钮,仅支持 Chrome/Edge 等现代浏览器 */
eyeDropper: Boolean,
/** 格式化色值。`enableAlpha` 为真时,`HEX8/RGBA/HSLA/HSVA` 有效 */
format: {
type: String as PropType<TdColorPickerPanelProps['format']>,
Expand Down
76 changes: 58 additions & 18 deletions packages/components/color-picker/components/panel/header.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { defineComponent, PropType, ref, watch } from 'vue';

import { defineComponent, PropType, ref, watch, onUnmounted } from 'vue';
import { COLOR_MODES } from '@tdesign/common-js/color-picker/constants';
import { isEyeDropperSupported, openEyeDropper } from '../../utils/eyedropper';
import { useConfig } from '@tdesign/shared-hooks';
import { RadioButton as TRadioButton, RadioGroup as TRadioGroup } from '../../../radio';
import props from '../../color-picker-panel-props';
Expand All @@ -17,35 +17,75 @@ export default defineComponent({
},
onModeChange: {
type: Function,
default: () => {
return () => {};
},
default: () => () => {},
},
onEyeDropperPick: {
type: Function as PropType<(hex: string) => void>,
default: () => () => {},
},
},
setup(props) {
const { globalConfig } = useConfig('colorPicker');
const baseClassName = useBaseClassName();
const modeValue = ref(props.mode);
const handleModeChange = (v: string) => props.onModeChange(v);
const picking = ref(false);
const supported = isEyeDropperSupported();
let abortCtrl: AbortController | null = null;

watch(
() => props.mode,
(v) => (modeValue.value = v),
);
return () => {
if (props.colorModes?.length === 1) {
return null;

onUnmounted(() => {
abortCtrl?.abort();
});

const handleModeChange = (v: string) => props.onModeChange(v);

const handleEyeDropperClick = async () => {
if (picking.value || !supported) return;
abortCtrl = new AbortController();
picking.value = true;
try {
const hex = await openEyeDropper(abortCtrl.signal);
if (hex) props.onEyeDropperPick(hex);
} finally {
picking.value = false;
abortCtrl = null;
}
};

return () => {
const showModeSwitch = (props.colorModes?.length ?? 0) !== 1;
const showEyeDropper = props.eyeDropper;
if (!showModeSwitch && !showEyeDropper) return null;

return (
<div class={`${baseClassName.value}__head`}>
<div class={`${baseClassName.value}__mode`}>
<TRadioGroup variant="default-filled" size="small" v-model={modeValue.value} onChange={handleModeChange}>
{Object.keys(COLOR_MODES).map((key) => (
<TRadioButton key={key} value={key}>
{Reflect.get(globalConfig.value, COLOR_MODES[key as keyof typeof COLOR_MODES])}
</TRadioButton>
))}
</TRadioGroup>
</div>
{showModeSwitch && (
<div class={`${baseClassName.value}__mode`}>
<TRadioGroup variant="default-filled" size="small" v-model={modeValue.value} onChange={handleModeChange}>
{Object.keys(COLOR_MODES).map((key) => (
<TRadioButton key={key} value={key}>
{Reflect.get(globalConfig.value, COLOR_MODES[key as keyof typeof COLOR_MODES])}
</TRadioButton>
))}
</TRadioGroup>
</div>
)}
{showEyeDropper && (
<button
class={[`${baseClassName.value}__eyedropper`, !supported && 't-is-disabled']}
title={globalConfig.value.eyeDropper ?? '吸色'}
disabled={!supported || picking.value}
onClick={handleEyeDropperClick}
>
<svg viewBox="0 0 16 16" width="1em" height="1em" fill="currentColor" aria-hidden="true">
<path d="M13.354.646a2.207 2.207 0 0 0-3.121 0L8.75 2.129l-.604-.604a1 1 0 1 0-1.414 1.414l.25.25L1.47 9.2A2.5 2.5 0 0 0 .75 11v.25H.5a.5.5 0 0 0 0 1h.25V12.5a.5.5 0 0 0 1 0v-.25H2a2.5 2.5 0 0 0 1.8-.72l5.982-5.982.25.25a1 1 0 1 0 1.414-1.414l-.604-.604 1.483-1.483a2.207 2.207 0 0 0 0-3.121zM3.094 11.78A1.5 1.5 0 0 1 2 12.25H1.75v-.25A1.5 1.5 0 0 1 2.22 10.906L8 5.121l.879.879-5.785 5.78z" />
</svg>
</button>
)}
</div>
);
};
Expand Down
12 changes: 11 additions & 1 deletion packages/components/color-picker/components/panel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ export default defineComponent({
},
);

const handleEyeDropperPick = (hex: string) => {
color.value.update(hex);
emitColorChange('eyedropper');
};

/**
* mode change
* @param newMode
Expand Down Expand Up @@ -289,7 +294,12 @@ export default defineComponent({

return (
<div class={[`${baseClassName.value}__panel`, props.disabled ? statusClassNames.disabled : false]}>
<PanelHeader {...props} mode={mode.value} onModeChange={handleModeChange} />
<PanelHeader
{...props}
mode={mode.value}
onModeChange={handleModeChange}
onEyeDropperPick={handleEyeDropperPick}
/>
<div class={[`${baseClassName.value}__body`]}>
{isGradient.value ? (
<LinearGradient
Expand Down
15 changes: 13 additions & 2 deletions packages/components/color-picker/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ export interface TdColorPickerProps {
* @default true
*/
enableMultipleGradient?: boolean;
/**
* 是否开启吸色功能,启用后面板顶部出现吸色按钮,仅支持 Chrome/Edge 等现代浏览器
* @default false
*/
eyeDropper?: boolean;
/**
* 格式化色值。`enableAlpha` 为真时,`HEX8/RGBA/HSLA/HSVA` 有效
* @default RGB
Expand Down Expand Up @@ -133,13 +138,18 @@ export interface TdColorPickerPanelProps {
* @default true
*/
enableMultipleGradient?: boolean;
/**
* 是否开启吸色功能,启用后面板顶部出现吸色按钮,仅支持 Chrome/Edge 等现代浏览器
* @default false
*/
eyeDropper?: boolean;
/**
* 格式化色值。`enableAlpha` 为真时,`HEX8/RGBA/HSLA/HSVA` 有效
* @default RGB
*/
format?: 'HEX' | 'HEX8' | 'RGB' | 'RGBA' | 'HSL' | 'HSLA' | 'HSV' | 'HSVA' | 'CMYK' | 'CSS';
/**
* 最近使用的颜色。值为 [] 表示以组件内部的最近使用颜色”为准,值长度大于 0 则以该值为准显示最近使用颜色”。值为 false 或 null 则完全不显示最近使用颜色”
* 最近使用的颜色。值为 [] 表示以组件内部的最近使用颜色”为准,值长度大于 0 则以该值为准显示最近使用颜色”。值为 false 或 null 则完全不显示最近使用颜色”
* @default []
*/
recentColors?: Array<string> | boolean | null;
Expand Down Expand Up @@ -198,7 +208,8 @@ export type ColorPickerChangeTrigger =
| 'palette-alpha-bar'
| 'input'
| 'preset'
| 'recent';
| 'recent'
| 'eyedropper';

export interface ColorObject {
alpha: number;
Expand Down
26 changes: 26 additions & 0 deletions packages/components/color-picker/utils/eyedropper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
interface EyeDropperInstance {
open(options?: { signal?: AbortSignal }): Promise<{ sRGBHex: string }>;
}

interface EyeDropperConstructor {
new (): EyeDropperInstance;
}

function getEyeDropperCtor(): EyeDropperConstructor | undefined {
if (typeof window === 'undefined') return undefined;
const { EyeDropper } = window as Window & { EyeDropper?: unknown };
return typeof EyeDropper === 'function' ? (EyeDropper as EyeDropperConstructor) : undefined;
}

export const isEyeDropperSupported = (): boolean => getEyeDropperCtor() !== undefined;

export const openEyeDropper = async (signal?: AbortSignal): Promise<string | null> => {
const Ctor = getEyeDropperCtor();
if (!Ctor) return null;
try {
const result = await new Ctor().open(signal ? { signal } : undefined);
return result.sRGBHex;
} catch {
return null;
}
};
5 changes: 5 additions & 0 deletions packages/components/config-provider/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,11 @@ export interface ColorPickerConfig {
* @default ''
*/
clearConfirmText?: string;
/**
* 语言配置,“吸色” 吸色按钮文案
* @default ''
*/
eyeDropper?: string;
/**
* 语言配置,“最近使用颜色” 区域标题文本
* @default ''
Expand Down
Loading