Skip to content

Commit 382ee14

Browse files
authored
Merge pull request #277 from moderntribe/feature/dynamic-color-picker
[FEATURE] Dynamic Color Picker Component
2 parents 31e90e0 + c7e9b90 commit 382ee14

File tree

6 files changed

+135
-296
lines changed

6 files changed

+135
-296
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { useSettings } from '@wordpress/block-editor';
2+
import { BaseControl, ColorPalette } from '@wordpress/components';
3+
import { __ } from '@wordpress/i18n';
4+
5+
export default function DynamicColorPicker( {
6+
colorAttribute,
7+
colorValue,
8+
onChange,
9+
controlLabel = __( 'Select Color', 'tribe' ),
10+
useOpacity = false,
11+
showTransparentOption = true,
12+
colorsToUse = [],
13+
} ) {
14+
/**
15+
* useSettings returns an array where each item is the return value of the
16+
* setting requested. In this case, we are requesting only one setting,
17+
* 'color.palette', so accessing the first item of the array gives us our
18+
* colors defined in theme.json
19+
*/
20+
const themeColors = useSettings( 'color.palette' );
21+
22+
/**
23+
* @function getThemeColors
24+
*
25+
* @description Retrieves the theme colors and formats them for use in the ColorPalette component.
26+
*/
27+
const getThemeColors = () => {
28+
if ( ! themeColors || ! Array.isArray( themeColors[ 0 ] ) ) {
29+
return [];
30+
}
31+
32+
const colors = themeColors[ 0 ].map( ( { name, color } ) => ( {
33+
name,
34+
color,
35+
} ) );
36+
37+
// If the showTransparentOption flag is true, add a 'Transparent' option
38+
if ( showTransparentOption ) {
39+
colors.push( {
40+
name: __( 'Transparent', 'tribe' ),
41+
color: 'transparent',
42+
} );
43+
}
44+
45+
return colors;
46+
};
47+
48+
return (
49+
<BaseControl __nextHasNoMarginBottom>
50+
<BaseControl.VisualLabel>{ controlLabel }</BaseControl.VisualLabel>
51+
<ColorPalette
52+
__experimentalIsRenderedInSidebar={ true }
53+
colors={
54+
colorsToUse.length > 0 ? colorsToUse : getThemeColors()
55+
}
56+
disableCustomColors={ false }
57+
enableAlpha={ useOpacity }
58+
value={ colorValue }
59+
clearable={ false }
60+
onChange={ ( value ) =>
61+
onChange( {
62+
[ colorAttribute ]: value,
63+
} )
64+
}
65+
/>
66+
</BaseControl>
67+
);
68+
}

wp-content/themes/core/blocks/tribe/icon-picker/IconPicker.js renamed to wp-content/themes/core/assets/js/components/IconPicker.js

Lines changed: 35 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import {
66
TextControl,
77
ToggleControl,
88
} from '@wordpress/components';
9-
import { useSettings } from '@wordpress/block-editor';
10-
import { formatIconName } from './utils';
11-
import { ICONS_LIST } from './icons/icons-list';
9+
import DynamicColorPicker from 'components/DynamicColorPicker';
10+
import { formatIconName } from 'blocks/tribe/icon-picker/utils';
11+
import { ICONS_LIST } from 'blocks/tribe/icon-picker/icons/icons-list';
1212

1313
export default function IconPicker( {
1414
selectedIcon,
@@ -29,44 +29,22 @@ export default function IconPicker( {
2929
const [ filteredIcons, setFilteredIcons ] = useState( sortedIcons );
3030

3131
/**
32-
* Option 1: Use theme.json color palette
33-
* useSettings('color.palette') returns an array of arrays but should only return one array of objects, so we can just use the first one.
32+
* By default, the Icon Picker will use the color palette defined in
33+
* theme.json. By defining a custom array of colors and passing it to the
34+
* DynamicColorPicker components, we can give a custom set of
35+
* colors to the editor.
3436
*/
35-
const themeColors = useSettings( 'color.palette' );
36-
const COLORS =
37-
themeColors && Array.isArray( themeColors[ 0 ] )
38-
? [
39-
...themeColors[ 0 ].map( ( { name, color } ) => ( {
40-
name,
41-
value: color,
42-
} ) ),
43-
{
44-
name: __( 'Transparent', 'tribe' ),
45-
value: 'transparent',
46-
},
47-
]
48-
: [];
49-
50-
// Option 2: Use custom colors
5137
// const COLORS = [
52-
// { name: __( 'Blue', 'tribe' ), value: '#0078d4' },
53-
// { name: __( 'Purple', 'tribe' ), value: '#8661c5' },
54-
// { name: __( 'Gray', 'tribe' ), value: '#737373' },
55-
// { name: __( 'Light Gray', 'tribe' ), value: '#d2d2d2' },
56-
// { name: __( 'Dark Gray', 'tribe' ), value: '#505050' },
57-
// { name: __( 'Teal', 'tribe' ), value: '#008575' },
58-
// { name: __( 'White', 'tribe' ), value: '#ffffff' },
59-
// { name: __( 'Transparent', 'tribe' ), value: 'transparent' },
38+
// { name: __( 'Blue', 'tribe' ), color: '#0078d4' },
39+
// { name: __( 'Purple', 'tribe' ), color: '#8661c5' },
40+
// { name: __( 'Gray', 'tribe' ), color: '#737373' },
41+
// { name: __( 'Light Gray', 'tribe' ), color: '#d2d2d2' },
42+
// { name: __( 'Dark Gray', 'tribe' ), color: '#505050' },
43+
// { name: __( 'Teal', 'tribe' ), color: '#008575' },
44+
// { name: __( 'White', 'tribe' ), color: '#ffffff' },
45+
// { name: __( 'Transparent', 'tribe' ), color: 'transparent' },
6046
// ];
6147

62-
// Warn developer if themeColors is not properly set
63-
if ( COLORS.length === 0 ) {
64-
console.error(
65-
'[tribe/icon-picker] No colors found. ' +
66-
'Ensure your theme defines `settings.color.palette` in theme.json, or switch to a custom color list.'
67-
);
68-
}
69-
7048
useEffect( () => {
7149
setFilteredIcons(
7250
sortedIcons.filter( ( { key } ) =>
@@ -161,96 +139,26 @@ export default function IconPicker( {
161139
if ( tab.name === 'colors' ) {
162140
return (
163141
<>
164-
<h4 style={ { margin: '0 0 6px' } }>
165-
{ __( 'Icon color', 'tribe' ) }
166-
</h4>
167-
<div className="color-picker">
168-
<div className="color-grid">
169-
{ COLORS.filter(
170-
( c ) => c.value !== 'transparent'
171-
).map( ( colorObj ) => (
172-
<div
173-
key={ colorObj.value }
174-
role="button"
175-
title={ colorObj.name }
176-
tabIndex={ 0 }
177-
className={ `color-item ${
178-
selectedIconColor ===
179-
colorObj.value
180-
? 'selected'
181-
: ''
182-
}` }
183-
style={ {
184-
backgroundColor: colorObj.value,
185-
} }
186-
onClick={ () =>
187-
onChange( {
188-
selectedIconColor:
189-
colorObj.value,
190-
} )
191-
}
192-
onKeyDown={ ( e ) => {
193-
if (
194-
e.key === 'Enter' ||
195-
e.key === ' '
196-
) {
197-
onChange( {
198-
selectedIconColor:
199-
colorObj.value,
200-
} );
201-
}
202-
} }
203-
data-color={ colorObj.name
204-
.toLowerCase()
205-
.replace( / /g, '-' ) }
206-
></div>
207-
) ) }
208-
</div>
209-
</div>
210-
<h4 style={ { margin: '20px 0 6px' } }>
211-
{ __( 'Background Color', 'tribe' ) }
212-
</h4>
213-
<div className="color-picker">
214-
<div className="color-grid">
215-
{ COLORS.map( ( colorObj ) => (
216-
<div
217-
key={ colorObj.value }
218-
role="button"
219-
title={ colorObj.name }
220-
tabIndex={ 0 }
221-
className={ `color-item ${
222-
selectedBgColor ===
223-
colorObj.value
224-
? 'selected'
225-
: ''
226-
}` }
227-
style={ {
228-
backgroundColor: colorObj.value,
229-
} }
230-
onClick={ () =>
231-
onChange( {
232-
selectedBgColor:
233-
colorObj.value,
234-
} )
235-
}
236-
onKeyDown={ ( e ) => {
237-
if (
238-
e.key === 'Enter' ||
239-
e.key === ' '
240-
) {
241-
onChange( {
242-
selectedBgColor:
243-
colorObj.value,
244-
} );
245-
}
246-
} }
247-
data-color={ colorObj.name
248-
.toLowerCase()
249-
.replace( / /g, '-' ) }
250-
></div>
251-
) ) }
252-
</div>
253-
</div>
142+
<DynamicColorPicker
143+
controlLabel={ __( 'Icon Color', 'tribe' ) }
144+
colorAttribute={ 'selectedIconColor' }
145+
colorValue={ selectedIconColor }
146+
showTransparentOption={ false }
147+
onChange={ ( changed ) =>
148+
onChange( { ...changed } )
149+
}
150+
/>
151+
<DynamicColorPicker
152+
controlLabel={ __(
153+
'Background Color',
154+
'tribe'
155+
) }
156+
colorAttribute={ 'selectedBgColor' }
157+
colorValue={ selectedBgColor }
158+
onChange={ ( changed ) =>
159+
onChange( { ...changed } )
160+
}
161+
/>
254162
</>
255163
);
256164
}

wp-content/themes/core/blocks/tribe/icon-card/edit.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
} from '@wordpress/components';
1919
import ServerSideRender from '@wordpress/server-side-render';
2020
import { useMemo, useState } from '@wordpress/element';
21-
import IconPicker from 'blocks/tribe/icon-picker/IconPicker';
21+
import IconPicker from 'components/IconPicker';
2222
import { formatIconName } from 'blocks/tribe/icon-picker/utils';
2323
import { ICONS_LIST } from 'blocks/tribe/icon-picker/icons/icons-list';
2424

wp-content/themes/core/blocks/tribe/icon-picker/edit.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { __ } from '@wordpress/i18n';
22
import { PanelRow } from '@wordpress/components';
33
import { InspectorControls, useBlockProps } from '@wordpress/block-editor';
44
import { formatIconName } from './utils';
5-
import IconPicker from './IconPicker';
5+
import IconPicker from 'components/IconPicker';
66
import { ICONS_LIST } from './icons/icons-list';
77

88
import './editor.pcss';

wp-content/themes/core/blocks/tribe/icon-picker/editor.pcss

Lines changed: 5 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
margin-bottom: 20px;
1515
margin-left: -15px;
1616
margin-right: -15px;
17-
width: calc(100% + 30px);
17+
width: calc( 100% + 30px );
1818
justify-content: center;
1919
gap: 8px;
2020
border-bottom: 3px solid #e2e2e2;
@@ -26,21 +26,15 @@
2626

2727
&:active,
2828
&:focus-visible {
29-
color: var(
30-
--wp-components-color-accent,
31-
var(--color-blue)
32-
);
29+
color: var( --wp-components-color-accent, var( --color-blue ) );
3330
}
3431

3532
&.active-tab {
3633
border-color: var(
3734
--wp-components-color-accent,
38-
var(--color-blue)
39-
);
40-
color: var(
41-
--wp-components-color-accent,
42-
var(--color-blue)
35+
var( --color-blue )
4336
);
37+
color: var( --wp-components-color-accent, var( --color-blue ) );
4438
}
4539
}
4640

@@ -86,7 +80,7 @@
8680

8781
.icon-grid {
8882
display: grid;
89-
grid-template-columns: repeat(auto-fit, minmax(41px, 1fr));
83+
grid-template-columns: repeat( auto-fit, minmax( 41px, 1fr ) );
9084
gap: 6px;
9185
margin-bottom: 10px;
9286
padding: 10px;
@@ -123,82 +117,4 @@
123117
display: block;
124118
}
125119
}
126-
127-
/* Color picker */
128-
.color-picker {
129-
padding: 6px;
130-
background: #fff;
131-
border: 1px solid #ddd;
132-
}
133-
134-
.color-grid {
135-
display: grid;
136-
grid-template-columns: repeat(4, 1fr);
137-
gap: 5px;
138-
padding: 5px;
139-
}
140-
141-
.color-item {
142-
width: 100%;
143-
height: auto;
144-
aspect-ratio: 1/1;
145-
border-radius: 3px;
146-
cursor: pointer;
147-
text-align: center;
148-
display: flex;
149-
align-items: center;
150-
justify-content: center;
151-
position: relative;
152-
153-
/* Color gradient to represent transparent background */
154-
--transparent: rgba(226, 226, 226, 0.5);
155-
156-
&[data-color="transparent"] {
157-
background: linear-gradient(
158-
45deg,
159-
var(--transparent) 25%,
160-
transparent 25%,
161-
transparent 50%,
162-
var(--transparent) 50%,
163-
var(--transparent) 75%,
164-
transparent 75%,
165-
transparent
166-
);
167-
background-size: 20px 20px;
168-
}
169-
170-
&[data-color="white"],
171-
&[data-color="transparent"] {
172-
border: 1px solid #e4e4e4;
173-
}
174-
}
175-
176-
.color-item.selected {
177-
178-
&::before {
179-
content: "";
180-
width: 25px;
181-
height: 25px;
182-
position: absolute;
183-
display: block;
184-
right: 5px;
185-
top: 5px;
186-
border-radius: 50%;
187-
background-color: rgba(0, 0, 0, 0.4);
188-
}
189-
190-
&::after {
191-
content: "\F147";
192-
font-family: dashicons, sans-serif;
193-
width: 25px;
194-
height: 25px;
195-
position: absolute;
196-
display: block;
197-
right: 6px;
198-
top: 6px;
199-
font-size: 20px;
200-
line-height: 25px;
201-
color: #fff;
202-
}
203-
}
204120
}

0 commit comments

Comments
 (0)