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,14 @@
|
|
1
|
+
.wp-block-columns {
|
2
|
+
--#{$variable-prefix}gutter-x: #{$grid-gutter-width};
|
3
|
+
--#{$variable-prefix}gutter-y: 0;
|
4
|
+
display: flex;
|
5
|
+
margin-top: calc(var(--#{$variable-prefix}gutter-y) * -1); // stylelint-disable-line function-disallowed-list
|
6
|
+
margin-right: calc(var(--#{$variable-prefix}gutter-x) / -2); // stylelint-disable-line function-disallowed-list
|
7
|
+
margin-left: calc(var(--#{$variable-prefix}gutter-x) / -2); // stylelint-disable-line function-disallowed-list
|
8
|
+
|
9
|
+
&:not(.is-style-no-stack) {
|
10
|
+
@include media-breakpoint-down(lg) {
|
11
|
+
display: block;
|
12
|
+
}
|
13
|
+
}
|
14
|
+
}
|
@@ -0,0 +1,10 @@
|
|
1
|
+
.block-editor-block-list__block[data-type="core/image"] {
|
2
|
+
.block-editor-media-placeholder__url-input-container,
|
3
|
+
.components-form-file-upload {
|
4
|
+
display: none;
|
5
|
+
}
|
6
|
+
}
|
7
|
+
.components-dropdown-menu__toggle[aria-label="Change alignment"] {
|
8
|
+
display: none !important;
|
9
|
+
}
|
10
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
.wp-block-image {
|
2
|
+
margin-bottom: $paragraph-margin-bottom;
|
3
|
+
|
4
|
+
img {
|
5
|
+
max-width: 100%;
|
6
|
+
}
|
7
|
+
figcaption {
|
8
|
+
margin-top: 5px;
|
9
|
+
font-size: 0.9rem;
|
10
|
+
}
|
11
|
+
&.is-style-padded {
|
12
|
+
max-width: 75%;
|
13
|
+
margin-left: auto;
|
14
|
+
margin-right: auto;
|
15
|
+
}
|
16
|
+
&.is-style-rounded {
|
17
|
+
img {
|
18
|
+
border-radius: $border-radius;
|
19
|
+
}
|
20
|
+
}
|
21
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
.wp-block-separator {
|
2
|
+
&.is-style-dots {
|
3
|
+
background: none !important;
|
4
|
+
border: none;
|
5
|
+
text-align: center;
|
6
|
+
max-width: none;
|
7
|
+
line-height: 1;
|
8
|
+
height: 1.2*$spacer !important;
|
9
|
+
opacity: 1;
|
10
|
+
&:before{
|
11
|
+
content: "\00b7 \00b7 \00b7";
|
12
|
+
font-size: 1.2*$spacer;
|
13
|
+
letter-spacing: 2em;
|
14
|
+
padding-left: 2em;
|
15
|
+
font-family: serif;
|
16
|
+
color: $black;
|
17
|
+
}
|
18
|
+
}
|
19
|
+
}
|
@@ -0,0 +1,67 @@
|
|
1
|
+
.wp-block-table{
|
2
|
+
margin-bottom: $paragraph-margin-bottom;
|
3
|
+
overflow-x: auto;
|
4
|
+
table {
|
5
|
+
--#{$variable-prefix}table-bg: #{$table-bg};
|
6
|
+
--#{$variable-prefix}table-striped-color: #{$table-striped-color};
|
7
|
+
--#{$variable-prefix}table-striped-bg: #{$table-striped-bg};
|
8
|
+
--#{$variable-prefix}table-active-color: #{$table-active-color};
|
9
|
+
--#{$variable-prefix}table-active-bg: #{$table-active-bg};
|
10
|
+
--#{$variable-prefix}table-hover-color: #{$table-hover-color};
|
11
|
+
--#{$variable-prefix}table-hover-bg: #{$table-hover-bg};
|
12
|
+
|
13
|
+
width: 100%;
|
14
|
+
color: $table-color;
|
15
|
+
vertical-align: $table-cell-vertical-align;
|
16
|
+
border-color: $table-border-color;
|
17
|
+
overflow-x: auto;
|
18
|
+
-webkit-overflow-scrolling: touch;
|
19
|
+
> :not(caption) > * > * {
|
20
|
+
padding: $table-cell-padding-y $table-cell-padding-x;
|
21
|
+
background-color: var(--#{$variable-prefix}table-bg);
|
22
|
+
border-bottom-width: $table-border-width;
|
23
|
+
box-shadow: inset 0 0 0 9999px var(--#{$variable-prefix}table-accent-bg);
|
24
|
+
}
|
25
|
+
|
26
|
+
> tbody {
|
27
|
+
vertical-align: inherit;
|
28
|
+
}
|
29
|
+
> tbody > tr > th {
|
30
|
+
text-align: center;
|
31
|
+
}
|
32
|
+
|
33
|
+
> thead {
|
34
|
+
vertical-align: bottom;
|
35
|
+
background: $secondary;
|
36
|
+
color: $white;
|
37
|
+
}
|
38
|
+
> thead > tr > th:first-child{
|
39
|
+
text-align: center;
|
40
|
+
}
|
41
|
+
|
42
|
+
// Highlight border color between thead, tbody and tfoot.
|
43
|
+
> :not(:last-child) > :last-child > * {
|
44
|
+
border-bottom-color: $table-group-separator-color;
|
45
|
+
}
|
46
|
+
//border
|
47
|
+
> :not(caption) > * {
|
48
|
+
border-width: $table-border-width 0;
|
49
|
+
|
50
|
+
// stylelint-disable-next-line selector-max-universal
|
51
|
+
> * {
|
52
|
+
border-width: 0 $table-border-width;
|
53
|
+
}
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
> figcaption {
|
58
|
+
margin-top: $paragraph-margin-bottom / 4;
|
59
|
+
}
|
60
|
+
|
61
|
+
&.is-style-striped {
|
62
|
+
table > tbody > tr:nth-of-type(#{$table-striped-order}) {
|
63
|
+
--#{$variable-prefix}table-accent-bg: var(--#{$variable-prefix}table-striped-bg);
|
64
|
+
color: var(--#{$variable-prefix}table-striped-color);
|
65
|
+
}
|
66
|
+
}
|
67
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
// Override this file if you want to include any styling before default Block Editor styles are loaded. This would be a good place to add variables to customise Bootstrap
|
@@ -0,0 +1 @@
|
|
1
|
+
// Override this file if you want to include backend block styles to the Block Editor and not have to worry about losing any base styles
|
@@ -0,0 +1 @@
|
|
1
|
+
// Override this file if you want to include frontend block styles to the Block Editor and not have to worry about losing any base styles
|
@@ -0,0 +1,108 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import ReactDOM from 'react-dom';
|
3
|
+
import classnames from 'classnames';
|
4
|
+
|
5
|
+
import { PanelBody, ToggleControl } from '@wordpress/components';
|
6
|
+
import { InnerBlocks, InspectorControls, PlainText } from '@wordpress/block-editor'
|
7
|
+
import { box as icon } from '@wordpress/icons';
|
8
|
+
|
9
|
+
const name = 'be/accordion';
|
10
|
+
|
11
|
+
export { name };
|
12
|
+
|
13
|
+
export const settings = {
|
14
|
+
title: 'Accordion',
|
15
|
+
description: 'Accordions are elements that help you organize and navigate multiple documents in a single container.',
|
16
|
+
icon,
|
17
|
+
category: 'layout',
|
18
|
+
attributes: {
|
19
|
+
blockId: {
|
20
|
+
type: 'string'
|
21
|
+
},
|
22
|
+
title: {
|
23
|
+
source: 'text',
|
24
|
+
selector: '.accordion-button'
|
25
|
+
},
|
26
|
+
isOpenByDefault: {
|
27
|
+
type: 'boolean',
|
28
|
+
default: true
|
29
|
+
}
|
30
|
+
},
|
31
|
+
example: {
|
32
|
+
attributes: {
|
33
|
+
title: 'Open by default accordion'
|
34
|
+
},
|
35
|
+
innerBlocks: [
|
36
|
+
{
|
37
|
+
name: 'core/paragraph',
|
38
|
+
attributes: {
|
39
|
+
content: 'Use an accordion to structure and optionally collapse content'
|
40
|
+
}
|
41
|
+
}
|
42
|
+
]
|
43
|
+
},
|
44
|
+
edit({clientId, attributes, className, setAttributes, isSelected}) {
|
45
|
+
const { blockId } = attributes;
|
46
|
+
if ( ! blockId ) {
|
47
|
+
setAttributes( { blockId: `id-${clientId}` } );
|
48
|
+
} else if ( blockId != clientId ) {
|
49
|
+
setAttributes( { blockId: `id-${clientId}` } );
|
50
|
+
}
|
51
|
+
|
52
|
+
// TODO: Update this to check if this OR any of the innerblocks are selected
|
53
|
+
// let inlineStyle = (isSelected || attributes.isOpenByDefault) ? { display: 'block' } : {};
|
54
|
+
let inlineStyle = { display: 'block' };
|
55
|
+
|
56
|
+
return [
|
57
|
+
<InspectorControls>
|
58
|
+
<PanelBody title={ 'Accordion settings' }>
|
59
|
+
<ToggleControl
|
60
|
+
label={ 'Open by default' }
|
61
|
+
onChange={ content => setAttributes({ isOpenByDefault: content }) }
|
62
|
+
checked={ attributes.isOpenByDefault }
|
63
|
+
/>
|
64
|
+
|
65
|
+
</PanelBody>
|
66
|
+
</InspectorControls>,
|
67
|
+
|
68
|
+
<div className="wp-block-be-accordion accordion">
|
69
|
+
<div className="accordion-item">
|
70
|
+
<h2 className="accordion-header">
|
71
|
+
<PlainText
|
72
|
+
onChange={ content => setAttributes({ title: content }) }
|
73
|
+
value={ attributes.title }
|
74
|
+
placeholder="Your accordion title"
|
75
|
+
className="accordion-button"
|
76
|
+
/>
|
77
|
+
</h2>
|
78
|
+
<div className="accordion-collapse collapse show">
|
79
|
+
<div className="accordion-body" style={ inlineStyle }>
|
80
|
+
<InnerBlocks/>
|
81
|
+
</div>
|
82
|
+
</div>
|
83
|
+
</div>
|
84
|
+
</div>
|
85
|
+
];
|
86
|
+
},
|
87
|
+
save({ attributes }) {
|
88
|
+
let buttonVisibilityClass = attributes.isOpenByDefault ? "collapsed" : "";
|
89
|
+
let accordionVisibilityClass = attributes.isOpenByDefault ? "show" : "";
|
90
|
+
|
91
|
+
return (
|
92
|
+
<div className="wp-block-be-accordion accordion">
|
93
|
+
<div className="accordion-item">
|
94
|
+
<h2 className="accordion-header">
|
95
|
+
<button className={ "accordion-button " + buttonVisibilityClass } type="button" data-bs-toggle="collapse" data-bs-target={ "#" + attributes.blockId }>
|
96
|
+
{ attributes.title }
|
97
|
+
</button>
|
98
|
+
</h2>
|
99
|
+
<div id={attributes.blockId} className={ "accordion-collapse collapse " + accordionVisibilityClass }>
|
100
|
+
<div className="accordion-body">
|
101
|
+
<InnerBlocks.Content />
|
102
|
+
</div>
|
103
|
+
</div>
|
104
|
+
</div>
|
105
|
+
</div>
|
106
|
+
);
|
107
|
+
}
|
108
|
+
};
|
@@ -0,0 +1,51 @@
|
|
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
|
+
import { box as icon } from '@wordpress/icons';
|
11
|
+
|
12
|
+
const name = 'be/alert';
|
13
|
+
|
14
|
+
export { name };
|
15
|
+
|
16
|
+
export const settings = {
|
17
|
+
title: 'Callout (Alert)',
|
18
|
+
description: 'Container to help draw attention to content.',
|
19
|
+
icon,
|
20
|
+
category: 'formatting',
|
21
|
+
styles: [
|
22
|
+
{ name: 'primary', label: 'Primary', isDefault: true },
|
23
|
+
{ name: 'secondary', label: 'Secondary' },
|
24
|
+
{ name: 'success', label: 'Success' },
|
25
|
+
{ name: 'danger', label: 'Danger' },
|
26
|
+
{ name: 'warning', label: 'Warning' },
|
27
|
+
{ name: 'info', label: 'Info' },
|
28
|
+
{ name: 'light', label: 'Light' },
|
29
|
+
{ name: 'dark', label: 'Dark' }
|
30
|
+
],
|
31
|
+
example: {
|
32
|
+
innerBlocks: [
|
33
|
+
{
|
34
|
+
name: 'core/paragraph',
|
35
|
+
attributes: {
|
36
|
+
content: 'Use a callout to grab the users attention.'
|
37
|
+
}
|
38
|
+
}
|
39
|
+
]
|
40
|
+
},
|
41
|
+
edit(props) {
|
42
|
+
return (
|
43
|
+
<div role="alert" className={ 'alert ' + props.className }>
|
44
|
+
<InnerBlocks/>
|
45
|
+
</div>
|
46
|
+
);
|
47
|
+
},
|
48
|
+
save() {
|
49
|
+
return <div role="alert" className='alert'><InnerBlocks.Content /></div>;
|
50
|
+
}
|
51
|
+
};
|
@@ -0,0 +1,205 @@
|
|
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 { box as icon } from '@wordpress/icons';
|
14
|
+
|
15
|
+
const name = 'be/card';
|
16
|
+
|
17
|
+
export { name };
|
18
|
+
|
19
|
+
export const settings = {
|
20
|
+
title: 'Card',
|
21
|
+
description: 'Group a piece of content in an eye catching container.',
|
22
|
+
icon,
|
23
|
+
category: 'formatting',
|
24
|
+
example: {
|
25
|
+
attributes: {
|
26
|
+
title: 'Container example',
|
27
|
+
imageUrl: 'https://s.w.org/images/core/5.3/MtBlanc1.jpg',
|
28
|
+
}
|
29
|
+
},
|
30
|
+
attributes: {
|
31
|
+
title: {
|
32
|
+
source: 'text',
|
33
|
+
selector: '.card-title'
|
34
|
+
},
|
35
|
+
body: {
|
36
|
+
type: 'array',
|
37
|
+
source: 'children',
|
38
|
+
selector: '.card-content'
|
39
|
+
},
|
40
|
+
imageAlt: {
|
41
|
+
attribute: 'alt',
|
42
|
+
selector: '.card-img-top'
|
43
|
+
},
|
44
|
+
imageUrl: {
|
45
|
+
attribute: 'src',
|
46
|
+
selector: '.card-img-top'
|
47
|
+
},
|
48
|
+
hasCallToAction: {
|
49
|
+
type: 'boolean'
|
50
|
+
},
|
51
|
+
callToAction: {
|
52
|
+
type: 'text'
|
53
|
+
},
|
54
|
+
url: {
|
55
|
+
type: 'text'
|
56
|
+
},
|
57
|
+
openInNewTab: {
|
58
|
+
type: 'boolean'
|
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
|
72
|
+
</Button>
|
73
|
+
}
|
74
|
+
<img
|
75
|
+
src={ attributes.imageUrl }
|
76
|
+
className="card-img-top"
|
77
|
+
/>
|
78
|
+
</>
|
79
|
+
);
|
80
|
+
}
|
81
|
+
else {
|
82
|
+
return (
|
83
|
+
<div className="block-editor-image-placeholder">
|
84
|
+
<Button
|
85
|
+
onClick={ openEvent }
|
86
|
+
className="button button-large"
|
87
|
+
>
|
88
|
+
Pick an image
|
89
|
+
</Button>
|
90
|
+
</div>
|
91
|
+
);
|
92
|
+
}
|
93
|
+
};
|
94
|
+
return ([
|
95
|
+
<InspectorControls>
|
96
|
+
<PanelBody title='Card settings'>
|
97
|
+
<TextControl
|
98
|
+
label="URL"
|
99
|
+
value={ attributes.url }
|
100
|
+
onChange={ content => setAttributes({ url: content }) }
|
101
|
+
/>
|
102
|
+
{ attributes.url && (
|
103
|
+
<ToggleControl
|
104
|
+
label="Display Call To Action"
|
105
|
+
checked={ attributes.hasCallToAction }
|
106
|
+
onChange={ content => setAttributes({ hasCallToAction: content }) }
|
107
|
+
/>
|
108
|
+
)}
|
109
|
+
{ attributes.url && (
|
110
|
+
<ToggleControl
|
111
|
+
label="Open in new Tab?"
|
112
|
+
checked={ attributes.openInNewTab }
|
113
|
+
onChange={ content => setAttributes({ openInNewTab: content }) }
|
114
|
+
/>
|
115
|
+
)}
|
116
|
+
</PanelBody>
|
117
|
+
</InspectorControls>,
|
118
|
+
<div className={ 'card ' + className }>
|
119
|
+
<MediaUpload
|
120
|
+
onSelect={ media => { setAttributes({ imageAlt: media.alt, imageUrl: media.url }); } }
|
121
|
+
type="image"
|
122
|
+
value={ attributes.imageID }
|
123
|
+
render={ ({ open }) => getImageButton(open) }
|
124
|
+
/>
|
125
|
+
<div className='card-body'>
|
126
|
+
<PlainText
|
127
|
+
onChange={ content => setAttributes({ title: content }) }
|
128
|
+
value={ attributes.title }
|
129
|
+
placeholder="Your card title"
|
130
|
+
className="card-title h2"
|
131
|
+
/>
|
132
|
+
<RichText
|
133
|
+
onChange={ content => setAttributes({ body: content }) }
|
134
|
+
value={ attributes.body }
|
135
|
+
multiline="p"
|
136
|
+
placeholder="Your card text"
|
137
|
+
/>
|
138
|
+
</div>
|
139
|
+
{ attributes.hasCallToAction && attributes.url &&
|
140
|
+
<PlainText
|
141
|
+
onChange={ content => setAttributes({ callToAction: content }) }
|
142
|
+
value={ attributes.callToAction }
|
143
|
+
placeholder="Your Call To Action"
|
144
|
+
className="card-link"
|
145
|
+
/>
|
146
|
+
}
|
147
|
+
</div>
|
148
|
+
]);
|
149
|
+
},
|
150
|
+
save({ attributes }) {
|
151
|
+
const linkTarget = (attributes.openInNewTab) ? '_blank' : '_self';
|
152
|
+
const cardImage = (src, alt) => {
|
153
|
+
if(!src) return null;
|
154
|
+
|
155
|
+
return (
|
156
|
+
<img
|
157
|
+
className="card-img-top"
|
158
|
+
src={ src }
|
159
|
+
alt={ alt }
|
160
|
+
/>
|
161
|
+
);
|
162
|
+
}
|
163
|
+
|
164
|
+
return (
|
165
|
+
<div className="card">
|
166
|
+
{ attributes.url ? (
|
167
|
+
<a
|
168
|
+
href={ attributes.url }
|
169
|
+
target= { linkTarget }
|
170
|
+
>
|
171
|
+
{ cardImage(attributes.imageUrl, attributes.imageAlt) }
|
172
|
+
</a>
|
173
|
+
) : (
|
174
|
+
cardImage(attributes.imageUrl, attributes.imageAlt)
|
175
|
+
)}
|
176
|
+
<div className="card-body">
|
177
|
+
<h3 className="card-title">
|
178
|
+
{ attributes.url ? (
|
179
|
+
<a
|
180
|
+
href={ attributes.url }
|
181
|
+
target= { linkTarget }
|
182
|
+
>
|
183
|
+
{ attributes.title }
|
184
|
+
</a>
|
185
|
+
) : (
|
186
|
+
attributes.title
|
187
|
+
)}
|
188
|
+
</h3>
|
189
|
+
<div className='card-content'>
|
190
|
+
{ attributes.body }
|
191
|
+
</div>
|
192
|
+
</div>
|
193
|
+
{ attributes.hasCallToAction && attributes.url &&
|
194
|
+
<RichText.Content
|
195
|
+
tagName="a"
|
196
|
+
className='card-link'
|
197
|
+
href={ attributes.url }
|
198
|
+
target= { linkTarget }
|
199
|
+
value={ attributes.callToAction }
|
200
|
+
/>
|
201
|
+
}
|
202
|
+
</div>
|
203
|
+
);
|
204
|
+
}
|
205
|
+
};
|