block_editor 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +147 -0
  4. data/Rakefile +18 -0
  5. data/app/assets/config/block_editor_manifest.js +1 -0
  6. data/app/assets/stylesheets/block_editor/backend.scss +4 -0
  7. data/app/assets/stylesheets/block_editor/backend/blocks.scss +0 -0
  8. data/app/assets/stylesheets/block_editor/frontend.scss +1 -0
  9. data/app/assets/stylesheets/block_editor/frontend/blocks.scss +0 -0
  10. data/app/controllers/block_editor/application_controller.rb +4 -0
  11. data/app/helpers/block_editor/application_helper.rb +11 -0
  12. data/app/javascript/block_editor/blocks/button/edit.js +240 -0
  13. data/app/javascript/block_editor/blocks/column/edit.js +93 -0
  14. data/app/javascript/block_editor/blocks/image/edit.js +656 -0
  15. data/app/javascript/block_editor/blocks/index.js +263 -0
  16. data/app/javascript/block_editor/components/block-editor/index.js +88 -0
  17. data/app/javascript/block_editor/components/block-editor/styles.scss +39 -0
  18. data/app/javascript/block_editor/components/header/index.js +45 -0
  19. data/app/javascript/block_editor/components/header/redo.js +36 -0
  20. data/app/javascript/block_editor/components/header/styles.scss +14 -0
  21. data/app/javascript/block_editor/components/header/undo.js +36 -0
  22. data/app/javascript/block_editor/components/media-upload/index.js +37 -0
  23. data/app/javascript/block_editor/components/notices/index.js +26 -0
  24. data/app/javascript/block_editor/components/notices/styles.scss +9 -0
  25. data/app/javascript/block_editor/components/sidebar/index.js +31 -0
  26. data/app/javascript/block_editor/components/sidebar/styles.scss +43 -0
  27. data/app/javascript/block_editor/stores/action-types.js +4 -0
  28. data/app/javascript/block_editor/stores/actions.js +41 -0
  29. data/app/javascript/block_editor/stores/controls.js +21 -0
  30. data/app/javascript/block_editor/stores/index.js +30 -0
  31. data/app/javascript/block_editor/stores/reducer.js +20 -0
  32. data/app/javascript/block_editor/stores/resolvers.js +10 -0
  33. data/app/javascript/block_editor/stores/selectors.js +13 -0
  34. data/app/javascript/controllers/block_editor_controller.jsx +42 -0
  35. data/app/javascript/controllers/index.js +6 -0
  36. data/app/javascript/packs/block_editor/application.js +2 -0
  37. data/app/javascript/packs/block_editor/application.scss +108 -0
  38. data/app/jobs/block_editor/application_job.rb +4 -0
  39. data/app/mailers/block_editor/application_mailer.rb +6 -0
  40. data/app/models/block_editor/application_record.rb +5 -0
  41. data/app/models/block_editor/block_list.rb +7 -0
  42. data/app/models/concerns/block_editor/listable.rb +24 -0
  43. data/app/views/layouts/block_editor/application.html.erb +15 -0
  44. data/config/initializers/webpacker_extension.rb +12 -0
  45. data/config/routes.rb +2 -0
  46. data/config/webpack/development.js +5 -0
  47. data/config/webpack/environment.js +3 -0
  48. data/config/webpack/production.js +5 -0
  49. data/config/webpack/test.js +5 -0
  50. data/config/webpacker.yml +92 -0
  51. data/db/migrate/20210312032114_create_block_lists.rb +11 -0
  52. data/lib/block_editor.rb +26 -0
  53. data/lib/block_editor/block_list_renderer.rb +43 -0
  54. data/lib/block_editor/blocks/base.rb +32 -0
  55. data/lib/block_editor/engine.rb +34 -0
  56. data/lib/block_editor/instance.rb +19 -0
  57. data/lib/block_editor/version.rb +3 -0
  58. data/lib/tasks/block_editor_tasks.rake +59 -0
  59. 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;