PluginProbe
Extendify / 3.2.1
Extendify v3.2.1
3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 All 127 releases
extendify / src / QuickEdit / components / toolbar / TextAlignButtons.jsx

TextAlignButtons.jsx in Extendify 3.2.1, at src/QuickEdit/components/toolbar/TextAlignButtons.jsx

125 lines 3.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { Button, Popover } from '@wordpress/components';
2 import { useDispatch, useSelect } from '@wordpress/data';
3 import { useRef, useState } from '@wordpress/element';
4 import { __ } from '@wordpress/i18n';
5 import { alignCenter, alignLeft, alignRight } from '@wordpress/icons';
6
7 // Allowlist of block types whose render path applies `has-text-align-*`
8 // from `attributes.style.typography.textAlign` via useBlockProps.
9 const SUPPORTED = new Set(['core/heading', 'core/paragraph']);
10
11 const OPTIONS = [
12 {
13 value: 'left',
14 icon: alignLeft,
15 label: __('Align text left', 'extendify-local'),
16 },
17 {
18 value: 'center',
19 icon: alignCenter,
20 label: __('Align text center', 'extendify-local'),
21 },
22 {
23 value: 'right',
24 icon: alignRight,
25 label: __('Align text right', 'extendify-local'),
26 },
27 ];
28
29 const iconFor = (textAlign) =>
30 OPTIONS.find((o) => o.value === textAlign)?.icon ?? alignLeft;
31
32 // Gutenberg ships text-alignment as a BlockControls group, but the QE bar
33 // hides the collapsed-toolbar overflow that group rides in. Restore it as
34 // a single-button dropdown matching Gutenberg's own AlignmentControl
35 // shape (one trigger → popover with three options).
36 //
37 // Modern heading + paragraph render alignment from
38 // `style.typography.textAlign` via useBlockProps. Writing there directly
39 // avoids each block's deprecated flat `textAlign` / `align` attribute and
40 // matches the shape Gutenberg's AlignmentControl produces.
41 export const TextAlignButtons = () => {
42 const [open, setOpen] = useState(false);
43 const wrapRef = useRef(null);
44
45 const selected = useSelect((select) => {
46 const editor = select('core/block-editor');
47 const clientId = editor.getSelectedBlockClientId();
48 if (!clientId) return null;
49 const block = editor.getBlock(clientId);
50 if (!block || !SUPPORTED.has(block.name)) return null;
51 return {
52 clientId,
53 style: block.attributes?.style ?? null,
54 textAlign: block.attributes?.style?.typography?.textAlign ?? null,
55 };
56 }, []);
57
58 const dispatch = useDispatch('core/block-editor');
59
60 if (!selected) return null;
61
62 const setAlign = (value) => {
63 const next = selected.textAlign === value ? undefined : value;
64 const typography = { ...(selected.style?.typography || {}) };
65 if (next) {
66 typography.textAlign = next;
67 } else {
68 delete typography.textAlign;
69 }
70 const style = { ...(selected.style || {}) };
71 if (Object.keys(typography).length) {
72 style.typography = typography;
73 } else {
74 delete style.typography;
75 }
76 dispatch?.updateBlockAttributes?.(selected.clientId, {
77 style: Object.keys(style).length ? style : undefined,
78 });
79 setOpen(false);
80 };
81
82 return (
83 <div className="relative inline-flex items-center" ref={wrapRef}>
84 <Button
85 className="extendify-quick-edit-text-align-button"
86 label={__('Align text', 'extendify-local')}
87 aria-expanded={open}
88 icon={iconFor(selected.textAlign)}
89 onMouseDown={(ev) => ev.preventDefault()}
90 onClick={() => setOpen((v) => !v)}
91 />
92 {open ? (
93 <Popover
94 placement="bottom-start"
95 onClose={() => setOpen(false)}
96 focusOnMount="firstElement"
97 className="extendify-quick-edit extendify-quick-edit-text-align-popover"
98 anchor={wrapRef.current}
99 >
100 <div
101 className="extendify-quick-edit-text-align-popover-inner"
102 role="listbox"
103 aria-label={__('Text alignment', 'extendify-local')}
104 >
105 {OPTIONS.map(({ value, icon, label }) => (
106 <Button
107 key={value}
108 role="option"
109 aria-selected={selected.textAlign === value}
110 className="extendify-quick-edit-text-align-option"
111 variant={selected.textAlign === value ? 'primary' : 'tertiary'}
112 icon={icon}
113 onClick={() => setAlign(value)}
114 onMouseDown={(ev) => ev.preventDefault()}
115 >
116 {label}
117 </Button>
118 ))}
119 </div>
120 </Popover>
121 ) : null}
122 </div>
123 );
124 };
125