Skip to content

Commit

Permalink
feat(solid): add color picker (#810)
Browse files Browse the repository at this point in the history
  • Loading branch information
cschroeter committed Apr 25, 2023
1 parent 8339a4c commit ecccae4
Show file tree
Hide file tree
Showing 27 changed files with 516 additions and 4 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
| Accordion | 🟢 | 🟢 | 🟢 |
| Carousel (Beta) | 🟢 |||
| Checkbox | 🟢 | 🟢 | 🟢 |
| Color Picker (Beta) | 🟢 | ||
| Color Picker (Beta) | 🟢 | 🟢 ||
| Date Picker (Beta) | 🟡 |||
| Dialog | 🟢 | 🟢 | 🟢 |
| Combobox | 🟢 | 🟢 | 🟢 |
Expand Down
4 changes: 4 additions & 0 deletions packages/react/src/color-picker/color-picker.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
output {
font-size: 14px;
}

[data-scope='color-picker'][data-part='content'] {
max-width: 260px;
display: flex;
Expand Down
6 changes: 3 additions & 3 deletions packages/react/src/color-picker/color-picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ export type ColorPickerProps = Assign<

export const ColorPicker = (props: ColorPickerProps) => {
const { children, ...useColorPickerProps } = props
const datePicker = useColorPicker(useColorPickerProps)
const view = runIfFn(children, datePicker)
const colorPicker = useColorPicker(useColorPickerProps)
const view = runIfFn(children, colorPicker)

return <ColorPickerProvider value={datePicker}>{view}</ColorPickerProvider>
return <ColorPickerProvider value={colorPicker}>{view}</ColorPickerProvider>
}
1 change: 1 addition & 0 deletions packages/solid/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"@zag-js/accordion": "0.6.0",
"@zag-js/anatomy": "0.1.4",
"@zag-js/checkbox": "0.6.0",
"@zag-js/color-picker": "0.6.0",
"@zag-js/combobox": "0.6.0",
"@zag-js/dialog": "0.6.0",
"@zag-js/editable": "0.6.0",
Expand Down
7 changes: 7 additions & 0 deletions packages/solid/src/color-picker/color-picker-area-context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { AreaProps } from '@zag-js/color-picker/dist/color-picker.types'
import { createContext } from '../create-context'

export const [ColorPickerAreaProvider, useColorPickerAreaContext] = createContext<AreaProps>({
hookName: 'useColorPickerAreaContext',
providerName: '<ColorPickerAreaProvider />',
})
15 changes: 15 additions & 0 deletions packages/solid/src/color-picker/color-picker-area-gradient.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { mergeProps } from 'solid-js'
import { ark, type HTMLArkProps } from '../factory'
import { useColorPickerAreaContext } from './color-picker-area-context'
import { useColorPickerContext } from './color-picker-context'

export type ColorPickerAreaGradientProps = HTMLArkProps<'div'>

export const ColorPickerAreaGradient = (props: ColorPickerAreaGradientProps) => {
const colorPicker = useColorPickerContext()
const area = useColorPickerAreaContext()

const mergedProps = mergeProps(colorPicker().getAreaGradientProps(area), props)

return <ark.div {...mergedProps} />
}
15 changes: 15 additions & 0 deletions packages/solid/src/color-picker/color-picker-area-thumb.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { mergeProps } from 'solid-js'
import { ark, type HTMLArkProps } from '../factory'
import { useColorPickerAreaContext } from './color-picker-area-context'
import { useColorPickerContext } from './color-picker-context'

export type ColorPickerAreaThumbProps = HTMLArkProps<'div'>

export const ColorPickerAreaThumb = (props: ColorPickerAreaThumbProps) => {
const colorPicker = useColorPickerContext()
const area = useColorPickerAreaContext()

const mergedProps = mergeProps(colorPicker().getAreaThumbProps(area), props)

return <ark.div {...mergedProps} />
}
21 changes: 21 additions & 0 deletions packages/solid/src/color-picker/color-picker-area.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { AreaProps } from '@zag-js/color-picker/dist/color-picker.types'
import { mergeProps } from 'solid-js'
import { createSplitProps } from '../create-split-props'
import { ark, type HTMLArkProps } from '../factory'
import { ColorPickerAreaProvider } from './color-picker-area-context'
import { useColorPickerContext } from './color-picker-context'

export type ColorPickerAreaProps = HTMLArkProps<'div'> & AreaProps

export const ColorPickerArea = (props: ColorPickerAreaProps) => {
const [areaProps, divprops] = createSplitProps<AreaProps>()(props, ['xChannel', 'yChannel'])
const colorPicker = useColorPickerContext()
const mergedProps = mergeProps(colorPicker().getAreaProps(areaProps), divprops)

console.log({ areaProps })
return (
<ColorPickerAreaProvider value={areaProps}>
<ark.div {...mergedProps} />
</ColorPickerAreaProvider>
)
}
19 changes: 19 additions & 0 deletions packages/solid/src/color-picker/color-picker-channel-input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { Assign } from '@polymorphic-factory/solid'
import type { ChannelInputProps } from '@zag-js/color-picker/dist/color-picker.types'
import { mergeProps } from 'solid-js'
import { createSplitProps } from '../create-split-props'
import { ark, type HTMLArkProps } from '../factory'
import { useColorPickerContext } from './color-picker-context'

export type ColorPickerChannelInputProps = Assign<HTMLArkProps<'input'>, ChannelInputProps>

export const ColorPickerChannelInput = (props: ColorPickerChannelInputProps) => {
const [channelProps, inputProps] = createSplitProps<ChannelInputProps>()(props, [
'channel',
'orientation',
])
const colorPicker = useColorPickerContext()
const mergedProps = mergeProps(colorPicker().getChannelInputProps(channelProps), inputProps)

return <ark.input {...mergedProps} />
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { ChannelProps } from '@zag-js/color-picker/dist/color-picker.types'
import { createContext } from '../create-context'

export const [ColorPickerSliderProvider, useColorPickerSliderContext] = createContext<ChannelProps>(
{
hookName: 'useColorPickerSliderContext',
providerName: '<ColorPickerSliderProvider />',
},
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { mergeProps } from 'solid-js'
import { ark, type HTMLArkProps } from '../factory'
import { useColorPickerSliderContext } from './color-picker-channel-slider-context'
import { useColorPickerContext } from './color-picker-context'

export type ColorPickerSliderThumbProps = HTMLArkProps<'div'>

export const ColorPickerSliderThumb = (props: ColorPickerSliderThumbProps) => {
const colorPicker = useColorPickerContext()
const slider = useColorPickerSliderContext()
const mergedProps = mergeProps(colorPicker().getChannelSliderThumbProps(slider), props)

return <ark.div {...mergedProps} />
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { ChannelProps } from '@zag-js/color-picker/dist/color-picker.types'
import { mergeProps } from 'solid-js'
import { createSplitProps } from '../create-split-props'
import { ark, type HTMLArkProps } from '../factory'
import { ColorPickerSliderProvider } from './color-picker-channel-slider-context'
import { useColorPickerContext } from './color-picker-context'

export type ColorPickerSliderTrackProps = HTMLArkProps<'div'> & ChannelProps

export const ColorPickerSliderTrack = (props: ColorPickerSliderTrackProps) => {
const [channelProps, divProps] = createSplitProps<ChannelProps>()(props, [
'channel',
'orientation',
])
const colorPicker = useColorPickerContext()
const mergedProps = mergeProps(colorPicker().getChannelSliderTrackProps(channelProps), divProps)

return (
<ColorPickerSliderProvider value={channelProps}>
<ark.div {...mergedProps} />
{/* <ark.div {...colorPicker().getChannelSliderBackgroundProps(channelProps)}> */}
{/* {children}
</ark.div> */}
</ColorPickerSliderProvider>
)
}
10 changes: 10 additions & 0 deletions packages/solid/src/color-picker/color-picker-content.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ark, type HTMLArkProps } from '../factory'
import { useColorPickerContext } from './color-picker-context'

export type ColorPickerContentProps = HTMLArkProps<'div'>

export const ColorPickerContent = (props: ColorPickerContentProps) => {
const colorPicker = useColorPickerContext()

return <ark.div {...colorPicker().contentProps} {...props} />
}
9 changes: 9 additions & 0 deletions packages/solid/src/color-picker/color-picker-context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { createContext } from '../create-context'
import { type UseColorPickerReturn } from './use-color-picker'

export type ColorPickerContext = UseColorPickerReturn

export const [ColorPickerProvider, useColorPickerContext] = createContext<ColorPickerContext>({
hookName: 'useColorPickerContext',
providerName: '<ColorPickerProvider />',
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ark, type HTMLArkProps } from '../factory'
import { useColorPickerContext } from './color-picker-context'

export type ColorPickerEyeDropperTriggerProps = HTMLArkProps<'div'>

export const ColorPickerEyeDropperTrigger = (props: ColorPickerEyeDropperTriggerProps) => {
const dialog = useColorPickerContext()

return <ark.div {...dialog().eyeDropperTriggerProps} {...props} />
}
8 changes: 8 additions & 0 deletions packages/solid/src/color-picker/color-picker-swatch-group.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { ark, type HTMLArkProps } from '../factory'
import { parts } from './color-picker.anatomy'

export type ColorPickerSwatchGroupProps = HTMLArkProps<'div'>

export const ColorPickerSwatchGroup = (props: ColorPickerSwatchGroupProps) => (
<ark.div {...parts.swatchGroup.attrs} {...props} />
)
20 changes: 20 additions & 0 deletions packages/solid/src/color-picker/color-picker-swatch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { Assign } from '@polymorphic-factory/solid'
import type { SwatchProps } from '@zag-js/color-picker/dist/color-picker.types'
import { mergeProps } from 'solid-js'
import { createSplitProps } from '../create-split-props'
import { ark, type HTMLArkProps } from '../factory'
import { useColorPickerContext } from './color-picker-context'

export type ColorPickerSwatchProps = Assign<HTMLArkProps<'button'>, SwatchProps>

export const ColorPickerSwatch = (props: ColorPickerSwatchProps) => {
const [swatchProps, buttonProps] = createSplitProps<SwatchProps>()(props, ['readOnly', 'value'])
const colorPicker = useColorPickerContext()
const mergedProps = mergeProps(colorPicker().getSwatchProps(swatchProps), buttonProps)

return (
<ark.button {...mergedProps} disabled={swatchProps.readOnly}>
<ark.div {...colorPicker().getSwatchBackgroundProps(swatchProps)} />
</ark.button>
)
}
21 changes: 21 additions & 0 deletions packages/solid/src/color-picker/color-picker.anatomy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { createAnatomy } from '@zag-js/anatomy'
// TODO replace with anatomy from @zag-js/color-picker
// import { anatomy } from '@zag-js/color-picker'

const anatomy = createAnatomy('color-picker', [
'area',
'areaThumb',
'areaGradient',
'channelSliderTrack',
'channelSliderTrackBg',
'channelSliderThumb',
'channelInput',
'swatch',
'swatchBg',
'content',
'label',
'eyeDropTrigger',
])

export const colorPickerAnatomy = anatomy.extendWith('swatchGroup')
export const parts = colorPickerAnatomy.build()
76 changes: 76 additions & 0 deletions packages/solid/src/color-picker/color-picker.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
output {
font-size: 14px;
display: flex;
gap: 8px;
align-items: center;
}

[data-scope='color-picker'][data-part='content'] {
max-width: 260px;
display: flex;
flex-direction: column;
box-sizing: border-box;
gap: 16px;
padding: 24px;
border: 1px solid #d5d5d5;
}

[data-scope='color-picker'][data-part='area'] {
height: 200px;
border-radius: 4px;
border: 1px solid #ebebeb;
}

[data-scope='color-picker'][data-part='area-gradient'] {
background: rgb(142, 142, 142);
border-radius: 4px;
height: 200px;
}

[data-scope='color-picker'][data-part='area-thumb'],
[data-scope='color-picker'][data-part='channel-slider-thumb'] {
border: 2px solid white;
border-radius: 9999px;
box-sizing: border-box;
transform: translate(-50%, -50%);
box-shadow: black 0px 0px 0px 1px, black 0px 0px 0px 1px inset;
width: 16px;
height: 16px;
}

[data-scope='color-picker'][data-part='channel-slider-track'] {
height: 20px;
border-radius: 4px;
}

[data-scope='color-picker'][data-part='channel-slider-track-bg'] {
border-radius: 4px;
}

[data-scope='color-picker'][data-part='channel-input'] {
border-radius: 4px;
width: 100%;
border: 1px solid #c2c2c2;
}

[data-scope='color-picker'][data-part='channel-input']::-webkit-outer-spin-button,
[data-scope='color-picker'][data-part='channel-input']::-webkit-inner-spin-button {
-webkit-appearance: none;
}

[data-scope='color-picker'][data-part='swatch-group'] {
display: flex;
gap: 4px;
}

[data-scope='color-picker'][data-part='swatch'] {
all: unset;
border-radius: 4px;
width: 20px;
height: 20px;
flex-shrink: 0;
}

[data-scope='color-picker'][data-part='swatch-bg'] {
border-radius: 4px;
}
56 changes: 56 additions & 0 deletions packages/solid/src/color-picker/color-picker.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import {
ColorPicker,
ColorPickerArea,
ColorPickerAreaGradient,
ColorPickerAreaThumb,
ColorPickerChannelInput,
ColorPickerContent,
ColorPickerEyeDropperTrigger,
ColorPickerSliderThumb,
ColorPickerSliderTrack,
ColorPickerSwatch,
ColorPickerSwatchGroup,
} from './'
import './color-picker.css'

export const Basic = () => (
<ColorPicker value="hsla(10, 81%, 59%, 1)">
{(api) => {
const [hue, saturation, lightness] = api.channels
return (
<ColorPickerContent>
<output>
<ColorPickerSwatch value={api.value} readOnly />
<span>{api.value}</span>
</output>

<ColorPickerArea xChannel={saturation} yChannel={lightness}>
<ColorPickerAreaGradient />
<ColorPickerAreaThumb />
</ColorPickerArea>

<ColorPickerSliderTrack channel={hue}>
<ColorPickerSliderThumb />
</ColorPickerSliderTrack>
<ColorPickerSliderTrack channel="alpha">
<ColorPickerSliderThumb />
</ColorPickerSliderTrack>

<ColorPickerChannelInput channel={hue} />
<ColorPickerChannelInput channel={saturation} />
<ColorPickerChannelInput channel={lightness} />
<ColorPickerChannelInput channel="alpha" />
<ColorPickerChannelInput channel="hex" />

<ColorPickerSwatchGroup>
<ColorPickerSwatch value="hsla(153, 46%, 13%, 1)" />
<ColorPickerSwatch value="hsla(356, 100%, 54%, 1)" />
</ColorPickerSwatchGroup>
<ColorPickerEyeDropperTrigger>
<button>Pick color</button>
</ColorPickerEyeDropperTrigger>
</ColorPickerContent>
)
}}
</ColorPicker>
)
Loading

1 comment on commit ecccae4

@vercel
Copy link

@vercel vercel bot commented on ecccae4 Apr 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.