block_editor 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +147 -0
- data/Rakefile +18 -0
- data/app/assets/config/block_editor_manifest.js +1 -0
- data/app/assets/stylesheets/block_editor/backend.scss +4 -0
- data/app/assets/stylesheets/block_editor/backend/blocks.scss +0 -0
- data/app/assets/stylesheets/block_editor/frontend.scss +1 -0
- data/app/assets/stylesheets/block_editor/frontend/blocks.scss +0 -0
- data/app/controllers/block_editor/application_controller.rb +4 -0
- data/app/helpers/block_editor/application_helper.rb +11 -0
- data/app/javascript/block_editor/blocks/button/edit.js +240 -0
- data/app/javascript/block_editor/blocks/column/edit.js +93 -0
- data/app/javascript/block_editor/blocks/image/edit.js +656 -0
- data/app/javascript/block_editor/blocks/index.js +263 -0
- data/app/javascript/block_editor/components/block-editor/index.js +88 -0
- data/app/javascript/block_editor/components/block-editor/styles.scss +39 -0
- data/app/javascript/block_editor/components/header/index.js +45 -0
- data/app/javascript/block_editor/components/header/redo.js +36 -0
- data/app/javascript/block_editor/components/header/styles.scss +14 -0
- data/app/javascript/block_editor/components/header/undo.js +36 -0
- data/app/javascript/block_editor/components/media-upload/index.js +37 -0
- data/app/javascript/block_editor/components/notices/index.js +26 -0
- data/app/javascript/block_editor/components/notices/styles.scss +9 -0
- data/app/javascript/block_editor/components/sidebar/index.js +31 -0
- data/app/javascript/block_editor/components/sidebar/styles.scss +43 -0
- data/app/javascript/block_editor/stores/action-types.js +4 -0
- data/app/javascript/block_editor/stores/actions.js +41 -0
- data/app/javascript/block_editor/stores/controls.js +21 -0
- data/app/javascript/block_editor/stores/index.js +30 -0
- data/app/javascript/block_editor/stores/reducer.js +20 -0
- data/app/javascript/block_editor/stores/resolvers.js +10 -0
- data/app/javascript/block_editor/stores/selectors.js +13 -0
- data/app/javascript/controllers/block_editor_controller.jsx +42 -0
- data/app/javascript/controllers/index.js +6 -0
- data/app/javascript/packs/block_editor/application.js +2 -0
- data/app/javascript/packs/block_editor/application.scss +108 -0
- data/app/jobs/block_editor/application_job.rb +4 -0
- data/app/mailers/block_editor/application_mailer.rb +6 -0
- data/app/models/block_editor/application_record.rb +5 -0
- data/app/models/block_editor/block_list.rb +7 -0
- data/app/models/concerns/block_editor/listable.rb +24 -0
- data/app/views/layouts/block_editor/application.html.erb +15 -0
- data/config/initializers/webpacker_extension.rb +12 -0
- data/config/routes.rb +2 -0
- data/config/webpack/development.js +5 -0
- data/config/webpack/environment.js +3 -0
- data/config/webpack/production.js +5 -0
- data/config/webpack/test.js +5 -0
- data/config/webpacker.yml +92 -0
- data/db/migrate/20210312032114_create_block_lists.rb +11 -0
- data/lib/block_editor.rb +26 -0
- data/lib/block_editor/block_list_renderer.rb +43 -0
- data/lib/block_editor/blocks/base.rb +32 -0
- data/lib/block_editor/engine.rb +34 -0
- data/lib/block_editor/instance.rb +19 -0
- data/lib/block_editor/version.rb +3 -0
- data/lib/tasks/block_editor_tasks.rake +59 -0
- metadata +131 -0
@@ -0,0 +1,263 @@
|
|
1
|
+
/**
|
2
|
+
* External dependencies
|
3
|
+
*/
|
4
|
+
import React from 'react';
|
5
|
+
import ReactDOM from 'react-dom';
|
6
|
+
import classnames from 'classnames';
|
7
|
+
import { assign } from 'lodash';
|
8
|
+
|
9
|
+
/**
|
10
|
+
* WordPress dependencies
|
11
|
+
*/
|
12
|
+
import '@wordpress/core-data';
|
13
|
+
import { registerCoreBlocks } from '@wordpress/block-library'
|
14
|
+
import '@wordpress/block-editor';
|
15
|
+
import {
|
16
|
+
registerBlockType,
|
17
|
+
unregisterBlockType,
|
18
|
+
registerBlockStyle,
|
19
|
+
unregisterBlockStyle,
|
20
|
+
unregisterBlockVariation
|
21
|
+
} from '@wordpress/blocks';
|
22
|
+
import { createHigherOrderComponent } from '@wordpress/compose'
|
23
|
+
import { addFilter } from '@wordpress/hooks';
|
24
|
+
|
25
|
+
/**
|
26
|
+
* Internal dependencies
|
27
|
+
*/
|
28
|
+
import ColumnEdit from './column/edit';
|
29
|
+
import ButtonEdit from './button/edit';
|
30
|
+
import ImageEdit from './image/edit';
|
31
|
+
import MediaUpload from '../components/media-upload';
|
32
|
+
|
33
|
+
export const registerBlocks = () => {
|
34
|
+
const replaceButtonBlockEdit = ( settings, name ) => {
|
35
|
+
if ( name !== 'core/button' ) {
|
36
|
+
return settings;
|
37
|
+
}
|
38
|
+
|
39
|
+
return assign( {}, settings, {
|
40
|
+
edit: ButtonEdit, // Removes & replaces some styling options
|
41
|
+
attributes: assign( {}, settings.attributes, {
|
42
|
+
hasHollowStyle: {
|
43
|
+
type: 'boolean',
|
44
|
+
default: false
|
45
|
+
},
|
46
|
+
hasLargeStyle: {
|
47
|
+
type: 'boolean',
|
48
|
+
default: false
|
49
|
+
}
|
50
|
+
})
|
51
|
+
} );
|
52
|
+
}
|
53
|
+
|
54
|
+
const replaceColumnBlockEdit = ( settings, name ) => {
|
55
|
+
if ( name !== 'core/column' ) {
|
56
|
+
return settings;
|
57
|
+
}
|
58
|
+
|
59
|
+
return assign( {}, settings, {
|
60
|
+
edit: ColumnEdit // Removes column width options
|
61
|
+
} );
|
62
|
+
}
|
63
|
+
|
64
|
+
const replaceImageBlockEdit = ( settings, name ) => {
|
65
|
+
if ( name !== 'core/image' ) {
|
66
|
+
return settings;
|
67
|
+
}
|
68
|
+
|
69
|
+
return assign( {}, settings, {
|
70
|
+
edit: ImageEdit // Removes ImageSizeControl options
|
71
|
+
} );
|
72
|
+
}
|
73
|
+
|
74
|
+
const applyExtraClass = ( extraProps, blockType, attributes ) => {
|
75
|
+
if ( blockType.name !== 'core/button' ) {
|
76
|
+
return extraProps;
|
77
|
+
}
|
78
|
+
|
79
|
+
if (attributes.hasLargeStyle) {
|
80
|
+
extraProps.className = classnames( extraProps.className, 'large' );
|
81
|
+
}
|
82
|
+
|
83
|
+
if (attributes.hasHollowStyle) {
|
84
|
+
extraProps.className = classnames( extraProps.className, 'is-style-hollow' );
|
85
|
+
}
|
86
|
+
|
87
|
+
return extraProps;
|
88
|
+
}
|
89
|
+
|
90
|
+
const withClientIdClassName = createHigherOrderComponent( ( BlockListBlock ) => {
|
91
|
+
return ( props ) => {
|
92
|
+
if ( props.name !== 'core/button' ) {
|
93
|
+
return <BlockListBlock { ...props } />;
|
94
|
+
}
|
95
|
+
|
96
|
+
let classNames = '';
|
97
|
+
if (props.attributes.hasLargeStyle) {
|
98
|
+
classNames = classnames( classNames, 'large' );
|
99
|
+
}
|
100
|
+
|
101
|
+
if (props.attributes.hasHollowStyle) {
|
102
|
+
classNames = classnames( classNames, 'is-style-hollow' );
|
103
|
+
}
|
104
|
+
|
105
|
+
return <BlockListBlock { ...props } className={ classNames } />;
|
106
|
+
};
|
107
|
+
}, 'withClientIdClassName' );
|
108
|
+
|
109
|
+
const replaceMediaUpload = () => MediaUpload;
|
110
|
+
|
111
|
+
addFilter(
|
112
|
+
'editor.BlockListBlock',
|
113
|
+
'block-editor/filters/core-button-block-list',
|
114
|
+
withClientIdClassName
|
115
|
+
);
|
116
|
+
|
117
|
+
addFilter(
|
118
|
+
'blocks.registerBlockType',
|
119
|
+
'block-editor/filters/core-button',
|
120
|
+
replaceButtonBlockEdit
|
121
|
+
);
|
122
|
+
|
123
|
+
addFilter(
|
124
|
+
'blocks.getSaveContent.extraProps',
|
125
|
+
'block-editor/filters/core-button-classes',
|
126
|
+
applyExtraClass
|
127
|
+
);
|
128
|
+
|
129
|
+
addFilter(
|
130
|
+
'blocks.registerBlockType',
|
131
|
+
'block-editor/filters/core-column',
|
132
|
+
replaceColumnBlockEdit
|
133
|
+
);
|
134
|
+
|
135
|
+
addFilter(
|
136
|
+
'blocks.registerBlockType',
|
137
|
+
'block-editor/filters/core-image',
|
138
|
+
replaceImageBlockEdit
|
139
|
+
);
|
140
|
+
|
141
|
+
addFilter(
|
142
|
+
'editor.MediaUpload',
|
143
|
+
'block-editor/filters/media-upload',
|
144
|
+
replaceMediaUpload
|
145
|
+
);
|
146
|
+
|
147
|
+
// Register WP blocks
|
148
|
+
registerCoreBlocks();
|
149
|
+
|
150
|
+
// Unregister WP blocks which are not supported
|
151
|
+
unregisterBlockType('core/gallery');
|
152
|
+
unregisterBlockType('core/quote');
|
153
|
+
unregisterBlockType('core/shortcode');
|
154
|
+
unregisterBlockType('core/archives');
|
155
|
+
unregisterBlockType('core/audio');
|
156
|
+
unregisterBlockType('core/calendar');
|
157
|
+
unregisterBlockType('core/categories');
|
158
|
+
unregisterBlockType('core/code');
|
159
|
+
unregisterBlockType('core/cover');
|
160
|
+
unregisterBlockType('core/embed');
|
161
|
+
unregisterBlockType('core-embed/twitter');
|
162
|
+
unregisterBlockType('core-embed/youtube');
|
163
|
+
unregisterBlockType('core-embed/facebook');
|
164
|
+
unregisterBlockType('core-embed/instagram');
|
165
|
+
unregisterBlockType('core-embed/wordpress');
|
166
|
+
unregisterBlockType('core-embed/soundcloud');
|
167
|
+
unregisterBlockType('core-embed/spotify');
|
168
|
+
unregisterBlockType('core-embed/flickr');
|
169
|
+
unregisterBlockType('core-embed/vimeo');
|
170
|
+
unregisterBlockType('core-embed/animoto');
|
171
|
+
unregisterBlockType('core-embed/cloudup');
|
172
|
+
unregisterBlockType('core-embed/collegehumor');
|
173
|
+
unregisterBlockType('core-embed/crowdsignal');
|
174
|
+
unregisterBlockType('core-embed/dailymotion');
|
175
|
+
unregisterBlockType('core-embed/hulu');
|
176
|
+
unregisterBlockType('core-embed/imgur');
|
177
|
+
unregisterBlockType('core-embed/issuu');
|
178
|
+
unregisterBlockType('core-embed/kickstarter');
|
179
|
+
unregisterBlockType('core-embed/meetup-com');
|
180
|
+
unregisterBlockType('core-embed/mixcloud');
|
181
|
+
unregisterBlockType('core-embed/polldaddy');
|
182
|
+
unregisterBlockType('core-embed/reddit');
|
183
|
+
unregisterBlockType('core-embed/reverbnation');
|
184
|
+
unregisterBlockType('core-embed/screencast');
|
185
|
+
unregisterBlockType('core-embed/scribd');
|
186
|
+
unregisterBlockType('core-embed/slideshare');
|
187
|
+
unregisterBlockType('core-embed/smugmug');
|
188
|
+
unregisterBlockType('core-embed/speaker');
|
189
|
+
unregisterBlockType('core-embed/speaker-deck');
|
190
|
+
unregisterBlockType('core-embed/tiktok');
|
191
|
+
unregisterBlockType('core-embed/ted');
|
192
|
+
unregisterBlockType('core-embed/tumblr');
|
193
|
+
unregisterBlockType('core-embed/videopress');
|
194
|
+
unregisterBlockType('core-embed/wordpress-tv');
|
195
|
+
unregisterBlockType('core-embed/amazon-kindle');
|
196
|
+
unregisterBlockType('core/file');
|
197
|
+
unregisterBlockType('core/media-text');
|
198
|
+
unregisterBlockType('core/latest-comments');
|
199
|
+
unregisterBlockType('core/latest-posts');
|
200
|
+
unregisterBlockType('core/more');
|
201
|
+
unregisterBlockType('core/nextpage');
|
202
|
+
unregisterBlockType('core/preformatted');
|
203
|
+
unregisterBlockType('core/pullquote');
|
204
|
+
unregisterBlockType('core/rss');
|
205
|
+
unregisterBlockType('core/search');
|
206
|
+
// unregisterBlockType('core/reusable-block'); // ?
|
207
|
+
// unregisterBlockType('core/reusable'); // ?
|
208
|
+
unregisterBlockType('core/social-links');
|
209
|
+
unregisterBlockType('core/social-link');
|
210
|
+
unregisterBlockType('core/spacer');
|
211
|
+
unregisterBlockType('core/subhead');
|
212
|
+
unregisterBlockType('core/tag-cloud');
|
213
|
+
unregisterBlockType('core/text-columns');
|
214
|
+
unregisterBlockType('core/verse');
|
215
|
+
unregisterBlockType('core/video');
|
216
|
+
|
217
|
+
// Unregister WP block styles
|
218
|
+
unregisterBlockStyle('core/separator', 'wide');
|
219
|
+
unregisterBlockStyle('core/button', 'fill');
|
220
|
+
unregisterBlockStyle('core/button', 'outline');
|
221
|
+
unregisterBlockStyle('core/image', 'default');
|
222
|
+
unregisterBlockStyle('core/image', 'rounded');
|
223
|
+
unregisterBlockStyle('core/table', 'regular');
|
224
|
+
unregisterBlockStyle('core/table', 'stripes');
|
225
|
+
|
226
|
+
// Unregister WP block variations
|
227
|
+
unregisterBlockVariation('core/columns', 'two-columns-one-third-two-thirds');
|
228
|
+
unregisterBlockVariation('core/columns', 'two-columns-two-thirds-one-third');
|
229
|
+
unregisterBlockVariation('core/columns', 'three-columns-wider-center');
|
230
|
+
|
231
|
+
// Register custom block styles
|
232
|
+
registerBlockStyle( 'core/button', {
|
233
|
+
name: 'primary',
|
234
|
+
label: 'Primary',
|
235
|
+
isDefault: true
|
236
|
+
} );
|
237
|
+
registerBlockStyle( 'core/button', {
|
238
|
+
name: 'secondary',
|
239
|
+
label: 'Secondary'
|
240
|
+
} );
|
241
|
+
registerBlockStyle( 'core/table', {
|
242
|
+
name: 'striped',
|
243
|
+
label: 'Striped',
|
244
|
+
isDefault: true
|
245
|
+
} );
|
246
|
+
registerBlockStyle( 'core/table', {
|
247
|
+
name: 'unstriped',
|
248
|
+
label: 'Unstriped'
|
249
|
+
} );
|
250
|
+
registerBlockStyle( 'core/image', {
|
251
|
+
name: 'default',
|
252
|
+
label: 'Default',
|
253
|
+
isDefault: true
|
254
|
+
} );
|
255
|
+
registerBlockStyle( 'core/image', {
|
256
|
+
name: 'padded',
|
257
|
+
label: 'Padded'
|
258
|
+
} );
|
259
|
+
registerBlockStyle( 'core/columns', {
|
260
|
+
name: 'no-stack',
|
261
|
+
label: 'No Stacking'
|
262
|
+
} );
|
263
|
+
};
|
@@ -0,0 +1,88 @@
|
|
1
|
+
import React from 'react'
|
2
|
+
import ReactDOM from 'react-dom'
|
3
|
+
/**
|
4
|
+
* WordPress dependencies
|
5
|
+
*/
|
6
|
+
import '@wordpress/editor'; // This shouldn't be necessary - currently required otherwise notices fails to initialized, think the data store is being registered
|
7
|
+
import '@wordpress/format-library';
|
8
|
+
import { useSelect, useDispatch } from '@wordpress/data';
|
9
|
+
import { useEffect, useState, useMemo } from '@wordpress/element';
|
10
|
+
import { serialize, parse } from '@wordpress/blocks';
|
11
|
+
import { InterfaceSkeleton as EditorSkeleton } from '@wordpress/interface';
|
12
|
+
import {
|
13
|
+
BlockEditorKeyboardShortcuts,
|
14
|
+
BlockEditorProvider,
|
15
|
+
BlockList,
|
16
|
+
BlockInspector,
|
17
|
+
WritingFlow,
|
18
|
+
ObserveTyping,
|
19
|
+
BlockBreadcrumb,
|
20
|
+
} from '@wordpress/block-editor';
|
21
|
+
import {
|
22
|
+
Popover,
|
23
|
+
SlotFillProvider,
|
24
|
+
DropZoneProvider,
|
25
|
+
} from '@wordpress/components';
|
26
|
+
|
27
|
+
/**
|
28
|
+
* Internal dependencies
|
29
|
+
*/
|
30
|
+
import Sidebar from '../sidebar';
|
31
|
+
import Header from '../header';
|
32
|
+
import Notices from '../notices';
|
33
|
+
import '../../stores'; // TODO: Think this store registering needs to be moved somewhere else so that it happens everytime a BlockEditor is initialized
|
34
|
+
|
35
|
+
function BlockEditor( { input, settings: _settings } ) {
|
36
|
+
const blocks = useSelect((select) => select("block-editor").getBlocks());
|
37
|
+
const { updateBlocks } = useDispatch("block-editor");
|
38
|
+
|
39
|
+
function handleInput(newBlocks, persist) {
|
40
|
+
updateBlocks(newBlocks);
|
41
|
+
input.value = serialize(newBlocks);
|
42
|
+
}
|
43
|
+
|
44
|
+
function handleChange(newBlocks) {
|
45
|
+
updateBlocks(newBlocks, true);
|
46
|
+
input.value = serialize(newBlocks);
|
47
|
+
}
|
48
|
+
|
49
|
+
return (
|
50
|
+
<SlotFillProvider>
|
51
|
+
<DropZoneProvider>
|
52
|
+
<EditorSkeleton
|
53
|
+
sidebar={<Sidebar />}
|
54
|
+
content={
|
55
|
+
<>
|
56
|
+
<Notices />
|
57
|
+
<BlockEditorProvider
|
58
|
+
value={ blocks }
|
59
|
+
onInput={ handleInput }
|
60
|
+
onChange={ handleChange }
|
61
|
+
settings={ _settings }
|
62
|
+
>
|
63
|
+
<Header />
|
64
|
+
<BlockBreadcrumb />
|
65
|
+
<Sidebar.InspectorFill>
|
66
|
+
<BlockInspector />
|
67
|
+
</Sidebar.InspectorFill>
|
68
|
+
<div className="block-editor__inner-wrapper">
|
69
|
+
<BlockEditorKeyboardShortcuts.Register />
|
70
|
+
<BlockEditorKeyboardShortcuts />
|
71
|
+
<WritingFlow>
|
72
|
+
<ObserveTyping>
|
73
|
+
<BlockList className="editor-styles-wrapper" />
|
74
|
+
</ObserveTyping>
|
75
|
+
</WritingFlow>
|
76
|
+
</div>
|
77
|
+
</BlockEditorProvider>
|
78
|
+
</>
|
79
|
+
}
|
80
|
+
/>
|
81
|
+
<Popover.Slot />
|
82
|
+
</DropZoneProvider>
|
83
|
+
</SlotFillProvider>
|
84
|
+
);
|
85
|
+
}
|
86
|
+
|
87
|
+
export default BlockEditor;
|
88
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
.block-editor__block-list {
|
2
|
+
padding-bottom: $grid-unit-30;
|
3
|
+
padding-top: $grid-unit-30 + 5;
|
4
|
+
margin-left: auto;
|
5
|
+
margin-right: auto;
|
6
|
+
}
|
7
|
+
|
8
|
+
.block-editor-block-list__layout {
|
9
|
+
margin-bottom: 50px;
|
10
|
+
}
|
11
|
+
|
12
|
+
.block-editor__inner-wrapper {
|
13
|
+
-webkit-overflow-scrolling: touch;
|
14
|
+
background: $white;
|
15
|
+
height: calc(100% - #{$header-height});
|
16
|
+
left: 0;
|
17
|
+
overflow: auto;
|
18
|
+
position: absolute;
|
19
|
+
top: $header-height;
|
20
|
+
width: 100%;
|
21
|
+
|
22
|
+
@include break-small {
|
23
|
+
width: calc(100% - #{$sidebar-width});
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
// Grab the default Editor styles for visual consistency with WP
|
28
|
+
.editor-styles-wrapper {
|
29
|
+
// We need the styles scoped to "body" to be added to the
|
30
|
+
// div.editor-styles wrapper
|
31
|
+
font-family: $editor-font;
|
32
|
+
font-size: $editor-font-size;
|
33
|
+
line-height: $editor-line-height;
|
34
|
+
color: #555;
|
35
|
+
|
36
|
+
// Important all other selectors scoped underneath
|
37
|
+
// `div.editor-styles-wrapper`
|
38
|
+
@import "~@wordpress/editor/src/editor-styles.scss";
|
39
|
+
}
|
@@ -0,0 +1,45 @@
|
|
1
|
+
import React from 'react'
|
2
|
+
import ReactDOM from 'react-dom'
|
3
|
+
|
4
|
+
/**
|
5
|
+
* WordPress dependencies
|
6
|
+
*/
|
7
|
+
import { NavigableToolbar } from '@wordpress/block-editor';
|
8
|
+
import { Button } from '@wordpress/components';
|
9
|
+
import { minus as minimizeIcon } from '@wordpress/icons';
|
10
|
+
import { plus as maximizeIcon } from '@wordpress/icons';
|
11
|
+
|
12
|
+
import HistoryUndo from './undo';
|
13
|
+
import HistoryRedo from './redo';
|
14
|
+
|
15
|
+
export default function Header() {
|
16
|
+
return (
|
17
|
+
<div
|
18
|
+
className="block-editor__header"
|
19
|
+
role="region"
|
20
|
+
tabIndex="-1"
|
21
|
+
>
|
22
|
+
<NavigableToolbar
|
23
|
+
className="edit-post-header-toolbar"
|
24
|
+
>
|
25
|
+
<HistoryUndo />
|
26
|
+
<HistoryRedo />
|
27
|
+
<Button
|
28
|
+
icon={ maximizeIcon }
|
29
|
+
label={ 'Fullscreen' }
|
30
|
+
// shortcut={ displayShortcut.primary( 'x' ) }
|
31
|
+
className="block-editor__size-toggle-button block-editor__size-toggle-button__maximize"
|
32
|
+
onClick={() => document.querySelector('.block-editor').classList.add('block-editor__fullscreen')}
|
33
|
+
/>
|
34
|
+
<Button
|
35
|
+
icon={ minimizeIcon }
|
36
|
+
label={ 'Minimize' }
|
37
|
+
// shortcut={ displayShortcut.primary( 'x' ) }
|
38
|
+
className="block-editor__size-toggle-button block-editor__size-toggle-button__minimize"
|
39
|
+
onClick={() => document.querySelector('.block-editor').classList.remove('block-editor__fullscreen')}
|
40
|
+
/>
|
41
|
+
|
42
|
+
</NavigableToolbar>
|
43
|
+
</div>
|
44
|
+
);
|
45
|
+
}
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import ReactDOM from 'react-dom';
|
3
|
+
import { __ } from '@wordpress/i18n';
|
4
|
+
import { Button } from '@wordpress/components';
|
5
|
+
import { withSelect, withDispatch } from '@wordpress/data';
|
6
|
+
import { compose } from '@wordpress/compose';
|
7
|
+
import { displayShortcut } from '@wordpress/keycodes';
|
8
|
+
import { redo as redoIcon } from '@wordpress/icons';
|
9
|
+
|
10
|
+
function HistoryRedo( { hasRedo, redo, ...props } ) {
|
11
|
+
return (
|
12
|
+
<Button
|
13
|
+
{ ...props }
|
14
|
+
icon={ redoIcon }
|
15
|
+
label={ __( 'Redo' ) }
|
16
|
+
shortcut={ displayShortcut.primary( 'x' ) }
|
17
|
+
// If there are no redo levels we don't want to actually disable this
|
18
|
+
// button, because it will remove focus for keyboard users.
|
19
|
+
// See: https://github.com/WordPress/gutenberg/issues/3486
|
20
|
+
aria-disabled={ ! hasRedo }
|
21
|
+
onClick={ hasRedo ? redo : undefined }
|
22
|
+
className="editor-history__redo"
|
23
|
+
/>
|
24
|
+
);
|
25
|
+
}
|
26
|
+
|
27
|
+
const EnhancedHistoryRedo = compose( [
|
28
|
+
withSelect( ( select ) => ( {
|
29
|
+
hasRedo: select( 'block-editor' ).hasRedo(),
|
30
|
+
} ) ),
|
31
|
+
withDispatch( ( dispatch ) => ( {
|
32
|
+
redo: dispatch( 'block-editor' ).redo,
|
33
|
+
} ) ),
|
34
|
+
] )( HistoryRedo );
|
35
|
+
|
36
|
+
export default EnhancedHistoryRedo;
|