block_editor 0.1.3 → 1.0.0
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.
- checksums.yaml +4 -4
- data/README.md +35 -14
- data/app/assets/images/block_editor/contact-form-block.svg +1 -0
- data/app/assets/stylesheets/block_editor/_utilities.scss +16 -0
- data/app/assets/stylesheets/block_editor/backend.scss +70 -2
- data/app/assets/stylesheets/block_editor/blocks/_backend.scss +7 -0
- data/app/assets/stylesheets/block_editor/blocks/_frontend.scss +11 -0
- data/app/assets/stylesheets/block_editor/blocks/be-accordion/_frontend.scss +3 -0
- data/app/assets/stylesheets/block_editor/blocks/be-alert/_frontend.scss +13 -0
- data/app/assets/stylesheets/block_editor/blocks/be-card/_backend.scss +7 -0
- data/app/assets/stylesheets/block_editor/{backend/blocks.scss → blocks/be-card/_frontend.scss} +0 -0
- data/app/assets/stylesheets/block_editor/blocks/be-contact-form/_backend.scss +22 -0
- data/app/assets/stylesheets/block_editor/blocks/be-cover/_backend.scss +8 -0
- data/app/assets/stylesheets/block_editor/blocks/be-cover/_frontend.scss +37 -0
- data/app/assets/stylesheets/block_editor/blocks/block/_backend.scss +11 -0
- data/app/assets/stylesheets/block_editor/blocks/button/_frontend.scss +46 -0
- data/app/assets/stylesheets/block_editor/blocks/buttons/_backend.scss +10 -0
- data/app/assets/stylesheets/block_editor/blocks/buttons/_frontend.scss +3 -0
- data/app/assets/stylesheets/block_editor/blocks/column/_frontend.scss +18 -0
- data/app/assets/stylesheets/block_editor/blocks/columns/_backend.scss +8 -0
- data/app/assets/stylesheets/block_editor/blocks/columns/_frontend.scss +14 -0
- data/app/assets/stylesheets/block_editor/blocks/image/_backend.scss +10 -0
- data/app/assets/stylesheets/block_editor/blocks/image/_frontend.scss +21 -0
- data/app/assets/stylesheets/block_editor/blocks/seperator/_frontend.scss +19 -0
- data/app/assets/stylesheets/block_editor/blocks/table/_frontend.scss +67 -0
- data/app/assets/stylesheets/block_editor/host_app/_variables.scss +1 -0
- data/app/assets/stylesheets/block_editor/host_app/blocks/_backend.scss +1 -0
- data/app/assets/stylesheets/block_editor/host_app/blocks/_frontend.scss +1 -0
- data/app/javascript/block_editor/blocks/be-accordion/index.js +108 -0
- data/app/javascript/block_editor/blocks/be-alert/index.js +51 -0
- data/app/javascript/block_editor/blocks/be-card/index.js +205 -0
- data/app/javascript/block_editor/blocks/be-contact-form/index.js +24 -0
- data/app/javascript/block_editor/blocks/be-cover/index.js +135 -0
- data/app/javascript/block_editor/blocks/block/edit-panel/index.js +132 -0
- data/app/javascript/block_editor/blocks/block/edit.js +163 -0
- data/app/javascript/block_editor/blocks/button/edit.js +0 -13
- data/app/javascript/block_editor/blocks/index.js +51 -102
- data/app/javascript/block_editor/components/block-editor/index.js +107 -36
- data/app/javascript/block_editor/components/block-editor/popover-wrapper.js +60 -0
- data/app/javascript/block_editor/components/block-editor/styles.scss +0 -11
- data/app/javascript/block_editor/components/header/index.js +28 -6
- data/app/javascript/block_editor/components/header/redo.js +1 -1
- data/app/javascript/block_editor/components/header/styles.scss +12 -11
- data/app/javascript/block_editor/components/media-upload/index.js +3 -3
- data/app/javascript/block_editor/components/sidebar/index.js +1 -3
- data/app/javascript/block_editor/components/sidebar/styles.scss +35 -35
- data/app/javascript/block_editor/stores/actions.js +12 -0
- data/app/javascript/block_editor/stores/reducer.js +23 -3
- data/app/javascript/block_editor/stores/selectors.js +14 -3
- data/app/javascript/controllers/block_editor_controller.jsx +15 -8
- data/app/javascript/controllers/index.js +2 -0
- data/app/javascript/packs/block_editor/application.scss +70 -26
- data/app/models/block_editor/block_list.rb +45 -1
- data/app/models/block_editor/block_list_connection.rb +6 -0
- data/app/views/block_editor/blocks/be/contact-form/_block.html +3 -0
- data/db/migrate/20210506220328_create_block_list_connections.rb +8 -0
- data/lib/block_editor.rb +3 -1
- data/lib/block_editor/block_list_renderer.rb +12 -6
- data/lib/block_editor/blocks/base.rb +1 -1
- data/lib/block_editor/blocks/contact_form.rb +11 -0
- data/lib/block_editor/blocks/reusable.rb +16 -0
- data/lib/block_editor/version.rb +1 -1
- data/package.json +16 -7
- data/yarn.lock +727 -530
- metadata +42 -8
- data/app/assets/stylesheets/block_editor/frontend.scss +0 -1
- data/app/assets/stylesheets/block_editor/frontend/blocks.scss +0 -0
- data/app/javascript/block_editor/blocks/image/edit.js +0 -656
@@ -0,0 +1,24 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import ReactDOM from 'react-dom';
|
3
|
+
|
4
|
+
import { widget as icon } from '@wordpress/icons';
|
5
|
+
|
6
|
+
const name = 'be/contact-form';
|
7
|
+
|
8
|
+
export { name };
|
9
|
+
|
10
|
+
export const settings = {
|
11
|
+
title: 'Contact Form',
|
12
|
+
description: 'Allow users to get in touch using a contact form.',
|
13
|
+
icon,
|
14
|
+
category: 'widgets',
|
15
|
+
edit({attributes, className, setAttributes, isSelected}) {
|
16
|
+
return ([
|
17
|
+
<div className={ className }>
|
18
|
+
<p>Contact Form</p>
|
19
|
+
<div className='be-contact-form-outline'>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
]);
|
23
|
+
}
|
24
|
+
};
|
@@ -0,0 +1,135 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import ReactDOM from 'react-dom';
|
3
|
+
|
4
|
+
import {
|
5
|
+
InnerBlocks
|
6
|
+
} from '@wordpress/block-editor';
|
7
|
+
import { registerBlockStyle } from '@wordpress/blocks';
|
8
|
+
import { createBlock } from '@wordpress/blocks';
|
9
|
+
import { rawHandler } from '@wordpress/blocks';
|
10
|
+
|
11
|
+
import { TextControl, PanelBody, ToggleControl, Button } from '@wordpress/components';
|
12
|
+
import { InspectorControls, RichText, MediaUpload, PlainText } from '@wordpress/block-editor'
|
13
|
+
import { cover as icon } from '@wordpress/icons';
|
14
|
+
|
15
|
+
const BLOCKS_TEMPLATE = [
|
16
|
+
[ 'core/heading', { content: 'Example Cover Title' } ],
|
17
|
+
[ 'core/paragraph', { content: 'Break up content and draw attention to something using a cover block' } ]
|
18
|
+
];
|
19
|
+
const ALLOWED_BLOCKS = [ 'core/buttons', 'core/heading', 'core/paragraph', 'core/list' ];
|
20
|
+
const name = 'be/cover';
|
21
|
+
|
22
|
+
export { name };
|
23
|
+
|
24
|
+
export const settings = {
|
25
|
+
title: 'Cover',
|
26
|
+
description: 'Add an image with a text overlay - great for breaking up content.',
|
27
|
+
icon,
|
28
|
+
category: 'formatting',
|
29
|
+
example: {
|
30
|
+
innerBlocks: [
|
31
|
+
{
|
32
|
+
name: 'core/heading',
|
33
|
+
attributes: {
|
34
|
+
content: 'Example Cover Title'
|
35
|
+
}
|
36
|
+
},
|
37
|
+
{
|
38
|
+
name: 'core/paragraph',
|
39
|
+
attributes: {
|
40
|
+
content: 'Break up content and draw attention to something using a cover block'
|
41
|
+
}
|
42
|
+
},
|
43
|
+
{
|
44
|
+
name: 'core/button',
|
45
|
+
attributes: {
|
46
|
+
content: 'Example Call To Action'
|
47
|
+
}
|
48
|
+
}
|
49
|
+
]
|
50
|
+
},
|
51
|
+
attributes: {
|
52
|
+
imageAlt: {
|
53
|
+
attribute: 'alt',
|
54
|
+
selector: '.wp-block-be-cover-image'
|
55
|
+
},
|
56
|
+
imageUrl: {
|
57
|
+
attribute: 'src',
|
58
|
+
selector: '.wp-block-be-cover-image'
|
59
|
+
}
|
60
|
+
},
|
61
|
+
edit({attributes, className, setAttributes, isSelected}) {
|
62
|
+
const getImageButton = (openEvent) => {
|
63
|
+
if(attributes.imageUrl) {
|
64
|
+
return (
|
65
|
+
<>
|
66
|
+
{ isSelected &&
|
67
|
+
<Button
|
68
|
+
onClick={ openEvent }
|
69
|
+
className="button"
|
70
|
+
>
|
71
|
+
Edit image
|
72
|
+
</Button>
|
73
|
+
}
|
74
|
+
<img
|
75
|
+
src={ attributes.imageUrl }
|
76
|
+
className="wp-block-be-cover-image"
|
77
|
+
/>
|
78
|
+
</>
|
79
|
+
);
|
80
|
+
}
|
81
|
+
else {
|
82
|
+
return (
|
83
|
+
<>
|
84
|
+
{ isSelected &&
|
85
|
+
<Button
|
86
|
+
onClick={ openEvent }
|
87
|
+
className="button button-large"
|
88
|
+
>
|
89
|
+
Pick an image
|
90
|
+
</Button>
|
91
|
+
}
|
92
|
+
</>
|
93
|
+
);
|
94
|
+
}
|
95
|
+
};
|
96
|
+
return (
|
97
|
+
<div className={ className }>
|
98
|
+
<MediaUpload
|
99
|
+
onSelect={ media => { setAttributes({ imageAlt: media.alt, imageUrl: media.url }); } }
|
100
|
+
type="image"
|
101
|
+
value={ attributes.imageID }
|
102
|
+
render={ ({ open }) => getImageButton(open) }
|
103
|
+
/>
|
104
|
+
<div className='wp-block-be-cover-content'>
|
105
|
+
<InnerBlocks
|
106
|
+
allowedBlocks={ ALLOWED_BLOCKS }
|
107
|
+
template={ BLOCKS_TEMPLATE }
|
108
|
+
/>
|
109
|
+
</div>
|
110
|
+
</div>
|
111
|
+
);
|
112
|
+
},
|
113
|
+
save({ attributes }) {
|
114
|
+
const cardImage = (src, alt) => {
|
115
|
+
if(!src) return null;
|
116
|
+
|
117
|
+
return (
|
118
|
+
<img
|
119
|
+
className="wp-block-be-cover-image"
|
120
|
+
src={ src }
|
121
|
+
alt={ alt }
|
122
|
+
/>
|
123
|
+
);
|
124
|
+
}
|
125
|
+
|
126
|
+
return (
|
127
|
+
<div>
|
128
|
+
{ cardImage(attributes.imageUrl, attributes.imageAlt) }
|
129
|
+
<div className='wp-block-be-cover-content'>
|
130
|
+
<InnerBlocks.Content />
|
131
|
+
</div>
|
132
|
+
</div>
|
133
|
+
);
|
134
|
+
},
|
135
|
+
};
|
@@ -0,0 +1,132 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import ReactDOM from 'react-dom';
|
3
|
+
|
4
|
+
/**
|
5
|
+
* WordPress dependencies
|
6
|
+
*/
|
7
|
+
import { Button } from '@wordpress/components';
|
8
|
+
import { useInstanceId, usePrevious } from '@wordpress/compose';
|
9
|
+
import { useEffect, useRef } from '@wordpress/element';
|
10
|
+
import { __ } from '@wordpress/i18n';
|
11
|
+
|
12
|
+
/** @typedef {import('@wordpress/element').WPComponent} WPComponent */
|
13
|
+
|
14
|
+
/**
|
15
|
+
* ReusableBlockEditPanel props.
|
16
|
+
*
|
17
|
+
* @typedef WPReusableBlockEditPanelProps
|
18
|
+
*
|
19
|
+
* @property {boolean} isEditDisabled Is editing the reusable
|
20
|
+
* block disabled.
|
21
|
+
* @property {boolean} isEditing Is the reusable block
|
22
|
+
* being edited.
|
23
|
+
* @property {boolean} isSaving Is the reusable block
|
24
|
+
* being saved.
|
25
|
+
* @property {()=>void} onCancel Callback to run when
|
26
|
+
* editing is canceled.
|
27
|
+
* @property {(newTitle:string)=>void} onChangeTitle Callback to run when the
|
28
|
+
* title input value is
|
29
|
+
* changed.
|
30
|
+
* @property {()=>void} onEdit Callback to run when
|
31
|
+
* editing begins.
|
32
|
+
* @property {()=>void} onSave Callback to run when
|
33
|
+
* saving.
|
34
|
+
* @property {string} title Title of the reusable
|
35
|
+
* block.
|
36
|
+
*/
|
37
|
+
|
38
|
+
/**
|
39
|
+
* Panel for enabling the editing and saving of a reusable block.
|
40
|
+
*
|
41
|
+
* @param {WPReusableBlockEditPanelProps} props Component props.
|
42
|
+
*
|
43
|
+
* @return {WPComponent} The panel.
|
44
|
+
*/
|
45
|
+
export default function ReusableBlockEditPanel( {
|
46
|
+
isEditDisabled,
|
47
|
+
isEditing,
|
48
|
+
isSaving,
|
49
|
+
onChangeTitle,
|
50
|
+
onEdit,
|
51
|
+
onSave,
|
52
|
+
title,
|
53
|
+
} ) {
|
54
|
+
const instanceId = useInstanceId( ReusableBlockEditPanel );
|
55
|
+
const titleField = useRef();
|
56
|
+
const editButton = useRef();
|
57
|
+
const wasEditing = usePrevious( isEditing );
|
58
|
+
const wasSaving = usePrevious( isSaving );
|
59
|
+
|
60
|
+
// Select the title input when the form opens.
|
61
|
+
useEffect( () => {
|
62
|
+
if ( ! wasEditing && isEditing ) {
|
63
|
+
titleField.current.select();
|
64
|
+
}
|
65
|
+
}, [ isEditing ] );
|
66
|
+
|
67
|
+
// Move focus back to the Edit button after pressing the Escape key or Save.
|
68
|
+
useEffect( () => {
|
69
|
+
if ( ( wasEditing || wasSaving ) && ! isEditing && ! isSaving ) {
|
70
|
+
editButton.current.focus();
|
71
|
+
}
|
72
|
+
}, [ isEditing, isSaving ] );
|
73
|
+
|
74
|
+
function handleFormSubmit( event ) {
|
75
|
+
event.preventDefault();
|
76
|
+
onSave();
|
77
|
+
}
|
78
|
+
|
79
|
+
function handleTitleChange( event ) {
|
80
|
+
onChangeTitle( event.target.value );
|
81
|
+
}
|
82
|
+
|
83
|
+
return (
|
84
|
+
<>
|
85
|
+
{ ! isEditing && ! isSaving && (
|
86
|
+
<div className="reusable-block-edit-panel">
|
87
|
+
<b className="reusable-block-edit-panel__info">{ title }</b>
|
88
|
+
<Button
|
89
|
+
ref={ editButton }
|
90
|
+
isSecondary
|
91
|
+
className="reusable-block-edit-panel__button"
|
92
|
+
disabled={ isEditDisabled }
|
93
|
+
onClick={ onEdit }
|
94
|
+
>
|
95
|
+
{ __( 'Edit' ) }
|
96
|
+
</Button>
|
97
|
+
</div>
|
98
|
+
) }
|
99
|
+
{ ( isEditing || isSaving ) && (
|
100
|
+
<form
|
101
|
+
className="reusable-block-edit-panel"
|
102
|
+
onSubmit={ handleFormSubmit }
|
103
|
+
>
|
104
|
+
<label
|
105
|
+
htmlFor={ `reusable-block-edit-panel__title-${ instanceId }` }
|
106
|
+
className="reusable-block-edit-panel__label"
|
107
|
+
>
|
108
|
+
{ __( 'Name:' ) }
|
109
|
+
</label>
|
110
|
+
<input
|
111
|
+
ref={ titleField }
|
112
|
+
type="text"
|
113
|
+
disabled={ isSaving }
|
114
|
+
className="reusable-block-edit-panel__title"
|
115
|
+
value={ title }
|
116
|
+
onChange={ handleTitleChange }
|
117
|
+
id={ `reusable-block-edit-panel__title-${ instanceId }` }
|
118
|
+
/>
|
119
|
+
<Button
|
120
|
+
type="submit"
|
121
|
+
isSecondary
|
122
|
+
isBusy={ isSaving }
|
123
|
+
disabled={ ! title || isSaving }
|
124
|
+
className="reusable-block-edit-panel__button"
|
125
|
+
>
|
126
|
+
{ __( 'Save' ) }
|
127
|
+
</Button>
|
128
|
+
</form>
|
129
|
+
) }
|
130
|
+
</>
|
131
|
+
);
|
132
|
+
}
|
@@ -0,0 +1,163 @@
|
|
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 { useEntityBlockEditor } from '@wordpress/core-data';
|
9
|
+
import { useCallback } from '@wordpress/element';
|
10
|
+
import {
|
11
|
+
Placeholder,
|
12
|
+
Spinner,
|
13
|
+
Disabled,
|
14
|
+
ToolbarGroup,
|
15
|
+
ToolbarButton,
|
16
|
+
} from '@wordpress/components';
|
17
|
+
import { __ } from '@wordpress/i18n';
|
18
|
+
import {
|
19
|
+
BlockEditorProvider,
|
20
|
+
WritingFlow,
|
21
|
+
BlockList,
|
22
|
+
BlockControls,
|
23
|
+
useBlockProps,
|
24
|
+
} from '@wordpress/block-editor';
|
25
|
+
|
26
|
+
/**
|
27
|
+
* Internal dependencies
|
28
|
+
*/
|
29
|
+
import ReusableBlockEditPanel from './edit-panel';
|
30
|
+
|
31
|
+
export default function ReusableBlockEdit( {
|
32
|
+
attributes: { ref },
|
33
|
+
clientId,
|
34
|
+
isSelected,
|
35
|
+
} ) {
|
36
|
+
const recordArgs = [ 'postType', 'wp_block', ref ];
|
37
|
+
|
38
|
+
const {
|
39
|
+
reusableBlock,
|
40
|
+
hasResolved,
|
41
|
+
isEditing,
|
42
|
+
isSaving,
|
43
|
+
canUserUpdate,
|
44
|
+
settings,
|
45
|
+
} = useSelect(
|
46
|
+
( select ) => ( {
|
47
|
+
reusableBlock: select( 'core' ).getEditedEntityRecord(
|
48
|
+
...recordArgs
|
49
|
+
),
|
50
|
+
hasResolved: select( 'core' ).hasFinishedResolution(
|
51
|
+
'getEditedEntityRecord',
|
52
|
+
recordArgs
|
53
|
+
),
|
54
|
+
isSaving: select( 'core' ).isSavingEntityRecord( ...recordArgs ),
|
55
|
+
canUserUpdate: select( 'core' ).canUser( 'update', 'blocks', ref ),
|
56
|
+
isEditing: select(
|
57
|
+
'core/reusable-blocks'
|
58
|
+
).__experimentalIsEditingReusableBlock( clientId ),
|
59
|
+
settings: select( 'core/block-editor' ).getSettings(),
|
60
|
+
} ),
|
61
|
+
[ ref, clientId ]
|
62
|
+
);
|
63
|
+
|
64
|
+
const { editEntityRecord, saveEditedEntityRecord } = useDispatch( 'core' );
|
65
|
+
const { __experimentalSetEditingReusableBlock } = useDispatch(
|
66
|
+
'core/reusable-blocks'
|
67
|
+
);
|
68
|
+
const setIsEditing = useCallback(
|
69
|
+
( value ) => {
|
70
|
+
__experimentalSetEditingReusableBlock( clientId, value );
|
71
|
+
},
|
72
|
+
[ clientId ]
|
73
|
+
);
|
74
|
+
|
75
|
+
const {
|
76
|
+
__experimentalConvertBlockToStatic: convertBlockToStatic,
|
77
|
+
} = useDispatch( 'core/reusable-blocks' );
|
78
|
+
|
79
|
+
const { createSuccessNotice, createErrorNotice } = useDispatch(
|
80
|
+
'core/notices'
|
81
|
+
);
|
82
|
+
const save = useCallback( async function () {
|
83
|
+
try {
|
84
|
+
await saveEditedEntityRecord( ...recordArgs );
|
85
|
+
createSuccessNotice( __( 'Block updated.' ), {
|
86
|
+
type: 'snackbar',
|
87
|
+
} );
|
88
|
+
} catch ( error ) {
|
89
|
+
createErrorNotice( error.message, {
|
90
|
+
type: 'snackbar',
|
91
|
+
} );
|
92
|
+
}
|
93
|
+
}, recordArgs );
|
94
|
+
|
95
|
+
const [ blocks, onInput, onChange ] = useEntityBlockEditor(
|
96
|
+
'postType',
|
97
|
+
'wp_block',
|
98
|
+
{ id: ref }
|
99
|
+
);
|
100
|
+
|
101
|
+
const blockProps = useBlockProps();
|
102
|
+
|
103
|
+
if ( ! hasResolved ) {
|
104
|
+
return (
|
105
|
+
<div { ...blockProps }>
|
106
|
+
<Placeholder>
|
107
|
+
<Spinner />
|
108
|
+
</Placeholder>
|
109
|
+
</div>
|
110
|
+
);
|
111
|
+
}
|
112
|
+
|
113
|
+
if ( ! reusableBlock ) {
|
114
|
+
return (
|
115
|
+
<div { ...blockProps }>
|
116
|
+
<Placeholder>
|
117
|
+
{ __( 'Block has been deleted or is unavailable.' ) }
|
118
|
+
</Placeholder>
|
119
|
+
</div>
|
120
|
+
);
|
121
|
+
}
|
122
|
+
|
123
|
+
let element = (
|
124
|
+
<BlockEditorProvider
|
125
|
+
value={ blocks }
|
126
|
+
onInput={ onInput }
|
127
|
+
onChange={ onChange }
|
128
|
+
settings={ settings }
|
129
|
+
>
|
130
|
+
<WritingFlow>
|
131
|
+
<BlockList />
|
132
|
+
</WritingFlow>
|
133
|
+
</BlockEditorProvider>
|
134
|
+
);
|
135
|
+
|
136
|
+
if ( ! isEditing ) {
|
137
|
+
element = <Disabled>{ element }</Disabled>;
|
138
|
+
}
|
139
|
+
|
140
|
+
return (
|
141
|
+
<div { ...blockProps }>
|
142
|
+
<div className="block-library-block__reusable-block-container">
|
143
|
+
{ ( isSelected || isEditing ) && (
|
144
|
+
<ReusableBlockEditPanel
|
145
|
+
isEditing={ isEditing }
|
146
|
+
title={ reusableBlock.title }
|
147
|
+
isSaving={ isSaving }
|
148
|
+
isEditDisabled={ ! canUserUpdate }
|
149
|
+
onEdit={ () => setIsEditing( true ) }
|
150
|
+
onChangeTitle={ ( title ) =>
|
151
|
+
editEntityRecord( ...recordArgs, { title } )
|
152
|
+
}
|
153
|
+
onSave={ () => {
|
154
|
+
save();
|
155
|
+
setIsEditing( false );
|
156
|
+
} }
|
157
|
+
/>
|
158
|
+
) }
|
159
|
+
{ element }
|
160
|
+
</div>
|
161
|
+
</div>
|
162
|
+
);
|
163
|
+
}
|