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,14 @@
1
+ .block-editor__header {
2
+ align-items: center;
3
+ background: $white;
4
+ border-bottom: 1px solid $light-gray-500;
5
+ display: flex;
6
+ height: $header-height;
7
+ justify-content: space-between;
8
+ left: 0;
9
+ // Stick the toolbar to the top, because the admin bar is not fixed on mobile.
10
+ position: sticky;
11
+ right: 0;
12
+ top: 0;
13
+ z-index: 1;
14
+ }
@@ -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 { undo as undoIcon } from '@wordpress/icons';
9
+
10
+ function HistoryUndo( { hasUndo, undo, ...props } ) {
11
+ return (
12
+ <Button
13
+ { ...props }
14
+ icon={ undoIcon }
15
+ label={ __( 'Undo' ) }
16
+ shortcut={ displayShortcut.primary( 'z' ) }
17
+ // If there are no undo 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={ ! hasUndo }
21
+ onClick={ hasUndo ? undo : undefined }
22
+ className="editor-history__undo"
23
+ />
24
+ );
25
+ }
26
+
27
+ const EnhancedHistoryUndo = compose( [
28
+ withSelect( ( select ) => ( {
29
+ hasUndo: select( 'block-editor' ).hasUndo(),
30
+ } ) ),
31
+ withDispatch( ( dispatch ) => ( {
32
+ undo: dispatch( 'block-editor' ).undo,
33
+ } ) ),
34
+ ] )( HistoryUndo );
35
+
36
+ export default EnhancedHistoryUndo;
@@ -0,0 +1,37 @@
1
+ import React from 'react'
2
+ import ReactDOM from 'react-dom'
3
+
4
+ /**
5
+ * WordPress dependencies
6
+ */
7
+ import { Component } from '@wordpress/element';
8
+
9
+ class MediaUpload extends Component {
10
+ constructor( {
11
+ allowedTypes,
12
+ gallery = false,
13
+ unstableFeaturedImageFlow = false,
14
+ modalClass,
15
+ multiple = false,
16
+ title = 'Select or Upload Media',
17
+ } ) {
18
+ super( ...arguments );
19
+ this.openUploader = this.openUploader.bind( this );
20
+ }
21
+
22
+ openUploader() {
23
+ if (window.MediaUploader == undefined) {
24
+ console.log('window.MediaUploader undefined. Pulling in random image from Unsplash')
25
+
26
+ this.props.onSelect( { url: 'https://source.unsplash.com/random/800x500' });
27
+ } else {
28
+ this.MediaUploader.open(this.props.onSelect);
29
+ }
30
+ }
31
+
32
+ render() {
33
+ return this.props.render( { open: this.openUploader } );
34
+ }
35
+ }
36
+
37
+ export default MediaUpload;
@@ -0,0 +1,26 @@
1
+ import React from 'react'
2
+ import ReactDOM from 'react-dom'
3
+
4
+ /**
5
+ * WordPress dependencies
6
+ */
7
+ import { useSelect, useDispatch } from '@wordpress/data';
8
+ import { SnackbarList } from '@wordpress/components';
9
+
10
+ export default function Notices() {
11
+ const notices = useSelect(
12
+ ( select ) =>
13
+ select( 'core/notices' )
14
+ .getNotices()
15
+ .filter( ( notice ) => notice.type === 'snackbar' ),
16
+ []
17
+ );
18
+ const { removeNotice } = useDispatch( 'core/notices' );
19
+ return (
20
+ <SnackbarList
21
+ className="edit-site-notices"
22
+ notices={ notices }
23
+ onRemove={ removeNotice }
24
+ />
25
+ );
26
+ }
@@ -0,0 +1,9 @@
1
+ .edit-site-notices {
2
+ bottom: 20px;
3
+ padding-left: 16px;
4
+ padding-right: 16px;
5
+ position: fixed;
6
+ right: 0;
7
+ }
8
+
9
+ @include editor-left(".edit-site-notices");
@@ -0,0 +1,31 @@
1
+ import React from 'react'
2
+ import ReactDOM from 'react-dom'
3
+
4
+ /**
5
+ * WordPress dependencies
6
+ */
7
+ import { createSlotFill, Panel } from '@wordpress/components';
8
+ import { __ } from '@wordpress/i18n';
9
+
10
+ const { Slot: InspectorSlot, Fill: InspectorFill } = createSlotFill(
11
+ 'StandAloneBlockEditorSidebarInspector'
12
+ );
13
+
14
+ function Sidebar() {
15
+ return (
16
+ <div
17
+ className="block-editor__sidebar"
18
+ role="region"
19
+ aria-label={ __( 'Standalone Block Editor advanced settings.' ) }
20
+ tabIndex="-1"
21
+ >
22
+ <Panel header={ __( 'Inspector' ) }>
23
+ <InspectorSlot bubblesVirtually />
24
+ </Panel>
25
+ </div>
26
+ );
27
+ }
28
+
29
+ Sidebar.InspectorFill = InspectorFill;
30
+
31
+ export default Sidebar;
@@ -0,0 +1,43 @@
1
+ .block-editor__sidebar {
2
+ background: $white;
3
+ border-left: $border-width solid $light-gray-500;
4
+ bottom: 0;
5
+ color: $dark-gray-500;
6
+ height: 100vh;
7
+ overflow: hidden;
8
+ position: absolute;
9
+ right: 0;
10
+ top: 0;
11
+ width: $sidebar-width;
12
+ //z-index: z-index(".edit-site-sidebar");
13
+
14
+ @include break-small() {
15
+ -webkit-overflow-scrolling: touch;
16
+ height: auto;
17
+ overflow: auto;
18
+ // top: $admin-bar-height-big + $header-height;
19
+ }
20
+
21
+ // @include break-medium() {
22
+ // top: $admin-bar-height + $header-height;
23
+ // }
24
+
25
+ @include break-small() {
26
+ display: block;
27
+ }
28
+
29
+ > .components-panel {
30
+ border-left: 0;
31
+ border-right: 0;
32
+ margin-bottom: -1px;
33
+ margin-top: -1px;
34
+
35
+ > .components-panel__header {
36
+ background: $light-gray-200;
37
+ }
38
+ }
39
+
40
+ .block-editor-block-inspector__card {
41
+ margin: 0;
42
+ }
43
+ }
@@ -0,0 +1,4 @@
1
+ export const UPDATE_BLOCKS = "UPDATE_BLOCKS";
2
+ export const PERSIST_BLOCKS = "PERSIST_BLOCKS";
3
+ export const FETCH_BLOCKS_FROM_STORAGE = "FETCH_BLOCKS_FROM_STORAGE";
4
+ export const PERSIST_BLOCKS_TO_STORAGE = "PERSIST_BLOCKS_TO_STORAGE";
@@ -0,0 +1,41 @@
1
+ import {
2
+ UPDATE_BLOCKS,
3
+ PERSIST_BLOCKS,
4
+ FETCH_BLOCKS_FROM_STORAGE,
5
+ PERSIST_BLOCKS_TO_STORAGE,
6
+ } from "./action-types";
7
+ import { ActionCreators as ReduxUndo } from "redux-undo";
8
+
9
+
10
+ export function undo() {
11
+ return ReduxUndo.undo();
12
+ }
13
+
14
+ export function redo() {
15
+ return ReduxUndo.redo();
16
+ }
17
+
18
+ export function *updateBlocks( blocks, persist = false ) {
19
+
20
+ if( persist ) {
21
+ yield persistBlocksToStorage(blocks);
22
+ }
23
+
24
+ return {
25
+ type: persist ? PERSIST_BLOCKS : UPDATE_BLOCKS,
26
+ blocks,
27
+ };
28
+ }
29
+
30
+ export function fetchBlocksFromStorage() {
31
+ return {
32
+ type: FETCH_BLOCKS_FROM_STORAGE,
33
+ };
34
+ };
35
+
36
+ export function persistBlocksToStorage(blocks) {
37
+ return {
38
+ type: PERSIST_BLOCKS_TO_STORAGE,
39
+ blocks,
40
+ };
41
+ }
@@ -0,0 +1,21 @@
1
+ import {
2
+ FETCH_BLOCKS_FROM_STORAGE,
3
+ PERSIST_BLOCKS_TO_STORAGE,
4
+ } from "./action-types";
5
+ import { serialize } from "@wordpress/blocks";
6
+
7
+ export default {
8
+ [PERSIST_BLOCKS_TO_STORAGE](action) {
9
+ return new Promise((resolve, reject) => {
10
+ window.localStorage.setItem("blockEditorBlocks", serialize(action.blocks));
11
+ resolve(action.blocks);
12
+ });
13
+ },
14
+ [FETCH_BLOCKS_FROM_STORAGE]() {
15
+ return new Promise((resolve, reject) => {
16
+ const storedBlocks =
17
+ window.localStorage.getItem("blockEditorBlocks") || [];
18
+ resolve(storedBlocks);
19
+ });
20
+ },
21
+ };
@@ -0,0 +1,30 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { registerStore } from '@wordpress/data';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import reducer from './reducer';
10
+ import * as selectors from './selectors';
11
+ import * as actions from './actions';
12
+ import * as resolvers from "./resolvers";
13
+ import controls from './controls';
14
+
15
+ /**
16
+ * Module Constants
17
+ */
18
+ const MODULE_KEY = 'block-editor';
19
+
20
+ const store = registerStore(MODULE_KEY, {
21
+ reducer,
22
+ selectors,
23
+ actions,
24
+ controls,
25
+ resolvers,
26
+ });
27
+
28
+ window.blockEditorStore = store;
29
+
30
+ export default store;
@@ -0,0 +1,20 @@
1
+ import undoable, { groupByActionTypes, includeAction } from "redux-undo";
2
+ import { UPDATE_BLOCKS, PERSIST_BLOCKS } from "./action-types";
3
+
4
+ function blocksReducer(state = [], action) {
5
+ switch (action.type) {
6
+ case UPDATE_BLOCKS:
7
+ case PERSIST_BLOCKS:
8
+ const { blocks } = action;
9
+
10
+ return {
11
+ blocks,
12
+ };
13
+ }
14
+
15
+ return state;
16
+ }
17
+
18
+ export default undoable(blocksReducer, {
19
+ filter: includeAction(PERSIST_BLOCKS),
20
+ });
@@ -0,0 +1,10 @@
1
+ import { parse } from "@wordpress/blocks";
2
+ import { fetchBlocksFromStorage, updateBlocks } from "./actions";
3
+
4
+ export function *getBlocks() {
5
+ const rawBlocks = yield fetchBlocksFromStorage();
6
+ const persist = false;
7
+ const blocks = parse(rawBlocks);
8
+ yield updateBlocks(blocks, persist);
9
+ return blocks;
10
+ }
@@ -0,0 +1,13 @@
1
+ import { createRegistrySelector } from '@wordpress/data';
2
+
3
+ export const getBlocks = ( state ) => {
4
+ return state.present.blocks || [];
5
+ }
6
+
7
+ export const hasUndo = (state) => {
8
+ return state.past?.length;
9
+ };
10
+
11
+ export const hasRedo = (state) => {
12
+ return state.future?.length;
13
+ };
@@ -0,0 +1,42 @@
1
+ import React from 'react'
2
+ import ReactDOM from 'react-dom'
3
+ import PropTypes from 'prop-types'
4
+
5
+ import { Controller } from "stimulus"
6
+
7
+ import { render } from '@wordpress/element'
8
+
9
+ import BlockEditor from '../block_editor/components/block-editor'
10
+ import { registerBlocks } from '../block_editor/blocks'
11
+
12
+ registerBlocks()
13
+
14
+ export default class extends Controller {
15
+ static targets = [ "output", "input" ]
16
+
17
+ connect() {
18
+ const settings = {
19
+ imageSizes: false,
20
+ disableCustomFontSizes: true,
21
+ fontSizes: false,
22
+ disableCustomColors: true,
23
+ colors: false,
24
+ disableCustomGradients: true,
25
+ __experimentalDisableDropCap: true,
26
+ __experimentalDisableCustomLineHeight: true,
27
+ mediaUpload: function uploadMedia( {
28
+ allowedTypes,
29
+ additionalData = {},
30
+ filesList,
31
+ maxUploadFileSize,
32
+ onError = noop,
33
+ onFileChange,
34
+ wpAllowedMimeTypes = null,
35
+ } ) {
36
+ }
37
+ }
38
+
39
+ window.localStorage.setItem("blockEditorBlocks", this.inputTarget.value);
40
+ this.editor = render( <BlockEditor input={ this.inputTarget } settings={ settings } />, this.outputTarget )
41
+ }
42
+ }
@@ -0,0 +1,6 @@
1
+ import { Application } from "stimulus"
2
+ import { definitionsFromContext } from "stimulus/webpack-helpers"
3
+
4
+ const application = Application.start()
5
+ const context = require.context("controllers", true, /_controller\.jsx$/)
6
+ application.load(definitionsFromContext(context))
@@ -0,0 +1,2 @@
1
+ import "controllers"
2
+ import './application.scss'
@@ -0,0 +1,108 @@
1
+ $primary-color: #1b8ecf;
2
+
3
+ @function theme($selector) {
4
+ @return $primary-color;
5
+ }
6
+
7
+ @function color($color) {
8
+ @return $color;
9
+ }
10
+
11
+ @import "~@wordpress/base-styles/colors";
12
+ @import "~@wordpress/base-styles/variables";
13
+ @import "~@wordpress/base-styles/mixins";
14
+ @import "~@wordpress/base-styles/breakpoints";
15
+ @import "~@wordpress/base-styles/animations";
16
+ @import "~@wordpress/base-styles/z-index";
17
+
18
+ .block-editor {
19
+ label {
20
+ font-size: $default-font-size;
21
+ }
22
+ }
23
+
24
+ @import "~@wordpress/components/src/style";
25
+ @import "~@wordpress/block-editor/src/style";
26
+ @import "~@wordpress/block-library/src/style";
27
+
28
+ $header-height: 50px;
29
+
30
+ // Internal
31
+ @import "../../block_editor/components/sidebar/styles";
32
+ @import "../../block_editor/components/header/styles";
33
+ @import "../../block_editor/components/block-editor/styles";
34
+
35
+ .block-editor {
36
+ @include reset;
37
+ position: relative;
38
+ margin: 1rem 0;
39
+ padding: 0;
40
+ font-family: $default-font;
41
+ font-size: $default-font-size;
42
+ border: 1px solid #f3f4f5;
43
+ min-height: 500px;
44
+
45
+ a,
46
+ div {
47
+ outline: 0;
48
+ }
49
+
50
+ .block-editor-url-popover__additional-controls,
51
+ .block-editor-media-replace-flow__options .components-form-file-upload {
52
+ display: none;
53
+ }
54
+
55
+ &:not(.block-editor__fullscreen) {
56
+ .block-editor__size-toggle-button__minimize {
57
+ display: none;
58
+ }
59
+ }
60
+
61
+ &.block-editor__fullscreen {
62
+ .block-editor__size-toggle-button__maximize {
63
+ display: none;
64
+ }
65
+ }
66
+
67
+ &__size-toggle-button {
68
+ position: absolute;
69
+ right: 0;
70
+ top: 7px;
71
+ z-index: 1;
72
+ }
73
+
74
+ &__fullscreen {
75
+ position: absolute;
76
+ top: 0px;
77
+ right: 0px;
78
+ bottom: 0px;
79
+ left: 0px;
80
+ padding: 0;
81
+ z-index: 99;
82
+ margin: 0;
83
+ .block-editor {
84
+ height: 100%;
85
+ }
86
+ }
87
+ }
88
+
89
+ // In order to use mix-blend-mode, this element needs to have an explicitly set background-color.
90
+ // We scope it to .wp-toolbar to be wp-admin only, to prevent bleed into other implementations.
91
+ html.wp-toolbar {
92
+ background: $white;
93
+ }
94
+
95
+ // The modals are shown outside the .block-editor wrapper, they need these styles.
96
+ .block-editor, .components-modal__frame {
97
+ @include reset;
98
+ }
99
+
100
+ .block-editor-block-breadcrumb {
101
+ position: absolute;
102
+ bottom: 0;
103
+ z-index: 1;
104
+ background-color: $white;
105
+ width: 100%;
106
+ border-top: 1px solid #f3f4f5;
107
+ padding: 0.5rem 0;
108
+ }