Skip to content

Instantly share code, notes, and snippets.

@MarkAtOmniux
Created October 25, 2023 15:25
Show Gist options
  • Save MarkAtOmniux/b630051f26d6759bd3a83387de97d15f to your computer and use it in GitHub Desktop.
Save MarkAtOmniux/b630051f26d6759bd3a83387de97d15f to your computer and use it in GitHub Desktop.
PayloadCMS Color Picker Field
import React from 'react';
import { Props } from 'payload/components/views/Cell';
import './styles.scss';
const Cell: React.FC<Props> = (props) => {
const { cellData } = props;
if (!cellData) return null;
return (
<div
className={`chip`}
style={{ backgroundColor: cellData as string }}
>
</div>
)
}
export default Cell;
import React, { useEffect, useState, useCallback, Fragment } from 'react';
import { useFieldType } from 'payload/components/forms';
import { usePreferences } from 'payload/components/preferences';
import { Label } from 'payload/components/forms';
import { Props } from 'payload/components/fields/Text';
import './styles.scss';
import { validateHexColor } from '.';
import { HexColorInput, HexColorPicker } from 'react-colorful';
const defaultColor = '#9A9A9A';
const baseClass = 'custom-color-picker';
const preferenceKey = 'color-picker-color';
const InputField: React.FC<Props> = (props) => {
const { path, label, required, defaultValue } = props;
const { value = '', setValue } = useFieldType({
path,
validate: validateHexColor,
});
const { getPreference, setPreference } = usePreferences();
const [color, setColor] = useState(value ?? defaultValue ?? defaultColor);
const [isAdding, setIsAdding] = useState(false);
const [colorToAdd, setColorToAdd] = useState(value ?? defaultValue ?? defaultColor);
useEffect(() => {
const mergeColorsFromPreferences = async () => {
const colorPreference = await getPreference<string>(
path + '-' + preferenceKey
);
if (colorPreference) {
setColor(colorPreference);
}
};
mergeColorsFromPreferences();
}, [getPreference, setColor]);
const handleAddColor = useCallback((val?: string) => {
console.log(val)
setIsAdding(false);
setValue(colorToAdd);
// update state with new colors
setColor(colorToAdd);
// store the user color preferences for future use
setPreference(path + '-' + preferenceKey, colorToAdd);
}, [color, setPreference, colorToAdd, setIsAdding, setValue]);
return (
<div className={baseClass}>
<Label htmlFor={path} label={label} required={required} />
{isAdding && (
<div>
<HexColorPicker
onBlur={(e) => {
if (
e.relatedTarget === null &&
validateHexColor(colorToAdd) === true
) {
handleAddColor();
}
}}
onChange={setColorToAdd}
color={colorToAdd}
/>
<HexColorInput
prefixed
alpha
onKeyDown={ ({ code }) => {
if (code === 'Enter') {
if (validateHexColor(colorToAdd) === true
) {
handleAddColor();
}
}
}}
onBlur={(e) => {
if (
e.relatedTarget === null &&
validateHexColor(colorToAdd) === true
) {
handleAddColor();
}
}}
className={`${baseClass}__input`}
color={colorToAdd}
onChange={setColorToAdd}
/>
</div>
)}
{!isAdding && (
<Fragment>
<button
type='button'
className={`chip chip--clickable`}
style={{ backgroundColor: color }}
aria-label={color}
onClick={() => {
setValue(color);
setIsAdding(true);
}}
/>
</Fragment>
)}
</div>
);
};
export default InputField;
import { Field as FieldType, RichTextField } from 'payload/types';
import Cell from './Cell';
import Field from './Field';
import { TextField } from 'payload/dist/fields/config/types';
export const validateHexColor = (value: string = ''): true | string => {
return (
value.match(/^#(?:[0-9a-fA-F]{3}){1,2}$/) !== null ||
`Please give a valid hex color`
);
};
type ColorPicker = (name: string, overrides?: Partial<TextField>) => FieldType;
export const colorField: ColorPicker = (
name = 'colorField',
overrides = {}
) => {
return {
name: name,
type: 'text',
validate: validateHexColor,
admin: {
components: {
Field,
Cell,
},
},
...overrides,
};
};
@import '~payload/scss';
.add-color.btn {
margin: 0;
padding: 0;
border: $style-stroke-width-m solid #fff;
}
.custom-color-picker {
max-width: fit-content;
margin-right: 1rem;
&__btn.btn {
margin: base(0.5);
&:first-of-type {
margin-left: unset;
}
}
&__input {
// Payload exports a mixin from the vars file for quickly applying formInput rules to the class for our input
@include formInput;
}
&__colors {
display: flex;
flex-wrap: wrap;
list-style: none;
padding: 0;
margin: 0;
}
}
.chip {
border-radius: 50%;
border: $style-stroke-width-m solid #fff;
height: base(1.25);
width: base(1.25);
margin-right: base(0.5);
box-shadow: none;
&--selected {
box-shadow: 0 0 0 $style-stroke-width-m $color-dark-gray;
}
&--clickable {
cursor: pointer;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment