Skip to content

Commit 6ed4c50

Browse files
committed
feat: add color pallets
1 parent fae76cc commit 6ed4c50

6 files changed

Lines changed: 306 additions & 9 deletions

File tree

classes/Visualizer/Module/Admin.php

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ class Visualizer_Module_Admin extends Visualizer_Module {
3030

3131
const NAME = __CLASS__;
3232

33+
const OPTION_GLOBAL_SETTINGS = 'visualizer_global_settings';
34+
3335
/**
3436
* Library page suffix.
3537
*
@@ -48,6 +50,14 @@ class Visualizer_Module_Admin extends Visualizer_Module {
4850
*/
4951
private $_supportPage;
5052

53+
/**
54+
* Settings page suffix.
55+
*
56+
* @access private
57+
* @var string
58+
*/
59+
private $_settingsPage;
60+
5161
/**
5262
* Constructor.
5363
*
@@ -65,6 +75,8 @@ public function __construct( Visualizer_Plugin $plugin ) {
6575
$this->_addAction( 'admin_footer', 'renderTemplates' );
6676
$this->_addAction( 'admin_enqueue_scripts', 'enqueueLibraryScripts', null, 0 );
6777
$this->_addAction( 'admin_enqueue_scripts', 'enqueue_support_page' );
78+
$this->_addAction( 'admin_enqueue_scripts', 'enqueueSettingsScripts' );
79+
$this->_addAction( 'admin_post_visualizer_save_global_settings', 'saveGlobalSettings' );
6880
$this->_addAction( 'admin_menu', 'registerAdminMenu' );
6981
$this->_addFilter( 'media_view_strings', 'setupMediaViewStrings' );
7082
$this->_addFilter( 'plugin_action_links', 'getPluginActionLinks', 10, 2 );
@@ -835,6 +847,15 @@ public function registerAdminMenu() {
835847
'admin.php?page=' . Visualizer_Plugin::NAME . '&vaction=addnew'
836848
);
837849

850+
$this->_settingsPage = add_submenu_page(
851+
Visualizer_Plugin::NAME,
852+
__( 'Settings', 'visualizer' ),
853+
__( 'Settings', 'visualizer' ),
854+
'manage_options',
855+
'viz-settings',
856+
array( $this, 'renderSettingsPage' )
857+
);
858+
838859
$this->_supportPage = add_submenu_page(
839860
Visualizer_Plugin::NAME,
840861
__( 'Support', 'visualizer' ),
@@ -1058,6 +1079,86 @@ public function renderSupportPage() {
10581079
include_once VISUALIZER_ABSPATH . '/templates/support.php';
10591080
}
10601081

1082+
/**
1083+
* Enqueues scripts/styles for the Settings page.
1084+
*
1085+
* @access public
1086+
*/
1087+
public function enqueueSettingsScripts( string $hook_suffix ): void {
1088+
if ( $this->_settingsPage !== $hook_suffix ) {
1089+
return;
1090+
}
1091+
wp_enqueue_style( 'wp-color-picker' );
1092+
wp_enqueue_script( 'wp-color-picker' );
1093+
}
1094+
1095+
/**
1096+
* Renders the global style settings page.
1097+
*
1098+
* @access public
1099+
*/
1100+
public function renderSettingsPage(): void {
1101+
if ( ! current_user_can( 'manage_options' ) ) {
1102+
wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'visualizer' ) );
1103+
}
1104+
$settings = self::getGlobalSettings();
1105+
include_once VISUALIZER_ABSPATH . '/templates/global-settings.php';
1106+
}
1107+
1108+
/**
1109+
* Returns the global style settings option.
1110+
*
1111+
* @return array<string, string>
1112+
* @access public
1113+
* @static
1114+
*/
1115+
public static function getGlobalSettings(): array {
1116+
$defaults = array(
1117+
'color_primary' => '',
1118+
'color_secondary' => '',
1119+
);
1120+
$saved = get_option( self::OPTION_GLOBAL_SETTINGS, array() );
1121+
return wp_parse_args( $saved, $defaults );
1122+
}
1123+
1124+
/**
1125+
* Handles saving of the global style settings.
1126+
*
1127+
* @access public
1128+
*/
1129+
public function saveGlobalSettings(): void {
1130+
if ( ! current_user_can( 'manage_options' ) ) {
1131+
wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'visualizer' ) );
1132+
}
1133+
1134+
check_admin_referer( 'visualizer_save_global_settings' );
1135+
1136+
$color_primary = sanitize_hex_color( wp_unslash( $_POST['visualizer_color_primary'] ?? '' ) );
1137+
$color_secondary = sanitize_hex_color( wp_unslash( $_POST['visualizer_color_secondary'] ?? '' ) );
1138+
$clear = ! empty( $_POST['visualizer_clear_settings'] );
1139+
1140+
if ( $clear ) {
1141+
delete_option( self::OPTION_GLOBAL_SETTINGS );
1142+
} else {
1143+
$settings = array(
1144+
'color_primary' => $color_primary ? $color_primary : '',
1145+
'color_secondary' => $color_secondary ? $color_secondary : '',
1146+
);
1147+
update_option( self::OPTION_GLOBAL_SETTINGS, $settings );
1148+
}
1149+
1150+
wp_safe_redirect(
1151+
add_query_arg(
1152+
array(
1153+
'page' => 'viz-settings',
1154+
'updated' => $clear ? 'cleared' : 'true',
1155+
),
1156+
admin_url( 'admin.php' )
1157+
)
1158+
);
1159+
exit;
1160+
}
1161+
10611162
/**
10621163
* Renders visualizer library page.
10631164
*

classes/Visualizer/Module/Chart.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -804,7 +804,12 @@ private function _handleDataAndSettingsPage() {
804804
}
805805
// save meta data only when it is NOT being canceled.
806806
if ( ! $is_canceled ) {
807-
update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $_POST );
807+
$post_settings = $_POST;
808+
$existing = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
809+
if ( isset( $existing['colors'] ) && is_array( $existing['colors'] ) && ! isset( $post_settings['colors'] ) ) {
810+
$post_settings['colors'] = $existing['colors'];
811+
}
812+
update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $post_settings );
808813

809814
// we will keep a parameter called 'internal_title' that will be set to the given title or, if empty, the chart ID
810815
// this will help in searching with the chart id.

classes/Visualizer/Module/Utility.php

Lines changed: 82 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class Visualizer_Module_Utility extends Visualizer_Module {
3636
* @since 3.3.0
3737
*
3838
* @access private
39-
* @var _CHART_COLORS
39+
* @var string[]
4040
*/
4141
private static $_CHART_COLORS = array(
4242
'#e6194b', '#3cb44b', '#ffe119', '#4363d8', '#f58231', '#911eb4', '#46f0f0', '#f032e6', '#bcf60c', '#fabebe', '#008080', '#e6beff', '#9a6324', '#fffac8', '#800000', '#aaffc3', '#808000', '#ffd8b1', '#000075', '#808080',
@@ -106,17 +106,83 @@ private static function hex2rgba( $color, $opacity = false ) {
106106
}
107107

108108
/**
109-
* Gets a random color from the array of chart colors and returns it as well as its transparent equivalent.
110-
*
111-
* @since 3.3.0
109+
* Returns a color at a specific index from the palette, with its transparent equivalent.
112110
*
111+
* @return array{string, string}
113112
* @access private
114113
*/
115-
private static function get_random_color() {
116-
$color = self::$_CHART_COLORS[ rand( 0, count( self::$_CHART_COLORS ) - 1 ) ];
114+
private static function get_color_at( int $index ): array {
115+
$colors = self::get_color_palette();
116+
$color = $colors[ $index % count( $colors ) ];
117117
return array( self::hex2rgba( $color, 0.5 ), $color );
118118
}
119119

120+
/**
121+
* Mixes a hex color toward white by the given factor (0 = original, 1 = white).
122+
*
123+
* @access private
124+
*/
125+
private static function tint_color( string $hex, float $factor ): string {
126+
$hex = ltrim( $hex, '#' );
127+
if ( strlen( $hex ) === 3 ) {
128+
$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
129+
}
130+
$r = (int) round( hexdec( substr( $hex, 0, 2 ) ) + ( 255 - hexdec( substr( $hex, 0, 2 ) ) ) * $factor );
131+
$g = (int) round( hexdec( substr( $hex, 2, 2 ) ) + ( 255 - hexdec( substr( $hex, 2, 2 ) ) ) * $factor );
132+
$b = (int) round( hexdec( substr( $hex, 4, 2 ) ) + ( 255 - hexdec( substr( $hex, 4, 2 ) ) ) * $factor );
133+
return sprintf( '#%02x%02x%02x', $r, $g, $b );
134+
}
135+
136+
/**
137+
* Returns the color palette to use for charts.
138+
*
139+
* When global primary/secondary colors are configured, generates a palette of
140+
* alternating tints: [primary, secondary, primary@25%, secondary@25%, ...].
141+
* Falls back to the built-in colors otherwise.
142+
*
143+
* @return string[]
144+
* @access private
145+
*/
146+
private static function get_color_palette(): array {
147+
$global = self::get_global_style_defaults();
148+
$primary = $global['color_primary'];
149+
$secondary = $global['color_secondary'];
150+
151+
if ( empty( $primary ) && empty( $secondary ) ) {
152+
return self::$_CHART_COLORS;
153+
}
154+
155+
$bases = array_filter( array( $primary, $secondary ) );
156+
$factors = array( 0, 0.25, 0.5, 0.75 );
157+
$palette = array();
158+
159+
foreach ( $factors as $factor ) {
160+
foreach ( $bases as $base ) {
161+
$palette[] = $factor === 0 ? $base : self::tint_color( $base, $factor );
162+
}
163+
}
164+
165+
return $palette;
166+
}
167+
168+
/**
169+
* Returns the global style defaults stored in the plugin settings.
170+
*
171+
* @return array<string, string>
172+
* @access public
173+
* @static
174+
*/
175+
public static function get_global_style_defaults(): array {
176+
$option = get_option( Visualizer_Module_Admin::OPTION_GLOBAL_SETTINGS, array() );
177+
return wp_parse_args(
178+
$option,
179+
array(
180+
'color_primary' => '',
181+
'color_secondary' => '',
182+
)
183+
);
184+
}
185+
120186
/**
121187
* Sets some defaults in the chart.
122188
*
@@ -178,6 +244,14 @@ private static function set_defaults_google( $chart, $post_status ) {
178244
$attributes['candlestick']['risingColor']['fill'] = '#3366cc';
179245
break;
180246
}
247+
248+
// Apply global color defaults to new Google Charts (not Geo — it uses colorAxis).
249+
if ( 'geo' !== $type ) {
250+
$global = self::get_global_style_defaults();
251+
if ( ! empty( $global['color_primary'] ) || ! empty( $global['color_secondary'] ) ) {
252+
$attributes['colors'] = self::get_color_palette();
253+
}
254+
}
181255
}
182256

183257
if ( $attributes ) {
@@ -218,7 +292,7 @@ private static function set_defaults_chartjs( $chart, $post_status ) {
218292
// fall through.
219293
case 'bar':
220294
for ( $i = 0; $i < $max; $i++ ) {
221-
$colors = self::get_random_color();
295+
$colors = self::get_color_at( $i );
222296
$attributes[] = array( 'backgroundColor' => $colors[0], 'hoverBackgroundColor' => $colors[1] );
223297
}
224298
break;
@@ -228,7 +302,7 @@ private static function set_defaults_chartjs( $chart, $post_status ) {
228302
// fall through.
229303
case 'area':
230304
for ( $i = 0; $i < $max; $i++ ) {
231-
$colors = self::get_random_color();
305+
$colors = self::get_color_at( $i );
232306
$attributes[] = array( 'borderColor' => $colors[0] );
233307
}
234308
break;

classes/Visualizer/Render/Sidebar/Google.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,4 +453,22 @@ protected function _renderViewSettings() {
453453
self::_renderSectionEnd();
454454
self::_renderGroupEnd();
455455
}
456+
457+
/**
458+
* Renders advanced settings and hidden inputs for programmatically-set keys
459+
* (e.g. the global-preset colors palette) that have no visible sidebar control.
460+
*
461+
* @access protected
462+
*/
463+
protected function _renderAdvancedSettings(): void {
464+
parent::_renderAdvancedSettings();
465+
466+
// @phpstan-ignore-next-line (property accessed via magic __get from _data array)
467+
$colors = $this->colors;
468+
if ( ! empty( $colors ) && is_array( $colors ) ) {
469+
foreach ( $colors as $color ) {
470+
echo '<input type="hidden" name="colors[]" value="', esc_attr( $color ), '">';
471+
}
472+
}
473+
}
456474
}

classes/Visualizer/Render/Sidebar/Type/GoogleCharts/Geo.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,4 +465,14 @@ protected function _renderViewSettings() {
465465
self::_renderSectionEnd();
466466
self::_renderGroupEnd();
467467
}
468+
469+
/**
470+
* Geo charts use colorAxis for their colour scheme, not the global preset palette.
471+
* Skip the hidden colors[] inputs that the Google base class would otherwise inject.
472+
*
473+
* @access protected
474+
*/
475+
protected function _renderAdvancedSettings(): void {
476+
Visualizer_Render_Sidebar::_renderAdvancedSettings();
477+
}
468478
}

0 commit comments

Comments
 (0)