@wordpress/format-library 3.0.14 → 3.0.18-next.33ec3857e2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,6 +17,8 @@ import { removeFormat } from '@wordpress/rich-text';
17
17
  */
18
18
  import { default as InlineColorUI, getActiveColors } from './inline';
19
19
 
20
+ export const transparentValue = 'rgba(0, 0, 0, 0)';
21
+
20
22
  const name = 'core/text-color';
21
23
  const title = __( 'Highlight' );
22
24
 
@@ -30,7 +32,7 @@ function getComputedStyleProperty( element, property ) {
30
32
 
31
33
  if (
32
34
  property === 'background-color' &&
33
- value === 'rgba(0, 0, 0, 0)' &&
35
+ value === transparentValue &&
34
36
  element.parentElement
35
37
  ) {
36
38
  return getComputedStyleProperty( element.parentElement, property );
@@ -47,7 +49,7 @@ function fillComputedColors( element, { color, backgroundColor } ) {
47
49
  return {
48
50
  color: color || getComputedStyleProperty( element, 'color' ),
49
51
  backgroundColor:
50
- backgroundColor === 'rgba(0, 0, 0, 0)'
52
+ backgroundColor === transparentValue
51
53
  ? getComputedStyleProperty( element, 'background-color' )
52
54
  : backgroundColor,
53
55
  };
@@ -139,7 +141,7 @@ export const textColor = {
139
141
  if ( key !== 'style' ) return value;
140
142
  // We should not add a background-color if it's already set
141
143
  if ( value && value.includes( 'background-color' ) ) return value;
142
- const addedCSS = [ 'background-color', 'rgba(0, 0, 0, 0)' ].join( ':' );
144
+ const addedCSS = [ 'background-color', transparentValue ].join( ':' );
143
145
  // Prepend `addedCSS` to avoid a double `;;` as any the existing CSS
144
146
  // rules will already include a `;`.
145
147
  return value ? [ addedCSS, value ].join( ';' ) : addedCSS;
@@ -0,0 +1,191 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { isEmpty } from 'lodash';
5
+ import { View } from 'react-native';
6
+
7
+ /**
8
+ * WordPress dependencies
9
+ */
10
+ import { __ } from '@wordpress/i18n';
11
+ import { useCallback, useMemo, useState } from '@wordpress/element';
12
+ import { BlockControls, useSetting } from '@wordpress/block-editor';
13
+ import { ToolbarGroup, ToolbarButton } from '@wordpress/components';
14
+ import { Icon, textColor as textColorIcon } from '@wordpress/icons';
15
+ import { removeFormat } from '@wordpress/rich-text';
16
+ import { usePreferredColorSchemeStyle } from '@wordpress/compose';
17
+
18
+ /**
19
+ * Internal dependencies
20
+ */
21
+ import { getActiveColors } from './inline.js';
22
+ import { transparentValue } from './index.js';
23
+ import { default as InlineColorUI } from './inline';
24
+ import styles from './style.scss';
25
+
26
+ const name = 'core/text-color';
27
+ const title = __( 'Text color' );
28
+
29
+ const EMPTY_ARRAY = [];
30
+
31
+ function getComputedStyleProperty( element, property ) {
32
+ const {
33
+ props: { style = {} },
34
+ } = element;
35
+
36
+ if ( property === 'background-color' ) {
37
+ const { backgroundColor, baseColors } = style;
38
+
39
+ if ( backgroundColor !== 'transparent' ) {
40
+ return backgroundColor;
41
+ } else if ( baseColors && baseColors?.color?.background ) {
42
+ return baseColors?.color?.background;
43
+ }
44
+
45
+ return 'transparent';
46
+ }
47
+ }
48
+
49
+ function fillComputedColors( element, { color, backgroundColor } ) {
50
+ if ( ! color && ! backgroundColor ) {
51
+ return;
52
+ }
53
+
54
+ return {
55
+ color: color || getComputedStyleProperty( element, 'color' ),
56
+ backgroundColor: getComputedStyleProperty(
57
+ element,
58
+ 'background-color'
59
+ ),
60
+ };
61
+ }
62
+
63
+ function TextColorEdit( {
64
+ value,
65
+ onChange,
66
+ isActive,
67
+ activeAttributes,
68
+ contentRef,
69
+ } ) {
70
+ const allowCustomControl = useSetting( 'color.custom' );
71
+ const colors = useSetting( 'color.palette' ) || EMPTY_ARRAY;
72
+ const [ isAddingColor, setIsAddingColor ] = useState( false );
73
+ const enableIsAddingColor = useCallback( () => setIsAddingColor( true ), [
74
+ setIsAddingColor,
75
+ ] );
76
+ const disableIsAddingColor = useCallback( () => setIsAddingColor( false ), [
77
+ setIsAddingColor,
78
+ ] );
79
+ const colorIndicatorStyle = useMemo(
80
+ () =>
81
+ fillComputedColors(
82
+ contentRef,
83
+ getActiveColors( value, name, colors )
84
+ ),
85
+ [ value, colors ]
86
+ );
87
+
88
+ const hasColorsToChoose = ! isEmpty( colors ) || ! allowCustomControl;
89
+
90
+ const onPressButton = useCallback( () => {
91
+ if ( hasColorsToChoose ) {
92
+ enableIsAddingColor();
93
+ } else {
94
+ onChange( removeFormat( value, name ) );
95
+ }
96
+ }, [ hasColorsToChoose, value ] );
97
+
98
+ const outlineStyle = usePreferredColorSchemeStyle(
99
+ styles[ 'components-inline-color__outline' ],
100
+ styles[ 'components-inline-color__outline--dark' ]
101
+ );
102
+
103
+ if ( ! hasColorsToChoose && ! isActive ) {
104
+ return null;
105
+ }
106
+
107
+ const isActiveStyle = {
108
+ ...colorIndicatorStyle,
109
+ ...( ! colorIndicatorStyle?.backgroundColor
110
+ ? { backgroundColor: 'transparent' }
111
+ : {} ),
112
+ ...styles[ 'components-inline-color--is-active' ],
113
+ };
114
+
115
+ const customContainerStyles =
116
+ styles[ 'components-inline-color__button-container' ];
117
+
118
+ return (
119
+ <>
120
+ <BlockControls>
121
+ <ToolbarGroup>
122
+ { isActive && (
123
+ <View style={ outlineStyle } pointerEvents="none" />
124
+ ) }
125
+
126
+ <ToolbarButton
127
+ name="text-color"
128
+ isActive={ isActive }
129
+ icon={
130
+ <Icon
131
+ icon={ textColorIcon }
132
+ style={
133
+ colorIndicatorStyle?.color && {
134
+ color: colorIndicatorStyle.color,
135
+ }
136
+ }
137
+ />
138
+ }
139
+ title={ title }
140
+ extraProps={ {
141
+ isActiveStyle,
142
+ customContainerStyles,
143
+ } }
144
+ // If has no colors to choose but a color is active remove the color onClick
145
+ onClick={ onPressButton }
146
+ />
147
+ </ToolbarGroup>
148
+ </BlockControls>
149
+ { isAddingColor && (
150
+ <InlineColorUI
151
+ name={ name }
152
+ onClose={ disableIsAddingColor }
153
+ activeAttributes={ activeAttributes }
154
+ value={ value }
155
+ onChange={ onChange }
156
+ contentRef={ contentRef }
157
+ />
158
+ ) }
159
+ </>
160
+ );
161
+ }
162
+
163
+ export const textColor = {
164
+ name,
165
+ title,
166
+ tagName: 'mark',
167
+ className: 'has-inline-color',
168
+ attributes: {
169
+ style: 'style',
170
+ class: 'class',
171
+ },
172
+ /*
173
+ * Since this format relies on the <mark> tag, it's important to
174
+ * prevent the default yellow background color applied by most
175
+ * browsers. The solution is to detect when this format is used with a
176
+ * text color but no background color, and in such cases to override
177
+ * the default styling with a transparent background.
178
+ *
179
+ * @see https://github.com/WordPress/gutenberg/pull/35516
180
+ */
181
+ __unstableFilterAttributeValue( key, value ) {
182
+ if ( key !== 'style' ) return value;
183
+ // We should not add a background-color if it's already set
184
+ if ( value && value.includes( 'background-color' ) ) return value;
185
+ const addedCSS = [ 'background-color', transparentValue ].join( ':' );
186
+ // Prepend `addedCSS` to avoid a double `;;` as any the existing CSS
187
+ // rules will already include a `;`.
188
+ return value ? [ addedCSS, value ].join( ';' ) : addedCSS;
189
+ },
190
+ edit: TextColorEdit,
191
+ };
@@ -28,25 +28,27 @@ import { __ } from '@wordpress/i18n';
28
28
  /**
29
29
  * Internal dependencies
30
30
  */
31
- import { textColor as settings } from './index';
31
+ import { textColor as settings, transparentValue } from './index';
32
32
 
33
33
  function parseCSS( css = '' ) {
34
34
  return css.split( ';' ).reduce( ( accumulator, rule ) => {
35
35
  if ( rule ) {
36
36
  const [ property, value ] = rule.split( ':' );
37
37
  if ( property === 'color' ) accumulator.color = value;
38
- if ( property === 'background-color' )
38
+ if ( property === 'background-color' && value !== transparentValue )
39
39
  accumulator.backgroundColor = value;
40
40
  }
41
41
  return accumulator;
42
42
  }, {} );
43
43
  }
44
44
 
45
- function parseClassName( className = '', colorSettings ) {
45
+ export function parseClassName( className = '', colorSettings ) {
46
46
  return className.split( ' ' ).reduce( ( accumulator, name ) => {
47
- const match = name.match( /^has-([^-]+)-color$/ );
48
- if ( match ) {
49
- const [ , colorSlug ] = name.match( /^has-([^-]+)-color$/ );
47
+ // `colorSlug` could contain dashes, so simply match the start and end.
48
+ if ( name.startsWith( 'has-' ) && name.endsWith( '-color' ) ) {
49
+ const colorSlug = name
50
+ .replace( /^has-/, '' )
51
+ .replace( /-color$/, '' );
50
52
  const colorObject = getColorObjectByAttributeValues(
51
53
  colorSettings,
52
54
  colorSlug
@@ -88,7 +90,7 @@ function setColors( value, name, colorSettings, colors ) {
88
90
  styles.push( [ 'background-color', backgroundColor ].join( ':' ) );
89
91
  } else {
90
92
  // Override default browser color for mark element.
91
- styles.push( [ 'background-color', 'rgba(0, 0, 0, 0)' ].join( ':' ) );
93
+ styles.push( [ 'background-color', transparentValue ].join( ':' ) );
92
94
  }
93
95
 
94
96
  if ( color ) {
@@ -0,0 +1,163 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useCallback, useMemo } from '@wordpress/element';
5
+ import {
6
+ applyFormat,
7
+ removeFormat,
8
+ getActiveFormat,
9
+ } from '@wordpress/rich-text';
10
+ import {
11
+ useSetting,
12
+ getColorClassName,
13
+ getColorObjectByColorValue,
14
+ } from '@wordpress/block-editor';
15
+ import { BottomSheet, ColorSettings } from '@wordpress/components';
16
+
17
+ /**
18
+ * Internal dependencies
19
+ */
20
+ import { textColor as settings } from './index';
21
+ import { transparentValue } from './index.js';
22
+ import { parseClassName } from './inline.js';
23
+
24
+ function parseCSS( css = '' ) {
25
+ return css.split( ';' ).reduce( ( accumulator, rule ) => {
26
+ if ( rule ) {
27
+ const [ property, value ] = rule.replace( / /g, '' ).split( ':' );
28
+ if ( property === 'color' ) accumulator.color = value;
29
+ if ( property === 'background-color' && value !== transparentValue )
30
+ accumulator.backgroundColor = value;
31
+ }
32
+ return accumulator;
33
+ }, {} );
34
+ }
35
+
36
+ function getActiveColors( value, name, colorSettings ) {
37
+ const activeColorFormat = getActiveFormat( value, name );
38
+
39
+ if ( ! activeColorFormat ) {
40
+ return {};
41
+ }
42
+
43
+ return {
44
+ ...parseCSS( activeColorFormat.attributes.style ),
45
+ ...parseClassName( activeColorFormat.attributes.class, colorSettings ),
46
+ };
47
+ }
48
+
49
+ function setColors( value, name, colorSettings, colors ) {
50
+ const { color, backgroundColor } = {
51
+ ...getActiveColors( value, name, colorSettings ),
52
+ ...colors,
53
+ };
54
+
55
+ if ( ! color && ! backgroundColor ) {
56
+ return removeFormat( value, name );
57
+ }
58
+
59
+ const styles = [];
60
+ const classNames = [];
61
+ const attributes = {};
62
+
63
+ if ( backgroundColor ) {
64
+ styles.push( [ 'background-color', backgroundColor ].join( ':' ) );
65
+ } else {
66
+ // Override default browser color for mark element.
67
+ styles.push( [ 'background-color', transparentValue ].join( ':' ) );
68
+ }
69
+
70
+ if ( color ) {
71
+ const colorObject = getColorObjectByColorValue( colorSettings, color );
72
+
73
+ if ( colorObject ) {
74
+ classNames.push( getColorClassName( 'color', colorObject.slug ) );
75
+ styles.push( [ 'color', colorObject.color ].join( ':' ) );
76
+ } else {
77
+ styles.push( [ 'color', color ].join( ':' ) );
78
+ }
79
+ }
80
+
81
+ if ( styles.length ) attributes.style = styles.join( ';' );
82
+ if ( classNames.length ) attributes.class = classNames.join( ' ' );
83
+
84
+ const format = { type: name, attributes };
85
+
86
+ // For cases when there is no text selected, formatting is forced
87
+ // for the first empty character
88
+ if (
89
+ value?.start === value?.end &&
90
+ ( value?.text.length === 0 ||
91
+ value.text?.charAt( value.end - 1 ) === ' ' )
92
+ ) {
93
+ return applyFormat( value, format, value?.start - 1, value?.end + 1 );
94
+ }
95
+
96
+ return applyFormat( value, format );
97
+ }
98
+
99
+ function ColorPicker( { name, value, onChange } ) {
100
+ const property = 'color';
101
+ const colors = useSetting( 'color.palette' ) || settings.colors;
102
+ const colorSettings = {
103
+ colors,
104
+ };
105
+
106
+ const onColorChange = useCallback(
107
+ ( color ) => {
108
+ if ( color !== '' ) {
109
+ onChange(
110
+ setColors( value, name, colors, { [ property ]: color } )
111
+ );
112
+ // Remove formatting if the color was reset, there's no
113
+ // current selection and the previous character is a space
114
+ } else if (
115
+ value?.start === value?.end &&
116
+ value.text?.charAt( value?.end - 1 ) === ' '
117
+ ) {
118
+ onChange(
119
+ removeFormat( value, name, value.end - 1, value.end )
120
+ );
121
+ } else {
122
+ onChange( removeFormat( value, name ) );
123
+ }
124
+ },
125
+ [ colors, onChange, property ]
126
+ );
127
+ const activeColors = useMemo(
128
+ () => getActiveColors( value, name, colors ),
129
+ [ name, value, colors ]
130
+ );
131
+
132
+ return (
133
+ <ColorSettings
134
+ colorValue={ activeColors[ property ] }
135
+ onColorChange={ onColorChange }
136
+ defaultSettings={ colorSettings }
137
+ hideNavigation
138
+ />
139
+ );
140
+ }
141
+
142
+ export default function InlineColorUI( { name, value, onChange, onClose } ) {
143
+ return (
144
+ <BottomSheet
145
+ isVisible
146
+ onClose={ onClose }
147
+ hideHeader
148
+ contentStyle={ { paddingLeft: 0, paddingRight: 0 } }
149
+ hasNavigation
150
+ leftButton={ null }
151
+ >
152
+ <BottomSheet.NavigationContainer animate main>
153
+ <BottomSheet.NavigationScreen name="text-color">
154
+ <ColorPicker
155
+ name={ name }
156
+ value={ value }
157
+ onChange={ onChange }
158
+ />
159
+ </BottomSheet.NavigationScreen>
160
+ </BottomSheet.NavigationContainer>
161
+ </BottomSheet>
162
+ );
163
+ }
@@ -0,0 +1,23 @@
1
+ .components-inline-color--is-active {
2
+ border-radius: 18px;
3
+ }
4
+
5
+ .components-inline-color__outline {
6
+ border-color: $light-dim;
7
+ top: 6px;
8
+ bottom: 6px;
9
+ left: 11px;
10
+ right: 11px;
11
+ border-radius: 24px;
12
+ border-width: $border-width;
13
+ position: absolute;
14
+ z-index: 2;
15
+ }
16
+
17
+ .components-inline-color__outline--dark {
18
+ border-color: $dark-ultra-dim;
19
+ }
20
+
21
+ .components-inline-color__button-container {
22
+ padding: 6px;
23
+ }