Skip to content

Instantly share code, notes, and snippets.

@litzinger
Created December 12, 2022 16:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save litzinger/5d2cf84ab31a40190dc913c6a6ad3949 to your computer and use it in GitHub Desktop.
Save litzinger/5d2cf84ab31a40190dc913c6a6ad3949 to your computer and use it in GitHub Desktop.
Refactor the EE ColorPicker fieldtype to support named colors
<?php
/**
* ExpressionEngine (https://expressionengine.com)
*
* @link https://expressionengine.com/
* @copyright Copyright (c) 2003-2022, Packet Tide, LLC (https://www.packettide.com)
* @license https://expressionengine.com/license
*/
use Mexitek\PHPColors\Color;
class Colorpicker_ft extends EE_Fieldtype
{
public $info = [];
public $disable_frontedit = true;
public $size = 'small';
public $supportedEvaluationRules = ['equal', 'notEqual', 'isEmpty', 'isNotEmpty'];
public $defaultEvaluationRule = 'notEqual';
public $default_settings = [
'allowed_colors' => 'any',
// The default color to use on invalid field input
'colorpicker_default_color' => '',
// An array of colors
'value_swatches' => null,
'manual_swatches' => '',
// How the swatches should be crated
// v = By values with a grid
// m = Manually with a textarea
'populate_swatches' => 'v'
];
public function __construct()
{
$addon = ee('Addon')->get('colorpicker');
$this->info = [
'name' => $addon->getName(),
'version' => $addon->getVersion()
];
}
/**
* Validates the field's value
*/
public function validate($value)
{
$value = trim($value);
if ($value == '') {
return true;
}
// Is it a valid 6 digit hex color?
if (! preg_match('/#([a-f0-9]{6})\b/i', $value)) {
ee()->lang->loadfile('channel');
return ee()->lang->line('invalid_hex_code');
}
// Enforce that the color is one of the swatches or default color when in the swatches mode
if ($this->get_setting('allowed_colors') == 'swatches') {
$swatches = array_map('strtoupper', $this->getSwatches());
if (! in_array(strtoupper($value), $swatches)
and $this->get_setting('colorpicker_default_color') !== $value) {
ee()->lang->loadfile('fieldtypes');
return ee()->lang->line('colorpicker_color_not_allowed');
}
}
return true;
}
/**
* Saves the field's value
*
* @see EE_Fieldtype::save()
*/
public function save($data)
{
return $data;
}
/**
* Displays the color picker field
*
* @see EE_Fieldtype::display_field()
*/
public function display_field($data)
{
ee()->cp->add_js_script('file', array('library/simplecolor', 'components/colorpicker'));
return $this->createColorPicker([
'allowedColors' => $this->get_setting('allowed_colors'),
'inputId' => $this->field_id,
'inputName' => $this->field_name,
'initialColor' => $data,
'swatches' => $this->getSwatches(),
'defaultColor' => $this->get_setting('colorpicker_default_color'),
// 'disabled' => $this->get_setting('field_disabled')
]);
}
/**
* Creates a color picker input with the specified values
* The values are passed to the react color picker component
*/
private function createColorPicker($info, $disabled = false)
{
$data = base64_encode(json_encode($info));
$disabled = $disabled ? 'disabled' : '';
return "<input name=\"{$info['inputName']}\" data-colorpicker-react=\"{$data}\" data-input-value=\"\" {$disabled}/>";
}
// -----------------------------------------------------------------------
// Template Tag
// -----------------------------------------------------------------------
/**
* Replace the field tag on the frontend.
*
* @param string $data Stored data for the field
* @param array $params The tag's parameters
* @param mixed $tagdata If the tag is a pair, the tagdata. Otherwise FALSE.
* @return string
*/
public function replace_tag($data, $params = array(), $tagdata = false)
{
// Data is preformatted, just return it!
return $data;
}
/**
* @param string $data Stored data for the field
* @param array $params The tag's parameters
* @param mixed $tagdata If the tag is a pair, the tagdata. Otherwise FALSE.
* @return string
*/
public function replace_name($data, $params = [], $tagdata = false)
{
$swatches = $this->get_setting('value_swatches');
$collection = [];
foreach ($swatches as $swatch) {
if (strpos($swatch, '|') !== false) {
$parts = explode('|', $swatch);
$collection[$parts[0]] = $parts[1];
}
}
if (array_key_exists($data, $collection)) {
return $collection[$data];
}
return $data;
}
/**
* :contrast_color modifier
*
* Returns a black or white color in contrast to the fields color
*/
public function replace_contrast_color($data, $params = [], $tagdata = false)
{
try {
$color = new Color($data);
$contrast = $color->isLight() ? '#000000' : '#ffffff';
} catch (\Exception $e) {
$contrast = '#ffffff';
}
return $contrast;
}
// -----------------------------------------------------------------------
// Settings
// -----------------------------------------------------------------------
/**
* Displays the field's settings
*/
public function display_settings($data)
{
ee()->lang->loadfile('fieldtypes');
ee()->cp->add_js_script('file', array('library/simplecolor', 'components/colorpicker'));
// The settings contain color picker fields,
// so when the user chooses the color picker fieldtype, render them
ee()->javascript->output('$(document).ready(function () {
$("input[name=field_type]").change(function () {
setTimeout(function () {
ColorPicker.renderFields()
}, 100);
})
})');
$data = array_merge($this->default_settings, $data);
$grid = $this->getSwatchesMiniGrid($data);
$settings = [
// ColorPicker type
[
'title' => 'colorpicker_allowed_colors',
'desc' => 'colorpicker_allowed_colors_desc',
'fields' => [
'allowed_colors' => [
'type' => 'radio',
'value' => $data['allowed_colors'],
'choices' => [
'any' => lang('colorpicker_allowed_colors_any'),
'swatches' => lang('colorpicker_allowed_colors_swatches')
]
]
]
],
// Default Color
[
'title' => 'colorpicker_default_color',
'desc' => 'colorpicker_default_color_desc',
'fields' => [
'colorpicker_default_color' => [
'type' => 'html',
'content' => $this->createColorPicker([
'allowedColors' => 'any',
'initialColor' => $data['colorpicker_default_color'],
'inputName' => 'colorpicker_default_color'
])
],
]
],
[
'title' => 'swatches',
'desc' => 'colorpicker_swatches_options_desc',
'fields' => array(
'populate_swatches_with_values' => array(
'type' => 'radio',
'name' => 'populate_swatches',
'choices' => array(
'v' => lang('colorpicker_swatches_populate_values'),
),
'value' => $data['populate_swatches']
),
'value_swatches' => [
'type' => 'html',
'margin_left' => true,
'content' => ee('View')->make('ee:_shared/form/mini_grid')->render($grid->viewData())
],
'populate_swatches_manually' => array(
'type' => 'radio',
'name' => 'populate_swatches',
'choices' => array(
'm' => lang('colorpicker_swatches_populate_manually'),
),
'value' => $data['populate_swatches']
),
'manual_swatches' => array(
'type' => 'textarea',
'margin_left' => true,
'value' => $data['manual_swatches']
)
)
]
];
return ['field_options_colorpicker' => [
'label' => 'field_options',
'group' => 'colorpicker',
'settings' => $settings
]];
}
/**
* Validate the settings
*
* This is called before the settings are fully saved
*
* @param mixed settings data
* @return mixed validation result
*/
public function validate_settings($data)
{
return;
}
/**
* Saves the field's settings
*/
public function save_settings($data)
{
$value_colors = [];
// Get the values colors
if (isset($data['value_swatches']['rows'])) {
$data['value_swatches'] = $data['value_swatches']['rows'];
foreach ($data['value_swatches'] as $row) {
$colorName = isset($row['name']) && $row['name'] !== '' ? '|' . $row['name'] : '';
$value_colors[] = $row['color'] . $colorName;
}
}
$data['value_swatches'] = $value_colors;
// Merge the rest of the settings with their defaults
$all = array_merge($this->default_settings, $data);
return array_intersect_key($all, $this->default_settings);
}
private function getSwatches()
{
if ($this->get_setting('populate_swatches') == 'm') {
$manual_colors = [];
foreach (explode("\n", $this->get_setting('manual_swatches')) as $color) {
$manual_colors[] = trim($color);
}
return $manual_colors;
} else {
$swatches = $this->get_setting('value_swatches');
$collection = [];
foreach ($swatches as $swatch) {
if (strpos($swatch, '|') !== false) {
$parts = explode('|', $swatch);
$collection[] = $parts[0];
} else {
$collection[] = $swatch;
}
}
return $collection;
}
}
private function getSwatchesMiniGrid($data)
{
$grid = ee('CP/MiniGridInput', array('field_name' => 'value_swatches'));
$grid->loadAssets();
$grid->setColumns([
'colors' => [
'label' => '',
],
'name' => [
'label' => '',
],
]);
$grid->setNoResultsText(lang('no_colorpicker_swatches'), lang('add_new'));
$grid->setBlankRow([
['html' => $this->createColorPicker(['inputName' => 'color'], true)],
['html' => form_input('name', '', 'placeholder="Name"')]
]);
$grid->setData([]);
// Populate the grid with the currently saved swatches
if (isset($data['value_swatches'])) {
$pairs = [];
$i = 1;
foreach ($data['value_swatches'] as $color) {
if (strpos($color, '|') !== false) {
$parts = explode('|', $color);
$color = $parts[0];
$name = $parts[1];
}
$pairs[] = array(
'attrs' => array('row_id' => $i),
'columns' => array(
[
'html' => $this->createColorPicker([
'initialColor' => $color,
'inputName' => 'color'
])
],
[
'html' => form_input('name', $name ?? '')
]
)
);
$i++;
}
$grid->setData($pairs);
}
return $grid;
}
/**
* Accept all content types.
*
* @param string The name of the content type
* @return bool Accepts all content types
*/
public function accepts_content_type($name)
{
return true;
}
}
{exp:channel:entries entry_id="630"}
{title}
{color_picker} <-- Still works and displays just the hex color for backwards compatibility
{color_picker:name} <-- New variable name
{color_picker:contrast_color} <-- Still works and is backwards compatible
{/exp:channel:entries}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment