@sanity/color-input 2.30.1 → 3.0.0-studio-v3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -6
- package/lib/index.d.ts +85 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +606 -0
- package/lib/index.js.map +1 -0
- package/lib/index.modern.js +594 -0
- package/lib/index.modern.js.map +1 -0
- package/package.json +57 -17
- package/src/ColorInput.tsx +110 -0
- package/src/ColorPicker.tsx +139 -0
- package/src/ColorPickerFields.tsx +144 -0
- package/src/index.ts +16 -0
- package/src/schemas/color.tsx +79 -0
- package/src/schemas/hslaColor.ts +11 -0
- package/src/schemas/hsvaColor.ts +11 -0
- package/src/schemas/rgbaColor.ts +11 -0
- package/lib/ColorInput.js +0 -144
- package/lib/ColorPicker.js +0 -159
- package/lib/ColorPickerFields.js +0 -138
- package/lib/schemas/color.js +0 -81
- package/lib/schemas/hslaColor.js +0 -29
- package/lib/schemas/hsvaColor.js +0 -29
- package/lib/schemas/rgbaColor.js +0 -29
- package/sanity.json +0 -32
package/package.json
CHANGED
|
@@ -1,13 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/color-input",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0-studio-v3.0",
|
|
4
4
|
"description": "Color input",
|
|
5
|
-
"main": "lib/index.js",
|
|
6
|
-
"author": "Sanity.io <hello@sanity.io>",
|
|
7
|
-
"license": "MIT",
|
|
8
5
|
"scripts": {
|
|
9
|
-
"clean": "rimraf lib
|
|
6
|
+
"clean": "rimraf lib",
|
|
7
|
+
"prebuild": "npm run clean && tsc --noEmit",
|
|
8
|
+
"build": "parcel build",
|
|
9
|
+
"watch": "parcel watch",
|
|
10
|
+
"prepublishOnly": "npm run build",
|
|
11
|
+
"compile": "tsc --noEmit",
|
|
12
|
+
"lint": "eslint .",
|
|
13
|
+
"format": "prettier src -w",
|
|
14
|
+
"prepare": "husky install"
|
|
15
|
+
},
|
|
16
|
+
"source": "./src/index.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"require": "./lib/index.js",
|
|
20
|
+
"default": "./lib/index.modern.js"
|
|
21
|
+
}
|
|
10
22
|
},
|
|
23
|
+
"main": "./lib/index.js",
|
|
24
|
+
"module": "./lib/index.modern.js",
|
|
25
|
+
"types": "./lib/index.d.ts",
|
|
26
|
+
"files": [
|
|
27
|
+
"src",
|
|
28
|
+
"lib"
|
|
29
|
+
],
|
|
30
|
+
"author": "Sanity.io <hello@sanity.io>",
|
|
31
|
+
"license": "MIT",
|
|
11
32
|
"keywords": [
|
|
12
33
|
"sanity",
|
|
13
34
|
"cms",
|
|
@@ -18,22 +39,39 @@
|
|
|
18
39
|
"sanity-plugin"
|
|
19
40
|
],
|
|
20
41
|
"dependencies": {
|
|
21
|
-
"@sanity/icons": "^1.2.
|
|
42
|
+
"@sanity/icons": "^1.2.8",
|
|
22
43
|
"@sanity/ui": "^0.37.9",
|
|
23
|
-
"lodash": "^4.17.
|
|
24
|
-
"react-color": "^2.
|
|
44
|
+
"lodash": "^4.17.21",
|
|
45
|
+
"react-color": "^2.19.3"
|
|
25
46
|
},
|
|
26
47
|
"devDependencies": {
|
|
27
|
-
"@
|
|
28
|
-
"@
|
|
29
|
-
"
|
|
30
|
-
"
|
|
48
|
+
"@commitlint/cli": "^16.1.0",
|
|
49
|
+
"@commitlint/config-conventional": "^15.0.0",
|
|
50
|
+
"@parcel/packager-ts": "^2.6.0",
|
|
51
|
+
"@parcel/transformer-typescript-types": "^2.6.0",
|
|
52
|
+
"@types/react-color": "^2.17.6",
|
|
53
|
+
"@types/styled-components": "^5.1.25",
|
|
54
|
+
"@typescript-eslint/eslint-plugin": "^5.12.0",
|
|
55
|
+
"@typescript-eslint/parser": "^5.12.0",
|
|
56
|
+
"eslint": "^8.7.0",
|
|
57
|
+
"eslint-config-prettier": "^8.3.0",
|
|
58
|
+
"eslint-config-sanity": "^5.1.0",
|
|
59
|
+
"eslint-plugin-prettier": "4.0.0",
|
|
60
|
+
"eslint-plugin-react": "^7.28.0",
|
|
61
|
+
"eslint-plugin-react-hooks": "^4.5.0",
|
|
62
|
+
"husky": "^7.0.0",
|
|
63
|
+
"lint-staged": "^12.4.1",
|
|
64
|
+
"parcel": "^2.6.0",
|
|
65
|
+
"prettier": "^2.6.2",
|
|
66
|
+
"react": "^17.0.0 || ^18.0.0",
|
|
67
|
+
"rimraf": "^3.0.2",
|
|
68
|
+
"sanity": "purple-unicorn",
|
|
69
|
+
"styled-components": "^5.2.0",
|
|
70
|
+
"typescript": "^4.6.4"
|
|
31
71
|
},
|
|
32
72
|
"peerDependencies": {
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"prop-types": "^15.6 || ^16",
|
|
36
|
-
"react": "^16.9 || ^17",
|
|
73
|
+
"react": "^17.0.0 || ^18.0.0",
|
|
74
|
+
"sanity": "purple-unicorn",
|
|
37
75
|
"styled-components": "^5.2.0"
|
|
38
76
|
},
|
|
39
77
|
"bugs": {
|
|
@@ -48,5 +86,7 @@
|
|
|
48
86
|
"publishConfig": {
|
|
49
87
|
"access": "public"
|
|
50
88
|
},
|
|
51
|
-
"
|
|
89
|
+
"engines": {
|
|
90
|
+
"node": ">=14.0.0"
|
|
91
|
+
}
|
|
52
92
|
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'
|
|
2
|
+
import {set, setIfMissing, unset} from 'sanity/form'
|
|
3
|
+
import {debounce} from 'lodash'
|
|
4
|
+
import {Button} from '@sanity/ui'
|
|
5
|
+
import {AddIcon} from '@sanity/icons'
|
|
6
|
+
import {ColorPicker} from './ColorPicker'
|
|
7
|
+
import {ObjectInputProps} from 'sanity'
|
|
8
|
+
import {HSLColor, HSVColor, RGBColor} from 'react-color'
|
|
9
|
+
import {ObjectSchemaType} from '@sanity/types'
|
|
10
|
+
|
|
11
|
+
const DEFAULT_COLOR: ColorValue & {source: string} = {
|
|
12
|
+
hex: '#24a3e3',
|
|
13
|
+
hsl: {h: 200, s: 0.7732, l: 0.5156, a: 1},
|
|
14
|
+
hsv: {h: 200, s: 0.8414, v: 0.8901, a: 1},
|
|
15
|
+
rgb: {r: 46, g: 163, b: 227, a: 1},
|
|
16
|
+
source: 'hex',
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ColorValue {
|
|
20
|
+
hex: string
|
|
21
|
+
hsl: HSLColor
|
|
22
|
+
hsv: HSVColor
|
|
23
|
+
rgb: RGBColor
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ColorOptions {
|
|
27
|
+
disableAlpha?: boolean
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type ColorSchemaType = ObjectSchemaType & {
|
|
31
|
+
options?: ColorOptions
|
|
32
|
+
}
|
|
33
|
+
export type ColorInputProps = ObjectInputProps<ColorValue, ColorSchemaType>
|
|
34
|
+
|
|
35
|
+
export function ColorInput(props: ColorInputProps) {
|
|
36
|
+
const {onChange, schemaType: type, readOnly, value} = props
|
|
37
|
+
const focusRef = useRef<HTMLButtonElement>(null)
|
|
38
|
+
|
|
39
|
+
// use local state so we can have instant ui updates while debouncing patch emits
|
|
40
|
+
const [color, setColor] = useState(value)
|
|
41
|
+
useEffect(() => setColor(value), [value])
|
|
42
|
+
|
|
43
|
+
const emitSetColor = useCallback(
|
|
44
|
+
(nextColor: ColorValue) => {
|
|
45
|
+
const fieldPatches = type.fields
|
|
46
|
+
.filter((field) => field.name in nextColor)
|
|
47
|
+
.map((field) => {
|
|
48
|
+
const nextFieldValue = nextColor[field.name as keyof ColorValue]
|
|
49
|
+
const isObject = field.type.jsonType === 'object'
|
|
50
|
+
return set(
|
|
51
|
+
isObject ? Object.assign({_type: field.type.name}, nextFieldValue) : nextFieldValue,
|
|
52
|
+
[field.name]
|
|
53
|
+
)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
onChange([
|
|
57
|
+
setIfMissing({_type: type.name}),
|
|
58
|
+
set(type.name, ['_type']),
|
|
59
|
+
set(nextColor.rgb?.a, ['alpha']),
|
|
60
|
+
...fieldPatches,
|
|
61
|
+
])
|
|
62
|
+
},
|
|
63
|
+
[onChange, type]
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
// The color picker emits onChange events continuously while the user is sliding the
|
|
67
|
+
// hue/saturation/alpha selectors. This debounces the event to avoid excessive patches
|
|
68
|
+
const debouncedColorChange = useMemo(() => debounce(emitSetColor, 100), [emitSetColor])
|
|
69
|
+
const handleColorChange = useCallback(
|
|
70
|
+
(nextColor: ColorValue) => {
|
|
71
|
+
setColor(nextColor)
|
|
72
|
+
debouncedColorChange(nextColor)
|
|
73
|
+
},
|
|
74
|
+
[debouncedColorChange, setColor]
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
const handleCreateColor = useCallback(() => {
|
|
78
|
+
setColor(DEFAULT_COLOR)
|
|
79
|
+
emitSetColor(DEFAULT_COLOR)
|
|
80
|
+
}, [emitSetColor])
|
|
81
|
+
|
|
82
|
+
const handleUnset = useCallback(() => {
|
|
83
|
+
setColor(undefined)
|
|
84
|
+
onChange(unset())
|
|
85
|
+
}, [onChange])
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<>
|
|
89
|
+
{value ? (
|
|
90
|
+
<ColorPicker
|
|
91
|
+
/* ref={this.focusRef}*/
|
|
92
|
+
color={color}
|
|
93
|
+
onChange={handleColorChange}
|
|
94
|
+
readOnly={readOnly || (typeof type.readOnly === 'boolean' && type.readOnly)}
|
|
95
|
+
disableAlpha={type.options?.disableAlpha}
|
|
96
|
+
onUnset={handleUnset}
|
|
97
|
+
/>
|
|
98
|
+
) : (
|
|
99
|
+
<Button
|
|
100
|
+
icon={AddIcon}
|
|
101
|
+
mode="ghost"
|
|
102
|
+
text="Create color"
|
|
103
|
+
ref={focusRef}
|
|
104
|
+
disabled={Boolean(readOnly)}
|
|
105
|
+
onClick={handleCreateColor}
|
|
106
|
+
/>
|
|
107
|
+
)}
|
|
108
|
+
</>
|
|
109
|
+
)
|
|
110
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import {Alpha, Checkboard, Hue, Saturation} from 'react-color/lib/components/common'
|
|
3
|
+
import {CustomPicker, HEXColor, HSLColor, HSVColor, RGBColor} from 'react-color'
|
|
4
|
+
import {Box, Button, Card, Flex, Inline, Stack, Text} from '@sanity/ui'
|
|
5
|
+
import {TrashIcon} from '@sanity/icons'
|
|
6
|
+
import styled from 'styled-components'
|
|
7
|
+
import {ColorPickerFields} from './ColorPickerFields'
|
|
8
|
+
import {CustomPickerInjectedProps} from 'react-color/lib/components/common/ColorWrap'
|
|
9
|
+
import {ColorValue} from './ColorInput'
|
|
10
|
+
|
|
11
|
+
const ColorBox = styled(Box)`
|
|
12
|
+
position: absolute;
|
|
13
|
+
top: 0;
|
|
14
|
+
left: 0;
|
|
15
|
+
width: 100%;
|
|
16
|
+
height: 100%;
|
|
17
|
+
`
|
|
18
|
+
|
|
19
|
+
const ReadOnlyContainer = styled(Flex)`
|
|
20
|
+
margin-top: 6rem;
|
|
21
|
+
background-color: var(--card-bg-color);
|
|
22
|
+
position: relative;
|
|
23
|
+
width: 100%;
|
|
24
|
+
`
|
|
25
|
+
|
|
26
|
+
export interface ColorPickerProps
|
|
27
|
+
extends CustomPickerInjectedProps<HSLColor | HSVColor | RGBColor | HEXColor> {
|
|
28
|
+
width?: string
|
|
29
|
+
disableAlpha: boolean
|
|
30
|
+
readOnly?: boolean
|
|
31
|
+
onUnset: () => void
|
|
32
|
+
color: ColorValue
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const ColorPickerInner = (props: ColorPickerProps) => {
|
|
36
|
+
const {
|
|
37
|
+
width,
|
|
38
|
+
color: {rgb, hex, hsv, hsl},
|
|
39
|
+
onChange,
|
|
40
|
+
onUnset,
|
|
41
|
+
disableAlpha,
|
|
42
|
+
readOnly,
|
|
43
|
+
} = props
|
|
44
|
+
return (
|
|
45
|
+
<div style={{width}}>
|
|
46
|
+
<Card padding={1} border radius={1}>
|
|
47
|
+
<Stack space={2}>
|
|
48
|
+
{!readOnly && (
|
|
49
|
+
<>
|
|
50
|
+
<Card overflow="hidden" style={{position: 'relative', height: '5em'}}>
|
|
51
|
+
<Saturation onChange={onChange} hsl={hsl} hsv={hsv} />
|
|
52
|
+
</Card>
|
|
53
|
+
|
|
54
|
+
<Card
|
|
55
|
+
shadow={1}
|
|
56
|
+
radius={3}
|
|
57
|
+
overflow="hidden"
|
|
58
|
+
style={{position: 'relative', height: '10px'}}
|
|
59
|
+
>
|
|
60
|
+
<Hue hsl={hsl} onChange={!readOnly && onChange} />
|
|
61
|
+
</Card>
|
|
62
|
+
|
|
63
|
+
{!disableAlpha && (
|
|
64
|
+
<Card
|
|
65
|
+
shadow={1}
|
|
66
|
+
radius={3}
|
|
67
|
+
overflow="hidden"
|
|
68
|
+
style={{position: 'relative', height: '10px'}}
|
|
69
|
+
>
|
|
70
|
+
<Alpha rgb={rgb} hsl={hsl} onChange={onChange} />
|
|
71
|
+
</Card>
|
|
72
|
+
)}
|
|
73
|
+
</>
|
|
74
|
+
)}
|
|
75
|
+
<Flex>
|
|
76
|
+
<Card
|
|
77
|
+
flex={1}
|
|
78
|
+
radius={2}
|
|
79
|
+
overflow="hidden"
|
|
80
|
+
style={{position: 'relative', minWidth: '4em'}}
|
|
81
|
+
>
|
|
82
|
+
<Checkboard />
|
|
83
|
+
<ColorBox
|
|
84
|
+
style={{
|
|
85
|
+
backgroundColor: `rgba(${rgb?.r},${rgb?.g},${rgb?.b},${rgb?.a})`,
|
|
86
|
+
}}
|
|
87
|
+
/>
|
|
88
|
+
|
|
89
|
+
{readOnly && (
|
|
90
|
+
<ReadOnlyContainer
|
|
91
|
+
padding={2}
|
|
92
|
+
paddingBottom={1}
|
|
93
|
+
sizing="border"
|
|
94
|
+
justify="space-between"
|
|
95
|
+
>
|
|
96
|
+
<Stack space={3} marginTop={1}>
|
|
97
|
+
<Text size={3} weight="bold">
|
|
98
|
+
{hex}
|
|
99
|
+
</Text>
|
|
100
|
+
|
|
101
|
+
<Inline space={3}>
|
|
102
|
+
<Text size={1}>
|
|
103
|
+
<strong>RGB: </strong>
|
|
104
|
+
{rgb?.r} {rgb?.g} {rgb?.b}
|
|
105
|
+
</Text>
|
|
106
|
+
<Text size={1}>
|
|
107
|
+
<strong>HSL: </strong> {Math.round(hsl?.h ?? 0)} {Math.round(hsl?.s ?? 0)}%{' '}
|
|
108
|
+
{Math.round(hsl?.l ?? 0)}
|
|
109
|
+
</Text>
|
|
110
|
+
</Inline>
|
|
111
|
+
</Stack>
|
|
112
|
+
</ReadOnlyContainer>
|
|
113
|
+
)}
|
|
114
|
+
</Card>
|
|
115
|
+
|
|
116
|
+
{!readOnly && (
|
|
117
|
+
<Flex align="flex-start" marginLeft={2}>
|
|
118
|
+
<Box style={{width: 200}}>
|
|
119
|
+
<ColorPickerFields
|
|
120
|
+
rgb={rgb}
|
|
121
|
+
hsl={hsl}
|
|
122
|
+
hex={hex}
|
|
123
|
+
onChange={onChange}
|
|
124
|
+
disableAlpha={disableAlpha}
|
|
125
|
+
/>
|
|
126
|
+
</Box>
|
|
127
|
+
<Box marginLeft={2}>
|
|
128
|
+
<Button onClick={onUnset} title="Delete color" icon={TrashIcon} tone="critical" />
|
|
129
|
+
</Box>
|
|
130
|
+
</Flex>
|
|
131
|
+
)}
|
|
132
|
+
</Flex>
|
|
133
|
+
</Stack>
|
|
134
|
+
</Card>
|
|
135
|
+
</div>
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export const ColorPicker = CustomPicker(ColorPickerInner)
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import React, {useCallback, useMemo} from 'react'
|
|
2
|
+
// @ts-expect-error missing export
|
|
3
|
+
import {isValidHex} from 'react-color/lib/helpers/color'
|
|
4
|
+
import {EditableInput} from 'react-color/lib/components/common'
|
|
5
|
+
import {Box, Flex, useTheme} from '@sanity/ui'
|
|
6
|
+
import {ColorChangeHandler, HEXColor, HSLColor, HSVColor, RGBColor} from 'react-color'
|
|
7
|
+
import {EditableInputStyles} from 'react-color/lib/components/common/EditableInput'
|
|
8
|
+
|
|
9
|
+
interface ColorPickerFieldsProps {
|
|
10
|
+
rgb?: RGBColor
|
|
11
|
+
hsl?: HSLColor
|
|
12
|
+
hex?: string
|
|
13
|
+
disableAlpha: boolean
|
|
14
|
+
onChange: ColorChangeHandler<HSLColor | HSVColor | RGBColor | HEXColor>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const ColorPickerFields = ({
|
|
18
|
+
onChange,
|
|
19
|
+
rgb,
|
|
20
|
+
hsl,
|
|
21
|
+
hex,
|
|
22
|
+
disableAlpha,
|
|
23
|
+
}: ColorPickerFieldsProps) => {
|
|
24
|
+
const {sanity} = useTheme()
|
|
25
|
+
|
|
26
|
+
const inputStyles: EditableInputStyles = useMemo(
|
|
27
|
+
() => ({
|
|
28
|
+
input: {
|
|
29
|
+
width: '80%',
|
|
30
|
+
padding: '4px 10% 3px',
|
|
31
|
+
border: 'none',
|
|
32
|
+
boxShadow: `inset 0 0 0 1px ${sanity.color.input.default.enabled.border}`,
|
|
33
|
+
color: sanity.color.input.default.enabled.fg,
|
|
34
|
+
backgroundColor: sanity.color.input.default.enabled.bg,
|
|
35
|
+
fontSize: sanity.fonts.text.sizes[0].fontSize,
|
|
36
|
+
textAlign: 'center',
|
|
37
|
+
},
|
|
38
|
+
label: {
|
|
39
|
+
display: 'block',
|
|
40
|
+
textAlign: 'center',
|
|
41
|
+
fontSize: sanity.fonts.label.sizes[0].fontSize,
|
|
42
|
+
color: sanity.color.base.fg,
|
|
43
|
+
paddingTop: '3px',
|
|
44
|
+
paddingBottom: '4px',
|
|
45
|
+
textTransform: 'capitalize',
|
|
46
|
+
},
|
|
47
|
+
}),
|
|
48
|
+
[sanity]
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
const handleChange: ColorChangeHandler<Record<string, string>> = useCallback(
|
|
52
|
+
(data) => {
|
|
53
|
+
if ('hex' in data && data.hex && isValidHex(data.hex)) {
|
|
54
|
+
onChange({
|
|
55
|
+
hex: data.hex,
|
|
56
|
+
source: 'hex',
|
|
57
|
+
})
|
|
58
|
+
} else if (
|
|
59
|
+
rgb &&
|
|
60
|
+
(('r' in data && data.r) || ('g' in data && data.g) || ('b' in data && data.b))
|
|
61
|
+
) {
|
|
62
|
+
onChange({
|
|
63
|
+
r: Number(data.r) || rgb.r,
|
|
64
|
+
g: Number(data.g) || rgb.g,
|
|
65
|
+
b: Number(data.b) || rgb.b,
|
|
66
|
+
a: rgb.a,
|
|
67
|
+
source: 'rgb',
|
|
68
|
+
})
|
|
69
|
+
} else if (hsl && 'a' in data && data.a) {
|
|
70
|
+
let alpha = Number(data.a)
|
|
71
|
+
if (alpha < 0) {
|
|
72
|
+
alpha = 0
|
|
73
|
+
} else if (alpha > 100) {
|
|
74
|
+
alpha = 100
|
|
75
|
+
}
|
|
76
|
+
alpha /= 100
|
|
77
|
+
|
|
78
|
+
onChange({
|
|
79
|
+
h: hsl.h,
|
|
80
|
+
s: hsl.s,
|
|
81
|
+
l: hsl.l,
|
|
82
|
+
a: alpha,
|
|
83
|
+
source: 'hsl',
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
[onChange, hsl, rgb]
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<Flex>
|
|
92
|
+
<Box flex={2} marginRight={1}>
|
|
93
|
+
<EditableInput
|
|
94
|
+
style={inputStyles}
|
|
95
|
+
label="hex"
|
|
96
|
+
value={hex?.replace('#', '')}
|
|
97
|
+
onChange={handleChange}
|
|
98
|
+
/>
|
|
99
|
+
</Box>
|
|
100
|
+
<Box flex={1} marginRight={1}>
|
|
101
|
+
<EditableInput
|
|
102
|
+
style={inputStyles}
|
|
103
|
+
label="r"
|
|
104
|
+
value={rgb?.r}
|
|
105
|
+
onChange={handleChange}
|
|
106
|
+
dragLabel
|
|
107
|
+
dragMax={255}
|
|
108
|
+
/>
|
|
109
|
+
</Box>
|
|
110
|
+
<Box flex={1} marginRight={1}>
|
|
111
|
+
<EditableInput
|
|
112
|
+
style={inputStyles}
|
|
113
|
+
label="g"
|
|
114
|
+
value={rgb?.g}
|
|
115
|
+
onChange={handleChange}
|
|
116
|
+
dragLabel
|
|
117
|
+
dragMax={255}
|
|
118
|
+
/>
|
|
119
|
+
</Box>
|
|
120
|
+
<Box flex={1} marginRight={1}>
|
|
121
|
+
<EditableInput
|
|
122
|
+
style={inputStyles}
|
|
123
|
+
label="b"
|
|
124
|
+
value={rgb?.b}
|
|
125
|
+
onChange={handleChange}
|
|
126
|
+
dragLabel
|
|
127
|
+
dragMax={255}
|
|
128
|
+
/>
|
|
129
|
+
</Box>
|
|
130
|
+
{!disableAlpha && (
|
|
131
|
+
<Box flex={1}>
|
|
132
|
+
<EditableInput
|
|
133
|
+
style={inputStyles}
|
|
134
|
+
label="a"
|
|
135
|
+
value={Math.round((rgb?.a ?? 1) * 100)}
|
|
136
|
+
onChange={handleChange}
|
|
137
|
+
dragLabel
|
|
138
|
+
dragMax={100}
|
|
139
|
+
/>
|
|
140
|
+
</Box>
|
|
141
|
+
)}
|
|
142
|
+
</Flex>
|
|
143
|
+
)
|
|
144
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {createPlugin} from 'sanity'
|
|
2
|
+
import {hslaColor} from './schemas/hslaColor'
|
|
3
|
+
import {rgbaColor} from './schemas/rgbaColor'
|
|
4
|
+
import {color} from './schemas/color'
|
|
5
|
+
import {hsvaColor} from './schemas/hsvaColor'
|
|
6
|
+
|
|
7
|
+
export const colorInput = createPlugin({
|
|
8
|
+
name: '@sanity/color-input',
|
|
9
|
+
schema: {
|
|
10
|
+
types: [hslaColor, hsvaColor, rgbaColor, color],
|
|
11
|
+
},
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
export {hslaColor, rgbaColor, color, hsvaColor}
|
|
15
|
+
export {ColorInput} from './ColorInput'
|
|
16
|
+
export type {ColorValue, ColorInputProps, ColorOptions, ColorSchemaType} from './ColorInput'
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import {ColorInput} from '../ColorInput'
|
|
3
|
+
|
|
4
|
+
const round = (val: number = 1) => Math.round(val * 100)
|
|
5
|
+
|
|
6
|
+
export const color = {
|
|
7
|
+
name: 'color',
|
|
8
|
+
type: 'object',
|
|
9
|
+
title: 'Color',
|
|
10
|
+
components: {input: ColorInput},
|
|
11
|
+
fields: [
|
|
12
|
+
{
|
|
13
|
+
title: 'Hex',
|
|
14
|
+
name: 'hex',
|
|
15
|
+
type: 'string',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
title: 'Alpha',
|
|
19
|
+
name: 'alpha',
|
|
20
|
+
type: 'number',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
title: 'Hue Saturation Lightness',
|
|
24
|
+
name: 'hsl',
|
|
25
|
+
type: 'hslaColor',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
title: 'Hue Saturation Value',
|
|
29
|
+
name: 'hsv',
|
|
30
|
+
type: 'hsvaColor',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
title: 'Red Green Blue (rgb)',
|
|
34
|
+
name: 'rgb',
|
|
35
|
+
type: 'rgbaColor',
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
preview: {
|
|
39
|
+
select: {
|
|
40
|
+
title: 'hex',
|
|
41
|
+
alpha: 'alpha',
|
|
42
|
+
hex: 'hex',
|
|
43
|
+
hsl: 'hsl',
|
|
44
|
+
},
|
|
45
|
+
prepare({
|
|
46
|
+
title,
|
|
47
|
+
hex,
|
|
48
|
+
hsl,
|
|
49
|
+
alpha,
|
|
50
|
+
}: {
|
|
51
|
+
title?: string
|
|
52
|
+
alpha?: number
|
|
53
|
+
hex?: string
|
|
54
|
+
hsl?: {h: number; s: number; l: number}
|
|
55
|
+
}) {
|
|
56
|
+
let subtitle = hex || 'No color set'
|
|
57
|
+
if (hsl) {
|
|
58
|
+
subtitle = `H:${round(hsl.h)} S:${round(hsl.s)} L:${round(hsl.l)} A:${round(alpha)}`
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
title: title,
|
|
62
|
+
subtitle: subtitle,
|
|
63
|
+
media: () => (
|
|
64
|
+
<div
|
|
65
|
+
style={{
|
|
66
|
+
backgroundColor: hex ?? '#000',
|
|
67
|
+
opacity: alpha ?? 1,
|
|
68
|
+
position: 'absolute',
|
|
69
|
+
height: '100%',
|
|
70
|
+
width: '100%',
|
|
71
|
+
top: '0',
|
|
72
|
+
left: '0',
|
|
73
|
+
}}
|
|
74
|
+
/>
|
|
75
|
+
),
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const hslaColor = {
|
|
2
|
+
title: 'Hue Saturation Lightness',
|
|
3
|
+
name: 'hslaColor',
|
|
4
|
+
type: 'object',
|
|
5
|
+
fields: [
|
|
6
|
+
{name: 'h', type: 'number', title: 'Hue'},
|
|
7
|
+
{name: 's', type: 'number', title: 'Saturation'},
|
|
8
|
+
{name: 'l', type: 'number', title: 'Lightness'},
|
|
9
|
+
{name: 'a', type: 'number', title: 'Alpha'},
|
|
10
|
+
],
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const hsvaColor = {
|
|
2
|
+
title: 'Hue Saturation Value',
|
|
3
|
+
name: 'hsvaColor',
|
|
4
|
+
type: 'object',
|
|
5
|
+
fields: [
|
|
6
|
+
{name: 'h', type: 'number', title: 'Hue'},
|
|
7
|
+
{name: 's', type: 'number', title: 'Saturation'},
|
|
8
|
+
{name: 'v', type: 'number', title: 'Value'},
|
|
9
|
+
{name: 'a', type: 'number', title: 'Alpha'},
|
|
10
|
+
],
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const rgbaColor = {
|
|
2
|
+
title: 'Red Green Blue (rgb)',
|
|
3
|
+
name: 'rgbaColor',
|
|
4
|
+
type: 'object',
|
|
5
|
+
fields: [
|
|
6
|
+
{name: 'r', type: 'number', title: 'Red'},
|
|
7
|
+
{name: 'g', type: 'number', title: 'Green'},
|
|
8
|
+
{name: 'b', type: 'number', title: 'Blue'},
|
|
9
|
+
{name: 'a', type: 'number', title: 'Alpha'},
|
|
10
|
+
],
|
|
11
|
+
}
|