mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2025-02-17 13:20:52 +08:00
feat(color-picker): make it work
This commit is contained in:
parent
c236653f01
commit
049b00448d
@ -114,7 +114,7 @@
|
||||
"evtd": "^0.2.0",
|
||||
"highlight.js": "^10.7.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
"seemly": "^0.1.28",
|
||||
"seemly": "^0.1.29",
|
||||
"treemate": "^0.2.4",
|
||||
"vdirs": "^0.1.2",
|
||||
"vfonts": "^0.1.0",
|
||||
|
@ -21,7 +21,8 @@ export default defineComponent({
|
||||
onUpdateAlpha: {
|
||||
type: Function as PropType<(value: number) => void>,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
onComplete: Function as PropType<() => void>
|
||||
},
|
||||
setup (props) {
|
||||
const railRef = ref<HTMLElement | null>(null)
|
||||
@ -41,6 +42,7 @@ export default defineComponent({
|
||||
function handleMouseUp (): void {
|
||||
off('mousemove', document, handleMouseMove)
|
||||
off('mouseup', document, handleMouseUp)
|
||||
props.onComplete?.()
|
||||
}
|
||||
return {
|
||||
railRef,
|
||||
|
@ -6,7 +6,10 @@ import {
|
||||
PropType,
|
||||
toRef,
|
||||
watchEffect,
|
||||
VNode
|
||||
VNode,
|
||||
withDirectives,
|
||||
Transition,
|
||||
CSSProperties
|
||||
} from 'vue'
|
||||
import {
|
||||
hsv2rgb,
|
||||
@ -31,18 +34,41 @@ import Pallete from './Pallete'
|
||||
import type { PalleteInst } from './Pallete'
|
||||
import ColorInput from './ColorInput'
|
||||
import style from './styles/index.cssr'
|
||||
import { useStyle } from '../../_mixins'
|
||||
import { useMergedState } from 'vooks'
|
||||
import { call, MaybeArray } from '../../_utils'
|
||||
import type { ThemeProps } from '../../_mixins'
|
||||
import { useConfig, useTheme } from '../../_mixins'
|
||||
import { useIsMounted, useMergedState } from 'vooks'
|
||||
import { call, MaybeArray, useAdjustedTo } from '../../_utils'
|
||||
import { getModeFromValue } from './utils'
|
||||
import type { ColorPickerMode } from './utils'
|
||||
import { VBinder, VFollower, VTarget } from 'vueuc'
|
||||
import ColorPickerTrigger from './ColorPickerTrigger'
|
||||
import { clickoutside } from 'vdirs'
|
||||
import { colorPickerLight } from '../styles'
|
||||
import type { ColorPickerTheme } from '../styles'
|
||||
|
||||
export const colorPickerPanelProps = {
|
||||
...(useTheme.props as ThemeProps<ColorPickerTheme>),
|
||||
value: String,
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: undefined
|
||||
},
|
||||
defaultShow: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
defaultValue: {
|
||||
type: String as PropType<string | null>,
|
||||
default: null
|
||||
},
|
||||
to: useAdjustedTo.propTo,
|
||||
onComplete: Function as PropType<(value: string) => void>,
|
||||
'onUpdate:show': [Function, Array] as PropType<
|
||||
MaybeArray<(value: boolean) => void>
|
||||
>,
|
||||
onUpdateShow: [Function, Array] as PropType<
|
||||
MaybeArray<(value: boolean) => void>
|
||||
>,
|
||||
'onUpdate:value': [Function, Array] as PropType<
|
||||
MaybeArray<(value: string) => void>
|
||||
>,
|
||||
@ -56,9 +82,28 @@ export default defineComponent({
|
||||
props: colorPickerPanelProps,
|
||||
setup (props) {
|
||||
const palleteInstRef = ref<PalleteInst | null>(null)
|
||||
const selfRef = ref<HTMLElement | null>(null)
|
||||
let upcomingValue: string | null = null
|
||||
|
||||
useStyle('ColorPicker', style)
|
||||
const themeRef = useTheme(
|
||||
'ColorPicker',
|
||||
'ColorPicker',
|
||||
style,
|
||||
colorPickerLight,
|
||||
props
|
||||
)
|
||||
const uncontrolledShowRef = ref(props.defaultShow)
|
||||
const mergedShowRef = useMergedState(
|
||||
toRef(props, 'show'),
|
||||
uncontrolledShowRef
|
||||
)
|
||||
function doUpdateShow (value: boolean): void {
|
||||
const { onUpdateShow, 'onUpdate:show': _onUpdateShow } = props
|
||||
if (onUpdateShow) call(onUpdateShow, value)
|
||||
if (_onUpdateShow) call(_onUpdateShow, value)
|
||||
uncontrolledShowRef.value = value
|
||||
}
|
||||
|
||||
const uncontrolledValueRef = ref(props.defaultValue)
|
||||
const mergedValueRef = useMergedState(
|
||||
toRef(props, 'value'),
|
||||
@ -242,6 +287,15 @@ export default defineComponent({
|
||||
|
||||
function handleInputUpdateValue (value: string): void {
|
||||
doUpdateValue(value, 'input')
|
||||
handleComplete()
|
||||
}
|
||||
|
||||
function handleComplete (): void {
|
||||
const { value } = mergedValueRef
|
||||
// no value & only hue changes will complete with no value
|
||||
if (value) {
|
||||
props.onComplete?.(value)
|
||||
}
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
@ -258,6 +312,22 @@ export default defineComponent({
|
||||
upcomingValue = null
|
||||
})
|
||||
|
||||
const cssVarsRef = computed(() => {
|
||||
const {
|
||||
common: { cubicBezierEaseInOut },
|
||||
self: { textColor, color, fontSize, boxShadow, border, borderRadius }
|
||||
} = themeRef.value
|
||||
return {
|
||||
'--bezier': cubicBezierEaseInOut,
|
||||
'--text-color': textColor,
|
||||
'--color': color,
|
||||
'--font-size': fontSize,
|
||||
'--box-shadow': boxShadow,
|
||||
'--border': border,
|
||||
'--border-radius': borderRadius
|
||||
}
|
||||
})
|
||||
|
||||
function renderPanel (): VNode {
|
||||
const { value: rgba } = rgbaRef
|
||||
const { value: displayedMode } = displayedModeRef
|
||||
@ -265,26 +335,29 @@ export default defineComponent({
|
||||
return (
|
||||
<div
|
||||
class="n-color-picker-panel"
|
||||
style={{
|
||||
width: '240px',
|
||||
border: '1px solid rgba(0, 0, 0, .08)'
|
||||
}}
|
||||
onDragstart={(e) => {
|
||||
e.preventDefault()
|
||||
}}
|
||||
style={cssVarsRef.value as CSSProperties}
|
||||
>
|
||||
<Pallete
|
||||
ref={palleteInstRef}
|
||||
rgba={rgba}
|
||||
displayedHue={displayedHue}
|
||||
onUpdateSV={handleUpdateSv}
|
||||
onComplete={handleComplete}
|
||||
/>
|
||||
<div class="n-color-picker-control">
|
||||
<HueSlider hue={displayedHue} onUpdateHue={handleUpdateHue} />
|
||||
<HueSlider
|
||||
hue={displayedHue}
|
||||
onUpdateHue={handleUpdateHue}
|
||||
onComplete={handleComplete}
|
||||
/>
|
||||
<AlphaSlider
|
||||
rgba={rgba}
|
||||
alpha={displayedAlphaRef.value}
|
||||
onUpdateAlpha={handleUpdateAlpha}
|
||||
onComplete={handleComplete}
|
||||
/>
|
||||
<ColorInput
|
||||
mode={displayedMode}
|
||||
@ -298,11 +371,72 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
return {
|
||||
...useConfig(props),
|
||||
selfRef,
|
||||
hsla: hslaRef,
|
||||
rgba: rgbaRef,
|
||||
renderPanel
|
||||
mergedShow: mergedShowRef,
|
||||
isMounted: useIsMounted(),
|
||||
adjustedTo: useAdjustedTo(props),
|
||||
mergedValue: mergedValueRef,
|
||||
handleTriggerClick () {
|
||||
doUpdateShow(true)
|
||||
},
|
||||
handleClickOutside (e: MouseEvent) {
|
||||
if (selfRef.value?.contains(e.target as Node)) return
|
||||
doUpdateShow(false)
|
||||
},
|
||||
renderPanel,
|
||||
cssVars: cssVarsRef
|
||||
}
|
||||
},
|
||||
render () {
|
||||
return this.renderPanel()
|
||||
return (
|
||||
<div class="n-color-picker" ref="selfRef">
|
||||
<VBinder>
|
||||
{{
|
||||
default: () => [
|
||||
<VTarget>
|
||||
{{
|
||||
default: () => (
|
||||
<ColorPickerTrigger
|
||||
value={this.mergedValue}
|
||||
style={this.cssVars as CSSProperties}
|
||||
hsla={this.hsla}
|
||||
onClick={this.handleTriggerClick}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
</VTarget>,
|
||||
<VFollower
|
||||
placement="bottom-start"
|
||||
show={this.mergedShow}
|
||||
containerClass={this.namespace}
|
||||
teleportDisabled={this.adjustedTo === useAdjustedTo.tdkey}
|
||||
to={this.adjustedTo}
|
||||
>
|
||||
{{
|
||||
default: () => (
|
||||
<Transition
|
||||
name="n-fade-in-scale-up-transition"
|
||||
appear={this.isMounted}
|
||||
>
|
||||
{{
|
||||
default: () =>
|
||||
this.mergedShow
|
||||
? withDirectives(this.renderPanel(), [
|
||||
[clickoutside, this.handleClickOutside]
|
||||
])
|
||||
: null
|
||||
}}
|
||||
</Transition>
|
||||
)
|
||||
}}
|
||||
</VFollower>
|
||||
]
|
||||
}}
|
||||
</VBinder>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
@ -1,25 +1,40 @@
|
||||
import { RGBA, toRgbaString } from 'seemly'
|
||||
import { HSLA, toHslaString } from 'seemly'
|
||||
import { defineComponent, PropType, h } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ColorPickerTrigger',
|
||||
props: {
|
||||
rgba: {
|
||||
type: (Array as unknown) as PropType<RGBA | null>,
|
||||
value: {
|
||||
type: String as PropType<string | null>,
|
||||
default: null
|
||||
},
|
||||
hsla: {
|
||||
type: (Array as unknown) as PropType<HSLA | null>,
|
||||
default: null
|
||||
},
|
||||
onClick: Function as PropType<() => void>
|
||||
},
|
||||
render () {
|
||||
const { rgba } = this
|
||||
const { hsla, value } = this
|
||||
return (
|
||||
<div class="n-color-picker-trigger">
|
||||
<div class="n-color-picker-trigger" onClick={this.onClick}>
|
||||
<div
|
||||
class="n-color-picker-trigger__fill"
|
||||
style={{
|
||||
color: rgba ? toRgbaString(rgba) : ''
|
||||
backgroundColor: hsla ? toHslaString(hsla) : ''
|
||||
}}
|
||||
/>
|
||||
>
|
||||
{value && hsla ? (
|
||||
<div
|
||||
class="n-color-picker-trigger__value"
|
||||
style={{
|
||||
color: hsla[2] > 50 ? 'black' : 'white'
|
||||
}}
|
||||
>
|
||||
{value}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -20,7 +20,8 @@ export default defineComponent({
|
||||
onUpdateHue: {
|
||||
type: Function as PropType<(value: number) => void>,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
onComplete: Function as PropType<() => void>
|
||||
},
|
||||
setup (props) {
|
||||
const railRef = ref<HTMLElement | null>(null)
|
||||
@ -42,6 +43,7 @@ export default defineComponent({
|
||||
function handleMouseUp (): void {
|
||||
off('mousemove', document, handleMouseMove)
|
||||
off('mouseup', document, handleMouseUp)
|
||||
props.onComplete?.()
|
||||
}
|
||||
return {
|
||||
railRef,
|
||||
|
@ -24,7 +24,8 @@ export default defineComponent({
|
||||
onUpdateSV: {
|
||||
type: Function as PropType<(s: number, v: number) => void>,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
onComplete: Function as PropType<() => void>
|
||||
},
|
||||
setup (props) {
|
||||
const palleteRef = ref<HTMLElement | null>(null)
|
||||
@ -54,6 +55,7 @@ export default defineComponent({
|
||||
function handleMouseUp (): void {
|
||||
off('mousemove', document, handleMouseMove)
|
||||
off('mouseup', document, handleMouseUp)
|
||||
props.onComplete?.()
|
||||
}
|
||||
return {
|
||||
palleteRef,
|
||||
|
@ -1,16 +1,38 @@
|
||||
import { c, cB, cE, cM } from '../../../_utils/cssr'
|
||||
import fadeInScaleUpTransition from '../../../_styles/transitions/fade-in-scale-up.cssr'
|
||||
|
||||
// vars:
|
||||
// --color
|
||||
// --text-color
|
||||
// --border-radius
|
||||
// --font-size
|
||||
export default c([
|
||||
cB('color-picker', `
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
height: 34px;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
`),
|
||||
cB('color-picker-panel', `
|
||||
border-radius: 4px;
|
||||
margin: 4px 0;
|
||||
width: 240px;
|
||||
font-size: var(--font-size);
|
||||
color: var(--text-color);
|
||||
background-color: var(--color);
|
||||
transition:
|
||||
box-shadow .3s var(--bezier),
|
||||
color .3s var(--bezier),
|
||||
background-color .3s var(--bezier);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--box-shadow);
|
||||
padding: 12px;
|
||||
`, [
|
||||
fadeInScaleUpTransition(),
|
||||
cB('input', `
|
||||
text-align: center;
|
||||
`)
|
||||
]),
|
||||
cB('color-picker-control', `
|
||||
`),
|
||||
cB('color-picker-slider', `
|
||||
margin-bottom: 8px;
|
||||
position: relative;
|
||||
@ -19,7 +41,7 @@ export default c([
|
||||
`, [
|
||||
cE('grid', `
|
||||
height: 6px;
|
||||
background-image: linear-gradient(to right,#eee 6px,transparent 6px);
|
||||
background-image: linear-gradient(to right, rgba(0, 0, 0, .12) 6px,transparent 6px);
|
||||
background-size: 12px 6px;
|
||||
background-repeat: repeat;
|
||||
position: relative;
|
||||
@ -73,18 +95,24 @@ export default c([
|
||||
`)
|
||||
]),
|
||||
cB('color-picker-trigger', `
|
||||
display: inline-block;
|
||||
height: 34px;
|
||||
border: var(--border);
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
border-radius: var(--border-radius);
|
||||
transition: border-color .3s var(--bezier);
|
||||
cursor: pointer;
|
||||
`, [
|
||||
cE('value', `
|
||||
transition: color .3s var(--bezier);
|
||||
`),
|
||||
cE('fill', `
|
||||
text-align: center;
|
||||
border-radius: var(--border-radius);
|
||||
position: absolute;
|
||||
left: 4px;
|
||||
right: 4px;
|
||||
top: 4px;
|
||||
bottom: 4px;
|
||||
left: 6px;
|
||||
right: 6px;
|
||||
top: 6px;
|
||||
bottom: 6px;
|
||||
`)
|
||||
])
|
||||
])
|
||||
|
27
src/color-picker/styles/dark.ts
Normal file
27
src/color-picker/styles/dark.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { commonDark } from '../../_styles/common'
|
||||
import type { ColorPickerTheme } from './light'
|
||||
|
||||
const colorPickerDark: ColorPickerTheme = {
|
||||
name: 'ColorPicker',
|
||||
common: commonDark,
|
||||
self (vars) {
|
||||
const {
|
||||
fontSize,
|
||||
boxShadow2,
|
||||
popoverColor,
|
||||
textColor2,
|
||||
borderRadius,
|
||||
borderColor
|
||||
} = vars
|
||||
return {
|
||||
fontSize,
|
||||
boxShadow: boxShadow2,
|
||||
color: popoverColor,
|
||||
textColor: textColor2,
|
||||
borderRadius,
|
||||
border: `1px solid ${borderColor}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default colorPickerDark
|
3
src/color-picker/styles/index.ts
Normal file
3
src/color-picker/styles/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export { default as colorPickerDark } from './dark'
|
||||
export { default as colorPickerLight } from './light'
|
||||
export type { ColorPickerThemeVars, ColorPickerTheme } from './light'
|
37
src/color-picker/styles/light.ts
Normal file
37
src/color-picker/styles/light.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { commonLight } from '../../_styles/common'
|
||||
import type { ThemeCommonVars } from '../../_styles/common'
|
||||
import { createTheme } from '../../_mixins/use-theme'
|
||||
import { inputLight } from '../../input/styles'
|
||||
|
||||
const self = (vars: ThemeCommonVars) => {
|
||||
const {
|
||||
fontSize,
|
||||
boxShadow2,
|
||||
popoverColor,
|
||||
textColor2,
|
||||
borderRadius,
|
||||
borderColor
|
||||
} = vars
|
||||
return {
|
||||
fontSize,
|
||||
boxShadow: boxShadow2,
|
||||
color: popoverColor,
|
||||
textColor: textColor2,
|
||||
borderRadius,
|
||||
border: `1px solid ${borderColor}`
|
||||
}
|
||||
}
|
||||
|
||||
export type ColorPickerThemeVars = ReturnType<typeof self>
|
||||
|
||||
const colorPickerLight = createTheme({
|
||||
name: 'ColorPicker',
|
||||
common: commonLight,
|
||||
peers: {
|
||||
Input: inputLight
|
||||
},
|
||||
self
|
||||
})
|
||||
|
||||
export default colorPickerLight
|
||||
export type ColorPickerTheme = typeof colorPickerLight
|
@ -13,6 +13,7 @@ import type { CascaderTheme } from '../../cascader/styles'
|
||||
import type { CheckboxTheme } from '../../checkbox/styles'
|
||||
import type { CodeTheme } from '../../code/styles'
|
||||
import type { CollapseTheme } from '../../collapse/styles'
|
||||
import type { ColorPickerTheme } from '../../color-picker/styles'
|
||||
import type { DataTableTheme } from '../../data-table/styles'
|
||||
import type { DatePickerTheme } from '../../date-picker/styles'
|
||||
import type { DescriptionsTheme } from '../../descriptions/styles'
|
||||
@ -96,6 +97,7 @@ export interface GlobalThemeWithoutCommon {
|
||||
Checkbox?: CheckboxTheme
|
||||
Code?: CodeTheme
|
||||
Collapse?: CollapseTheme
|
||||
ColorPicker?: ColorPickerTheme
|
||||
DataTable?: DataTableTheme
|
||||
DatePicker?: DatePickerTheme
|
||||
Descriptions?: DescriptionsTheme
|
||||
|
@ -13,6 +13,7 @@ import { cascaderDark } from '../cascader/styles'
|
||||
import { checkboxDark } from '../checkbox/styles'
|
||||
import { codeDark } from '../code/styles'
|
||||
import { collapseDark } from '../collapse/styles'
|
||||
import { colorPickerDark } from '../color-picker/styles'
|
||||
import { dataTableDark } from '../data-table/styles'
|
||||
import { datePickerDark } from '../date-picker/styles'
|
||||
import { descriptionsDark } from '../descriptions/styles'
|
||||
@ -87,6 +88,7 @@ export const darkTheme: BuiltInGlobalTheme = {
|
||||
Checkbox: checkboxDark,
|
||||
Code: codeDark,
|
||||
Collapse: collapseDark,
|
||||
ColorPicker: colorPickerDark,
|
||||
DataTable: dataTableDark,
|
||||
DatePicker: datePickerDark,
|
||||
Descriptions: descriptionsDark,
|
||||
|
@ -15,6 +15,7 @@ import { cascaderLight } from '../cascader/styles'
|
||||
import { checkboxLight } from '../checkbox/styles'
|
||||
import { codeLight } from '../code/styles'
|
||||
import { collapseLight } from '../collapse/styles'
|
||||
import { colorPickerLight } from '../color-picker/styles'
|
||||
import { dataTableLight } from '../data-table/styles'
|
||||
import { datePickerLight } from '../date-picker/styles'
|
||||
import { descriptionsLight } from '../descriptions/styles'
|
||||
@ -89,6 +90,7 @@ export const lightTheme: BuiltInGlobalTheme = {
|
||||
Checkbox: checkboxLight,
|
||||
Code: codeLight,
|
||||
Collapse: collapseLight,
|
||||
ColorPicker: colorPickerLight,
|
||||
DataTable: dataTableLight,
|
||||
DatePicker: datePickerLight,
|
||||
Descriptions: descriptionsLight,
|
||||
|
Loading…
Reference in New Issue
Block a user