Skip to content

Commit

Permalink
Merge pull request #14944 from colemanw/color
Browse files Browse the repository at this point in the history
Better support for hookable menubar colors
  • Loading branch information
eileenmcnaughton authored Aug 2, 2019
2 parents 09dd2e5 + 0f4485f commit 38d6407
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 45 deletions.
38 changes: 18 additions & 20 deletions CRM/Core/Resources.php
Original file line number Diff line number Diff line change
Expand Up @@ -789,12 +789,13 @@ public function coreResourceList($region) {
$items[] = 'bower_components/smartmenus/dist/jquery.smartmenus.min.js';
$items[] = 'bower_components/smartmenus/dist/addons/keyboard/jquery.smartmenus.keyboard.min.js';
$items[] = 'js/crm.menubar.js';
// @see CRM_Core_Resources::renderMenubarStylesheet
$items[] = Civi::service('asset_builder')->getUrl('crm-menubar.css', [
'color' => Civi::settings()->get('menubar_color'),
'menubarColor' => Civi::settings()->get('menubar_color'),
'height' => 40,
'breakpoint' => 768,
'opacity' => .88,
]);
// Variables for crm.menubar.js
$items[] = [
'menubar' => [
'position' => $position,
Expand Down Expand Up @@ -861,7 +862,7 @@ public static function renderMenubarStylesheet(GenericHookEvent $e) {
return;
}
$e->mimeType = 'text/css';
$e->content = '';
$content = '';
$config = CRM_Core_Config::singleton();
$cms = strtolower($config->userFramework);
$cms = $cms === 'drupal' ? 'drupal7' : $cms;
Expand All @@ -871,26 +872,23 @@ public static function renderMenubarStylesheet(GenericHookEvent $e) {
"css/menubar-$cms.css",
];
foreach ($items as $item) {
$e->content .= file_get_contents(self::singleton()->getPath('civicrm', $item));
}
$color = $e->params['color'];
if (!CRM_Utils_Rule::color($color)) {
$color = Civi::settings()->getDefault('menubar_color');
$content .= file_get_contents(self::singleton()->getPath('civicrm', $item));
}
$params = $e->params;
// "color" is deprecated in favor of the more specific "menubarColor"
$menubarColor = $params['color'] ?? $params['menubarColor'];
$vars = [
'resourceBase' => rtrim($config->resourceBase, '/'),
'menubarHeight' => $e->params['height'] . 'px',
'breakMin' => $e->params['breakpoint'] . 'px',
'breakMax' => ($e->params['breakpoint'] - 1) . 'px',
'menubarColor' => $color,
'menuItemColor' => 'rgba(' . implode(', ', CRM_Utils_Color::getRgb($color)) . ", {$e->params['opacity']})",
'highlightColor' => CRM_Utils_Color::getHighlight($color),
'textColor' => CRM_Utils_Color::getContrast($color, '#333', '#ddd'),
'$resourceBase' => rtrim($config->resourceBase, '/'),
'$menubarHeight' => $params['height'] . 'px',
'$breakMin' => $params['breakpoint'] . 'px',
'$breakMax' => ($params['breakpoint'] - 1) . 'px',
'$menubarColor' => $menubarColor,
'$menuItemColor' => $params['menuItemColor'] ?? 'rgba(' . implode(', ', CRM_Utils_Color::getRgb($menubarColor)) . ", .9)",
'$highlightColor' => $params['highlightColor'] ?? CRM_Utils_Color::getHighlight($menubarColor),
'$textColor' => $params['textColor'] ?? CRM_Utils_Color::getContrast($menubarColor, '#333', '#ddd'),
];
$vars['highlightTextColor'] = CRM_Utils_Color::getContrast($vars['highlightColor'], '#333', '#ddd');
foreach ($vars as $var => $val) {
$e->content = str_replace('$' . $var, $val, $e->content);
}
$vars['$highlightTextColor'] = $params['highlightTextColor'] ?? CRM_Utils_Color::getContrast($vars['$highlightColor'], '#333', '#ddd');
$e->content = str_replace(array_keys($vars), array_values($vars), $content);
}

/**
Expand Down
95 changes: 78 additions & 17 deletions CRM/Utils/Color.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,49 +36,65 @@
*/
class CRM_Utils_Color {

const COLOR_FILE = '[civicrm.root]/bower_components/css-color-names/css-color-names.json';

/**
* Determine the appropriate text color for a given background.
*
* Based on YIQ value.
*
* @param string $hexcolor
* @param string $color
* @param string $black
* @param string $white
* @return string
*/
public static function getContrast($hexcolor, $black = 'black', $white = 'white') {
list($r, $g, $b) = self::getRgb($hexcolor);
public static function getContrast($color, $black = 'black', $white = 'white') {
list($r, $g, $b) = self::getRgb($color);
$yiq = (($r * 299) + ($g * 587) + ($b * 114)) / 1000;
return ($yiq >= 128) ? $black : $white;
}

/**
* Convert hex color to decimal
* Parse any color string into rgb decimal values
*
* Accepted formats:
* Full hex: "#ffffff"
* Short hex: "#fff"
* Color name "white"
* RGB notation: "rgb(255, 255, 255)"
*
* @param string $hexcolor
* @return array
* @param string $color
* @return int[]|null
* [red, green, blue]
*/
public static function getRgb($hexcolor) {
$hexcolor = trim($hexcolor, ' #');
if (strlen($hexcolor) === 3) {
$hexcolor = $hexcolor[0] . $hexcolor[0] . $hexcolor[1] . $hexcolor[1] . $hexcolor[2] . $hexcolor[2];
public static function getRgb($color) {
$color = str_replace(' ', '', $color);
$color = self::nameToHex($color) ?? $color;
if (strpos($color, 'rgb(') === 0) {
return explode(',', substr($color, 4, strpos($color, ')') - 4));
}
$color = ltrim($color, '#');
if (strlen($color) === 3) {
$color = $color[0] . $color[0] . $color[1] . $color[1] . $color[2] . $color[2];
}
if (!CRM_Utils_Rule::color('#' . $color)) {
return NULL;
}
return [
hexdec(substr($hexcolor, 0, 2)),
hexdec(substr($hexcolor, 2, 2)),
hexdec(substr($hexcolor, 4, 2)),
hexdec(substr($color, 0, 2)),
hexdec(substr($color, 2, 2)),
hexdec(substr($color, 4, 2)),
];
}

/**
* Calculate a highlight color from a base color
*
* @param $hexcolor
* @param $color
* @return string
*/
public static function getHighlight($hexcolor) {
$rgb = CRM_Utils_Color::getRgb($hexcolor);
public static function getHighlight($color) {
$rgb = self::getRgb($color);
$avg = array_sum($rgb) / 3;
foreach ($rgb as &$v) {
if ($avg > 242) {
Expand All @@ -90,7 +106,52 @@ public static function getHighlight($hexcolor) {
$v = min(255, intval((-.0035 * ($v - 242) ** 2) + 260));
}
}
return '#' . implode(array_map('dechex', $rgb));
return self::rgbToHex($rgb);
}

/**
* Convert named color (e.g. springgreen) to hex
*
* @param $colorName
* @return string|null
*/
public static function nameToHex($colorName) {
if (strpos($colorName, '#') !== FALSE || strpos($colorName, '(') !== FALSE) {
return NULL;
}
if (empty(Civi::$statics[__CLASS__]['names'])) {
Civi::$statics[__CLASS__]['names'] = json_decode(file_get_contents(Civi::paths()->getPath(self::COLOR_FILE)), TRUE);
}
return Civi::$statics[__CLASS__]['names'][strtolower($colorName)] ?? NULL;
}

/**
* Converts rgb array to hex string
*
* @param int[] $rgb
* @return string
*/
public static function rgbToHex($rgb) {
$ret = '#';
foreach ($rgb as $dec) {
$ret .= str_pad(dechex($dec), 2, '0', STR_PAD_LEFT);
}
return $ret;
}

/**
* Validate color input and convert it to standard hex notation
*
* @param string $color
* @return bool
*/
public static function normalize(&$color) {
$rgb = self::getRgb($color);
if ($rgb) {
$color = self::rgbToHex($rgb);
return TRUE;
}
return FALSE;
}

}
3 changes: 2 additions & 1 deletion bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
"phantomjs-polyfill": "^0.0.2",
"es6-promise": "^4.2.4",
"angular-xeditable": "^0.9.0",
"checklist-model": "~1"
"checklist-model": "~1",
"css-color-names": "~1"
},
"resolutions": {
"angular": "~1.5.11",
Expand Down
2 changes: 1 addition & 1 deletion settings/Core.setting.php
Original file line number Diff line number Diff line change
Expand Up @@ -1046,7 +1046,7 @@
'is_contact' => 0,
'description' => ts('Color of the CiviCRM main menu.'),
'help_text' => NULL,
'validate_callback' => 'CRM_Utils_Rule::color',
'validate_callback' => 'CRM_Utils_Color::normalize',
],
'requestableMimeTypes' => [
'group_name' => 'CiviCRM Preferences',
Expand Down
20 changes: 14 additions & 6 deletions tests/phpunit/CRM/Utils/ColorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,24 @@ public function contrastExamples() {
/**
* @dataProvider rgbExamples
*/
public function testGetRgb($hex, $rgb) {
$this->assertEquals($rgb, CRM_Utils_Color::getRgb($hex));
public function testGetRgb($color, $expectedRGB, $expectedHex) {
$rgb = CRM_Utils_Color::getRgb($color);
$this->assertEquals($expectedRGB, $rgb);
$this->assertEquals($expectedHex, CRM_Utils_Color::rgbToHex($rgb));
}

public function rgbExamples() {
return [
['#fff', [255, 255, 255]],
['#000000', [0, 0, 0]],
['#111', [17, 17, 17]],
[' fffc99 ', [255, 252, 153]],
['#fff', [255, 255, 255], '#ffffff'],
['white', [255, 255, 255], '#ffffff'],
['#000000', [0, 0, 0], '#000000'],
[' black', [0, 0, 0], '#000000'],
[' #111 ', [17, 17, 17], '#111111'],
[' fffc99 ', [255, 252, 153], '#fffc99'],
['blue', [0, 0, 255], '#0000ff'],
['Green', [0, 128, 0], '#008000'],
['rgb(12, 0, 123)', [12, 0, 123], '#0c007b'],
[' rgb ( 123, 0, 12 ) !important', [123, 0, 12], '#7b000c'],
];
}

Expand Down

0 comments on commit 38d6407

Please sign in to comment.