🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

solid-color

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

solid-color - npm Package Compare versions

Comparing version
0.0.4
to
0.0.5
+123
src/components/_common/Alpha.tsx
import { merge } from 'lodash-es'
import { JSX, mergeProps, onCleanup } from 'solid-js'
import { HslColor, RgbColor } from '../../types'
import * as alpha from '../../helpers/alpha'
import { Checkboard } from './Checkboard'
export interface AlphaProps {
rgb: RgbColor
hsl: HslColor
renderers?: any
direction?: string
a?: number
radius?: number
shadow?: string
styles?: Record<string, JSX.CSSProperties>
pointer?: <T extends object>(props: T) => JSX.Element
onChange?: (data: any, e: Event) => void
}
export const Alpha = (_props: AlphaProps) => {
const props = mergeProps(
{
direction: 'horizontal',
styles: {},
},
_props,
)
let container: HTMLDivElement
const styles = () => {
const { rgb } = props
return merge<Record<string, JSX.CSSProperties>, Record<string, JSX.CSSProperties>>(
{
alpha: {
position: 'absolute',
inset: '0px',
'border-radius': props.radius,
},
checkboard: {
position: 'absolute',
inset: '0px',
overflow: 'hidden',
'border-radius': props.radius,
},
gradient: {
position: 'absolute',
inset: '0px',
background:
props.direction === 'vertical'
? `linear-gradient(to bottom, rgba(${rgb.r},${rgb.g},${rgb.b}, 0) 0%,
rgba(${rgb.r},${rgb.g},${rgb.b}, 1) 100%)`
: `linear-gradient(to right, rgba(${rgb.r},${rgb.g},${rgb.b}, 0) 0%,
rgba(${rgb.r},${rgb.g},${rgb.b}, 1) 100%)`,
'box-shadow': props.shadow,
'border-radius': props.radius,
},
container: {
position: 'relative',
height: '100%',
margin: '0 3px',
},
pointer: {
position: 'absolute',
left: props.direction === 'vertical' ? 0 : `${rgb.a && rgb.a * 100}%`,
top: props.direction === 'vertical' ? `${rgb.a && rgb.a * 100}%` : undefined,
},
slider: {
width: '4px',
'border-radius': '1px',
height: '8px',
'box-shadow': '0 0 2px rgba(0, 0, 0, .6)',
background: '#fff',
'margin-top': '1px',
transform: 'translateX(-2px)',
},
},
props.styles,
)
}
const handleChange = (e: Event) => {
const change = alpha.calculateChange(e, props.hsl, props.direction, props.a, container)
change && typeof props.onChange === 'function' && props.onChange(change, e)
}
const handleMouseDown = (e: Event) => {
handleChange(e)
window.addEventListener('mousemove', handleChange)
window.addEventListener('mouseup', handleMouseUp)
}
const handleMouseUp = () => {
unbindEventListeners()
}
const unbindEventListeners = () => {
window.removeEventListener('mousemove', handleChange)
window.removeEventListener('mouseup', handleMouseUp)
}
onCleanup(() => unbindEventListeners())
return (
<div style={styles().alpha}>
<div style={styles().checkboard}>
<Checkboard renderers={props.renderers} />
</div>
<div style={styles().gradient} />
<div
style={styles().container}
ref={container!}
onMouseDown={handleMouseDown}
onMouseUp={handleChange}
onTouchMove={handleChange}
onTouchStart={handleChange}
>
<div style={styles().pointer}>
{props.pointer ? <props.pointer {...props} /> : <div style={styles().slider} />}
</div>
</div>
</div>
)
}
import * as checkboard from '../../helpers/checkboard'
import { JSX, mergeProps } from 'solid-js'
export type CheckboardProps = {
size?: number
white?: string
grey?: string
renderers?: any
borderRadius?: string | number
boxShadow?: string
children?: JSX.Element
}
export function Checkboard(_props: CheckboardProps) {
const props = mergeProps(
{
white: 'transparent',
grey: 'rgba(0,0,0,.08)',
size: 8,
renderers: {},
},
_props,
)
const styles = () => {
const { size, white, grey, borderRadius, boxShadow, renderers } = props
return {
grid: {
'border-radius': borderRadius,
'box-shadow': boxShadow,
position: 'absolute',
inset: '0px',
background: `url(${checkboard.get(white, grey, size, renderers.canvas)}) center left`,
} as JSX.CSSProperties,
}
}
// return isValidElement(children) ? (
// React.cloneElement(children, {
// ...children.props,
// style: { ...children.props.style, ...styles.grid },
// })
// ) : (
// <div style={styles.grid} />
// )
// 判断children是否是一个有效的元素
return props.children ? (
// clone
<div style={styles().grid}>{props.children}</div>
) : (
<div style={styles().grid} />
)
}
import {
Accessor,
Context,
createContext,
createEffect,
createMemo,
createSignal,
JSX,
mergeProps,
useContext,
} from 'solid-js'
import { ChangeColor, Color, ColorResult } from '../../types'
import * as color from '../../helpers/color'
import { debounce } from 'lodash-es'
export interface ColorPickerContextType {
colors: Accessor<ColorResult>
changeColor: (color: ChangeColor, event?: Event) => void
onSwatchHover?: (color: ChangeColor, event: Event) => void
}
export const ColorPickerContext = createContext<ColorPickerContextType | undefined>(undefined)
export interface ColorPickerProps {
children?: JSX.Element
defaultColor?: Color
color?: Color
onChange?: (color: ColorResult, event?: Event) => void
onChangeComplete?: (color: ColorResult) => void
onSwatchHover?: (color: ColorResult, event: Event) => void
}
export function ColorPickerProvider(_props: ColorPickerProps) {
const props = mergeProps({ defaultColor: { h: 250, s: 0.5, l: 0.2, a: 1 } }, _props)
const [colors, setColors] = createSignal<ColorResult>({
...color.toState(props.color ?? props.defaultColor, 0),
})
createEffect(() => {
if (props.color) {
setColors({ ...color.toState(props.color, 0) })
}
})
const handler = (fn: any, data: any, event: any) => fn(data, event)
const debouncedChangeHandler = createMemo(() => debounce(handler, 100), [])
const changeColor = (newColor: ChangeColor, event?: Event) => {
const isValidColor = color.simpleCheckForValidColor(newColor)
if (isValidColor) {
const newColors = color.toState(
newColor,
(typeof newColor !== 'string' && 'h' in newColor ? newColor.h : undefined) ||
colors().oldHue,
)
setColors(newColors)
props.onChangeComplete && debouncedChangeHandler()(props.onChangeComplete, newColors, event)
props.onChange && props.onChange(newColors, event)
}
}
const handleSwatchHover = (data: ChangeColor, event: Event) => {
const isValidColor = color.simpleCheckForValidColor(data)
if (isValidColor) {
const newColors = color.toState(
data,
(typeof data !== 'string' && 'h' in data ? data.h : undefined) || colors().oldHue,
)
props.onSwatchHover && props.onSwatchHover(newColors, event)
}
}
const store = {
colors,
changeColor,
onSwatchHover: props.onSwatchHover ? handleSwatchHover : undefined,
}
return <ColorPickerContext.Provider value={store}>{props.children}</ColorPickerContext.Provider>
}
export function useColorPicker() {
return useContext(ColorPickerContext as Context<ColorPickerContextType>)
}
export function withColorPicker<T extends object>(Component: (props: T) => JSX.Element) {
return (props: T & Omit<ColorPickerProps, 'children'>) => (
<ColorPickerProvider {...props}>
<Component {...props} />
</ColorPickerProvider>
)
}
import { merge } from 'lodash-es'
import { createEffect, createSignal, JSX, mergeProps, onCleanup, Show } from 'solid-js'
export interface EditableInputProps {
styles: Record<string, JSX.CSSProperties>
value?: string | number
label?: string
hideLabel?: boolean
placeholder?: string
arrowOffset?: number
dragLabel?: boolean
dragMax?: number
onChange?: (value: any, e: Event) => void
}
interface EditableInputState {
value: string
blurValue: string
borderColor?: string
}
export function EditableInput(_props: EditableInputProps) {
const props = mergeProps(
{
arrowOffset: 1,
hideLabel: false,
},
_props,
)
let inputRef: HTMLInputElement
const inputId = `sc-editable-input-${Math.random().toString().slice(2, 5)}`
const [state, setState] = createSignal<EditableInputState>({
value: String(props.value).toUpperCase(),
blurValue: String(props.value).toUpperCase(),
})
const DEFAULT_ARROW_OFFSET = 1
const UP_KEY_CODE = 38
const DOWN_KEY_CODE = 40
const VALID_KEY_CODES = [UP_KEY_CODE, DOWN_KEY_CODE]
const isValidKeyCode = (keyCode: number) => VALID_KEY_CODES.indexOf(keyCode) > -1
const getNumberValue = (value: string) => Number(String(value).replace(/%/g, ''))
const getValueObjectWithLabel = (value: string) => {
return {
[props.label!]: value,
}
}
const setUpdatedValue = (value: any, e: Event) => {
const onChangeValue = props.label ? getValueObjectWithLabel(value) : value
props.onChange && props.onChange(onChangeValue, e)
setState({ value, blurValue: value })
}
const handleBlur = () => {
if (state().blurValue) {
setState({ value: state().blurValue, blurValue: '' })
}
}
const handleChange = (e: Event) => {
setUpdatedValue((e.target as HTMLInputElement).value, e)
}
const handleDrag = (e: MouseEvent) => {
if (props.dragLabel) {
const newValue = Math.round(+props.value! + e.movementX)
if (newValue >= 0 && newValue <= props.dragMax!) {
props.onChange && props.onChange(getValueObjectWithLabel(String(newValue)), e)
}
}
}
const unbindEventListeners = () => {
window.removeEventListener('mousemove', handleDrag)
window.removeEventListener('mouseup', handleMouseUp)
}
const handleMouseUp = () => {
unbindEventListeners()
}
const handleMouseDown = (e: MouseEvent) => {
if (props.dragLabel) {
e.preventDefault()
handleDrag(e)
window.addEventListener('mousemove', handleDrag)
window.addEventListener('mouseup', handleMouseUp)
}
}
const handleKeyDown = (e: KeyboardEvent) => {
const value = getNumberValue((e.target as HTMLInputElement).value)
if (!isNaN(value) && isValidKeyCode(e.keyCode)) {
const offset = props.arrowOffset || DEFAULT_ARROW_OFFSET
const updatedValue = e.keyCode === UP_KEY_CODE ? value + offset : value - offset
setUpdatedValue(updatedValue, e)
}
}
createEffect(() => {
setState({
value: String(props.value).toUpperCase(),
blurValue: '',
})
})
const styles = () => {
return merge<Record<string, JSX.CSSProperties>, Record<string, JSX.CSSProperties>>(
{
wrap: {
position: 'relative',
},
},
props.styles,
)
}
onCleanup(() => unbindEventListeners())
return (
<div style={styles().wrap}>
<input
id={inputId}
ref={inputRef!}
style={styles().input}
spellcheck={false}
value={state().value}
placeholder={props.placeholder}
onBlur={handleBlur}
onChange={handleChange}
onKeyDown={handleKeyDown}
onInput={handleChange}
/>
<Show when={props.label && !props.hideLabel}>
<label style={styles().label} onMouseDown={handleMouseDown}>
{props.label}
</label>
</Show>
</div>
)
}
import { JSX, mergeProps, onCleanup } from 'solid-js'
import { calculateChange } from '../../helpers/hue'
import { HslColor } from '../../types'
interface HueProps {
children?: JSX.Element
direction?: string
radius?: number | string
shadow?: string
hsl: HslColor
styles?: Record<string, JSX.CSSProperties>
pointer?: <T extends object>(props: T) => JSX.Element
onChange?: (data: HslColor, e: MouseEvent) => void
}
export function Hue(_props: HueProps) {
const props = mergeProps(
{
direction: 'horizontal',
},
_props,
)
let container: HTMLDivElement
const styles = () => {
return {
hue: {
position: 'absolute',
inset: '0px',
'border-radius': typeof props.radius === 'string' ? props.radius : `${props.radius}px`,
'box-shadow': props.shadow,
},
container: {
padding: '0 2px',
position: 'relative',
height: '100%',
'border-radius': typeof props.radius === 'string' ? props.radius : `${props.radius}px`,
},
pointer: {
position: 'absolute',
left: props.direction === 'vertical' ? '0px' : `${(props.hsl.h * 100) / 360}%`,
top: props.direction === 'vertical' ? `${-((props.hsl.h * 100) / 360) + 100}%` : undefined,
},
slider: {
'margin-top': '1px',
width: '4px',
'border-radius': '1px',
height: '8px',
'box-shadow': '0 0 2px rgba(0, 0, 0, .6)',
background: '#fff',
transform: 'translateX(-2px)',
},
} as Record<string, JSX.CSSProperties>
}
const handleChange = (e: MouseEvent) => {
const change = calculateChange(e, props.direction, props.hsl, container)
change && typeof props.onChange === 'function' && props.onChange(change, e)
}
const unbindEventListeners = () => {
window.removeEventListener('mousemove', handleChange)
window.removeEventListener('mouseup', handleMouseUp)
}
const handleMouseUp = () => {
unbindEventListeners()
}
const handleMouseDown = (e: MouseEvent) => {
handleChange(e)
window.addEventListener('mousemove', handleChange)
window.addEventListener('mouseup', handleMouseUp)
}
onCleanup(() => unbindEventListeners())
return (
<div style={styles().hue}>
<div
ref={container!}
class={`hue-${props.direction}`}
style={styles().container}
onMouseDown={handleMouseDown}
>
<style>{`
.hue-horizontal {
background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0
33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
background: -webkit-linear-gradient(to right, #f00 0%, #ff0
17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
}
.hue-vertical {
background: linear-gradient(to top, #f00 0%, #ff0 17%, #0f0 33%,
#0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
background: -webkit-linear-gradient(to top, #f00 0%, #ff0 17%,
#0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
}
`}</style>
<div style={styles().pointer}>
{props.pointer ? <props.pointer {...props} /> : <div style={styles().slider} />}
</div>
</div>
</div>
)
}
import { mergeProps } from 'solid-js'
interface IconProps {
width?: number | string
height?: number | string
fill?: string
stroke?: string
onMouseOver?: (e: MouseEvent) => void
onMouseEnter?: (e: MouseEvent) => void
onMouseOut?: (e: MouseEvent) => void
}
export function CheckIcon(_props: IconProps) {
const props = mergeProps(
{
width: 24,
height: 24,
fill: 'white',
stroke: 'white',
},
_props,
)
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={`${props.width}px`}
height={`${props.height}px`}
fill={props.fill}
stroke={props.stroke}
stroke-width="0.5"
>
<path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z" />
</svg>
)
}
export function UnfoldMoreHorizontalIcon(_props: IconProps) {
const props = mergeProps(
{
width: 24,
height: 24,
},
_props,
)
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={`${props.width}px`}
height={`${props.height}px`}
stroke-width="0.5"
style={{ 'border-radius': '5px' }}
onMouseOver={props.onMouseOver}
onMouseEnter={props.onMouseEnter}
onMouseOut={props.onMouseOut}
>
<path d="M12,18.17L8.83,15L7.42,16.41L12,21L16.59,16.41L15.17,15M12,5.83L15.17,9L16.58,7.59L12,3L7.41,7.59L8.83,9L12,5.83Z" />
</svg>
)
}
export * from './Alpha'
export * from './EditableInput'
export * from './Raised'
export * from './Hue'
export * from './ColorPicker'
export * from './Checkboard'
export * from './Swatch'
export * from './Saturation'
export * from './Icons'
import { merge } from 'lodash-es'
import { JSX, mergeProps } from 'solid-js'
export interface RaisedProps {
background?: string
zDepth?: 0 | 1 | 2 | 3 | 4 | 5
radius?: number
styles?: Record<string, JSX.CSSProperties>
children?: JSX.Element
}
export function Raised(_props: RaisedProps) {
const props = mergeProps(
{
zDepth: 1,
radius: 2,
background: '#fff',
styles: {},
},
_props,
)
const styles = merge<Record<string, JSX.CSSProperties>, Record<string, JSX.CSSProperties>>(
{
wrap: {
position: 'relative',
display: 'inline-block',
},
content: {
position: 'relative',
},
bg: {
position: 'absolute',
inset: '0px',
'box-shadow':
props.zDepth === 1
? '0 2px 10px rgba(0,0,0,.12), 0 2px 5px rgba(0,0,0,.16)'
: `0 ${props.zDepth}px ${props.zDepth * 4}px rgba(0,0,0,.24)`,
'border-radius': props.radius,
background: props.background,
},
},
props.styles,
)
return (
<div style={styles.wrap}>
<div style={styles.bg} />
<div style={styles.content}>{props.children}</div>
</div>
)
}
import { merge } from 'lodash-es'
import { createEffect, JSX, mergeProps } from 'solid-js'
import { calculateChange } from '../../helpers/saturation'
import { ChangeColor, HslColor, HsvColor } from '../../types'
export type SaturationProps = {
hsl: HslColor
hsv: HsvColor
pointer?: JSX.Element
onChange?: (color: ChangeColor, event?: Event) => void
shadow?: JSX.CSSProperties['box-shadow']
radius?: JSX.CSSProperties['border-radius']
styles?: Record<string, JSX.CSSProperties>
}
export function Saturation(_props: SaturationProps) {
const props = mergeProps({ styles: {} }, _props)
let container: HTMLDivElement
createEffect(() => {
return () => {
unbindEventListeners()
}
}, [])
function handleChange(event: MouseEvent | TouchEvent) {
if (props.onChange) {
props.onChange(calculateChange(event, props.hsl, container), event as any)
}
}
function handleMouseDown(event: MouseEvent) {
handleChange(event)
if (container) {
container.addEventListener('mousemove', handleChange)
container.addEventListener('mouseup', handleMouseUp)
}
}
function handleMouseUp() {
unbindEventListeners()
}
function unbindEventListeners() {
if (container) {
container.removeEventListener('mousemove', handleChange)
container.removeEventListener('mouseup', handleMouseUp)
}
}
const styles = () => {
const { hsv, hsl, shadow, radius, styles } = props
return merge<Record<string, JSX.CSSProperties>, Record<string, JSX.CSSProperties>>(
{
color: {
position: 'absolute',
inset: '0px',
background: `hsl(${hsl.h},100%, 50%)`,
'border-radius': radius,
},
white: {
position: 'absolute',
inset: '0px',
'border-radius': radius,
},
black: {
position: 'absolute',
inset: '0px',
boxShadow: shadow,
'border-radius': radius,
},
pointer: {
position: 'absolute',
top: `${-(hsv.v * 100) + 100}%`,
left: `${hsv.s * 100}%`,
cursor: 'default',
},
circle: {
width: '4px',
height: '4px',
'box-shadow': `0 0 0 1.5px #fff, inset 0 0 1px 1px rgba(0,0,0,.3),
0 0 1px 2px rgba(0,0,0,.4)`,
'border-radius': '50%',
cursor: 'hand',
transform: 'translate(-2px, -2px)',
},
},
styles,
)
}
return (
<div
style={styles().color}
ref={container!}
onMouseDown={handleMouseDown}
onTouchMove={handleChange}
onTouchStart={handleChange}
>
<style>{`
.saturation-white {
background: -webkit-linear-gradient(to right, #fff, rgba(255,255,255,0));
background: linear-gradient(to right, #fff, rgba(255,255,255,0));
}
.saturation-black {
background: -webkit-linear-gradient(to top, #000, rgba(0,0,0,0));
background: linear-gradient(to top, #000, rgba(0,0,0,0));
}
`}</style>
<div style={styles().white} class="saturation-white">
<div style={styles().black} class="saturation-black" />
<div style={styles().pointer}>
{props.pointer ? props.pointer : <div style={styles().circle} />}
</div>
</div>
</div>
)
}
import { createSignal, JSX, mergeProps } from 'solid-js'
import { Checkboard } from './Checkboard'
import { useColorPicker } from './ColorPicker'
const ENTER = 13
export type SwatchProps = {
color: string
styles?: JSX.CSSProperties
onClick: any
title?: string
children?: JSX.Element
focused?: boolean
focusStyle?: JSX.CSSProperties
}
export function Swatch(_props: SwatchProps) {
const props = mergeProps(
{
onClick: () => {},
title: _props.color,
focusStyle: {},
},
_props,
)
const { onSwatchHover } = useColorPicker()
const transparent = props.color === 'transparent'
const [focused, setFocused] = createSignal(false)
const handleFocus = () => setFocused(true)
const handleBlur = () => setFocused(false)
const handleClick = (e: Event) => {
props.onClick(props.color, e)
handleFocus()
}
const handleKeyDown = (e: KeyboardEvent) => e.keyCode === ENTER && props.onClick(props.color, e)
const styles = () => {
return {
swatch: {
background: props.color,
height: '100%',
width: '100%',
cursor: 'pointer',
position: 'relative',
outline: 'none',
...props.styles,
...(focused() ? props.focusStyle : {}),
},
} as Record<string, JSX.CSSProperties>
}
return (
<div
style={styles().swatch}
onClick={handleClick}
title={props.title}
tabIndex={0}
onKeyDown={handleKeyDown}
onBlur={handleBlur}
onMouseOver={(event) => {
onSwatchHover && onSwatchHover(props.color, event)
}}
>
{props.children}
{transparent && (
<Checkboard
borderRadius={styles().swatch.borderRadius}
boxShadow="inset 0 0 0 1px rgba(0,0,0,0.1)"
/>
)}
</div>
)
}
export default Swatch
import { JSX, mergeProps } from 'solid-js'
import { Alpha, useColorPicker, withColorPicker } from '../_common'
import AlphaPointer from './AlphaPointer'
export type AlphaPickerProps = {
width?: string | number
height?: string | number
direction?: string
pointer?: <T extends object>(props: T) => JSX.Element
renderers?: any
className?: string
style?: Record<string, JSX.CSSProperties>
}
export function AlphaPicker(_props: AlphaPickerProps) {
const props = mergeProps(
{
width: '316px',
height: '16px',
direction: 'horizontal',
pointer: AlphaPointer,
className: '',
},
_props,
)
const { colors: currentColors, changeColor } = useColorPicker()
const styles: Record<string, JSX.CSSProperties> = {
picker: {
position: 'relative',
width: props.width,
height: props.height,
},
alpha: {
borderRadius: '2px',
...props.style,
},
}
return (
<div style={styles.picker} class={`alpha-picker ${props.className}`}>
<Alpha
{...styles.alpha}
rgb={currentColors().rgb}
hsl={currentColors().hsl}
pointer={props.pointer}
renderers={props.renderers}
onChange={changeColor}
direction={props.direction}
/>
</div>
)
}
export default withColorPicker(AlphaPicker)
import { JSX, mergeProps } from 'solid-js'
interface Props {
direction?: string
}
export default function AlphaPointer(_props: Props) {
const props = mergeProps({}, _props)
const styles: Record<string, JSX.CSSProperties> = {
picker: {
width: '18px',
height: '18px',
'border-radius': '50%',
transform: props.direction === 'vertical' ? 'translate(-3px, -9px)' : 'translate(-9px, -1px)',
'background-color': 'rgb(248, 248, 248)',
'box-shadow': '0 1px 4px 0 rgba(0, 0, 0, 0.37)',
},
}
return <div style={styles.picker} />
}
export { default as AlphaPicker } from './Alpha'
export type { AlphaPickerProps } from './Alpha'
import * as color from '../../helpers/color'
import { HexColor } from '../../types'
import { Checkboard, EditableInput, useColorPicker, withColorPicker } from '../_common'
import BlockSwatches from './BlockSwatches'
import { JSX, mergeProps } from 'solid-js'
import { merge } from 'lodash-es'
export type BlockPickerProps = {
width?: string | number
colors?: string[]
triangle?: 'top' | 'hide'
className?: string
styles?: Record<string, JSX.CSSProperties>
}
export const Block = (_props: BlockPickerProps) => {
const props = mergeProps(
{
colors: [
'#D9E3F0',
'#F47373',
'#697689',
'#37D67A',
'#2CCCE4',
'#555555',
'#dce775',
'#ff8a65',
'#ba68c8',
],
width: 170,
triangle: 'top',
styles: {},
className: '',
},
_props,
)
const { colors: currentColors, changeColor } = useColorPicker()
const transparent = currentColors().hex === 'transparent'
const handleChange = (hexCode: HexColor, e: Event) => {
color.isValidHex(hexCode) &&
changeColor(
{
hex: hexCode,
source: 'hex',
},
e,
)
}
const styles = () => {
const width = typeof props.width === 'number' ? `${props.width}px` : props.width
const { triangle, styles } = props
return merge<Record<string, JSX.CSSProperties>, Record<string, JSX.CSSProperties>>(
{
card: {
width,
background: '#fff',
'box-shadow': '0 1px rgba(0,0,0,.1)',
'border-radius': '6px',
position: 'relative',
},
head: {
height: '110px',
background: currentColors().hex,
'border-radius': '6px 6px 0 0',
display: 'flex',
'align-items': 'center',
'justify-content': 'center',
position: 'relative',
},
body: {
padding: '10px',
},
label: {
'font-size': '18px',
color: color.getContrastingColor(currentColors().hex),
position: 'relative',
},
triangle: {
width: '0px',
height: '0px',
'border-style': 'solid',
'border-width': '0 10px 10px 10px',
'border-color': `transparent transparent ${currentColors().hex} transparent`,
position: 'absolute',
top: '-10px',
left: '50%',
'margin-left': '-10px',
display: triangle === 'hide' ? 'none' : undefined,
},
input: {
width: '100%',
'font-size': '12px',
color: '#666',
border: '0px',
outline: 'none',
height: '22px',
'box-shadow': 'inset 0 0 0 1px #ddd',
'border-radius': '4px',
padding: '0 7px',
'box-sizing': 'border-box',
},
},
styles,
)
}
return (
<div style={styles().card} class={`block-picker ${props.className}`}>
<div style={styles().triangle} />
<div style={styles().head}>
{transparent && <Checkboard borderRadius="6px 6px 0 0" />}
<div style={styles().label}>{currentColors().hex}</div>
</div>
<div style={styles().body}>
<BlockSwatches colors={props.colors} onClick={handleChange} />
<EditableInput
styles={{ input: styles().input }}
value={currentColors().hex}
onChange={handleChange}
/>
</div>
</div>
)
}
export default withColorPicker(Block)
import { HexColor } from '../../types'
import { Swatch } from '../_common'
import { JSX, For } from 'solid-js'
interface Props {
colors: string[]
onClick: (hexCode: HexColor, e: Event) => void
}
export const BlockSwatches = ({ colors, onClick }: Props) => {
const styles: Record<string, JSX.CSSProperties> = {
swatches: {
'margin-right': '-10px',
},
swatch: {
width: '22px',
height: '22px',
float: 'left',
'margin-right': '10px',
'margin-bottom': '10px',
'border-radius': '4px',
},
clear: {
clear: 'both',
},
}
return (
<div style={styles.swatches}>
<For each={colors}>
{(c) => (
<Swatch
color={c}
styles={styles.swatch}
onClick={onClick}
focusStyle={{
'box-shadow': `0 0 4px ${c}`,
}}
/>
)}
</For>
<div style={styles.clear} />
</div>
)
}
export default BlockSwatches
export { default as BlockPicker } from './Block'
export type { BlockPickerProps } from './Block'
import { merge } from 'lodash-es'
import { JSX, mergeProps } from 'solid-js'
import { Alpha, Checkboard, Hue, Saturation, useColorPicker, withColorPicker } from '../_common'
import ChromeFields from './ChromeFields'
import ChromePointer from './ChromePointer'
import ChromePointerCircle from './ChromePointerCircle'
export interface ChromePickerProps {
width?: string | number
disableAlpha?: boolean
styles?: Record<string, JSX.CSSProperties>
renderers?: any
className?: string
defaultView?: 'hex' | 'rgb' | 'hsl'
}
export const Chrome = (_props: ChromePickerProps) => {
const props = mergeProps(
{
width: 225,
disableAlpha: false,
styles: {},
className: '',
},
_props,
)
const { colors, changeColor } = useColorPicker()
const styles = () => {
const width = typeof props.width === 'number' ? `${props.width}px` : props.width
return merge<Record<string, JSX.CSSProperties>, Record<string, JSX.CSSProperties>>(
{
picker: {
width,
background: '#fff',
'border-radius': '2px',
'box-shadow': '0 0 2px rgba(0,0,0,.3), 0 4px 8px rgba(0,0,0,.3)',
'box-sizing': 'initial',
'font-family': 'Menlo',
},
saturation: {
width: '100%',
'padding-bottom': '55%',
position: 'relative',
'border-radius': '2px 2px 0 0',
overflow: 'hidden',
},
Saturation: {
'border-radius': '2px 2px 0 0',
},
body: {
padding: '16px 16px 12px',
},
controls: {
display: 'flex',
},
color: {
width: props.disableAlpha ? '22px' : '32px',
},
swatch: {
'margin-top': props.disableAlpha ? '0px' : '6px',
width: props.disableAlpha ? '10px' : '16px',
height: props.disableAlpha ? '10px' : '16px',
'border-radius': '8px',
position: 'relative',
overflow: 'hidden',
},
active: {
position: 'absolute',
inset: '0px',
'border-radius': '8px',
'box-shadow': 'inset 0 0 0 1px rgba(0,0,0,.1)',
background: `rgba(${colors().rgb.r}, ${colors().rgb.g}, ${colors().rgb.b}, ${
colors().rgb.a
})`,
'z-index': 2,
},
toggles: {
flex: '1',
},
hue: {
height: '10px',
position: 'relative',
'margin-bottom': props.disableAlpha ? '0px' : '8px',
},
Hue: {
'border-radius': '2px',
},
alpha: {
height: '10px',
position: 'relative',
display: props.disableAlpha ? 'none' : undefined,
},
Alpha: {
'border-radius': '2px',
},
},
props.styles,
)
}
return (
<div style={styles().picker} class={`chrome-picker ${props.className}`}>
<div style={styles().saturation}>
<Saturation
styles={styles().Saturation}
hsl={colors().hsl}
hsv={colors().hsv}
pointer={<ChromePointerCircle />}
onChange={changeColor}
/>
</div>
<div style={styles().body}>
<div style={styles().controls} class="flexbox-fix">
<div style={styles().color}>
<div style={styles().swatch}>
<div style={styles().active} />
<Checkboard renderers={props.renderers} />
</div>
</div>
<div style={styles().toggles}>
<div style={styles().hue}>
<Hue
styles={styles().Hue}
hsl={colors().hsl}
pointer={ChromePointer}
onChange={changeColor}
/>
</div>
<div style={styles().alpha}>
<Alpha
direction="horizontal"
styles={styles().Alpha}
rgb={colors().rgb}
hsl={colors().hsl}
pointer={ChromePointer}
renderers={props.renderers}
onChange={changeColor}
/>
</div>
</div>
</div>
<ChromeFields
rgb={colors().rgb}
hsl={colors().hsl}
hex={colors().hex}
view={props.defaultView}
onChange={changeColor}
/>
</div>
</div>
)
}
export default withColorPicker(Chrome)
import { createEffect, createSignal, JSX, mergeProps, Show } from 'solid-js'
import { ChangeColor, HexColor, HslColor, RgbColor } from '../../types'
import * as color from '../../helpers/color'
import { EditableInput, UnfoldMoreHorizontalIcon } from '../_common'
interface Props {
view?: 'hex' | 'rgb' | 'hsl'
onChange: (data: ChangeColor, event: Event) => void
rgb: RgbColor
hsl: HslColor
hex: HexColor
disableAlpha?: boolean
}
export default function ChromeFields(_props: Props) {
const props = mergeProps({ view: 'hex' }, _props)
const [view, setView] = createSignal(props.view)
createEffect(() => {
if (props.hsl.a !== 1 && view() === 'hex') {
setView('rgb')
}
}, [])
createEffect(() => {
if (props.hsl.a !== 1 && view() === 'hex') {
setView('rgb')
}
}, [props])
function toggleViews() {
if (view() === 'hex') {
setView('rgb')
} else if (view() === 'rgb') {
setView('hsl')
} else if (view() === 'hsl') {
if (props.hsl.a === 1) {
setView('hex')
} else {
setView('rgb')
}
}
}
function handleChange(data: any, e: Event) {
if (data.hex) {
color.isValidHex(data.hex) &&
props.onChange(
{
hex: data.hex,
source: 'hex',
},
e,
)
} else if (data.r || data.g || data.b) {
props.onChange(
{
r: data.r || props.rgb.r,
g: data.g || props.rgb.g,
b: data.b || props.rgb.b,
source: 'rgb',
},
e,
)
} else if (data.a) {
if (data.a < 0) {
data.a = 0
} else if (data.a > 1) {
data.a = 1
}
props.onChange(
{
h: props.hsl.h,
s: props.hsl.s,
l: props.hsl.l,
a: Math.round(data.a * 100) / 100,
source: 'rgb',
},
e,
)
} else if (data.h || data.s || data.l) {
// Remove any occurances of '%'.
if (typeof data.s === 'string' && data.s.includes('%')) {
data.s = data.s.replace('%', '')
}
if (typeof data.l === 'string' && data.l.includes('%')) {
data.l = data.l.replace('%', '')
}
// We store HSL as a unit interval so we need to override the 1 input to 0.01
if (data.s == 1) {
data.s = 0.01
} else if (data.l == 1) {
data.l = 0.01
}
props.onChange(
{
h: data.h || props.hsl.h,
s: Number(data.s !== undefined ? data.s : props.hsl.s),
l: Number(data.l !== undefined ? data.l : props.hsl.l),
source: 'hsl',
},
e,
)
}
}
function showHighlight(e: MouseEvent) {
;(e.currentTarget as HTMLDivElement).style.backgroundColor = '#eee'
}
function hideHighlight(e: MouseEvent) {
;(e.currentTarget as HTMLDivElement).style.backgroundColor = 'transparent'
}
const styles = () => {
return {
wrap: {
'padding-top': '16px',
display: 'flex',
},
fields: {
flex: '1',
display: 'flex',
'margin-left': '-6px',
},
field: {
'padding-left': '6px',
width: '100%',
},
alpha: {
'padding-left': '6px',
width: '100%',
display: props.disableAlpha ? 'none' : undefined,
},
toggle: {
width: '32px',
'text-align': 'right',
position: 'relative',
},
icon: {
'margin-right': '-4px',
'margin-top': '12px',
cursor: 'pointer',
position: 'relative',
},
iconHighlight: {
position: 'absolute',
width: '24px',
height: '28px',
background: '#eee',
'border-radius': '4px',
top: '10px',
left: '12px',
display: 'none',
},
input: {
'font-size': '11px',
color: '#333',
width: '100%',
'border-radius': '2px',
border: 'none',
'box-shadow': 'inset 0 0 0 1px #dadada',
height: '21px',
'text-align': 'center',
},
label: {
'text-transform': 'uppercase',
'font-size': '11px',
'line-height': '11px',
color: '#969696',
'text-align': 'center',
display: 'block',
'margin-top': '12px',
},
svg: {
fill: '#333',
width: '24px',
height: '24px',
border: '1px transparent solid',
'border-radius': '5px',
},
} as Record<string, JSX.CSSProperties>
}
return (
<div style={styles().wrap} class="flexbox-fix">
<Show when={view() == 'hex'}>
<div style={styles().fields} class="flexbox-fix">
<div style={styles().field}>
<EditableInput
styles={{ input: styles().input, label: styles().label }}
label="hex"
value={props.hex}
onChange={handleChange}
/>
</div>
</div>
</Show>
<Show when={view() == 'rgb'}>
<div style={styles().fields} class="flexbox-fix">
<div style={styles().field}>
<EditableInput
styles={{ input: styles().input, label: styles().label }}
label="r"
value={props.rgb.r}
onChange={handleChange}
/>
</div>
<div style={styles().field}>
<EditableInput
styles={{ input: styles().input, label: styles().label }}
label="g"
value={props.rgb.g}
onChange={handleChange}
/>
</div>
<div style={styles().field}>
<EditableInput
styles={{ input: styles().input, label: styles().label }}
label="b"
value={props.rgb.b}
onChange={handleChange}
/>
</div>
<div style={styles().alpha}>
<EditableInput
styles={{ input: styles().input, label: styles().label }}
label="a"
value={props.rgb.a}
arrowOffset={0.01}
onChange={handleChange}
/>
</div>
</div>
</Show>
<Show when={view() == 'hsl'}>
<div style={styles().fields} class="flexbox-fix">
<div style={styles().field}>
<EditableInput
styles={{ input: styles().input, label: styles().label }}
label="h"
value={Math.round(props.hsl.h)}
onChange={handleChange}
/>
</div>
<div style={styles().field}>
<EditableInput
styles={{ input: styles().input, label: styles().label }}
label="s"
value={`${Math.round(props.hsl.s * 100)}%`}
onChange={handleChange}
/>
</div>
<div style={styles().field}>
<EditableInput
styles={{ input: styles().input, label: styles().label }}
label="l"
value={`${Math.round(props.hsl.l * 100)}%`}
onChange={handleChange}
/>
</div>
<div style={styles().alpha}>
<EditableInput
styles={{ input: styles().input, label: styles().label }}
label="a"
value={props.hsl.a}
arrowOffset={0.01}
onChange={handleChange}
/>
</div>
</div>
</Show>
<div style={styles().toggle}>
<div style={styles().icon} onClick={toggleViews}>
<UnfoldMoreHorizontalIcon
width="24"
height="24"
onMouseOver={showHighlight}
onMouseEnter={showHighlight}
onMouseOut={hideHighlight}
/>
</div>
</div>
</div>
)
}
export default function ChromePointer() {
return (
<div
style={{
width: '12px',
height: '12px',
'border-radius': '6px',
transform: 'translate(-6px, -1px)',
'background-color': 'rgb(248, 248, 248)',
'box-shadow': '0 1px 4px 0 rgba(0, 0, 0, 0.37)',
}}
/>
)
}
export default function ChromePointerCircle() {
return (
<div
style={{
width: '12px',
height: '12px',
'border-radius': '6px',
'box-shadow': 'inset 0 0 0 1px #fff',
transform: 'translate(-6px, -6px)',
}}
/>
)
}
export { default as ChromePicker } from './Chrome'
export type { ChromePickerProps } from './Chrome'
import { merge } from 'lodash-es'
import { For, JSX, mergeProps } from 'solid-js'
import { HexColor } from '../../types'
import { useColorPicker, withColorPicker } from '../_common'
import CircleSwatch from './CircleSwatch'
export type CirclePickerProps = {
width?: string | number
circleSize?: number
circleSpacing?: number
className?: string
colors?: string[]
styles?: Record<string, JSX.CSSProperties>
}
export function Circle(_props: CirclePickerProps) {
const props = mergeProps(
{
width: 252,
colors: [
'#F44336',
'#E91E63',
'#9C27B0',
'#673AB7',
'#3F51B5',
'#2196F3',
'#03A9F4',
'#00BCD4',
'#009688',
'#4CAF50',
'#8BC34A',
'#CDDC39',
'#FFEB3B',
'#FFC107',
'#FF9800',
'#FF5722',
'#795548',
'#607D8B',
],
circleSize: 28,
styles: {},
circleSpacing: 14,
className: '',
},
_props,
)
const { colors: currentColors, changeColor } = useColorPicker()
const styles = () => {
const { width, circleSpacing, styles } = props
return merge<Record<string, JSX.CSSProperties>, Record<string, JSX.CSSProperties>>(
{
card: {
width: `${width}px`,
display: 'flex',
'flex-wrap': 'wrap',
'margin-right': `${-circleSpacing}px`,
'margin-bottom': `${-circleSpacing}px`,
},
},
styles,
)
}
const handleChange = (hexCode: HexColor, e: Event) =>
changeColor({ hex: hexCode, source: 'hex' }, e)
return (
<div style={styles().card} class={`circle-picker ${props.className}`}>
<For each={props.colors}>
{(c) => (
<CircleSwatch
color={c}
onClick={handleChange}
active={currentColors().hex === c.toLowerCase()}
circleSize={props.circleSize}
circleSpacing={props.circleSpacing}
/>
)}
</For>
</div>
)
}
export default withColorPicker(Circle)
import { createSignal, JSX, mergeProps } from 'solid-js'
import { HexColor } from '../../types'
import { Swatch } from '../_common'
interface Props {
circleSize?: number
circleSpacing?: number
className?: string
color: string
active?: boolean
onClick: (hexCode: HexColor, e: Event) => void
}
export const CircleSwatch = (_props: Props) => {
const props = mergeProps(
{
circleSize: 28,
circleSpacing: 14,
},
_props,
)
const [hover, setHover] = createSignal(false)
const styles = () => {
const { circleSize, circleSpacing, color, active } = props
return {
swatch: {
width: `${circleSize}px`,
height: `${circleSize}px`,
'margin-right': `${circleSpacing}px`,
'margin-bottom': `${circleSpacing}px`,
transform: 'scale(1)',
transition: '100ms transform ease',
},
Swatch: {
'border-radius': '50%',
background: 'transparent',
'box-shadow': active
? `inset 0 0 0 3px ${color}`
: `inset 0 0 0 ${circleSize / 2 + 1}px ${color}`,
transition: '100ms box-shadow ease',
transform: hover() ? 'scale(1.2)' : undefined,
},
} as Record<string, JSX.CSSProperties>
}
return (
<div onMouseOver={() => setHover(true)} onMouseOut={() => setHover(false)}>
<div style={styles().swatch}>
<Swatch
styles={styles().Swatch}
color={props.color}
onClick={props.onClick}
focusStyle={{
'box-shadow': `${styles().Swatch.boxShadow}, 0 0 5px ${props.color}`,
}}
/>
</div>
</div>
)
}
export default CircleSwatch
export { default as CirclePicker } from './Circle'
export type { CirclePickerProps } from './Circle'
import { merge } from 'lodash-es'
import { For, JSX, mergeProps } from 'solid-js'
import * as color from '../../helpers/color'
import { Raised, useColorPicker, withColorPicker } from '../_common'
import CompactColor from './CompactColor'
import CompactFields from './CompactFields'
export type CompactPickerProps = {
colors?: string[]
styles?: Record<string, JSX.CSSProperties>
className?: string
}
export function Compact(_props: CompactPickerProps) {
const props = mergeProps(
{
colors: [
'#4D4D4D',
'#999999',
'#FFFFFF',
'#F44E3B',
'#FE9200',
'#FCDC00',
'#DBDF00',
'#A4DD00',
'#68CCCA',
'#73D8FF',
'#AEA1FF',
'#FDA1FF',
'#333333',
'#808080',
'#cccccc',
'#D33115',
'#E27300',
'#FCC400',
'#B0BC00',
'#68BC00',
'#16A5A5',
'#009CE0',
'#7B64FF',
'#FA28FF',
'#000000',
'#666666',
'#B3B3B3',
'#9F0500',
'#C45100',
'#FB9E00',
'#808900',
'#194D33',
'#0C797D',
'#0062B1',
'#653294',
'#AB149E',
],
styles: {},
className: '',
},
_props,
)
const { colors: currentColors, changeColor } = useColorPicker()
const styles = merge<Record<string, JSX.CSSProperties>, Record<string, JSX.CSSProperties>>(
{
Compact: {
background: '#f6f6f6',
'border-radius': '4px',
},
compact: {
'padding-top': '5px',
'padding-left': '5px',
'box-sizing': 'initial',
width: '240px',
},
clear: {
clear: 'both',
},
},
props.styles,
)
const handleChange = (data: any, e: Event) => {
if (data.hex) {
color.isValidHex(data.hex) &&
changeColor(
{
hex: data.hex,
source: 'hex',
},
e,
)
} else {
changeColor(data, e)
}
}
return (
<Raised styles={props.styles}>
<div style={styles.compact} class={`compact-picker ${props.className}`}>
<div>
<For each={props.colors}>
{(c) => (
<CompactColor
color={c}
active={c.toLowerCase() === currentColors().hex}
onClick={handleChange}
/>
)}
</For>
<div style={styles.clear} />
</div>
<CompactFields
hex={currentColors().hex}
rgb={currentColors().rgb}
onChange={handleChange}
/>
</div>
</Raised>
)
}
export default withColorPicker(Compact)
import { JSX, mergeProps } from 'solid-js'
import { getContrastingColor } from '../../helpers/color'
import { Swatch } from '../_common'
interface Props {
color: string
active: boolean
onClick: any
}
export default function CompactColor(_props: Props) {
const props = mergeProps({ onClick: () => {} }, _props)
const styles = () => {
const { color, active } = props
return {
color: {
background: color,
width: '15px',
height: '15px',
float: 'left',
'margin-right': '5px',
'margin-bottom': '5px',
position: 'relative',
cursor: 'pointer',
'box-shadow': color === '#FFFFFF' ? 'inset 0 0 0 1px #ddd' : undefined,
},
dot: {
position: 'absolute',
inset: '5px',
background:
color === '#FFFFFF'
? '#000'
: color === 'transparent'
? '#000'
: getContrastingColor(color),
'border-radius': '50%',
opacity: active ? 1 : 0,
},
} as Record<string, JSX.CSSProperties>
}
return (
<Swatch
styles={styles().color}
color={props.color}
onClick={props.onClick}
focusStyle={{ 'box-shadow': `0 0 4px ${props.color}` }}
>
<div style={styles().dot} />
</Swatch>
)
}
import { JSX, mergeProps } from 'solid-js'
import { HexColor, RgbColor } from '../../types'
import { EditableInput } from '../_common'
interface Props {
hex: HexColor
rgb: RgbColor
onChange: any
}
export default function CompactFields(_props: Props) {
const props = mergeProps({}, _props)
const styles = () => {
const hex = props.hex
return {
fields: {
display: 'flex',
'padding-bottom': '6px',
'padding-right': '5px',
position: 'relative',
},
active: {
position: 'absolute',
top: '6px',
left: '5px',
height: '9px',
width: '9px',
background: hex,
},
hexWrap: {
flex: '6',
position: 'relative',
},
hexInput: {
width: '80%',
padding: '0px',
'padding-left': '20%',
border: 'none',
outline: 'none',
background: 'none',
'font-size': '12px',
color: '#333',
height: '16px',
},
hexLabel: {
display: 'none',
},
rgbWrap: {
flex: '3',
position: 'relative',
},
rgbInput: {
width: '70%',
padding: '0px',
'padding-left': '30%',
border: 'none',
outline: 'none',
background: 'none',
'font-size': '12px',
color: '#333',
height: '16px',
},
rgbLabel: {
position: 'absolute',
top: '3px',
left: '0px',
'line-height': '16px',
'text-transform': 'uppercase',
'font-size': '12px',
color: '#999',
},
} as Record<string, JSX.CSSProperties>
}
const handleChange = (data: any, e: Event) => {
if (data.r || data.g || data.b) {
props.onChange(
{
r: data.r || props.rgb.r,
g: data.g || props.rgb.g,
b: data.b || props.rgb.b,
source: 'rgb',
},
e,
)
} else {
props.onChange(
{
hex: data.hex,
source: 'hex',
},
e,
)
}
}
return (
<div style={styles().fields} class="flexbox-fix">
<div style={styles().active} />
<EditableInput
styles={{
wrap: styles().hexWrap,
input: styles().hexInput,
label: styles().hexLabel,
}}
label="hex"
value={props.hex}
onChange={handleChange}
/>
<EditableInput
styles={{
wrap: styles().rgbWrap,
input: styles().rgbInput,
label: styles().rgbLabel,
}}
label="r"
value={props.rgb.r}
onChange={handleChange}
/>
<EditableInput
styles={{
wrap: styles().rgbWrap,
input: styles().rgbInput,
label: styles().rgbLabel,
}}
label="g"
value={props.rgb.g}
onChange={handleChange}
/>
<EditableInput
styles={{
wrap: styles().rgbWrap,
input: styles().rgbInput,
label: styles().rgbLabel,
}}
label="b"
value={props.rgb.b}
onChange={handleChange}
/>
</div>
)
}
export { default as CompactPicker } from './Compact'
export type { CompactPickerProps } from './Compact'
import { merge } from 'lodash-es'
import { For, JSX, mergeProps } from 'solid-js'
import { HexColor } from '../../types'
import { useColorPicker, withColorPicker } from '../_common'
import GithubSwatch from './GithubSwatch'
export type GithubPickerProps = {
width?: string | number
styles?: Record<string, JSX.CSSProperties>
className?: string
triangle?: 'hide' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
colors?: string[]
}
export function Github(_props: GithubPickerProps) {
const props = mergeProps(
{
width: 200,
colors: [
'#B80000',
'#DB3E00',
'#FCCB00',
'#008B02',
'#006B76',
'#1273DE',
'#004DCF',
'#5300EB',
'#EB9694',
'#FAD0C3',
'#FEF3BD',
'#C1E1C5',
'#BEDADC',
'#C4DEF6',
'#BED3F3',
'#D4C4FB',
],
triangle: 'top-left',
styles: {},
className: '',
},
_props,
)
const { changeColor } = useColorPicker()
const styles = () => {
const width = typeof props.width === 'number' ? `${props.width}px` : props.width
const { triangle } = props
return merge<Record<string, JSX.CSSProperties>, Record<string, JSX.CSSProperties>>(
{
card: {
width,
background: '#fff',
border: '1px solid rgba(0,0,0,0.2)',
'box-shadow': '0 3px 12px rgba(0,0,0,0.15)',
'border-radius': '4px',
position: 'relative',
padding: '5px',
display: 'flex',
'flex-wrap': 'wrap',
},
triangle: {
position: 'absolute',
border: '7px solid transparent',
'border-bottom-color': '#fff',
display: triangle === 'hide' ? 'none' : undefined,
top: triangle === 'top-left' || triangle === 'top-right' ? '-14px' : '37px',
left: triangle === 'top-left' || triangle === 'bottom-left' ? '10px' : undefined,
right: triangle === 'top-right' || triangle === 'bottom-right' ? '10px' : undefined,
transform:
triangle === 'bottom-left' || triangle === 'bottom-right'
? 'rotate(180deg)'
: undefined,
},
triangleShadow: {
position: 'absolute',
border: '8px solid transparent',
'border-bottom-color': 'rgba(0,0,0,0.15)',
display: triangle === 'hide' ? 'none' : undefined,
top: triangle === 'top-left' || triangle === 'top-right' ? '-16px' : '35px',
left: triangle === 'top-left' || triangle === 'bottom-left' ? '9px' : undefined,
right: triangle === 'top-right' || triangle === 'bottom-right' ? '9px' : undefined,
transform:
triangle === 'bottom-left' || triangle === 'bottom-right'
? 'rotate(180deg)'
: undefined,
},
},
props.styles,
)
}
const handleChange = (hex: HexColor, e: Event) => changeColor({ hex, source: 'hex' }, e)
return (
<div style={styles().card} class={`github-picker ${props.className}`}>
<div style={styles().triangleShadow} />
<div style={styles().triangle} />
<For each={props.colors}>{(c) => <GithubSwatch color={c} onClick={handleChange} />}</For>
</div>
)
}
export default withColorPicker(Github)
import { createSignal, JSX, mergeProps } from 'solid-js'
import { HexColor } from '../../types'
import { Swatch } from '../_common'
interface Props {
hover?: boolean
color: string
onClick?: (newColor: HexColor, event: Event) => void
}
export function GithubSwatch(_props: Props) {
const props = mergeProps({}, _props)
const hoverSwatch: JSX.CSSProperties = {
position: 'relative',
'z-index': 2,
outline: '2px solid #fff',
'box-shadow': '0 0 5px 2px rgba(0,0,0,0.25)',
}
const [hover, setHover] = createSignal(false)
const styles = () => {
return {
swatch: {
width: '25px',
height: '25px',
'font-size': '0',
...(hover() ? hoverSwatch : {}),
},
} as Record<string, JSX.CSSProperties>
}
return (
<div onMouseOver={() => setHover(true)} onMouseOut={() => setHover(false)}>
<div style={styles().swatch}>
<Swatch color={props.color} onClick={props.onClick} focusStyle={hoverSwatch} />
</div>
</div>
)
}
export default GithubSwatch
export { default as GithubPicker } from './Github'
export type { GithubPickerProps } from './Github'
import { merge } from 'lodash-es'
import { JSX, mergeProps } from 'solid-js'
import { Hue, Saturation, useColorPicker, withColorPicker } from '../_common'
import GoogleFields from './GoogleFields'
import GooglePointer from './GooglePointer'
import GooglePointerCircle from './GooglePointerCircle'
export type GooglePickerProps = {
width?: string | number
styles?: Record<string, JSX.CSSProperties>
header?: string
className?: string
}
export function Google(_props: GooglePickerProps) {
const props = mergeProps(
{
width: 652,
header: 'Color picker',
styles: {},
className: '',
},
_props,
)
const { colors, changeColor } = useColorPicker()
const styles = () => {
const width = typeof props.width === 'number' ? `${props.width}px` : props.width
return merge<Record<string, JSX.CSSProperties>, Record<string, JSX.CSSProperties>>(
{
picker: {
width,
background: '#fff',
border: '1px solid #dfe1e5',
'box-sizing': 'initial',
display: 'flex',
'flex-wrap': 'wrap',
'border-radius': '8px 8px 0px 0px',
},
head: {
height: '57px',
width: '100%',
'padding-top': '16px',
'padding-bottom': '16px',
'padding-left': '16px',
'font-size': '20px',
'box-sizing': 'border-box',
'font-family': 'Roboto-Regular,HelveticaNeue,Arial,sans-serif',
},
saturation: {
width: '70%',
padding: '0px',
position: 'relative',
overflow: 'hidden',
},
swatch: {
width: '30%',
height: '228px',
padding: '0px',
background: `rgba(${colors().rgb.r}, ${colors().rgb.g}, ${colors().rgb.b}, 1)`,
position: 'relative',
overflow: 'hidden',
},
body: {
margin: 'auto',
width: '95%',
},
controls: {
display: 'flex',
'box-sizing': 'border-box',
height: '52px',
'padding-top': '22px',
},
color: {
width: '32px',
},
hue: {
height: '8px',
position: 'relative',
margin: '0px 16px 0px 16px',
width: '100%',
},
Hue: {
'border-radius': '2px',
},
},
props.styles,
)
}
return (
<div style={styles().picker} class={`google-picker ${props.className}`}>
<div style={styles().head}>{props.header}</div>
<div style={styles().swatch} />
<div style={styles().saturation}>
<Saturation
hsl={colors().hsl}
hsv={colors().hsv}
pointer={<GooglePointerCircle hsl={colors().hsl} />}
onChange={changeColor}
/>
</div>
<div style={styles().body}>
<div style={styles().controls} class="flexbox-fix">
<div style={styles().hue}>
<Hue
styles={styles().Hue}
hsl={colors().hsl}
radius="4px"
pointer={GooglePointer}
onChange={changeColor}
/>
</div>
</div>
<GoogleFields
rgb={colors().rgb}
hsl={colors().hsl}
hex={colors().hex}
hsv={colors().hsv}
onChange={changeColor}
/>
</div>
</div>
)
}
export default withColorPicker(Google)
import { createEffect, createSignal, JSX, mergeProps } from 'solid-js'
import * as color from '../../helpers/color'
import { ChangeColor, HexColor, HslColor, HsvColor, RgbColor } from '../../types'
import { EditableInput } from '../_common'
interface Props {
rgb: RgbColor
hsl: HslColor
hex: HexColor
hsv: HsvColor
onChange: (color: ChangeColor, event: Event) => void
}
export default function GoogleFields(_props: Props) {
const props = mergeProps({}, _props)
const [rgbValue, setRgbValue] = createSignal('')
const [hslValue, setHslValue] = createSignal('')
const [hsvValue, setHsvValue] = createSignal('')
createEffect(() => {
setRgbValue(`${props.rgb.r}, ${props.rgb.g}, ${props.rgb.b}`)
setHslValue(
`${Math.round(props.hsl.h)}°, ${Math.round(props.hsl.s * 100)}%, ${Math.round(
props.hsl.l * 100,
)}%`,
)
setHsvValue(
`${Math.round(props.hsv.h)}°, ${Math.round(props.hsv.s * 100)}%, ${Math.round(
props.hsv.v * 100,
)}%`,
)
}, [props.rgb, props.hsl, props.hsv])
const handleChange = (data: any, e: Event) => {
if (data.hex) {
color.isValidHex(data.hex) &&
props.onChange(
{
hex: data.hex,
source: 'hex',
},
e,
)
} else if (data.rgb) {
const values = data.rgb.split(',')
color.isvalidColorString(data.rgb, 'rgb') &&
props.onChange(
{
r: values[0],
g: values[1],
b: values[2],
a: 1,
source: 'rgb',
},
e,
)
} else if (data.hsv) {
const values = data.hsv.split(',')
if (color.isvalidColorString(data.hsv, 'hsv')) {
values[2] = values[2].replace('%', '')
values[1] = values[1].replace('%', '')
values[0] = values[0].replace('°', '')
if (values[1] == 1) {
values[1] = 0.01
} else if (values[2] == 1) {
values[2] = 0.01
}
props.onChange(
{
h: Number(values[0]),
s: Number(values[1]),
v: Number(values[2]),
source: 'hsv',
} as ChangeColor,
e,
)
}
} else if (data.hsl) {
const values = data.hsl.split(',')
if (color.isvalidColorString(data.hsl, 'hsl')) {
values[2] = values[2].replace('%', '')
values[1] = values[1].replace('%', '')
values[0] = values[0].replace('°', '')
// @ts-ignore
if (props.hsvValue[1] == 1) {
// @ts-ignore
hsvValue[1] = 0.01
// @ts-ignore
} else if (hsvValue[2] == 1) {
// @ts-ignore
hsvValue[2] = 0.01
}
props.onChange(
{
h: Number(values[0]),
s: Number(values[1]),
v: Number(values[2]),
source: 'hsl',
} as ChangeColor,
e,
)
}
}
}
const styles: Record<string, JSX.CSSProperties> = {
wrap: {
display: 'flex',
height: '100px',
'margin-top': '4px',
},
fields: {
width: '100%',
},
column: {
'padding-top': '10px',
display: 'flex',
'justify-content': 'space-between',
},
double: {
padding: '0px 4.4px',
'box-sizing': 'border-box',
},
input: {
width: '100%',
height: '38px',
'box-sizing': 'border-box',
padding: '4px 10% 3px',
'text-align': 'center',
border: '1px solid #dadce0',
'font-size': '11px',
'text-transform': 'lowercase',
'border-radius': '5px',
outline: 'none',
'font-family': 'Roboto,Arial,sans-serif',
},
input2: {
height: '38px',
width: '100%',
border: '1px solid #dadce0',
'box-sizing': 'border-box',
'font-size': '11px',
'text-transform': 'lowercase',
'border-radius': '5px',
outline: 'none',
'padding-left': '10px',
'font-family': 'Roboto,Arial,sans-serif',
},
label: {
'text-align': 'center',
'font-size': '12px',
background: '#fff',
position: 'absolute',
'text-transform': 'uppercase',
color: '#3c4043',
width: '35px',
top: '-6px',
left: '0',
right: '0',
'margin-left': 'auto',
'margin-right': 'auto',
'font-family': 'Roboto,Arial,sans-serif',
},
label2: {
left: '10px',
'text-align': 'center',
'font-size': '12px',
background: '#fff',
position: 'absolute',
'text-transform': 'uppercase',
color: '#3c4043',
width: '32px',
top: '-6px',
'font-family': 'Roboto,Arial,sans-serif',
},
single: {
'flex-grow': 1,
margin: '0px 4.4px',
},
}
return (
<div style={styles.wrap} class="flexbox-fix">
<div style={styles.fields}>
<div style={styles.double}>
<EditableInput
styles={{ input: styles.input, label: styles.label }}
label="hex"
value={props.hex}
onChange={handleChange}
/>
</div>
<div style={styles.column}>
<div style={styles.single}>
<EditableInput
styles={{ input: styles.input2, label: styles.label2 }}
label="rgb"
value={rgbValue()}
onChange={handleChange}
/>
</div>
<div style={styles.single}>
<EditableInput
styles={{ input: styles.input2, label: styles.label2 }}
label="hsv"
value={hsvValue()}
onChange={handleChange}
/>
</div>
<div style={styles.single}>
<EditableInput
styles={{ input: styles.input2, label: styles.label2 }}
label="hsl"
value={hslValue()}
onChange={handleChange}
/>
</div>
</div>
</div>
</div>
)
}
import { mergeProps } from 'solid-js'
import { HslColor } from '../../types'
interface Props {
hsl?: HslColor
}
export default function GooglePointer(_props: Props) {
const props = mergeProps({ hsl: { a: 1, h: 249.94, l: 0.2, s: 0.5 } }, _props)
return (
<div
style={{
width: '20px',
height: '20px',
'border-radius': '22px',
transform: 'translate(-10px, -7px)',
background: `hsl(${Math.round(props.hsl.h)}, 100%, 50%)`,
border: '2px white solid',
}}
/>
)
}
import { mergeProps } from 'solid-js'
import { HslColor } from '../../types'
interface Props {
hsl?: HslColor
}
export default function GooglePointerCircle(_props: Props) {
const props = mergeProps({ hsl: { a: 1, h: 249.94, l: 0.2, s: 0.5 } }, _props)
return (
<div
style={{
width: '20px',
height: '20px',
'border-radius': '22px',
border: '2px #fff solid',
transform: 'translate(-12px, -13px)',
background: `hsl(${Math.round(props.hsl.h)}, ${Math.round(
props.hsl.s * 100,
)}%, ${Math.round(props.hsl.l * 100)}%)`,
}}
/>
)
}
export { default as GooglePicker } from './Google'
export type { GooglePickerProps } from './Google'
import { useColorPicker, withColorPicker } from '../_common/ColorPicker'
import { ChangeColor } from '../../types'
import { Hue } from '../_common'
import HuePointer from './HuePointer'
import { JSX, mergeProps } from 'solid-js'
import { merge } from 'lodash-es'
export type HuePickerProps = {
width?: string | number
height?: string | number
direction?: string
pointer?: typeof HuePointer
className?: string
styles?: Record<string, JSX.CSSProperties>
}
export function HuePicker(_props: HuePickerProps) {
const props = mergeProps(
{
width: '316px',
height: '16px',
direction: 'horizontal',
pointer: HuePointer,
styles: {},
className: '',
},
_props,
)
const { colors, changeColor } = useColorPicker()
const styles = merge<Record<string, JSX.CSSProperties>, Record<string, JSX.CSSProperties>>(
{
picker: {
position: 'relative',
width: props.width,
height: props.height,
},
hue: {
'border-radius': '2px',
},
},
props.styles,
)
// Overwrite to provide pure hue color
const handleChange = (data: ChangeColor) =>
changeColor({
a: 1,
h: typeof data !== 'string' && 'h' in data ? data.h : 0,
l: 0.5,
s: 1,
})
return (
<div style={styles.picker} class={`hue-picker ${props.className}`}>
<Hue
{...styles.hue}
hsl={colors().hsl}
pointer={props.pointer}
direction={props.direction}
onChange={handleChange}
/>
</div>
)
}
export default withColorPicker(HuePicker)
import { JSX } from 'solid-js'
interface Props {
direction?: string
}
export default function SliderPointer({ direction }: Props) {
const styles: Record<string, JSX.CSSProperties> = {
picker: {
width: '18px',
height: '18px',
'border-radius': '50%',
transform: direction === 'vertical' ? 'translate(-3px, -9px)' : 'translate(-9px, -1px)',
'background-color': 'rgb(248, 248, 248)',
'box-shadow': '0 1px 4px 0 rgba(0, 0, 0, 0.37)',
},
}
return <div style={styles.picker} />
}
export { default as HuePicker } from './Hue'
export type { HuePickerProps } from './Hue'
export { default as MaterialPicker } from './Material'
export type { MaterialPickerProps } from './Material'
import { merge } from 'lodash-es'
import { JSX, mergeProps } from 'solid-js'
import { ChangeColor, RgbColor } from '../../types'
import { EditableInput, Raised } from '../_common'
import { useColorPicker, withColorPicker } from '../_common/ColorPicker'
import { isValidHex } from '../../helpers/color'
export type MaterialPickerProps = {
styles?: Record<string, JSX.CSSProperties>
className?: string
}
export function Material(_props: MaterialPickerProps) {
const props = mergeProps({ styles: {}, className: '' }, _props)
const { colors, changeColor } = useColorPicker()
const styles = () => {
return merge<Record<string, JSX.CSSProperties>, Record<string, JSX.CSSProperties>>(
{
material: {
width: '98px',
height: '98px',
padding: '16px',
'font-family': 'Roboto',
},
hexWrap: {
position: 'relative',
},
hexInput: {
width: '100%',
'margin-top': '12px',
'font-size': '15px',
color: '#333',
padding: '0px',
border: '0px',
'border-bottom': `2px solid ${colors().hex}`,
outline: 'none',
height: '30px',
},
hexLabel: {
position: 'absolute',
top: '0px',
left: '0px',
'font-size': '11px',
color: '#999999',
'text-transform': 'capitalize',
},
hex: {},
rgbWrap: {
position: 'relative',
},
rgbInput: {
width: '100%',
'margin-top': '12px',
'font-size': '15px',
color: '#333',
padding: '0px',
border: '0px',
'border-bottom': '1px solid #eee',
outline: 'none',
height: '30px',
},
rgbLabel: {
position: 'absolute',
top: '0px',
left: '0px',
'font-size': '11px',
color: '#999999',
'text-transform': 'capitalize',
},
split: {
display: 'flex',
'margin-right': '-10px',
'padding-top': '11px',
},
third: {
flex: '1',
'padding-right': '10px',
},
},
props.styles,
)
}
const handleChange = (data: ChangeColor, e: Event) => {
if (typeof data !== 'string' && 'hex' in data) {
isValidHex(data.hex) &&
changeColor(
{
hex: data.hex,
source: 'hex',
},
e,
)
} else if (typeof data !== 'string' && ('r' in data || 'g' in data || 'b' in data)) {
data = data as unknown as RgbColor
changeColor(
{
r: data.r || colors().rgb.r,
g: data.g || colors().rgb.g,
b: data.b || colors().rgb.b,
source: 'rgb',
},
e,
)
}
}
return (
<Raised styles={props.styles}>
<div style={styles().material} class={`material-picker ${props.className}`}>
<EditableInput
styles={{
wrap: styles().hexWrap,
input: styles().hexInput,
label: styles().hexLabel,
}}
label="hex"
value={colors().hex}
onChange={handleChange}
/>
<div style={styles().split} class="flexbox-fix">
<div style={styles().third}>
<EditableInput
styles={{
wrap: styles().rgbWrap,
input: styles().rgbInput,
label: styles().rgbLabel,
}}
label="r"
value={colors().rgb.r}
onChange={handleChange}
/>
</div>
<div style={styles().third}>
<EditableInput
styles={{
wrap: styles().rgbWrap,
input: styles().rgbInput,
label: styles().rgbLabel,
}}
label="g"
value={colors().rgb.g}
/>
</div>
<div style={styles().third}>
<EditableInput
styles={{
wrap: styles().rgbWrap,
input: styles().rgbInput,
label: styles().rgbLabel,
}}
label="b"
value={colors().rgb.b}
onChange={handleChange}
/>
</div>
</div>
</div>
</Raised>
)
}
export default withColorPicker(Material)
export { default as PhotoshopPicker } from './Photoshop'
export type { PhotoshopPickerProps } from './Photoshop'
import { merge } from 'lodash-es'
import { createSignal, JSX, mergeProps } from 'solid-js'
import { Hue, Saturation, useColorPicker, withColorPicker } from '../_common'
import PhotoshopButton from './PhotoshopButton'
import PhotoshopFields from './PhotoshopFields'
import PhotoshopPointer from './PhotoshopPointer'
import PhotoshopPointerCircle from './PhotoshopPointerCircle'
import PhotoshopPreviews from './PhotoshopPreviews'
export type PhotoshopPickerProps = {
header?: string
styles?: Record<string, JSX.CSSProperties>
className?: string
onAccept?: () => void
onCancel?: () => void
}
function Photoshop(_props: PhotoshopPickerProps) {
const props = mergeProps(
{
header: 'Color Picker',
styles: {},
className: '',
},
_props,
)
const { colors, changeColor } = useColorPicker()
const [currentColor, setCurrentColor] = createSignal(colors().hex)
const styles = merge<Record<string, JSX.CSSProperties>, Record<string, JSX.CSSProperties>>(
{
picker: {
background: '#DCDCDC',
'border-radius': '4px',
'box-shadow': '0 0 0 1px rgba(0,0,0,.25), 0 8px 16px rgba(0,0,0,.15)',
'box-sizing': 'initial',
width: '513px',
},
head: {
'background-image': 'linear-gradient(-180deg, #F0F0F0 0%, #D4D4D4 100%)',
'border-bottom': '1px solid #B1B1B1',
'box-shadow': 'inset 0 1px 0 0 rgba(255,255,255,.2), inset 0 -1px 0 0 rgba(0,0,0,.02)',
height: '23px',
'line-height': '24px',
'border-radius': '4px 4px 0 0',
'font-size': '13px',
color: '#4D4D4D',
'text-align': 'center',
},
body: {
padding: '15px 15px 0',
display: 'flex',
},
saturation: {
width: '256px',
height: '256px',
position: 'relative',
border: '2px solid #B3B3B3',
'border-bottom': '2px solid #F0F0F0',
overflow: 'hidden',
},
hue: {
position: 'relative',
height: '256px',
width: '19px',
'margin-left': '10px',
border: '2px solid #B3B3B3',
'border-bottom': '2px solid #F0F0F0',
},
controls: {
width: '180px',
'margin-left': '10px',
},
top: {
display: 'flex',
},
previews: {
width: '60px',
},
actions: {
flex: '1',
'margin-left': '20px',
},
},
props.styles,
)
function handleAccept() {
props.onAccept && props.onAccept()
setCurrentColor(colors().hex)
}
return (
<div style={styles.picker} class={`photoshop-picker ${props.className}`}>
<div style={styles.head}>{props.header}</div>
<div style={styles.body} class="flexbox-fix">
<div style={styles.saturation}>
<Saturation
hsl={colors().hsl}
hsv={colors().hsv}
pointer={<PhotoshopPointerCircle hsl={colors().hsl} />}
onChange={changeColor}
/>
</div>
<div style={styles.hue}>
<Hue
direction="vertical"
hsl={colors().hsl}
pointer={PhotoshopPointer}
onChange={changeColor}
/>
</div>
<div style={styles.controls}>
<div style={styles.top} class="flexbox-fix">
<div style={styles.previews}>
<PhotoshopPreviews rgb={colors().rgb} currentColor={currentColor()} />
</div>
<div style={styles.actions}>
<PhotoshopButton label="OK" onClick={handleAccept} active />
<PhotoshopButton label="Cancel" onClick={props.onCancel} />
<PhotoshopFields
onChange={changeColor}
rgb={colors().rgb}
hsv={colors().hsv}
hex={colors().hex}
/>
</div>
</div>
</div>
</div>
</div>
)
}
export default withColorPicker(Photoshop)
import { JSX, mergeProps } from 'solid-js'
interface Props {
onClick?: () => void
label?: string
children?: JSX.Element
active?: boolean
}
export default function PhotoshopButton(_props: Props) {
const props = mergeProps({}, _props)
const styles = () => {
return {
button: {
'background-image': 'linear-gradient(-180deg, #FFFFFF 0%, #E6E6E6 100%)',
border: '1px solid #878787',
'border-radius': '2px',
height: '20px',
'box-shadow': props.active ? '0 0 0 1px #878787' : '0 1px 0 0 #EAEAEA',
'font-size': '14px',
color: '#000',
'line-height': '20px',
'text-align': 'center',
'margin-bottom': '10px',
cursor: 'pointer',
},
} as Record<string, JSX.CSSProperties>
}
return (
<div style={styles().button} onClick={props.onClick}>
{props.label || props.children}
</div>
)
}
import { JSX, mergeProps } from 'solid-js'
import * as color from '../../helpers/color'
import { HexColor, HsvColor, RgbColor } from '../../types'
import { EditableInput } from '../_common'
interface Props {
rgb: RgbColor
hsv: HsvColor
hex: HexColor
onChange: (data: any, event: Event) => void
}
export default function PhotoshopPicker(_props: Props) {
const props = mergeProps({}, _props)
const styles: Record<string, JSX.CSSProperties> = {
fields: {
'padding-top': '5px',
'padding-bottom': '9px',
width: '80px',
position: 'relative',
},
divider: {
height: '5px',
},
rgbWrap: {
position: 'relative',
},
rgbInput: {
'margin-left': '40%',
width: '40%',
height: '18px',
border: '1px solid #888888',
'box-shadow': 'inset 0 1px 1px rgba(0,0,0,.1), 0 1px 0 0 #ECECEC',
'margin-bottom': '5px',
'font-size': '13px',
'padding-left': '3px',
'margin-right': '10px',
},
rgbLabel: {
left: '0px',
top: '0px',
width: '34px',
'text-transform': 'uppercase',
'font-size': '13px',
height: '18px',
'line-height': '22px',
position: 'absolute',
},
hexWrap: {
position: 'relative',
},
hexInput: {
'margin-left': '20%',
width: '80%',
height: '18px',
border: '1px solid #888888',
'box-shadow': 'inset 0 1px 1px rgba(0,0,0,.1), 0 1px 0 0 #ECECEC',
'margin-bottom': '6px',
'font-size': '13px',
'padding-left': '3px',
},
hexLabel: {
position: 'absolute',
top: '0px',
left: '0px',
width: '14px',
'text-transform': 'uppercase',
'font-size': '13px',
height: '18px',
'line-height': '22px',
},
fieldSymbols: {
position: 'absolute',
top: '5px',
right: '-7px',
'font-size': '13px',
},
symbol: {
height: '20px',
'line-height': '22px',
'padding-bottom': '7px',
},
}
const handleChange = (data: any, e: Event) => {
if (data['#']) {
color.isValidHex(data['#']) &&
props.onChange(
{
hex: data['#'],
source: 'hex',
},
e,
)
} else if (data.r || data.g || data.b) {
props.onChange(
{
r: data.r || props.rgb.r,
g: data.g || props.rgb.g,
b: data.b || props.rgb.b,
source: 'rgb',
},
e,
)
} else if (data.h || data.s || data.v) {
props.onChange(
{
h: data.h || props.hsv.h,
s: data.s || props.hsv.s,
v: data.v || props.hsv.v,
source: 'hsv',
},
e,
)
}
}
return (
<div style={styles.fields}>
<EditableInput
styles={{
wrap: styles.rgbWrap,
input: styles.rgbInput,
label: styles.rgbLabel,
}}
label="h"
value={Math.round(props.hsv.h)}
onChange={handleChange}
/>
<EditableInput
styles={{
wrap: styles.rgbWrap,
input: styles.rgbInput,
label: styles.rgbLabel,
}}
label="s"
value={Math.round(props.hsv.s * 100)}
onChange={handleChange}
/>
<EditableInput
styles={{
wrap: styles.rgbWrap,
input: styles.rgbInput,
label: styles.rgbLabel,
}}
label="v"
value={Math.round(props.hsv.v * 100)}
onChange={handleChange}
/>
<div style={styles.divider} />
<EditableInput
styles={{
wrap: styles.rgbWrap,
input: styles.rgbInput,
label: styles.rgbLabel,
}}
label="r"
value={props.rgb.r}
onChange={handleChange}
/>
<EditableInput
styles={{
wrap: styles.rgbWrap,
input: styles.rgbInput,
label: styles.rgbLabel,
}}
label="g"
value={props.rgb.g}
onChange={handleChange}
/>
<EditableInput
styles={{
wrap: styles.rgbWrap,
input: styles.rgbInput,
label: styles.rgbLabel,
}}
label="b"
value={props.rgb.b}
onChange={handleChange}
/>
<div style={styles.divider} />
<EditableInput
styles={{
wrap: styles.hexWrap,
input: styles.hexInput,
label: styles.hexLabel,
}}
label="#"
value={props.hex.replace('#', '')}
onChange={handleChange}
/>
<div style={styles.fieldSymbols}>
<div style={styles.symbol}>°</div>
<div style={styles.symbol}>%</div>
<div style={styles.symbol}>%</div>
</div>
</div>
)
}
import { JSX } from 'solid-js'
export default function PhotoshopPointerCircle() {
const triangleStyles: JSX.CSSProperties = {
width: 0,
height: 0,
'border-style': 'solid',
'border-width': '4px 0 4px 6px',
'border-color': 'transparent transparent transparent #fff',
position: 'absolute',
top: '1px',
left: '1px',
}
const triangleBorderStyles: JSX.CSSProperties = {
width: 0,
height: 0,
'border-style': 'solid',
'border-width': '5px 0 5px 8px',
'border-color': 'transparent transparent transparent #555',
}
const styles: Record<string, JSX.CSSProperties> = {
left: {
...triangleBorderStyles,
transform: 'translate(-13px, -4px)',
},
leftInside: {
...triangleStyles,
transform: 'translate(-8px, -5px)',
},
right: {
...triangleBorderStyles,
transform: 'translate(20px, -14px) rotate(180deg)',
},
rightInside: {
...triangleStyles,
transform: 'translate(-8px, -5px)',
},
}
return (
<div style={styles.pointer}>
<div style={styles.left}>
<div style={styles.leftInside} />
</div>
<div style={styles.right}>
<div style={styles.rightInside} />
</div>
</div>
)
}
import { mergeProps } from 'solid-js'
import { HslColor } from '../../types'
interface Props {
hsl: HslColor
}
export default function PhotoshopPointerCircle(_props: Props) {
const props = mergeProps({}, _props)
return (
<div
style={{
width: '12px',
height: '12px',
borderRadius: '6px',
boxShadow: props.hsl.l > 0.5 ? 'inset 0 0 0 1px #000' : 'inset 0 0 0 1px #fff',
transform: 'translate(-6px, -6px)',
}}
/>
)
}
import { JSX, mergeProps } from 'solid-js'
import { RgbColor } from '../../types'
interface Props {
rgb: RgbColor
currentColor: string
}
export default function PhotoshopPreviews(_props: Props) {
const props = mergeProps({}, _props)
const styles = () => {
const { rgb, currentColor } = props
return {
swatches: {
border: '1px solid #B3B3B3',
'border-bottom': '1px solid #F0F0F0',
'margin-bottom': '2px',
'margin-top': '1px',
},
new: {
height: '34px',
background: `rgb(${rgb.r},${rgb.g}, ${rgb.b})`,
'box-shadow': 'inset 1px 0 0 #000, inset -1px 0 0 #000, inset 0 1px 0 #000',
},
current: {
height: '34px',
background: currentColor,
'box-shadow': 'inset 1px 0 0 #000, inset -1px 0 0 #000, inset 0 -1px 0 #000',
},
label: {
'font-size': '14px',
color: '#000',
'text-align': 'center',
},
} as Record<string, JSX.CSSProperties>
}
return (
<div>
<div style={styles().label}>new</div>
<div style={styles().swatches}>
<div style={styles().new} />
<div style={styles().current} />
</div>
<div style={styles().label}>current</div>
</div>
)
}
export { default as SketchPicker } from './Sketch'
export type { SketchPickerProps } from './Sketch'
import { merge } from 'lodash-es'
import { JSX, mergeProps } from 'solid-js'
import { Alpha, Checkboard, Hue, Saturation, useColorPicker, withColorPicker } from '../_common'
import SketchFields from './SketchFields'
import SketchPresetColors from './SketchPresetColors'
export type SketchPickerProps = {
disableAlpha?: boolean
width?: string | number
className?: string
presetColors?: string[]
styles?: Record<string, JSX.CSSProperties>
renderers?: any
}
export function Sketch(_props: SketchPickerProps) {
const props = mergeProps(
{
width: 200,
disableAlpha: false,
presetColors: [
'#D0021B',
'#F5A623',
'#F8E71C',
'#8B572A',
'#7ED321',
'#417505',
'#BD10E0',
'#9013FE',
'#4A90E2',
'#50E3C2',
'#B8E986',
'#000000',
'#4A4A4A',
'#9B9B9B',
'#FFFFFF',
],
styles: {},
className: '',
},
_props,
)
const { colors, changeColor } = useColorPicker()
const styles = () => {
const width = typeof props.width === 'number' ? `${props.width}px` : props.width
const rgb = colors().rgb
return merge<Record<string, JSX.CSSProperties>, Record<string, JSX.CSSProperties>>(
{
picker: {
width,
padding: '10px 10px 0',
'box-sizing': 'initial',
background: '#fff',
'border-radius': '4px',
'box-shadow': '0 0 0 1px rgba(0,0,0,.15), 0 8px 16px rgba(0,0,0,.15)',
},
saturation: {
width: '100%',
'padding-bottom': '75%',
position: 'relative',
overflow: 'hidden',
},
Saturation: {
'border-radius': '3px',
'box-shadow': 'inset 0 0 0 1px rgba(0,0,0,.15), inset 0 0 4px rgba(0,0,0,.25)',
},
controls: {
display: 'flex',
},
sliders: {
padding: '4px 0',
flex: '1',
},
color: {
width: '24px',
height: props.disableAlpha ? '10px' : '24px',
position: 'relative',
'margin-top': '4px',
'margin-left': '4px',
'border-radius': '3px',
},
activeColor: {
position: 'absolute',
inset: '0px',
'border-radius': '2px',
background: `rgba(${rgb.r},${rgb.g},${rgb.b},${rgb.a})`,
'box-shadow': 'inset 0 0 0 1px rgba(0,0,0,.15), inset 0 0 4px rgba(0,0,0,.25)',
},
hue: {
position: 'relative',
height: '10px',
overflow: 'hidden',
},
Hue: {
'border-radius': '2px',
'box-shadow': 'inset 0 0 0 1px rgba(0,0,0,.15), inset 0 0 4px rgba(0,0,0,.25)',
},
alpha: {
position: 'relative',
height: '10px',
'margin-top': '4px',
overflow: 'hidden',
display: props.disableAlpha ? 'none' : undefined,
},
Alpha: {
'border-radius': '2px',
'box-shadow': 'inset 0 0 0 1px rgba(0,0,0,.15), inset 0 0 4px rgba(0,0,0,.25)',
},
...props.styles,
},
props.styles,
)
}
return (
<div style={styles().picker} class={`sketch-picker ${props.className}`}>
<div style={styles().saturation}>
<Saturation
styles={styles().Saturation}
hsl={colors().hsl}
hsv={colors().hsv}
onChange={changeColor}
/>
</div>
<div style={styles().controls} class="flexbox-fix">
<div style={styles().sliders}>
<div style={styles().hue}>
<Hue styles={styles().Hue} hsl={colors().hsl} onChange={changeColor} />
</div>
<div style={styles().alpha}>
<Alpha
direction="horizontal"
styles={styles().Alpha}
rgb={colors().rgb}
hsl={colors().hsl}
renderers={props.renderers}
onChange={changeColor}
/>
</div>
</div>
<div style={styles().color}>
<Checkboard />
<div style={styles().activeColor} />
</div>
</div>
<SketchFields
rgb={colors().rgb}
hsl={colors().hsl}
hex={colors().hex}
onChange={changeColor}
disableAlpha={props.disableAlpha}
/>
<SketchPresetColors colors={props.presetColors} onClick={changeColor} />
</div>
)
}
export default withColorPicker(Sketch)
import { JSX, mergeProps } from 'solid-js'
import * as color from '../../helpers/color'
import { ChangeColor, HexColor, HslColor, RgbColor } from '../../types'
import { EditableInput } from '../_common'
export interface SliderPickerProps {
onChange?: (color: ChangeColor, event: Event) => void
rgb: RgbColor
hsl: HslColor
hex: HexColor
disableAlpha?: boolean
}
export const SketchFields = (_props: SliderPickerProps) => {
const props = mergeProps(
{
onChange: () => {},
},
_props,
)
const styles = () => {
return {
fields: {
display: 'flex',
'padding-top': '4px',
},
single: {
flex: '1',
'padding-left': '6px',
},
alpha: {
flex: '1',
'padding-left': '6px',
display: props.disableAlpha ? 'none' : undefined,
},
double: {
flex: '2',
},
input: {
width: '80%',
padding: '4px 10% 3px',
border: 'none',
'box-shadow': 'inset 0 0 0 1px #ccc',
'font-size': '11px',
},
label: {
display: 'block',
'text-align': 'center',
'font-size': '11px',
color: '#222',
'padding-top': '3px',
'padding-bottom': '4px',
'text-transform': 'capitalize',
},
} as Record<string, JSX.CSSProperties>
}
const handleChange = (data: any, e: Event) => {
if (typeof data !== 'string' && 'hex' in data) {
color.isValidHex(data.hex) &&
props.onChange(
{
hex: data.hex,
source: 'hex',
},
e,
)
} else if (typeof data !== 'string' && ('r' in data || 'g' in data || 'b' in data)) {
props.onChange(
{
r: data.r || props.rgb.r,
g: data.g || props.rgb.g,
b: data.b || props.rgb.b,
a: props.rgb.a,
source: 'rgb',
},
e,
)
} else if (typeof data !== 'string' && data.a) {
if (data.a < 0) {
data.a = 0
} else if (data.a > 100) {
data.a = 100
}
data.a /= 100
props.onChange(
{
h: props.hsl.h,
s: props.hsl.s,
l: props.hsl.l,
a: data.a,
source: 'rgb',
},
e,
)
}
}
return (
<div style={styles().fields} class="flexbox-fix">
<div style={styles().double}>
<EditableInput
styles={{ input: styles().input, label: styles().label }}
label="hex"
value={props.hex.replace('#', '')}
onChange={handleChange}
/>
</div>
<div style={styles().single}>
<EditableInput
styles={{ input: styles().input, label: styles().label }}
label="r"
value={props.rgb.r}
onChange={handleChange}
dragLabel={true}
dragMax={255}
/>
</div>
<div style={styles().single}>
<EditableInput
styles={{ input: styles().input, label: styles().label }}
label="g"
value={props.rgb.g}
onChange={handleChange}
dragLabel={true}
dragMax={255}
/>
</div>
<div style={styles().single}>
<EditableInput
styles={{ input: styles().input, label: styles().label }}
label="b"
value={props.rgb.b}
onChange={handleChange}
dragLabel={true}
dragMax={255}
/>
</div>
<div style={styles().alpha}>
<EditableInput
styles={{ input: styles().input, label: styles().label }}
label="a"
value={Math.round((props.rgb.a ?? 1) * 100)}
onChange={handleChange}
dragLabel={true}
dragMax={100}
/>
</div>
</div>
)
}
export default SketchFields
import { For, JSX, mergeProps } from 'solid-js'
import { ChangeColor } from '../../types'
import { Swatch } from '../_common'
interface Props {
colors: (string | { color: string; title?: string })[]
onClick?: (newColor: ChangeColor, event: MouseEvent) => void
}
export default function SketchPresetColors(_props: Props) {
const props = mergeProps({ onClick: () => {} }, _props)
const styles = () => {
return {
colors: {
margin: '0 -10px',
padding: '10px 0 0 10px',
'border-top': '1px solid #eee',
display: !props.colors || !props.colors.length ? 'none' : 'flex',
'flex-wrap': 'wrap',
position: 'relative',
},
swatchWrap: {
width: '16px',
height: '16px',
margin: '0 10px 10px 0',
},
swatch: {
'border-radius': '3px',
'box-shadow': 'inset 0 0 0 1px rgba(0,0,0,.15)',
},
} as Record<string, JSX.CSSProperties>
}
const handleClick = (hex: string, e: MouseEvent) => {
props.onClick(
{
hex,
source: 'hex',
},
e,
)
}
return (
<div style={styles().colors} class="flexbox-fix">
<For each={props.colors}>
{(colorObjOrString) => {
const c =
typeof colorObjOrString === 'string' ? { color: colorObjOrString } : colorObjOrString
return (
<div style={styles().swatchWrap}>
<Swatch
{...c}
styles={styles().swatch}
onClick={handleClick}
focusStyle={{
'box-shadow': `inset 0 0 0 1px rgba(0,0,0,.15), 0 0 4px ${c.color}`,
}}
/>
</div>
)
}}
</For>
</div>
)
}
export { default as SliderPicker } from './Slider'
export type { SliderPickerProps } from './Slider'
import { merge } from 'lodash-es'
import { JSX, mergeProps } from 'solid-js'
import { Hue, useColorPicker, withColorPicker } from '../_common'
import SliderPointer from './SliderPointer'
import SliderSwatches from './SliderSwatches'
export type SliderPickerProps = {
pointer?: typeof SliderPointer
styles?: Record<string, JSX.CSSProperties>
className?: string
}
const Slider = (_props: SliderPickerProps) => {
const props = mergeProps(
{
pointer: SliderPointer,
styles: {},
className: '',
},
_props,
)
const { colors, changeColor } = useColorPicker()
const styles = merge<Record<string, JSX.CSSProperties>, Record<string, JSX.CSSProperties>>(
{
hue: {
height: '12px',
position: 'relative',
},
Hue: {
'border-radius': '2px',
},
},
props.styles,
)
return (
<div style={styles.wrap || {}} class={`slider-picker ${props.className}`}>
<div style={styles.hue}>
<Hue radius={2} hsl={colors().hsl} pointer={props.pointer} onChange={changeColor} />
</div>
<div style={styles.swatches}>
<SliderSwatches hsl={colors().hsl} onClick={changeColor} />
</div>
</div>
)
}
export default withColorPicker(Slider)
import { JSX } from 'solid-js'
export default function SliderPointer() {
const styles: Record<string, JSX.CSSProperties> = {
picker: {
width: '14px',
height: '14px',
'border-radius': '6px',
transform: 'translate(-7px, -1px)',
'background-color': 'rgb(248, 248, 248)',
'box-shadow': '0 1px 4px 0 rgba(0, 0, 0, 0.37)',
},
}
return <div style={styles.picker} />
}
import { JSX, mergeProps } from 'solid-js'
import { HslColor } from '../../types'
interface Props {
hsl: HslColor
onClick: any
offset: number
active?: boolean
first?: boolean
last?: boolean
}
export default function SliderSwatch(_props: Props) {
const props = mergeProps(
{
onClick: () => {},
},
_props,
)
const styles = () => {
const { hsl, offset, active, first, last } = props
return {
swatch: {
height: '12px',
background: `hsl(${hsl.h}, 50%, ${offset * 100}%)`,
cursor: 'pointer',
'border-radius': active
? '3.6px/2px'
: first
? '2px 0 0 2px'
: last
? '0 2px 2px 0'
: undefined,
transform: active ? 'scaleY(1.8)' : undefined,
},
} as Record<string, JSX.CSSProperties>
}
const handleClick = (e: Event) => {
props.onClick(
{
h: props.hsl.h,
s: 0.5,
l: props.offset,
source: 'hsl',
},
e,
)
}
return <div style={styles().swatch} onClick={handleClick} />
}
import { JSX, mergeProps } from 'solid-js'
import { HslColor } from '../../types'
import SliderSwatch from './SliderSwatch'
interface Props {
onClick: any
hsl: HslColor
}
export default function SliderSwatches(_props: Props) {
const props = mergeProps({}, _props)
const styles: Record<string, JSX.CSSProperties> = {
swatches: {
'margin-top': '20px',
},
swatch: {
'box-sizing': 'border-box',
width: '20%',
'padding-right': '1px',
float: 'left',
},
clear: {
clear: 'both',
},
}
// Acceptible difference in floating point equality
const epsilon = 0.1
return (
<div style={styles.swatches}>
<div style={styles.swatch}>
<SliderSwatch
hsl={props.hsl}
offset={0.8}
active={Math.abs(props.hsl.l - 0.8) < epsilon && Math.abs(props.hsl.s - 0.5) < epsilon}
onClick={props.onClick}
first
/>
</div>
<div style={styles.swatch}>
<SliderSwatch
hsl={props.hsl}
offset={0.65}
active={Math.abs(props.hsl.l - 0.65) < epsilon && Math.abs(props.hsl.s - 0.5) < epsilon}
onClick={props.onClick}
/>
</div>
<div style={styles.swatch}>
<SliderSwatch
hsl={props.hsl}
offset={0.5}
active={Math.abs(props.hsl.l - 0.5) < epsilon && Math.abs(props.hsl.s - 0.5) < epsilon}
onClick={props.onClick}
/>
</div>
<div style={styles.swatch}>
<SliderSwatch
hsl={props.hsl}
offset={0.35}
active={Math.abs(props.hsl.l - 0.35) < epsilon && Math.abs(props.hsl.s - 0.5) < epsilon}
onClick={props.onClick}
/>
</div>
<div style={styles.swatch}>
<SliderSwatch
hsl={props.hsl}
offset={0.2}
active={Math.abs(props.hsl.l - 0.2) < epsilon && Math.abs(props.hsl.s - 0.5) < epsilon}
onClick={props.onClick}
last
/>
</div>
<div style={styles.clear} />
</div>
)
}
export { default as SwatchesPicker } from './Swatches'
export type { SwatchesPickerProps } from './Swatches'
import { merge } from 'lodash-es'
import { For, JSX, mergeProps } from 'solid-js'
import { Color } from '../../types'
import { Raised, useColorPicker, withColorPicker } from '../_common'
import SwatchesGroup from './SwatchesGroup'
export type SwatchesPickerProps = {
width?: string | number
height?: string | number
className?: string
styles?: Record<string, JSX.CSSProperties>
colors?: string[][]
}
export function Swatches(_props: SwatchesPickerProps) {
const props = mergeProps(
{
width: 320,
height: 240,
colors: [
['#B71C1C', '#D32F2F', '#F44336', '#E57373', '#FFCDD2'],
['#880E4F', '#C2185B', '#E91E63', '#F06292', '#F8BBD0'],
['#4A148C', '#7B1FA2', '#9C27B0', '#BA68C8', '#E1BEE7'],
['#311B92', '#512DA8', '#673AB7', '#9575CD', '#D1C4E9'],
['#1A237E', '#303F9F', '#3F51B5', '#7986CB', '#C5CAE9'],
['#0D47A1', '#1976D2', '#2196F3', '#64B5F6', '#BBDEFB'],
['#01579B', '#0288D1', '#03A9F4', '#4FC3F7', '#B3E5FC'],
['#006064', '#0097A7', '#00BCD4', '#4DD0E1', '#B2EBF2'],
['#004D40', '#00796B', '#009688', '#4DB6AC', '#B2DFDB'],
['#1B5E20', '#388E3C', '#4CAF50', '#81C784', '#C8E6C9'],
['#33691E', '#689F38', '#8BC34A', '#AED581', '#DCEDC8'],
['#827717', '#AFB42B', '#CDDC39', '#DCE775', '#F0F4C3'],
['#F57F17', '#FBC02D', '#FFEB3B', '#FFF176', '#FFF9C4'],
['#FF6F00', '#FFA000', '#FFC107', '#FFD54F', '#FFECB3'],
['#E65100', '#F57C00', '#FF9800', '#FFB74D', '#FFE0B2'],
['#BF360C', '#E64A19', '#FF5722', '#FF8A65', '#FFCCBC'],
['#3E2723', '#5D4037', '#795548', '#A1887F', '#D7CCC8'],
['#263238', '#455A64', '#607D8B', '#90A4AE', '#CFD8DC'],
['#000000', '#525252', '#969696', '#D9D9D9', '#FFFFFF'],
],
styles: {},
className: '',
},
_props,
)
const { colors: currentColors, changeColor } = useColorPicker()
const styles = () => {
const width = typeof props.width === 'number' ? `${props.width}px` : props.width
const height = typeof props.height === 'number' ? `${props.height}px` : props.height
return merge<Record<string, JSX.CSSProperties>, Record<string, JSX.CSSProperties>>(
{
picker: {
width,
height,
},
overflow: {
height,
'overflow-y': 'scroll',
},
body: {
padding: '16px 0 6px 16px',
},
clear: {
clear: 'both',
},
},
props.styles,
)
}
const handleChange = (data: Color, e: Event) =>
changeColor({ hex: data as string, source: 'hex' }, e)
return (
<div style={styles().picker} class={`swatches-picker ${props.className}`}>
<Raised>
<div style={styles().overflow}>
<div style={styles().body}>
<For each={props.colors}>
{(group) => (
<SwatchesGroup group={group} active={currentColors().hex} onClick={handleChange} />
)}
</For>
<div style={styles().clear} />
</div>
</div>
</Raised>
</div>
)
}
export default withColorPicker(Swatches)
import { JSX, mergeProps } from 'solid-js'
import { getContrastingColor } from '../../helpers/color'
import { Color } from '../../types'
import { Swatch } from '../_common'
import { CheckIcon } from '../_common'
interface Props {
color: string
onClick: (color: Color, event: Event) => void
first: boolean
last: boolean
active: boolean
}
export default function SwatchesColor(_props: Props) {
const props = mergeProps({ onClick: () => {} }, _props)
const styles = () => {
const { color, active, first, last } = props
return {
color: {
width: '40px',
height: '24px',
cursor: 'pointer',
background: color,
'margin-bottom': '1px',
overflow: first || last ? 'hidden' : undefined,
'border-radius': first ? '2px 2px 0 0' : last ? '0 0 2px 2px' : undefined,
'box-shadow': color === '#FFFFFF' ? 'inset 0 0 0 1px #ddd' : undefined,
},
check: {
color: color === '#FFFFFF' || color === 'transparent' ? '#333' : getContrastingColor(color),
'margin-left': '8px',
display: active ? 'block' : 'none',
margin: '0 auto',
},
} as Record<string, JSX.CSSProperties>
}
return (
<Swatch
color={props.color}
styles={styles().color}
onClick={props.onClick}
focusStyle={{ 'box-shadow': `0 0 4px ${props.color}` }}
>
<div style={styles().check}>
<CheckIcon width="24" height="24" fill="white" stroke="white" />
</div>
</Swatch>
)
}
import { For, JSX, mergeProps } from 'solid-js'
import { Color, HexColor } from '../../types'
import SwatchesColor from './SwatchesColor'
type Props = {
onClick: (color: Color, event: Event) => void
active: HexColor
group: string[]
}
export default function SwatchesGroup(_props: Props) {
const props = mergeProps({}, _props)
const styles: Record<string, JSX.CSSProperties> = {
group: {
'padding-bottom': '10px',
width: '40px',
float: 'left',
'margin-right': '10px',
},
}
return (
<div style={styles.group}>
<For each={props.group}>
{(color, i) => (
<SwatchesColor
color={color}
active={color.toLowerCase() === props.active}
first={i() === 0}
last={i() === props.group.length - 1}
onClick={props.onClick}
/>
)}
</For>
</div>
)
}
export { default as TwitterPicker } from './Twitter'
export type { TwitterPickerProps } from './Twitter'
import { merge } from 'lodash-es'
import { For, JSX, mergeProps } from 'solid-js'
import * as color from '../../helpers/color'
import { EditableInput, Swatch, useColorPicker, withColorPicker } from '../_common'
export type TwitterPickerProps = {
width?: string | number
triangle?: 'hide' | 'top-left' | 'top-right'
colors?: string[]
styles?: Record<string, JSX.CSSProperties>
className?: string
}
export const Twitter = (_props: TwitterPickerProps) => {
const props = mergeProps(
{
colors: [
'#FF6900',
'#FCB900',
'#7BDCB5',
'#00D084',
'#8ED1FC',
'#0693E3',
'#ABB8C3',
'#EB144C',
'#F78DA7',
'#9900EF',
],
width: 276,
triangle: 'top-left',
styles: {},
className: '',
},
_props,
)
const { colors: currentColors, changeColor } = useColorPicker()
const styles = () => {
const width = typeof props.width === 'number' ? `${props.width}px` : props.width
const { triangle } = props
return merge<Record<string, JSX.CSSProperties>, Record<string, JSX.CSSProperties>>(
{
card: {
width,
background: '#fff',
border: '0 solid rgba(0,0,0,0.25)',
'box-shadow': '0 1px 4px rgba(0,0,0,0.25)',
'border-radius': '4px',
position: 'relative',
},
body: {
padding: '15px 9px 9px 15px',
},
label: {
'font-size': '18px',
color: '#fff',
},
triangle: {
width: '0px',
height: '0px',
'border-style': 'solid',
'border-width': '0 9px 10px 9px',
'border-color': 'transparent transparent #fff transparent',
position: 'absolute',
display: triangle === 'hide' ? 'none' : undefined,
top: triangle === 'top-left' || triangle === 'top-right' ? '-10px' : undefined,
left: triangle === 'top-left' || triangle === 'top-right' ? '12px' : undefined,
},
triangleShadow: {
width: '0px',
height: '0px',
'border-style': 'solid',
'border-width': '0 9px 10px 9px',
'border-color': 'transparent transparent rgba(0,0,0,.1) transparent',
position: 'absolute',
display: triangle === 'hide' ? 'none' : undefined,
top: triangle === 'top-left' || triangle === 'top-right' ? '-11px' : undefined,
left: triangle === 'top-left' || triangle === 'top-right' ? '12px' : undefined,
},
hash: {
background: '#F0F0F0',
height: '30px',
width: '30px',
'border-radius': '4px 0 0 4px',
float: 'left',
color: '#98A1A4',
display: 'flex',
'align-items': 'center',
'justify-content': 'center',
},
input: {
width: '100px',
'font-size': '14px',
color: '#666',
border: '0px',
outline: 'none',
height: '28px',
'box-shadow': 'inset 0 0 0 1px #F0F0F0',
'box-sizing': 'content-box',
'border-radius': '0 4px 4px 0',
float: 'left',
'padding-left': '8px',
},
swatch: {
width: '30px',
height: '30px',
float: 'left',
'border-radius': '4px',
margin: '0 6px 6px 0',
},
clear: {
clear: 'both',
},
},
props.styles,
)
}
const handleChange = (hexcode: string, e: Event) => {
color.isValidHex(hexcode) &&
changeColor(
{
hex: hexcode,
source: 'hex',
},
e,
)
}
return (
<div style={styles().card} class={`twitter-picker ${props.className}`}>
<div style={styles().triangleShadow} />
<div style={styles().triangle} />
<div style={styles().body}>
<For each={props.colors}>
{(c) => (
<Swatch
color={c}
styles={styles().swatch}
onClick={handleChange}
focusStyle={{
'box-shadow': `0 0 4px ${c}`,
}}
/>
)}
</For>
<div style={styles().hash}>#</div>
<EditableInput
label={''}
styles={{ input: styles().input }}
value={currentColors().hex.replace('#', '')}
onChange={handleChange}
/>
<div style={styles().clear} />
</div>
</div>
)
}
export default withColorPicker(Twitter)
import { HslColor } from '../types'
export const calculateChange = (
e: any,
hsl: HslColor,
direction: string,
initialA: any,
container: HTMLDivElement,
) => {
const containerWidth = container.clientWidth
const containerHeight = container.clientHeight
const x = typeof e.pageX === 'number' ? e.pageX : e.touches[0].pageX
const y = typeof e.pageY === 'number' ? e.pageY : e.touches[0].pageY
const left = x - (container.getBoundingClientRect().left + window.pageXOffset)
const top = y - (container.getBoundingClientRect().top + window.pageYOffset)
if (direction === 'vertical') {
let a
if (top < 0) {
a = 0
} else if (top > containerHeight) {
a = 1
} else {
a = Math.round((top * 100) / containerHeight) / 100
}
if (hsl.a !== a) {
return {
h: hsl.h,
s: hsl.s,
l: hsl.l,
a,
source: 'rgb',
}
}
} else {
let a
if (left < 0) {
a = 0
} else if (left > containerWidth) {
a = 1
} else {
a = Math.round((left * 100) / containerWidth) / 100
}
if (initialA !== a) {
return {
h: hsl.h,
s: hsl.s,
l: hsl.l,
a,
source: 'rgb',
}
}
}
return null
}
const checkboardCache: { [key: string]: any } = {}
export const render = (c1: string, c2: string, size: number, serverCanvas: any) => {
if (typeof document === 'undefined' && !serverCanvas) {
return null
}
const canvas: HTMLCanvasElement = serverCanvas
? new serverCanvas()
: document.createElement('canvas')
canvas.width = size * 2
canvas.height = size * 2
const ctx = canvas.getContext('2d')
if (!ctx) {
return null
} // If no context can be found, return early.
ctx.fillStyle = c1
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = c2
ctx.fillRect(0, 0, size, size)
ctx.translate(size, size)
ctx.fillRect(0, 0, size, size)
return canvas.toDataURL()
}
export const get = (c1: string, c2: string, size: number, serverCanvas: any) => {
const key = `${c1}-${c2}-${size}${serverCanvas ? '-server' : ''}`
if (checkboardCache[key]) {
return checkboardCache[key]
}
const checkboard = render(c1, c2, size, serverCanvas)
checkboardCache[key] = checkboard
return checkboard
}
import { each } from 'lodash-es'
import tinycolor from 'tinycolor2'
export const simpleCheckForValidColor = (data: any) => {
const keysToCheck = ['r', 'g', 'b', 'a', 'h', 's', 'l', 'v']
let checked = 0
let passed = 0
each(keysToCheck, (letter) => {
if (data[letter]) {
checked += 1
if (!isNaN(data[letter])) {
passed += 1
}
if (letter === 's' || letter === 'l') {
const percentPatt = /^\d+%$/
if (percentPatt.test(data[letter])) {
passed += 1
}
}
}
})
return checked === passed ? data : false
}
export const toState = (data: any, oldHue?: number) => {
const color = data.hex ? tinycolor(data.hex) : tinycolor(data)
const hsl = color.toHsl()
const hsv = color.toHsv()
const rgb = color.toRgb()
const hex = color.toHex()
if (hsl.s === 0) {
hsl.h = oldHue || 0
hsv.h = oldHue || 0
}
const transparent = hex === '000000' && rgb.a === 0
return {
hsl,
hex: transparent ? 'transparent' : `#${hex}`,
rgb,
hsv,
oldHue: data.h || oldHue || hsl.h,
source: data.source,
}
}
export const isValidHex = (hex: any) => {
if (hex === 'transparent') {
return true
}
// disable hex4 and hex8
const lh = String(hex).charAt(0) === '#' ? 1 : 0
return hex.length !== 4 + lh && hex.length < 7 + lh && tinycolor(hex).isValid()
}
export const getContrastingColor = (data: any) => {
if (!data) {
return '#fff'
}
const col = toState(data)
if (col.hex === 'transparent') {
return 'rgba(0,0,0,0.4)'
}
const yiq = (col.rgb.r * 299 + col.rgb.g * 587 + col.rgb.b * 114) / 1000
return yiq >= 128 ? '#000' : '#fff'
}
export const red = {
hsl: { a: 1, h: 0, l: 0.5, s: 1 },
hex: '#ff0000',
rgb: { r: 255, g: 0, b: 0, a: 1 },
hsv: { h: 0, s: 1, v: 1, a: 1 },
}
export const isvalidColorString = (str: string, type: string) => {
const stringWithoutDegree = str.replace('°', '')
return tinycolor(`${type} (${stringWithoutDegree})`).isValid()
}
import { HslColor } from '../types'
export const calculateChange = (
e: any,
direction: string,
hsl: HslColor,
container: HTMLDivElement,
) => {
const containerWidth = container.clientWidth
const containerHeight = container.clientHeight
const x = typeof e.pageX === 'number' ? e.pageX : e.touches[0].pageX
const y = typeof e.pageY === 'number' ? e.pageY : e.touches[0].pageY
const left = x - (container.getBoundingClientRect().left + window.pageXOffset)
const top = y - (container.getBoundingClientRect().top + window.pageYOffset)
if (direction === 'vertical') {
let h
if (top < 0) {
h = 359
} else if (top > containerHeight) {
h = 0
} else {
const percent = -((top * 100) / containerHeight) + 100
h = (360 * percent) / 100
}
if (hsl.h !== h) {
return {
h,
s: hsl.s,
l: hsl.l,
a: hsl.a,
source: 'hsl',
}
}
} else {
let h
if (left < 0) {
h = 0
} else if (left > containerWidth) {
h = 359
} else {
const percent = (left * 100) / containerWidth
h = (360 * percent) / 100
}
if (hsl.h !== h) {
return {
h,
s: hsl.s,
l: hsl.l,
a: hsl.a,
source: 'hsl',
}
}
}
return null
}
import { HslColor } from '../types'
export function calculateChange(e: any, hsl: HslColor, container: HTMLDivElement) {
const { width: containerWidth, height: containerHeight } = container.getBoundingClientRect()
const x = typeof e.pageX === 'number' ? e.pageX : e.touches[0].pageX
const y = typeof e.pageY === 'number' ? e.pageY : e.touches[0].pageY
let left = x - (container.getBoundingClientRect().left + window.pageXOffset)
let top = y - (container.getBoundingClientRect().top + window.pageYOffset)
if (left < 0) {
left = 0
} else if (left > containerWidth) {
left = containerWidth
}
if (top < 0) {
top = 0
} else if (top > containerHeight) {
top = containerHeight
}
const saturation = left / containerWidth
const bright = 1 - top / containerHeight
return {
h: hsl.h,
s: saturation,
v: bright,
a: hsl.a,
source: 'hsv',
}
}
export * from './components/alpha'
export * from './components/material'
export * from './components/hue'
export * from './components/twitter'
export * from './components/block'
export * from './components/slider'
export * from './components/github'
export * from './components/compact'
export * from './components/swatches'
export * from './components/circle'
export * from './components/google'
export * from './components/chrome'
export * from './components/sketch'
export * from './components/photoshop'
export * from './components/hue'
export * from './types'
export type HexColor = string
export type HslColor = {
h: number
l: number
s: number
a?: number
}
export type HsvColor = {
h: number
s: number
v: number
a?: number
}
export type RgbColor = {
r: number
g: number
b: number
a?: number
}
export type Color = HexColor | HslColor | HsvColor | RgbColor
export type ColorResult = {
hex: HexColor
hsl: HslColor
hsv: HsvColor
rgb: RgbColor
oldHue: number
}
export type ChangeColor =
| HslColor
| HsvColor
| (RgbColor & { source?: string })
| { hex: HexColor; source: string }
| HexColor
export type Direction = 'horizontal' | 'vertical'
+1
-1
import { JSX } from 'solid-js';
export declare type CheckboardProps = {
export type CheckboardProps = {
size?: number;

@@ -4,0 +4,0 @@ white?: string;

@@ -19,2 +19,2 @@ import { Accessor, Context, JSX } from 'solid-js';

export declare function useColorPicker(): ColorPickerContextType;
export declare function withColorPicker<T extends object>(Component: (props: T) => JSX.Element): (props: T & Omit<ColorPickerProps, 'children'>) => JSX.Element;
export declare function withColorPicker<T extends object>(Component: (props: T) => JSX.Element): (props: T & Omit<ColorPickerProps, "children">) => JSX.Element;
import { JSX } from 'solid-js';
import { ChangeColor, HslColor, HsvColor } from '../../types';
export declare type SaturationProps = {
export type SaturationProps = {
hsl: HslColor;

@@ -5,0 +5,0 @@ hsv: HsvColor;

import { JSX } from 'solid-js';
export declare type SwatchProps = {
export type SwatchProps = {
color: string;

@@ -4,0 +4,0 @@ styles?: JSX.CSSProperties;

import { JSX } from 'solid-js';
export declare type AlphaPickerProps = {
export type AlphaPickerProps = {
width?: string | number;

@@ -4,0 +4,0 @@ height?: string | number;

import { JSX } from 'solid-js';
export declare type BlockPickerProps = {
export type BlockPickerProps = {
width?: string | number;

@@ -4,0 +4,0 @@ colors?: string[];

import { JSX } from 'solid-js';
export declare type CirclePickerProps = {
export type CirclePickerProps = {
width?: string | number;

@@ -4,0 +4,0 @@ circleSize?: number;

import { JSX } from 'solid-js';
export declare type CompactPickerProps = {
export type CompactPickerProps = {
colors?: string[];

@@ -4,0 +4,0 @@ styles?: Record<string, JSX.CSSProperties>;

import { JSX } from 'solid-js';
export declare type GithubPickerProps = {
export type GithubPickerProps = {
width?: string | number;

@@ -4,0 +4,0 @@ styles?: Record<string, JSX.CSSProperties>;

import { JSX } from 'solid-js';
export declare type GooglePickerProps = {
export type GooglePickerProps = {
width?: string | number;

@@ -4,0 +4,0 @@ styles?: Record<string, JSX.CSSProperties>;

import HuePointer from './HuePointer';
import { JSX } from 'solid-js';
export declare type HuePickerProps = {
export type HuePickerProps = {
width?: string | number;

@@ -5,0 +5,0 @@ height?: string | number;

import { JSX } from 'solid-js';
export declare type MaterialPickerProps = {
export type MaterialPickerProps = {
styles?: Record<string, JSX.CSSProperties>;

@@ -4,0 +4,0 @@ className?: string;

import { JSX } from 'solid-js';
export declare type PhotoshopPickerProps = {
export type PhotoshopPickerProps = {
header?: string;

@@ -4,0 +4,0 @@ styles?: Record<string, JSX.CSSProperties>;

import { JSX } from 'solid-js';
export declare type SketchPickerProps = {
export type SketchPickerProps = {
disableAlpha?: boolean;

@@ -4,0 +4,0 @@ width?: string | number;

import { JSX } from 'solid-js';
import SliderPointer from './SliderPointer';
export declare type SliderPickerProps = {
export type SliderPickerProps = {
pointer?: typeof SliderPointer;

@@ -5,0 +5,0 @@ styles?: Record<string, JSX.CSSProperties>;

import { JSX } from 'solid-js';
export declare type SwatchesPickerProps = {
export type SwatchesPickerProps = {
width?: string | number;

@@ -4,0 +4,0 @@ height?: string | number;

import { JSX } from 'solid-js';
import { Color, HexColor } from '../../types';
declare type Props = {
type Props = {
onClick: (color: Color, event: Event) => void;

@@ -5,0 +5,0 @@ active: HexColor;

import { JSX } from 'solid-js';
export declare type TwitterPickerProps = {
export type TwitterPickerProps = {
width?: string | number;

@@ -4,0 +4,0 @@ triangle?: 'hide' | 'top-left' | 'top-right';

@@ -1,3 +0,3 @@

export declare type HexColor = string;
export declare type HslColor = {
export type HexColor = string;
export type HslColor = {
h: number;

@@ -8,3 +8,3 @@ l: number;

};
export declare type HsvColor = {
export type HsvColor = {
h: number;

@@ -15,3 +15,3 @@ s: number;

};
export declare type RgbColor = {
export type RgbColor = {
r: number;

@@ -22,4 +22,4 @@ g: number;

};
export declare type Color = HexColor | HslColor | HsvColor | RgbColor;
export declare type ColorResult = {
export type Color = HexColor | HslColor | HsvColor | RgbColor;
export type ColorResult = {
hex: HexColor;

@@ -31,3 +31,3 @@ hsl: HslColor;

};
export declare type ChangeColor = HslColor | HsvColor | (RgbColor & {
export type ChangeColor = HslColor | HsvColor | (RgbColor & {
source?: string;

@@ -38,2 +38,2 @@ }) | {

} | HexColor;
export declare type Direction = 'horizontal' | 'vertical';
export type Direction = 'horizontal' | 'vertical';
{
"name": "solid-color",
"version": "0.0.4",
"version": "0.0.5",
"private": false,

@@ -32,12 +32,18 @@ "description": "",

"exports": {
".": {
"solid": "./dist/source/index.jsx",
"import": "./dist/esm/index.js",
"browser": {
"import": "./dist/esm/index.js",
"require": "./dist/cjs/index.js"
"solid": {
"development": "./dist/source/index.jsx",
"import": "./dist/source/index.jsx"
},
"development": {
"import": {
"types": "./dist/types/index.d.ts",
"default": "./dist/esm/index.js"
},
"require": "./dist/cjs/index.js",
"node": "./dist/cjs/index.js"
}
"require": "./dist/cjs/index.js"
},
"import": {
"types": "./dist/types/index.d.ts",
"default": "./dist/esm/index.js"
},
"require": "./dist/cjs/index.js"
},

@@ -49,2 +55,3 @@ "main": "dist/cjs/index.js",

"dist",
"src",
"README.md"

@@ -67,26 +74,26 @@ ],

"devDependencies": {
"@solidjs/testing-library": "^0.6.0",
"@testing-library/jest-dom": "^5.16.5",
"@types/lodash-es": "^4.17.6",
"@types/testing-library__jest-dom": "^5.14.5",
"@types/tinycolor2": "^1.4.3",
"@typescript-eslint/eslint-plugin": "^5.48.2",
"@typescript-eslint/parser": "^5.48.2",
"bumpp": "^8.2.1",
"eslint": "^8.32.0",
"eslint-plugin-no-only-tests": "^3.1.0",
"eslint-plugin-solid": "^0.9.3",
"jsdom": "^21.1.0",
"@solidjs/testing-library": "^0.8.10",
"@testing-library/jest-dom": "^6.6.3",
"@types/lodash-es": "^4.17.12",
"@types/testing-library__jest-dom": "^6.0.0",
"@types/tinycolor2": "^1.4.6",
"@typescript-eslint/eslint-plugin": "^8.31.1",
"@typescript-eslint/parser": "^8.31.1",
"bumpp": "^10.1.0",
"eslint": "^9.25.1",
"eslint-plugin-no-only-tests": "^3.3.0",
"eslint-plugin-solid": "^0.14.5",
"jsdom": "^26.1.0",
"lodash-es": "^4.17.21",
"prettier": "2.7.1",
"rollup": "^2.77.2",
"rollup-preset-solid": "^1.4.0",
"solid-js": "^1.4.8",
"taze": "^0.7.6",
"terser": "^5.16.3",
"tinycolor2": "^1.5.2",
"typescript": "^4.7.4",
"vite": "^3.0.4",
"vite-plugin-solid": "^2.3.0",
"vitest": "^0.27.3"
"rollup": "^4.40.1",
"rollup-preset-solid": "^3.0.0",
"solid-js": "^1.9.5",
"taze": "^19.0.4",
"terser": "^5.39.0",
"tinycolor2": "^1.6.0",
"typescript": "^5.8.3",
"vite": "^6.3.3",
"vite-plugin-solid": "^2.11.6",
"vitest": "^3.1.2"
},

@@ -96,3 +103,3 @@ "peerDependencies": {

},
"packageManager": "pnpm@7.5.0"
"packageManager": "pnpm@10.10.0"
}

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display