Skip to content

feat(colorPicker): new add colorPicker Component Full version available #26

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Dec 20, 2021
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { mount } from '@vue/test-utils';
import { ColorPicker } from '../index';

describe('color-picker test', () => {
it('color-picker init render', async () => {
// todo
})
})
17 changes: 17 additions & 0 deletions packages/devui-vue/devui/color-picker/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { App } from 'vue'
import ColorPicker from './src/color-picker'

ColorPicker.install = function (app: App): void {
app.component(ColorPicker.name, ColorPicker)
}

export { ColorPicker }

export default {
title: 'ColorPicker 颜色选择器',
category: '数据录入',
status: '80%', // TODO: 组件若开发完成则填入"100%",并删除该注释
install(app: App): void {
app.use(ColorPicker as any)
}
}
26 changes: 26 additions & 0 deletions packages/devui-vue/devui/color-picker/src/color-picker-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { PropType, ExtractPropTypes } from 'vue'
export const colorPickerProps = {
modelValue: {
type: [Object, String] as PropType<string | number>
},
mode: {
type: String
},
showAlpha: {
type: Boolean,
default: true
},
dotSize: {
type: Number,
default: 15
},
swatches: {
type: Array as PropType<string[]>
},
showHistory: {
type: Boolean,
default: true
}
} as const

export type ColorPickerProps = ExtractPropTypes<typeof colorPickerProps>
77 changes: 77 additions & 0 deletions packages/devui-vue/devui/color-picker/src/color-picker.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
@import '../../style/theme/color';

.devui-color-picker {
position: relative;
&-position {
position: absolute;
z-index: 9999;
background-color: $devui-connected-overlay-bg;
top: 0;
}

&-color-value {
display: flex;
position: absolute;
z-index: 4;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
font-weight: bold;
color: rgb(204, 15, 15);
}

&-container {
padding: 3px;
border: 1px solid rgb(224, 224, 230);
border-radius: 3px;
&-wrap {
width: 100%;
height: 26px;
box-sizing: content-box;
box-shadow: 3px 0 5px #00000014;
position: relative;
cursor: pointer;
overflow: hidden;
display: inline-block;
vertical-align: middle;

&-current-color {
top: 0;
right: 0;
left: 0;
position: absolute;
z-index: 3;
width: 100%;
height: 100%;
}
&-current-color-transparent {
top: 0;
right: 0;
left: 0;
overflow: hidden;
padding: 3px;
width: 100%;
height: 100%;
position: absolute;
z-index: 2;
}
&-transparent {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==);
background-repeat: repeat;
}
}
}
}

.color-picker-transition-enter-from,
.color-picker-transition-leave-to {
opacity: 0;
}
.color-picker-transition-enter-to,
.color-picker-transition-leave-from {
opacity: 1;
}
.color-picker-transition-enter-active,
.color-picker-transition-leave-active {
transition: opacity 0.2s ease-in-out;
}
178 changes: 178 additions & 0 deletions packages/devui-vue/devui/color-picker/src/color-picker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import {
defineComponent,
ref,
computed,
onMounted,
watch,
nextTick,
provide,
Teleport,
unref,
readonly,
Transition
} from 'vue'
import {
useReactive,
colorPickerResize,
isExhibitionColorPicker,
changeColorValue
} from './utils/composeable'
import { colorPickerProps, ColorPickerProps } from './color-picker-types'
import colorPanel from './components/color-picker-panel/color-picker-panel'
import './color-picker.scss'
import { parseColor, extractColor, RGBAtoCSS } from './utils/color-utils'
import { ColorPickerColor } from './utils/color-utils-types'
export default defineComponent({
name: 'DColorPicker',
components: {
colorPanel
},
props: colorPickerProps,
emits: ['update:modelValue'],
setup(props: ColorPickerProps, { emit }) {
const DEFAUTL_MODE = 'rgb'
const provideData = {
showAlpha: useReactive(() => props.showAlpha),
swatches: useReactive(() => props.swatches),
dotSize: useReactive(() => props.dotSize),
showHistory: useReactive(() => props.showHistory)
}
provide('provideData', readonly(provideData))
const initialColor = ref(null)
const colorCubeRef = ref<HTMLElement | null>()
const pickerRef = ref<HTMLElement | null>()
const containerRef = ref<HTMLElement | null>()
const left = ref(0)
const top = ref(0)
const isChangeTextColor = ref(true)
const showColorPicker = ref(false)
const formItemText = ref(`${props.mode ?? DEFAUTL_MODE}`)
const mode = ref(unref(props.mode))
onMounted(() => {
// resize 响应式 colorpicker
window.addEventListener('resize', resize)
// 点击展示 colorpicker
window.addEventListener('click', isExhibition)
})
// ** computeds
// colorpicker panel 组件位置
const colorPickerPostion = computed(() => {
if (colorCubeRef.value) {
return {
transform: `translate(${left.value}px, ${top.value}px)`
}
}
return null
})
// 交互触发item 颜色 面板 动态修改alpha后要还原 alpha 2021.12.18
const tiggerColor = computed(() => {
const currentColor = initialColor.value.rgba
const trigger = { ...currentColor, a: props.showAlpha ? currentColor.a : 1 }
return {
backgroundColor: `${RGBAtoCSS(trigger)}`
}
})
// 交互面板 的value 值 动态展示 根据不同 type
const formItemValue = computed(() => {
return extractColor(initialColor.value, '', formItemText.value, props.showAlpha)
})
// 动态 根据当前 透明度修改文本颜色 tips:根据不同 面板颜色 目前 不够优雅
const textColor = computed(() => {
// 数字代表 hsv 中的value 值 纵轴 动态切换 文本颜色
return changeColorValue(initialColor.value, 0.5)
})
// ** emits
// 动态 交互面板 文本展示颜色 tips:根据不同 面板颜色 目前 不够优雅
function changeTextColor(value: boolean): void {
isChangeTextColor.value = value
}
// 通过修改画板 颜色 修改 v-model 颜色
function changePaletteColor(colorMap: ColorPickerColor): void {
updateUserColor(colorMap)
}
// 通过用户点击触发修改 交互面板 文本类型
function changeTextModeType(type: string): void {
mode.value = type
formItemText.value = type
}

// 初始化的时候 确定 colopicker位置 由于 pickerref 默认 为 undefined 所以监听 showcolorpicker
watch(
() => showColorPicker.value,
(newValue) => {
const textPalette = colorCubeRef.value?.getBoundingClientRect()
newValue &&
nextTick(() => {
pickerRef.value.style.transform = `translate(${textPalette.left + 'px'}, ${
textPalette.top + window.scrollY + textPalette.height + 'px'
})`
})
}
)
// 监听用户输入 2021.12.10
watch(
() => props.modelValue,
(newValue) => {
// 全部转换成对象
updateUserColor(parseColor(newValue, initialColor.value))
},
{ immediate: true }
)
// 更新用户输入颜色 2021.12.10
function updateUserColor(color) {
initialColor.value = color
// 提取颜色 2021.12.10
const value = extractColor(initialColor.value, props.modelValue, mode.value, props.showAlpha)
emit('update:modelValue', value)
}
function resize() {
return colorPickerResize(colorCubeRef, top, left)
}
function isExhibition(event: Event) {
return isExhibitionColorPicker(event, colorCubeRef, pickerRef, showColorPicker)
}
return () => {
return (
<div class='devui-color-picker' ref={colorCubeRef}>
<div class='devui-color-picker-container'>
<div class='devui-color-picker-container-wrap'>
<div
class='devui-color-picker-container-wrap-current-color'
style={tiggerColor.value}
></div>
<div
class={[
'devui-color-picker-container-wrap-transparent',
'devui-color-picker-container-wrap-current-color-transparent'
]}
></div>
<div class='devui-color-picker-color-value'>
<p style={textColor.value}>{formItemValue.value}</p>
</div>
</div>
</div>
<Teleport to='body'>
<Transition name='color-picker-transition'>
{showColorPicker.value ? (
<div
ref={pickerRef}
style={colorPickerPostion.value}
class={['devui-color-picker-position']}
>
<color-panel
v-model={initialColor.value}
ref={containerRef}
mode={mode.value}
onChangeTextColor={changeTextColor}
onChangePaletteColor={changePaletteColor}
onChangeTextModeType={changeTextModeType}
></color-panel>
</div>
) : null}
</Transition>
</Teleport>
</div>
)
}
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
.devui-color-picker-alpha-slider {
position: relative;
margin-bottom: 15px;
width: 100%;
height: 14px;
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.08);
border-radius: 15px;

&.transparent {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==);
background-repeat: repeat;
}

&__bar {
position: relative;
width: 100%;
height: 100%;
border-radius: 15px;

&-pointer {
position: absolute;
width: 14px;
height: 14px;
}

&-handle {
width: 14px;
height: 14px;
border-radius: 6px;
transform: translate(-7px, -2px);
background-color: #f8f8f8;
margin-top: 2px;
box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.37);
cursor: pointer;

&.vertical {
transform: translate(0, -7px);
margin-top: 0;
}
}
}
}
Loading