@sanity/color-input 4.0.6 → 4.0.7

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.
@@ -1,97 +0,0 @@
1
- import {AddIcon} from '@sanity/icons'
2
- import {Button} from '@sanity/ui'
3
- import {startTransition, useCallback, useDeferredValue, useEffect, useRef, useState} from 'react'
4
- import {type ObjectInputProps, set, setIfMissing, unset} from 'sanity'
5
- import {useEffectEvent} from 'use-effect-event'
6
-
7
- import {ColorPicker} from './ColorPicker'
8
- import type {ColorSchemaType, ColorValue} from './types'
9
-
10
- const DEFAULT_COLOR: ColorValue & {source: string} = {
11
- hex: '#24a3e3',
12
- hsl: {h: 200, s: 0.7732, l: 0.5156, a: 1},
13
- hsv: {h: 200, s: 0.8414, v: 0.8901, a: 1},
14
- rgb: {r: 46, g: 163, b: 227, a: 1},
15
- source: 'hex',
16
- }
17
-
18
- export default function ColorInput(props: ObjectInputProps) {
19
- const {onChange, readOnly} = props
20
- const value = props.value as ColorValue | undefined
21
- const type = props.schemaType as ColorSchemaType
22
- const focusRef = useRef<HTMLButtonElement>(null)
23
-
24
- // use local state so we can have instant ui updates while debouncing patch emits
25
- const [color, setColor] = useState(value)
26
- // Marking the `setColor` in a transition allows React to interrupt render should the user start dragging the input before React is finished rendering
27
- useEffect(() => startTransition(() => setColor(value)), [value])
28
-
29
- // The color picker emits onChange events continuously while the user is sliding the
30
- // hue/saturation/alpha selectors. This debounces the event to avoid excessive patches
31
- // and massively improve render performance and avoid jank
32
- const [emitColor, setEmitColor] = useState<typeof value>(undefined)
33
- const debouncedColor = useDeferredValue(emitColor)
34
- const handleChange = useEffectEvent((nextColor: ColorValue) => {
35
- const fieldPatches = type.fields
36
- .filter((field) => field.name in nextColor)
37
- .map((field) => {
38
- const nextFieldValue = nextColor[field.name as keyof ColorValue]
39
- const isObject = field.type.jsonType === 'object'
40
- return set(
41
- isObject ? Object.assign({_type: field.type.name}, nextFieldValue) : nextFieldValue,
42
- [field.name],
43
- )
44
- })
45
-
46
- onChange([
47
- setIfMissing({_type: type.name}),
48
- set(type.name, ['_type']),
49
- set(nextColor.rgb?.a, ['alpha']),
50
- ...fieldPatches,
51
- ])
52
- })
53
- useEffect(() => {
54
- if (!debouncedColor) return undefined
55
- const raf = requestAnimationFrame(() => handleChange(debouncedColor))
56
- return () => cancelAnimationFrame(raf)
57
- }, [debouncedColor])
58
-
59
- const handleCreateColor = useCallback(() => {
60
- setColor(DEFAULT_COLOR)
61
- setEmitColor(DEFAULT_COLOR)
62
- }, [])
63
-
64
- const handleColorChange = useCallback((nextColor: ColorValue) => {
65
- setColor(nextColor)
66
- setEmitColor(nextColor)
67
- }, [])
68
-
69
- const handleUnset = useCallback(() => {
70
- setColor(undefined)
71
- onChange(unset())
72
- }, [onChange])
73
-
74
- return (
75
- <>
76
- {value && value.hex ? (
77
- <ColorPicker
78
- color={color}
79
- onChange={handleColorChange}
80
- readOnly={readOnly || (typeof type.readOnly === 'boolean' && type.readOnly)}
81
- disableAlpha={!!type.options?.disableAlpha}
82
- colorList={type.options?.colorList}
83
- onUnset={handleUnset}
84
- />
85
- ) : (
86
- <Button
87
- icon={AddIcon}
88
- mode="ghost"
89
- text="Create color"
90
- ref={focusRef}
91
- disabled={Boolean(readOnly)}
92
- onClick={handleCreateColor}
93
- />
94
- )}
95
- </>
96
- )
97
- }
package/src/ColorList.tsx DELETED
@@ -1,70 +0,0 @@
1
- import {Flex} from '@sanity/ui'
2
- import {memo} from 'react'
3
- import type {Color, ColorChangeHandler} from 'react-color'
4
- import {styled} from 'styled-components'
5
- import tinycolor from 'tinycolor2'
6
-
7
- const ColorListWrap = styled(Flex)`
8
- gap: 0.25em;
9
- `
10
-
11
- const ColorBoxContainer = styled.div`
12
- width: 2.1em;
13
- height: 2.1em;
14
- cursor: pointer;
15
- position: relative;
16
- overflow: hidden;
17
- border-radius: 3px;
18
- background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAADFJREFUOE9jZGBgEGHAD97gk2YcNYBhmIQBgWSAP52AwoAQwJvQRg1gACckQoC2gQgAIF8IscwEtKYAAAAASUVORK5CYII=')
19
- left center #fff;
20
- `
21
-
22
- const ColorBox = styled.div`
23
- border-radius: inherit;
24
- box-shadow: inset 0 0 0 1px var(--card-shadow-outline-color);
25
- content: '';
26
- position: absolute;
27
- inset: 0;
28
- z-index: 1;
29
- `
30
-
31
- interface ValidatedColor {
32
- color: Color
33
- backgroundColor: string
34
- }
35
-
36
- interface ColorListProps {
37
- colors?: Array<Color>
38
- onChange: ColorChangeHandler<Color>
39
- }
40
-
41
- const validateColors = (colors: Array<Color>) =>
42
- colors.reduce((cls: Array<ValidatedColor>, c) => {
43
- // @ts-expect-error fix types later
44
- const color = c.hex ? tinycolor(c.hex) : tinycolor(c)
45
- if (color.isValid()) {
46
- cls.push({
47
- color: c,
48
- backgroundColor: color.toRgbString(),
49
- })
50
- }
51
- return cls
52
- }, [])
53
-
54
- export const ColorList = memo(function ColorList({colors, onChange}: ColorListProps) {
55
- if (!colors) return null
56
- return (
57
- <ColorListWrap wrap="wrap">
58
- {validateColors(colors).map(({color, backgroundColor}, idx) => (
59
- <ColorBoxContainer
60
- key={`${backgroundColor}-${idx}`}
61
- onClick={() => {
62
- onChange(color)
63
- }}
64
- >
65
- <ColorBox style={{background: backgroundColor}} />
66
- </ColorBoxContainer>
67
- ))}
68
- </ColorListWrap>
69
- )
70
- })
@@ -1,152 +0,0 @@
1
- import {TrashIcon} from '@sanity/icons'
2
- import {Box, Button, Card, Flex, Inline, Stack, Text} from '@sanity/ui'
3
- import {type Color, CustomPicker} from 'react-color'
4
- import {Alpha, Checkboard, Hue, Saturation} from 'react-color/lib/components/common'
5
- import type {CustomPickerInjectedProps} from 'react-color/lib/components/common/ColorWrap'
6
- import {styled} from 'styled-components'
7
-
8
- import {ColorList} from './ColorList'
9
- import {ColorPickerFields} from './ColorPickerFields'
10
- import type {ColorValue} from './types'
11
-
12
- const ColorBox = styled(Box)`
13
- position: absolute;
14
- top: 0;
15
- left: 0;
16
- width: 100%;
17
- height: 100%;
18
- `
19
-
20
- const ReadOnlyContainer = styled(Flex)`
21
- margin-top: 6rem;
22
- background-color: var(--card-bg-color);
23
- position: relative;
24
- width: 100%;
25
- `
26
-
27
- export interface ColorPickerProps extends CustomPickerInjectedProps<Color> {
28
- width?: string
29
- disableAlpha: boolean
30
- colorList?: Array<Color>
31
- readOnly?: boolean
32
- onUnset: () => void
33
- color: ColorValue
34
- }
35
-
36
- const ColorPickerInner = (props: ColorPickerProps) => {
37
- const {
38
- width,
39
- color: {rgb, hex, hsv, hsl},
40
- onChange,
41
- onUnset,
42
- disableAlpha,
43
- colorList,
44
- readOnly,
45
- } = props
46
-
47
- if (!hsl || !hsv) {
48
- return null
49
- }
50
-
51
- return (
52
- <div style={{width}}>
53
- <Card padding={1} border radius={1}>
54
- <Stack space={2}>
55
- {!readOnly && (
56
- <>
57
- <Card overflow="hidden" style={{position: 'relative', height: '5em'}}>
58
- <Saturation onChange={onChange} hsl={hsl} hsv={hsv} />
59
- </Card>
60
-
61
- <Card
62
- shadow={1}
63
- radius={3}
64
- overflow="hidden"
65
- style={{position: 'relative', height: '10px'}}
66
- >
67
- <Hue hsl={hsl} onChange={!readOnly && onChange} />
68
- </Card>
69
-
70
- {!disableAlpha && (
71
- <Card
72
- shadow={1}
73
- radius={3}
74
- overflow="hidden"
75
- style={{position: 'relative', height: '10px', background: '#fff'}}
76
- >
77
- <Alpha rgb={rgb} hsl={hsl} onChange={onChange} />
78
- </Card>
79
- )}
80
- </>
81
- )}
82
- <Flex>
83
- <Card
84
- flex={1}
85
- radius={2}
86
- overflow="hidden"
87
- style={{position: 'relative', minWidth: '4em', background: '#fff'}}
88
- >
89
- <Checkboard
90
- size={8}
91
- white="transparent"
92
- grey="rgba(0,0,0,.08)"
93
- renderers={{} as {canvas: unknown}}
94
- />
95
- <ColorBox
96
- style={{
97
- backgroundColor: `rgba(${rgb?.r},${rgb?.g},${rgb?.b},${rgb?.a})`,
98
- }}
99
- />
100
-
101
- {readOnly && (
102
- <ReadOnlyContainer
103
- padding={2}
104
- paddingBottom={1}
105
- sizing="border"
106
- justify="space-between"
107
- >
108
- <Stack space={3} marginTop={1}>
109
- <Text size={3} weight="bold">
110
- {hex}
111
- </Text>
112
-
113
- <Inline space={3}>
114
- <Text size={1}>
115
- <strong>RGB: </strong>
116
- {rgb?.r} {rgb?.g} {rgb?.b}
117
- </Text>
118
- <Text size={1}>
119
- <strong>HSL: </strong> {Math.round(hsl?.h ?? 0)}{' '}
120
- {Math.round((hsl?.s ?? 0) * 100)}% {Math.round((hsl?.l ?? 0) * 100)}%
121
- </Text>
122
- </Inline>
123
- </Stack>
124
- </ReadOnlyContainer>
125
- )}
126
- </Card>
127
-
128
- {!readOnly && (
129
- <Flex align="flex-start" marginLeft={2}>
130
- <Box style={{width: 200}}>
131
- <ColorPickerFields
132
- rgb={rgb}
133
- hsl={hsl}
134
- hex={hex}
135
- onChange={onChange}
136
- disableAlpha={disableAlpha}
137
- />
138
- </Box>
139
- <Box marginLeft={2}>
140
- <Button onClick={onUnset} title="Delete color" icon={TrashIcon} tone="critical" />
141
- </Box>
142
- </Flex>
143
- )}
144
- </Flex>
145
- {colorList && <ColorList colors={colorList} onChange={onChange} />}
146
- </Stack>
147
- </Card>
148
- </div>
149
- )
150
- }
151
-
152
- export const ColorPicker = CustomPicker(ColorPickerInner)
@@ -1,144 +0,0 @@
1
- import {Box, Flex, useTheme} from '@sanity/ui'
2
- import {useCallback, useMemo} from 'react'
3
- import type {Color, ColorChangeHandler, HSLColor, RGBColor} from 'react-color'
4
- import {EditableInput} from 'react-color/lib/components/common'
5
- import type {EditableInputStyles} from 'react-color/lib/components/common/EditableInput'
6
- // @ts-expect-error missing export
7
- import {isValidHex} from 'react-color/lib/helpers/color'
8
-
9
- interface ColorPickerFieldsProps {
10
- rgb?: RGBColor
11
- hsl?: HSLColor
12
- hex?: string
13
- disableAlpha: boolean
14
- onChange: ColorChangeHandler<Color>
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
- }
@@ -1,3 +0,0 @@
1
- import {lazy} from 'react'
2
-
3
- export const ColorInput = lazy(() => import('./ColorInput'))
package/src/index.ts DELETED
@@ -1,18 +0,0 @@
1
- import {definePlugin} from 'sanity'
2
-
3
- import {color, type ColorDefinition} from './schemas/color'
4
- import {hslaColor} from './schemas/hslaColor'
5
- import {hsvaColor} from './schemas/hsvaColor'
6
- import {rgbaColor} from './schemas/rgbaColor'
7
-
8
- export const colorInput = definePlugin({
9
- name: '@sanity/color-input',
10
- schema: {
11
- types: [hslaColor, hsvaColor, rgbaColor, color],
12
- },
13
- })
14
-
15
- export {color, hslaColor, hsvaColor, rgbaColor}
16
- export {ColorInput} from './LazyColorInput'
17
- export type {ColorDefinition}
18
- export type {ColorInputProps, ColorOptions, ColorSchemaType, ColorValue} from './types'
@@ -1,98 +0,0 @@
1
- import {defineType, type ObjectDefinition} from 'sanity'
2
-
3
- import {ColorInput} from '../LazyColorInput'
4
- import {type ColorOptions} from '../types'
5
-
6
- const round = (val: number = 1) => Math.round(val * 100)
7
-
8
- const colorTypeName = 'color' as const
9
-
10
- /**
11
- * @public
12
- */
13
- export interface ColorDefinition extends Omit<ObjectDefinition, 'type' | 'fields' | 'options'> {
14
- type: typeof colorTypeName
15
- options?: ColorOptions
16
- }
17
-
18
- declare module '@sanity/types' {
19
- // makes type: 'color' narrow correctly when using defineTyp/defineField/defineArrayMember
20
- export interface IntrinsicDefinitions {
21
- color: ColorDefinition
22
- }
23
- }
24
-
25
- export const color = defineType({
26
- name: colorTypeName,
27
- type: 'object',
28
- title: 'Color',
29
- components: {input: ColorInput},
30
- fields: [
31
- {
32
- title: 'Hex',
33
- name: 'hex',
34
- type: 'string',
35
- },
36
- {
37
- title: 'Alpha',
38
- name: 'alpha',
39
- type: 'number',
40
- },
41
- {
42
- title: 'Hue Saturation Lightness',
43
- name: 'hsl',
44
- type: 'hslaColor',
45
- },
46
- {
47
- title: 'Hue Saturation Value',
48
- name: 'hsv',
49
- type: 'hsvaColor',
50
- },
51
- {
52
- title: 'Red Green Blue (rgb)',
53
- name: 'rgb',
54
- type: 'rgbaColor',
55
- },
56
- ],
57
- preview: {
58
- select: {
59
- title: 'hex',
60
- alpha: 'alpha',
61
- hex: 'hex',
62
- hsl: 'hsl',
63
- },
64
- prepare({
65
- title,
66
- hex,
67
- hsl,
68
- alpha,
69
- }: {
70
- title?: string
71
- alpha?: number
72
- hex?: string
73
- hsl?: {h: number; s: number; l: number}
74
- }) {
75
- let subtitle = hex || 'No color set'
76
- if (hsl) {
77
- subtitle = `H:${round(hsl.h)} S:${round(hsl.s)} L:${round(hsl.l)} A:${round(alpha)}`
78
- }
79
- return {
80
- title: title,
81
- subtitle: subtitle,
82
- media: () => (
83
- <div
84
- style={{
85
- backgroundColor: hex ?? '#000',
86
- opacity: alpha ?? 1,
87
- position: 'absolute',
88
- height: '100%',
89
- width: '100%',
90
- top: '0',
91
- left: '0',
92
- }}
93
- />
94
- ),
95
- }
96
- },
97
- },
98
- })
@@ -1,13 +0,0 @@
1
- import {defineType} from 'sanity'
2
-
3
- export const hslaColor = defineType({
4
- title: 'Hue Saturation Lightness',
5
- name: 'hslaColor',
6
- type: 'object',
7
- fields: [
8
- {name: 'h', type: 'number', title: 'Hue'},
9
- {name: 's', type: 'number', title: 'Saturation'},
10
- {name: 'l', type: 'number', title: 'Lightness'},
11
- {name: 'a', type: 'number', title: 'Alpha'},
12
- ],
13
- })
@@ -1,13 +0,0 @@
1
- import {defineType} from 'sanity'
2
-
3
- export const hsvaColor = defineType({
4
- title: 'Hue Saturation Value',
5
- name: 'hsvaColor',
6
- type: 'object',
7
- fields: [
8
- {name: 'h', type: 'number', title: 'Hue'},
9
- {name: 's', type: 'number', title: 'Saturation'},
10
- {name: 'v', type: 'number', title: 'Value'},
11
- {name: 'a', type: 'number', title: 'Alpha'},
12
- ],
13
- })
@@ -1,13 +0,0 @@
1
- import {defineType} from 'sanity'
2
-
3
- export const rgbaColor = defineType({
4
- title: 'Red Green Blue (rgb)',
5
- name: 'rgbaColor',
6
- type: 'object',
7
- fields: [
8
- {name: 'r', type: 'number', title: 'Red'},
9
- {name: 'g', type: 'number', title: 'Green'},
10
- {name: 'b', type: 'number', title: 'Blue'},
11
- {name: 'a', type: 'number', title: 'Alpha'},
12
- ],
13
- })
package/src/types.ts DELETED
@@ -1,19 +0,0 @@
1
- import type {Color, HSLColor, HSVColor, RGBColor} from 'react-color'
2
- import type {ObjectInputProps, ObjectOptions, ObjectSchemaType} from 'sanity'
3
-
4
- export interface ColorValue {
5
- hex: string
6
- hsl: HSLColor
7
- hsv: HSVColor
8
- rgb: RGBColor
9
- }
10
-
11
- export interface ColorOptions extends Omit<ObjectOptions, 'columns'> {
12
- disableAlpha?: boolean
13
- colorList?: Array<Color>
14
- }
15
-
16
- export type ColorSchemaType = Omit<ObjectSchemaType, 'options'> & {
17
- options?: ColorOptions
18
- }
19
- export type ColorInputProps = ObjectInputProps<ColorValue, ColorSchemaType>
@@ -1,11 +0,0 @@
1
- const {showIncompatiblePluginDialog} = require('@sanity/incompatible-plugin')
2
- const {name, version, sanityExchangeUrl} = require('./package.json')
3
-
4
- export default showIncompatiblePluginDialog({
5
- name: name,
6
- versions: {
7
- v3: version,
8
- v2: '^2.30.0',
9
- },
10
- sanityExchangeUrl,
11
- })