Skip to content

Commit 97bc73d

Browse files
Merge pull request #122 from closemarketing/carousel-editor
Carousel editor
2 parents 62450c0 + cb504ca commit 97bc73d

8 files changed

Lines changed: 828 additions & 851 deletions

assets/admin/settings.css

Lines changed: 38 additions & 558 deletions
Large diffs are not rendered by default.

assets/carousel/frontblocks-advanced-option.js

Lines changed: 288 additions & 21 deletions
Large diffs are not rendered by default.

assets/carousel/frontblocks-advanced-option.jsx

Lines changed: 394 additions & 138 deletions
Large diffs are not rendered by default.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/* Editor-only carousel preview styles */
2+
3+
/* Hide the native scrollbar on the carousel grid element */
4+
.frbl-carousel-noscrollbar {
5+
scrollbar-width: none;
6+
-ms-overflow-style: none;
7+
}
8+
.frbl-carousel-noscrollbar::-webkit-scrollbar {
9+
display: none;
10+
}
11+
12+
/* Navigation arrows – rendered in the outer document body as position:fixed */
13+
.frbl-editor-arrow {
14+
width: 32px;
15+
height: 32px;
16+
border-radius: 50%;
17+
border: none;
18+
cursor: pointer;
19+
display: flex;
20+
align-items: center;
21+
justify-content: center;
22+
padding: 0;
23+
transition: opacity 0.2s;
24+
}
25+
26+
.frbl-editor-arrow:hover {
27+
opacity: 0.8;
28+
}

assets/carousel/frontblocks-carousel.css

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
width: 100%;
88
max-width: 100%;
99
box-sizing: border-box;
10-
overflow: hidden;
10+
overflow: visible;
1111
margin: 0;
1212
padding: 0;
1313
}
@@ -123,45 +123,41 @@
123123
}
124124

125125
.frontblocks .glide__bullets {
126-
position: absolute;
127-
z-index: 2;
128-
bottom: -1em;
129-
left: 50%;
130-
display: inline-flex;
126+
display: flex;
127+
justify-content: center;
131128
list-style: none;
132-
transform: translateX(-50%);
133129
gap: 2px;
130+
margin: 0.75em 0 0;
131+
padding: 0;
134132
}
135133

136134
.frontblocks .glide__bullet {
137-
background-color: rgba(255, 255, 255, 0.5);
138-
width: 14px;
139-
height: 14px;
140-
padding: 10px;
135+
box-sizing: content-box;
136+
width: 10px;
137+
height: 10px;
138+
padding: 0;
141139
border-radius: 50%;
142-
border: 2px solid transparent;
143-
background-clip: content-box;
144-
transition: all 300ms ease-in-out;
140+
border: none;
141+
background-color: var(--frbl-bullet-bg, rgba(0, 0, 0, 0.25));
145142
cursor: pointer;
146-
line-height: 0;
147-
margin: 0;
143+
transition: background-color 300ms ease-in-out, transform 300ms ease-in-out;
144+
margin: 0 2px;
145+
flex-shrink: 0;
148146
}
149147

150148
.frontblocks .glide__bullet:focus {
151-
outline: 2px solid white;
149+
outline: 2px solid var(--frbl-bullet-color, rgba(0, 0, 0, 0.8));
152150
outline-offset: 2px;
153151
}
154152

155-
.frontblocks .glide__bullet:hover,
156-
.frontblocks .glide__bullet:focus {
157-
border: 2px solid white;
158-
background-color: rgba(255, 255, 255, 0.5);
159-
background-clip: content-box;
153+
.frontblocks .glide__bullet:hover {
154+
background-color: var(--frbl-bullet-color, rgba(0, 0, 0, 0.8));
155+
transform: scale(1.2);
160156
}
161157

162158
.frontblocks .glide__bullet--active {
163-
background-color: white;
164-
background-clip: content-box;
159+
background-color: var(--frbl-bullet-color, rgba(0, 0, 0, 0.8));
160+
transform: scale(1.2);
165161
}
166162

167163
.frontblocks.glide--swipeable {
@@ -262,3 +258,15 @@
262258
min-width: 0;
263259
flex-shrink: 0;
264260
}
261+
262+
/**
263+
* GenerateBlocks (and any other plugin) may inject column-gap via generated
264+
* utility classes on the carousel slides container. Glide manages its own
265+
* spacing through margin-right on each slide, so any external column-gap
266+
* breaks the width calculation and must be zeroed out.
267+
*/
268+
.frontblocks-carousel,
269+
.frontblocks .glide__slides {
270+
column-gap: 0 !important;
271+
gap: 0 !important;
272+
}

assets/carousel/frontblocks-carousel.js

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -72,20 +72,19 @@ window.addEventListener('load', function (event) {
7272
bullet.classList.add('glide__bullet');
7373
bullet.setAttribute('data-glide-dir', '=' + i);
7474
bullet.setAttribute('aria-label', 'Go to slide ' + (i + 1));
75-
bullet.style.backgroundColor = carouselbuttonsBackgroundColor;
7675
bullets.appendChild(bullet);
7776
}
7877

7978
wrapperParent.appendChild(bullets);
8079

81-
// Add custom CSS for active bullet color
82-
const style = document.createElement('style');
83-
style.textContent = `
84-
.frontblocks .glide__bullet.glide__bullet--active {
85-
background-color: ${carouselbuttonsColor};
86-
}
87-
`;
88-
document.head.appendChild(style);
80+
// Set bullet colors via CSS custom properties on the wrapper.
81+
// This avoids specificity conflicts with the stylesheet.
82+
if (carouselbuttonsColor) {
83+
wrapperParent.style.setProperty('--frbl-bullet-color', carouselbuttonsColor);
84+
}
85+
if (carouselbuttonsBackgroundColor && carouselbuttonsBackgroundColor !== 'transparent') {
86+
wrapperParent.style.setProperty('--frbl-bullet-bg', carouselbuttonsBackgroundColor);
87+
}
8988
}
9089

9190
if (carouselbuttons == 'arrows') {

assets/shape-animations/frontblocks-shape-animation-option.js

Lines changed: 22 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
1818
var _wp$components = wp.components,
1919
PanelBody = _wp$components.PanelBody,
2020
ToggleControl = _wp$components.ToggleControl,
21+
TextareaControl = _wp$components.TextareaControl,
2122
Button = _wp$components.Button,
22-
Notice = _wp$components.Notice,
23-
FormFileUpload = _wp$components.FormFileUpload;
23+
Notice = _wp$components.Notice;
2424

2525
/**
2626
* Add custom SVG animation controls to GenerateBlocks Shape block.
@@ -45,14 +45,6 @@ function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
4545
_useState4 = _slicedToArray(_useState3, 2),
4646
jsonPreview = _useState4[0],
4747
setJsonPreview = _useState4[1];
48-
var _useState5 = useState(''),
49-
_useState6 = _slicedToArray(_useState5, 2),
50-
fileName = _useState6[0],
51-
setFileName = _useState6[1];
52-
var _useState7 = useState(0),
53-
_useState8 = _slicedToArray(_useState7, 2),
54-
fileInputKey = _useState8[0],
55-
setFileInputKey = _useState8[1];
5648

5749
// Detect if JSON is Lottie format.
5850
var isLottieJson = function isLottieJson(parsed) {
@@ -100,46 +92,19 @@ function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
10092
}
10193
};
10294

103-
// Handle file upload.
104-
var handleFileUpload = function handleFileUpload(event) {
105-
var file = event.target.files[0];
106-
if (!file) {
107-
return;
108-
}
109-
110-
// Check if it's a JSON file.
111-
if (!file.name.endsWith('.json')) {
112-
setJsonError(__('Please select a JSON file', 'frontblocks'));
113-
setFileInputKey(function(prev) { return prev + 1; });
114-
return;
95+
// Handle JSON change.
96+
var handleJsonChange = function handleJsonChange(value) {
97+
setAttributes({
98+
frblCustomSvgAnimationJson: value
99+
});
100+
if (value.trim()) {
101+
validateJson(value);
102+
} else {
103+
setJsonError('');
104+
setJsonPreview(null);
115105
}
116-
117-
setFileName(file.name);
118-
119-
// Read file content.
120-
var reader = new FileReader();
121-
reader.onload = function(e) {
122-
var content = e.target.result;
123-
setAttributes({ frblCustomSvgAnimationJson: content });
124-
validateJson(content);
125-
setFileInputKey(function(prev) { return prev + 1; });
126-
};
127-
reader.onerror = function() {
128-
setJsonError(__('Error reading file', 'frontblocks'));
129-
setFileInputKey(function(prev) { return prev + 1; });
130-
};
131-
reader.readAsText(file);
132-
};
133-
134-
// Clear imported JSON.
135-
var handleClear = function handleClear() {
136-
setAttributes({ frblCustomSvgAnimationJson: '' });
137-
setJsonError('');
138-
setJsonPreview(null);
139-
setFileName('');
140-
setFileInputKey(function(prev) { return prev + 1; });
141106
};
142-
107+
143108
// Example JSON template.
144109
var exampleJson = JSON.stringify({
145110
"svg": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"48\" height=\"48\" fill=\"currentColor\"><path d=\"M12 2L2 7v10c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V7l-10-5z\"/></svg>",
@@ -152,20 +117,6 @@ function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
152117
"trigger": "load"
153118
}
154119
}, null, 2);
155-
156-
// Download example JSON.
157-
var handleDownloadExample = function handleDownloadExample() {
158-
var blob = new Blob([exampleJson], { type: 'application/json' });
159-
var url = URL.createObjectURL(blob);
160-
var a = document.createElement('a');
161-
a.href = url;
162-
a.download = 'example-animation.json';
163-
document.body.appendChild(a);
164-
a.click();
165-
document.body.removeChild(a);
166-
URL.revokeObjectURL(url);
167-
};
168-
169120
return wp.element.createElement(Fragment, {}, wp.element.createElement(BlockEdit, props), wp.element.createElement(InspectorControls, {}, wp.element.createElement(PanelBody, {
170121
title: __('FrontBlocks Custom SVG Animation', 'frontblocks'),
171122
initialOpen: false
@@ -181,36 +132,16 @@ function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
181132
}), frblCustomSvgAnimationEnabled && wp.element.createElement(Fragment, {}, wp.element.createElement('p', {
182133
style: {
183134
fontSize: '12px',
184-
marginBottom: '12px',
135+
marginBottom: '8px',
185136
color: '#757575'
186137
}
187-
}, __('Import a JSON file with your animation configuration:', 'frontblocks')), wp.element.createElement(FormFileUpload, {
188-
key: fileInputKey,
189-
accept: '.json',
190-
onChange: handleFileUpload,
191-
render: function(ref) {
192-
return wp.element.createElement(Button, {
193-
isSecondary: true,
194-
onClick: ref.openFileDialog,
195-
style: { marginBottom: '8px', width: '100%' }
196-
}, fileName ? __('Change JSON file', 'frontblocks') : __('Import JSON file', 'frontblocks'));
197-
}
198-
}), fileName && wp.element.createElement('div', {
199-
style: {
200-
display: 'flex',
201-
alignItems: 'center',
202-
justifyContent: 'space-between',
203-
marginBottom: '12px',
204-
padding: '8px',
205-
background: '#f6f7f7',
206-
borderRadius: '4px',
207-
fontSize: '12px'
208-
}
209-
}, wp.element.createElement('span', {}, '📄 ' + fileName), wp.element.createElement(Button, {
210-
isSmall: true,
211-
isDestructive: true,
212-
onClick: handleClear
213-
}, __('Clear', 'frontblocks'))), jsonError && wp.element.createElement(Notice, {
138+
}, __('Paste your JSON configuration below:', 'frontblocks')), wp.element.createElement(TextareaControl, {
139+
label: __('JSON Configuration', 'frontblocks'),
140+
value: frblCustomSvgAnimationJson,
141+
onChange: handleJsonChange,
142+
rows: 10,
143+
help: __('JSON with svg and animation properties', 'frontblocks')
144+
}), jsonError && wp.element.createElement(Notice, {
214145
status: 'error',
215146
isDismissible: false
216147
}, jsonError), jsonPreview && wp.element.createElement(Notice, {
@@ -227,7 +158,7 @@ function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
227158
fontWeight: '600',
228159
marginBottom: '8px'
229160
}
230-
}, __('📋 Download example JSON', 'frontblocks')), wp.element.createElement('pre', {
161+
}, __('📋 Show example JSON', 'frontblocks')), wp.element.createElement('pre', {
231162
style: {
232163
background: '#f6f7f7',
233164
padding: '12px',
@@ -237,23 +168,13 @@ function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
237168
maxHeight: '300px'
238169
}
239170
}, exampleJson), wp.element.createElement(Button, {
240-
isSecondary: true,
241-
isSmall: true,
242-
onClick: handleDownloadExample,
243-
style: {
244-
marginTop: '8px',
245-
marginRight: '8px'
246-
}
247-
}, __('Download example', 'frontblocks')), wp.element.createElement(Button, {
248171
isSecondary: true,
249172
isSmall: true,
250173
onClick: function onClick() {
251174
setAttributes({
252175
frblCustomSvgAnimationJson: exampleJson
253176
});
254-
setFileName('example-animation.json');
255177
validateJson(exampleJson);
256-
setFileInputKey(function(prev) { return prev + 1; });
257178
},
258179
style: {
259180
marginTop: '8px'

includes/Frontend/Carousel.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,30 @@ public function __construct() {
3333
*/
3434
private function init_hooks() {
3535
add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_block_editor_assets' ) );
36+
add_action( 'enqueue_block_assets', array( $this, 'enqueue_block_canvas_assets' ) );
3637
add_filter( 'render_block_generateblocks/grid', array( $this, 'add_custom_attributes_to_grid_block' ), 10, 2 );
3738
add_filter( 'render_block_generateblocks/element', array( $this, 'add_custom_attributes_to_element_block' ), 10, 2 );
3839
add_filter( 'render_block_core/group', array( $this, 'add_custom_attributes_to_core_group_block' ), 10, 2 );
3940
add_action( 'init', array( $this, 'register_custom_attributes' ), 5 );
4041
}
4142

43+
/**
44+
* Enqueue carousel CSS in the editor canvas (iframe).
45+
*
46+
* @return void
47+
*/
48+
public function enqueue_block_canvas_assets() {
49+
if ( ! is_admin() ) {
50+
return;
51+
}
52+
wp_enqueue_style(
53+
'frontblocks-carousel-editor',
54+
FRBL_PLUGIN_URL . 'assets/carousel/frontblocks-carousel-editor.css',
55+
array(),
56+
FRBL_VERSION
57+
);
58+
}
59+
4260
/**
4361
* Enqueue block editor assets.
4462
*

0 commit comments

Comments
 (0)