block_editor 0.1.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
};
|