@wordpress/format-library 3.0.1 → 3.0.5

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.
Files changed (66) hide show
  1. package/README.md +1 -1
  2. package/build/bold/index.js +5 -2
  3. package/build/bold/index.js.map +1 -1
  4. package/build/code/index.js +4 -2
  5. package/build/code/index.js.map +1 -1
  6. package/build/italic/index.js +5 -2
  7. package/build/italic/index.js.map +1 -1
  8. package/build/keyboard/index.js +4 -2
  9. package/build/keyboard/index.js.map +1 -1
  10. package/build/link/inline.js +99 -5
  11. package/build/link/inline.js.map +1 -1
  12. package/build/link/utils.js +112 -0
  13. package/build/link/utils.js.map +1 -1
  14. package/build/strikethrough/index.js +4 -2
  15. package/build/strikethrough/index.js.map +1 -1
  16. package/build/subscript/index.js +4 -2
  17. package/build/subscript/index.js.map +1 -1
  18. package/build/superscript/index.js +4 -2
  19. package/build/superscript/index.js.map +1 -1
  20. package/build/text-color/index.js +60 -21
  21. package/build/text-color/index.js.map +1 -1
  22. package/build/text-color/inline.js +105 -37
  23. package/build/text-color/inline.js.map +1 -1
  24. package/build/underline/index.js +4 -2
  25. package/build/underline/index.js.map +1 -1
  26. package/build-module/bold/index.js +5 -2
  27. package/build-module/bold/index.js.map +1 -1
  28. package/build-module/code/index.js +4 -2
  29. package/build-module/code/index.js.map +1 -1
  30. package/build-module/italic/index.js +5 -2
  31. package/build-module/italic/index.js.map +1 -1
  32. package/build-module/keyboard/index.js +4 -2
  33. package/build-module/keyboard/index.js.map +1 -1
  34. package/build-module/link/inline.js +103 -10
  35. package/build-module/link/inline.js.map +1 -1
  36. package/build-module/link/utils.js +110 -1
  37. package/build-module/link/utils.js.map +1 -1
  38. package/build-module/strikethrough/index.js +4 -2
  39. package/build-module/strikethrough/index.js.map +1 -1
  40. package/build-module/subscript/index.js +4 -2
  41. package/build-module/subscript/index.js.map +1 -1
  42. package/build-module/superscript/index.js +4 -2
  43. package/build-module/superscript/index.js.map +1 -1
  44. package/build-module/text-color/index.js +61 -22
  45. package/build-module/text-color/index.js.map +1 -1
  46. package/build-module/text-color/inline.js +104 -37
  47. package/build-module/text-color/inline.js.map +1 -1
  48. package/build-module/underline/index.js +6 -2
  49. package/build-module/underline/index.js.map +1 -1
  50. package/build-style/style-rtl.css +2 -25
  51. package/build-style/style.css +2 -25
  52. package/package.json +15 -15
  53. package/src/bold/index.js +2 -2
  54. package/src/code/index.js +2 -1
  55. package/src/italic/index.js +2 -2
  56. package/src/keyboard/index.js +2 -1
  57. package/src/link/inline.js +113 -8
  58. package/src/link/test/utils.js +362 -1
  59. package/src/link/utils.js +132 -1
  60. package/src/strikethrough/index.js +2 -1
  61. package/src/subscript/index.js +2 -1
  62. package/src/superscript/index.js +2 -1
  63. package/src/text-color/index.js +66 -23
  64. package/src/text-color/inline.js +125 -49
  65. package/src/text-color/style.scss +2 -24
  66. package/src/underline/index.js +3 -1
@@ -16,7 +16,7 @@ export const strikethrough = {
16
16
  className: null,
17
17
  edit( { isActive, value, onChange, onFocus } ) {
18
18
  function onClick() {
19
- onChange( toggleFormat( value, { type: name } ) );
19
+ onChange( toggleFormat( value, { type: name, title } ) );
20
20
  onFocus();
21
21
  }
22
22
 
@@ -26,6 +26,7 @@ export const strikethrough = {
26
26
  title={ title }
27
27
  onClick={ onClick }
28
28
  isActive={ isActive }
29
+ role="menuitemcheckbox"
29
30
  />
30
31
  );
31
32
  },
@@ -16,7 +16,7 @@ export const subscript = {
16
16
  className: null,
17
17
  edit( { isActive, value, onChange, onFocus } ) {
18
18
  function onToggle() {
19
- onChange( toggleFormat( value, { type: name } ) );
19
+ onChange( toggleFormat( value, { type: name, title } ) );
20
20
  }
21
21
 
22
22
  function onClick() {
@@ -30,6 +30,7 @@ export const subscript = {
30
30
  title={ title }
31
31
  onClick={ onClick }
32
32
  isActive={ isActive }
33
+ role="menuitemcheckbox"
33
34
  />
34
35
  );
35
36
  },
@@ -16,7 +16,7 @@ export const superscript = {
16
16
  className: null,
17
17
  edit( { isActive, value, onChange, onFocus } ) {
18
18
  function onToggle() {
19
- onChange( toggleFormat( value, { type: name } ) );
19
+ onChange( toggleFormat( value, { type: name, title } ) );
20
20
  }
21
21
 
22
22
  function onClick() {
@@ -30,6 +30,7 @@ export const superscript = {
30
30
  title={ title }
31
31
  onClick={ onClick }
32
32
  isActive={ isActive }
33
+ role="menuitemcheckbox"
33
34
  />
34
35
  );
35
36
  },
@@ -15,13 +15,44 @@ import { removeFormat } from '@wordpress/rich-text';
15
15
  /**
16
16
  * Internal dependencies
17
17
  */
18
- import { default as InlineColorUI, getActiveColor } from './inline';
18
+ import { default as InlineColorUI, getActiveColors } from './inline';
19
19
 
20
20
  const name = 'core/text-color';
21
- const title = __( 'Text color' );
21
+ const title = __( 'Highlight' );
22
22
 
23
23
  const EMPTY_ARRAY = [];
24
24
 
25
+ function getComputedStyleProperty( element, property ) {
26
+ const { ownerDocument } = element;
27
+ const { defaultView } = ownerDocument;
28
+ const style = defaultView.getComputedStyle( element );
29
+ const value = style.getPropertyValue( property );
30
+
31
+ if (
32
+ property === 'background-color' &&
33
+ value === 'rgba(0, 0, 0, 0)' &&
34
+ element.parentElement
35
+ ) {
36
+ return getComputedStyleProperty( element.parentElement, property );
37
+ }
38
+
39
+ return value;
40
+ }
41
+
42
+ function fillComputedColors( element, { color, backgroundColor } ) {
43
+ if ( ! color && ! backgroundColor ) {
44
+ return;
45
+ }
46
+
47
+ return {
48
+ color: color || getComputedStyleProperty( element, 'color' ),
49
+ backgroundColor:
50
+ backgroundColor === 'rgba(0, 0, 0, 0)'
51
+ ? getComputedStyleProperty( element, 'background-color' )
52
+ : backgroundColor,
53
+ };
54
+ }
55
+
25
56
  function TextColorEdit( {
26
57
  value,
27
58
  onChange,
@@ -38,15 +69,14 @@ function TextColorEdit( {
38
69
  const disableIsAddingColor = useCallback( () => setIsAddingColor( false ), [
39
70
  setIsAddingColor,
40
71
  ] );
41
- const colorIndicatorStyle = useMemo( () => {
42
- const activeColor = getActiveColor( name, value, colors );
43
- if ( ! activeColor ) {
44
- return undefined;
45
- }
46
- return {
47
- backgroundColor: activeColor,
48
- };
49
- }, [ value, colors ] );
72
+ const colorIndicatorStyle = useMemo(
73
+ () =>
74
+ fillComputedColors(
75
+ contentRef.current,
76
+ getActiveColors( value, name, colors )
77
+ ),
78
+ [ value, colors ]
79
+ );
50
80
 
51
81
  const hasColorsToChoose = ! isEmpty( colors ) || ! allowCustomControl;
52
82
  if ( ! hasColorsToChoose && ! isActive ) {
@@ -56,19 +86,13 @@ function TextColorEdit( {
56
86
  return (
57
87
  <>
58
88
  <RichTextToolbarButton
59
- key={ isActive ? 'text-color' : 'text-color-not-active' }
60
89
  className="format-library-text-color-button"
61
- name={ isActive ? 'text-color' : undefined }
90
+ isActive={ isActive }
62
91
  icon={
63
- <>
64
- <Icon icon={ textColorIcon } />
65
- { isActive && (
66
- <span
67
- className="format-library-text-color-button__indicator"
68
- style={ colorIndicatorStyle }
69
- />
70
- ) }
71
- </>
92
+ <Icon
93
+ icon={ textColorIcon }
94
+ style={ colorIndicatorStyle }
95
+ />
72
96
  }
73
97
  title={ title }
74
98
  // If has no colors to choose but a color is active remove the color onClick
@@ -77,6 +101,7 @@ function TextColorEdit( {
77
101
  ? enableIsAddingColor
78
102
  : () => onChange( removeFormat( value, name ) )
79
103
  }
104
+ role="menuitemcheckbox"
80
105
  />
81
106
  { isAddingColor && (
82
107
  <InlineColorUI
@@ -95,11 +120,29 @@ function TextColorEdit( {
95
120
  export const textColor = {
96
121
  name,
97
122
  title,
98
- tagName: 'span',
123
+ tagName: 'mark',
99
124
  className: 'has-inline-color',
100
125
  attributes: {
101
126
  style: 'style',
102
127
  class: 'class',
103
128
  },
129
+ /*
130
+ * Since this format relies on the <mark> tag, it's important to
131
+ * prevent the default yellow background color applied by most
132
+ * browsers. The solution is to detect when this format is used with a
133
+ * text color but no background color, and in such cases to override
134
+ * the default styling with a transparent background.
135
+ *
136
+ * @see https://github.com/WordPress/gutenberg/pull/35516
137
+ */
138
+ __unstableFilterAttributeValue( key, value ) {
139
+ if ( key !== 'style' ) return value;
140
+ // We should not add a background-color if it's already set
141
+ if ( value && value.includes( 'background-color' ) ) return value;
142
+ const addedCSS = [ 'background-color', 'rgba(0, 0, 0, 0)' ].join( ':' );
143
+ // Prepend `addedCSS` to avoid a double `;;` as any the existing CSS
144
+ // rules will already include a `;`.
145
+ return value ? [ addedCSS, value ].join( ';' ) : addedCSS;
146
+ },
104
147
  edit: TextColorEdit,
105
148
  };
@@ -16,75 +16,122 @@ import {
16
16
  } from '@wordpress/rich-text';
17
17
  import {
18
18
  ColorPalette,
19
- URLPopover,
20
19
  getColorClassName,
21
20
  getColorObjectByColorValue,
22
21
  getColorObjectByAttributeValues,
23
22
  store as blockEditorStore,
23
+ useCachedTruthy,
24
24
  } from '@wordpress/block-editor';
25
+ import { Popover, TabPanel } from '@wordpress/components';
26
+ import { __ } from '@wordpress/i18n';
25
27
 
26
28
  /**
27
29
  * Internal dependencies
28
30
  */
29
31
  import { textColor as settings } from './index';
30
32
 
31
- export function getActiveColor( formatName, formatValue, colors ) {
32
- const activeColorFormat = getActiveFormat( formatValue, formatName );
33
+ function parseCSS( css = '' ) {
34
+ return css.split( ';' ).reduce( ( accumulator, rule ) => {
35
+ if ( rule ) {
36
+ const [ property, value ] = rule.split( ':' );
37
+ if ( property === 'color' ) accumulator.color = value;
38
+ if ( property === 'background-color' )
39
+ accumulator.backgroundColor = value;
40
+ }
41
+ return accumulator;
42
+ }, {} );
43
+ }
44
+
45
+ function parseClassName( className = '', colorSettings ) {
46
+ return className.split( ' ' ).reduce( ( accumulator, name ) => {
47
+ const match = name.match( /^has-([^-]+)-color$/ );
48
+ if ( match ) {
49
+ const [ , colorSlug ] = name.match( /^has-([^-]+)-color$/ );
50
+ const colorObject = getColorObjectByAttributeValues(
51
+ colorSettings,
52
+ colorSlug
53
+ );
54
+ accumulator.color = colorObject.color;
55
+ }
56
+ return accumulator;
57
+ }, {} );
58
+ }
59
+
60
+ export function getActiveColors( value, name, colorSettings ) {
61
+ const activeColorFormat = getActiveFormat( value, name );
62
+
33
63
  if ( ! activeColorFormat ) {
34
- return;
64
+ return {};
35
65
  }
36
- const styleColor = activeColorFormat.attributes.style;
37
- if ( styleColor ) {
38
- return styleColor.replace( new RegExp( `^color:\\s*` ), '' );
66
+
67
+ return {
68
+ ...parseCSS( activeColorFormat.attributes.style ),
69
+ ...parseClassName( activeColorFormat.attributes.class, colorSettings ),
70
+ };
71
+ }
72
+
73
+ function setColors( value, name, colorSettings, colors ) {
74
+ const { color, backgroundColor } = {
75
+ ...getActiveColors( value, name, colorSettings ),
76
+ ...colors,
77
+ };
78
+
79
+ if ( ! color && ! backgroundColor ) {
80
+ return removeFormat( value, name );
81
+ }
82
+
83
+ const styles = [];
84
+ const classNames = [];
85
+ const attributes = {};
86
+
87
+ if ( backgroundColor ) {
88
+ styles.push( [ 'background-color', backgroundColor ].join( ':' ) );
89
+ } else {
90
+ // Override default browser color for mark element.
91
+ styles.push( [ 'background-color', 'rgba(0, 0, 0, 0)' ].join( ':' ) );
39
92
  }
40
- const currentClass = activeColorFormat.attributes.class;
41
- if ( currentClass ) {
42
- const colorSlug = currentClass.replace(
43
- /.*has-([^\s]*)-color.*/,
44
- '$1'
45
- );
46
- return getColorObjectByAttributeValues( colors, colorSlug ).color;
93
+
94
+ if ( color ) {
95
+ const colorObject = getColorObjectByColorValue( colorSettings, color );
96
+
97
+ if ( colorObject ) {
98
+ classNames.push( getColorClassName( 'color', colorObject.slug ) );
99
+ } else {
100
+ styles.push( [ 'color', color ].join( ':' ) );
101
+ }
47
102
  }
103
+
104
+ if ( styles.length ) attributes.style = styles.join( ';' );
105
+ if ( classNames.length ) attributes.class = classNames.join( ' ' );
106
+
107
+ return applyFormat( value, { type: name, attributes } );
48
108
  }
49
109
 
50
- const ColorPicker = ( { name, value, onChange } ) => {
110
+ function ColorPicker( { name, property, value, onChange } ) {
51
111
  const colors = useSelect( ( select ) => {
52
112
  const { getSettings } = select( blockEditorStore );
53
113
  return get( getSettings(), [ 'colors' ], [] );
54
- } );
114
+ }, [] );
55
115
  const onColorChange = useCallback(
56
116
  ( color ) => {
57
- if ( color ) {
58
- const colorObject = getColorObjectByColorValue( colors, color );
59
- onChange(
60
- applyFormat( value, {
61
- type: name,
62
- attributes: colorObject
63
- ? {
64
- class: getColorClassName(
65
- 'color',
66
- colorObject.slug
67
- ),
68
- }
69
- : {
70
- style: `color:${ color }`,
71
- },
72
- } )
73
- );
74
- } else {
75
- onChange( removeFormat( value, name ) );
76
- }
117
+ onChange(
118
+ setColors( value, name, colors, { [ property ]: color } )
119
+ );
77
120
  },
78
- [ colors, onChange ]
121
+ [ colors, onChange, property ]
122
+ );
123
+ const activeColors = useMemo(
124
+ () => getActiveColors( value, name, colors ),
125
+ [ name, value, colors ]
79
126
  );
80
- const activeColor = useMemo( () => getActiveColor( name, value, colors ), [
81
- name,
82
- value,
83
- colors,
84
- ] );
85
127
 
86
- return <ColorPalette value={ activeColor } onChange={ onColorChange } />;
87
- };
128
+ return (
129
+ <ColorPalette
130
+ value={ activeColors[ property ] }
131
+ onChange={ onColorChange }
132
+ />
133
+ );
134
+ }
88
135
 
89
136
  export default function InlineColorUI( {
90
137
  name,
@@ -93,15 +140,44 @@ export default function InlineColorUI( {
93
140
  onClose,
94
141
  contentRef,
95
142
  } ) {
96
- const anchorRef = useAnchorRef( { ref: contentRef, value, settings } );
143
+ /*
144
+ As you change the text color by typing a HEX value into a field,
145
+ the return value of document.getSelection jumps to the field you're editing,
146
+ not the highlighted text. Given that useAnchorRef uses document.getSelection,
147
+ it will return null, since it can't find the <mark> element within the HEX input.
148
+ This caches the last truthy value of the selection anchor reference.
149
+ */
150
+ const anchorRef = useCachedTruthy(
151
+ useAnchorRef( { ref: contentRef, value, settings } )
152
+ );
153
+
97
154
  return (
98
- <URLPopover
99
- value={ value }
155
+ <Popover
100
156
  onClose={ onClose }
101
157
  className="components-inline-color-popover"
102
158
  anchorRef={ anchorRef }
103
159
  >
104
- <ColorPicker name={ name } value={ value } onChange={ onChange } />
105
- </URLPopover>
160
+ <TabPanel
161
+ tabs={ [
162
+ {
163
+ name: 'color',
164
+ title: __( 'Text' ),
165
+ },
166
+ {
167
+ name: 'backgroundColor',
168
+ title: __( 'Background' ),
169
+ },
170
+ ] }
171
+ >
172
+ { ( tab ) => (
173
+ <ColorPicker
174
+ name={ name }
175
+ property={ tab.name }
176
+ value={ value }
177
+ onChange={ onChange }
178
+ />
179
+ ) }
180
+ </TabPanel>
181
+ </Popover>
106
182
  );
107
183
  }
@@ -1,19 +1,8 @@
1
- .components-inline-color__indicator {
2
- position: absolute;
3
- background: #000;
4
- height: 3px;
5
- width: 20px;
6
- bottom: 6px;
7
- left: auto;
8
- right: auto;
9
- margin: 0 5px;
10
- }
11
-
12
1
  .components-inline-color-popover {
13
2
 
14
3
  .components-popover__content {
15
- > div {
16
- padding: 20px 18px;
4
+ .components-tab-panel__tab-content {
5
+ padding: 16px;
17
6
  }
18
7
 
19
8
  .components-color-palette {
@@ -32,14 +21,3 @@
32
21
  }
33
22
  }
34
23
  }
35
-
36
- .format-library-text-color-button {
37
- position: relative;
38
- }
39
- .format-library-text-color-button__indicator {
40
- height: 4px;
41
- width: 20px;
42
- position: absolute;
43
- bottom: 10px;
44
- left: 8px;
45
- }
@@ -9,10 +9,11 @@ import {
9
9
  } from '@wordpress/block-editor';
10
10
 
11
11
  const name = 'core/underline';
12
+ const title = __( 'Underline' );
12
13
 
13
14
  export const underline = {
14
15
  name,
15
- title: __( 'Underline' ),
16
+ title,
16
17
  tagName: 'span',
17
18
  className: null,
18
19
  attributes: {
@@ -26,6 +27,7 @@ export const underline = {
26
27
  attributes: {
27
28
  style: 'text-decoration: underline;',
28
29
  },
30
+ title,
29
31
  } )
30
32
  );
31
33
  };