| 1 |
/** |
| 2 |
* WPVR Gutenberg Block |
| 3 |
* |
| 4 |
* A modern implementation of the WPVR tour block using ES6 and WordPress Block API |
| 5 |
*/ |
| 6 |
|
| 7 |
const { __ } = wp.i18n; |
| 8 |
const { registerBlockType } = wp.blocks; |
| 9 |
const { useEffect, useState } = wp.element; |
| 10 |
const { |
| 11 |
InspectorControls, |
| 12 |
useBlockProps |
| 13 |
} = wp.blockEditor; |
| 14 |
const { |
| 15 |
TextControl, |
| 16 |
SelectControl, |
| 17 |
ColorPalette, |
| 18 |
PanelBody, |
| 19 |
RangeControl, |
| 20 |
__experimentalUnitControl: UnitControl, |
| 21 |
__experimentalNumberControl: NumberControl |
| 22 |
} = wp.components; |
| 23 |
const { apiFetch } = wp; |
| 24 |
|
| 25 |
// Block icon as SVG |
| 26 |
const blockIcon = <svg width="20" height="20" viewBox="0 0 20 20"> |
| 27 |
<path d="M16.1,16.6h-2.5c-1,0-1.9-0.6-2.4-1.5L11,14.5c-0.2-0.4-0.5-0.6-0.9-0.6c-0.4,0-0.8,0.2-0.9,0.6l-0.3,0.6 c-0.4,0.9-1.3,1.5-2.4,1.5H3.9c-2.2,0-3.9-1.8-3.9-3.9V7.3c0-2.2,1.8-3.9,3.9-3.9h12.2c2.2,0,3.9,1.8,3.9,3.9v1.5 c0,0.4-0.3,0.8-0.8,0.8c-0.4,0-0.8-0.3-0.8-0.8V7.3c0-1.3-1.1-2.3-2.3-2.3H3.9C2.6,4.9,1.6,6,1.6,7.3v5.4c0,1.3,1.1,2.3,2.3,2.3 h2.6c0.4,0,0.8-0.2,0.9-0.6l0.3-0.6c0.4-0.9,1.3-1.5,2.4-1.5c1,0,1.9,0.6,2.4,1.5l0.3,0.6c0.2,0.4,0.5,0.6,0.9,0.6h2.5 c1.3,0,2.3-1.1,2.3-2.3c0-0.4,0.3-0.8,0.8-0.8c0.4,0,0.8,0.3,0.8,0.8C20,14.9,18.2,16.6,16.1,16.6L16.1,16.6z M16.7,9.4 c0-1.3-1.1-2.3-2.3-2.3C13,7.1,12,8.1,12,9.4s1.1,2.3,2.3,2.3C15.6,11.7,16.7,10.7,16.7,9.4L16.7,9.4z M15.1,9.4 c0,0.4-0.4,0.8-0.8,0.8c-0.4,0-0.8-0.4-0.8-0.8s0.4-0.8,0.8-0.8C14.8,8.6,15.1,9,15.1,9.4L15.1,9.4z M8,9.4C8,8.1,7,7.1,5.7,7.1 S3.3,8.1,3.3,9.4s1.1,2.3,2.3,2.3S8,10.7,8,9.4L8,9.4z M6.4,9.4c0,0.4-0.4,0.8-0.8,0.8c-0.4,0-0.8-0.4-0.8-0.8s0.4-0.8,0.8-0.8 C6.1,8.6,6.4,9,6.4,9.4L6.4,9.4z M6.4,9.4" /> |
| 28 |
</svg>; |
| 29 |
|
| 30 |
// Available colors for the border |
| 31 |
const BORDER_COLORS = [ |
| 32 |
{ name: 'red', color: '#f00' }, |
| 33 |
{ name: 'white', color: '#fff' }, |
| 34 |
{ name: 'blue', color: '#00f' }, |
| 35 |
{ name: 'black', color: '#000' }, |
| 36 |
{ name: 'gray', color: '#888' }, |
| 37 |
]; |
| 38 |
|
| 39 |
// Border style options |
| 40 |
const BORDER_STYLES = [ |
| 41 |
{ value: 'none', label: __('None', 'wpvr') }, |
| 42 |
{ value: 'solid', label: __('Solid', 'wpvr') }, |
| 43 |
{ value: 'dotted', label: __('Dotted', 'wpvr') }, |
| 44 |
{ value: 'dashed', label: __('Dashed', 'wpvr') }, |
| 45 |
{ value: 'double', label: __('Double', 'wpvr') }, |
| 46 |
]; |
| 47 |
|
| 48 |
// Width unit options |
| 49 |
const WIDTH_UNITS = [ |
| 50 |
{ value: 'px', label: 'px' }, |
| 51 |
{ value: '%', label: '%' }, |
| 52 |
{ value: 'vw', label: 'vw' }, |
| 53 |
{ value: 'fullwidth', label: __('Fullwidth', 'wpvr') }, |
| 54 |
]; |
| 55 |
|
| 56 |
// Height unit options |
| 57 |
const HEIGHT_UNITS = [ |
| 58 |
{ value: 'px', label: 'px' }, |
| 59 |
{ value: 'vh', label: 'vh' }, |
| 60 |
]; |
| 61 |
|
| 62 |
// Radius unit options |
| 63 |
const RADIUS_UNITS = [ |
| 64 |
{ value: 'px', label: 'px' }, |
| 65 |
{ value: '%', label: '%' }, |
| 66 |
]; |
| 67 |
|
| 68 |
/** |
| 69 |
* WPVR Block Edit component using functional component and hooks |
| 70 |
*/ |
| 71 |
function WpvrEdit({ attributes, setAttributes }) { |
| 72 |
const [tourOptions, setTourOptions] = useState([{ value: "0", label: __("None", "wpvr") }]); |
| 73 |
|
| 74 |
// Fetch tour data when component mounts |
| 75 |
useEffect(() => { |
| 76 |
apiFetch({ path: 'wpvr/v1/panodata' }) |
| 77 |
.then(data => { |
| 78 |
if (data && Array.isArray(data)) { |
| 79 |
setTourOptions(data); |
| 80 |
} |
| 81 |
}) |
| 82 |
.catch(error => { |
| 83 |
console.error('Error fetching WPVR tour data:', error); |
| 84 |
}); |
| 85 |
}, []); |
| 86 |
|
| 87 |
// Handle width unit change |
| 88 |
const handleWidthUnitChange = (value) => { |
| 89 |
|
| 90 |
if (value === 'fullwidth' && attributes.width === 'fullwidth') { |
| 91 |
setAttributes({ |
| 92 |
width: 'fullwidth', |
| 93 |
width_unit: '' |
| 94 |
}); |
| 95 |
} else { |
| 96 |
|
| 97 |
if( value === 'fullwidth' ){ |
| 98 |
setAttributes({ |
| 99 |
width: 'fullwidth', |
| 100 |
width_unit: '' |
| 101 |
}); |
| 102 |
}else { |
| 103 |
setAttributes({ |
| 104 |
width: attributes.width_unit === '' ? '600' : attributes.width, |
| 105 |
width_unit: value |
| 106 |
}); |
| 107 |
} |
| 108 |
|
| 109 |
|
| 110 |
} |
| 111 |
|
| 112 |
console.log(attributes.width, value); |
| 113 |
}; |
| 114 |
|
| 115 |
// Get block props with custom styles |
| 116 |
const blockProps = useBlockProps({ |
| 117 |
className: 'wpvr-block-preview', |
| 118 |
style: { |
| 119 |
width: attributes.width_unit ? `${attributes.width}${attributes.width_unit}` : (attributes.width === 'fullwidth' ? '100%' : undefined), |
| 120 |
height: `${attributes.height}${attributes.height_unit}`, |
| 121 |
borderRadius: attributes.radius ? `${attributes.radius}${attributes.radius_unit}` : undefined, |
| 122 |
borderStyle: attributes.border_style, |
| 123 |
borderColor: attributes.border_color, |
| 124 |
borderWidth: attributes.border_width ? `${attributes.border_width}px` : undefined, |
| 125 |
} |
| 126 |
}); |
| 127 |
|
| 128 |
|
| 129 |
return ( |
| 130 |
<> |
| 131 |
<InspectorControls> |
| 132 |
<PanelBody title={__('Tour Settings', 'wpvr')} initialOpen={true}> |
| 133 |
<SelectControl |
| 134 |
label={__('Select Tour', 'wpvr')} |
| 135 |
value={attributes.id} |
| 136 |
options={tourOptions} |
| 137 |
onChange={(value) => setAttributes({ id: value })} |
| 138 |
/> |
| 139 |
|
| 140 |
<div className="wpvr-dimension-control"> |
| 141 |
<p>{__('Tour Width', 'wpvr')}</p> |
| 142 |
<div className="wpvr-dimension-inputs"> |
| 143 |
<NumberControl |
| 144 |
value={attributes.width} |
| 145 |
onChange={(value) => setAttributes({ width: value })} |
| 146 |
disabled={attributes.width === 'fullwidth'} |
| 147 |
/> |
| 148 |
<SelectControl |
| 149 |
value={attributes.width === 'fullwidth' ? 'fullwidth' : attributes.width_unit} |
| 150 |
options={WIDTH_UNITS} |
| 151 |
onChange={handleWidthUnitChange} |
| 152 |
/> |
| 153 |
</div> |
| 154 |
</div> |
| 155 |
|
| 156 |
<div className="wpvr-dimension-control"> |
| 157 |
<p>{__('Tour Height', 'wpvr')}</p> |
| 158 |
<div className="wpvr-dimension-inputs"> |
| 159 |
<NumberControl |
| 160 |
value={attributes.height} |
| 161 |
onChange={(value) => setAttributes({ height: value })} |
| 162 |
/> |
| 163 |
<SelectControl |
| 164 |
value={attributes.height_unit} |
| 165 |
options={HEIGHT_UNITS} |
| 166 |
onChange={(value) => setAttributes({ height_unit: value })} |
| 167 |
/> |
| 168 |
</div> |
| 169 |
</div> |
| 170 |
|
| 171 |
<div className="wpvr-dimension-control"> |
| 172 |
<p>{__('Tour Mobile Height', 'wpvr')}</p> |
| 173 |
<div className="wpvr-dimension-inputs"> |
| 174 |
<NumberControl |
| 175 |
value={attributes.mobile_height} |
| 176 |
onChange={(value) => setAttributes({ mobile_height: value })} |
| 177 |
/> |
| 178 |
<SelectControl |
| 179 |
value={attributes.mobile_height_unit} |
| 180 |
options={HEIGHT_UNITS} |
| 181 |
onChange={(value) => setAttributes({ mobile_height_unit: value })} |
| 182 |
/> |
| 183 |
</div> |
| 184 |
</div> |
| 185 |
</PanelBody> |
| 186 |
|
| 187 |
<PanelBody title={__('Appearance', 'wpvr')} initialOpen={false}> |
| 188 |
<div className="wpvr-dimension-control"> |
| 189 |
<p>{__('Tour Border Radius', 'wpvr')}</p> |
| 190 |
<div className="wpvr-dimension-inputs"> |
| 191 |
<NumberControl |
| 192 |
value={attributes.radius} |
| 193 |
onChange={(value) => setAttributes({ radius: value })} |
| 194 |
/> |
| 195 |
<SelectControl |
| 196 |
value={attributes.radius_unit} |
| 197 |
options={RADIUS_UNITS} |
| 198 |
onChange={(value) => setAttributes({ radius_unit: value })} |
| 199 |
/> |
| 200 |
</div> |
| 201 |
</div> |
| 202 |
|
| 203 |
<RangeControl |
| 204 |
label={__('Tour Border Width', 'wpvr')} |
| 205 |
value={parseInt(attributes.border_width) || 0} |
| 206 |
onChange={(value) => setAttributes({ border_width: value.toString() })} |
| 207 |
min={0} |
| 208 |
max={20} |
| 209 |
/> |
| 210 |
|
| 211 |
<SelectControl |
| 212 |
label={__('Tour Border Style', 'wpvr')} |
| 213 |
value={attributes.border_style} |
| 214 |
options={BORDER_STYLES} |
| 215 |
onChange={(value) => setAttributes({ border_style: value })} |
| 216 |
/> |
| 217 |
|
| 218 |
<div className="wpvr-color-control"> |
| 219 |
<p>{__('Tour Border Color', 'wpvr')}</p> |
| 220 |
<ColorPalette |
| 221 |
colors={BORDER_COLORS} |
| 222 |
value={attributes.border_color} |
| 223 |
onChange={(value) => setAttributes({ border_color: value })} |
| 224 |
disableCustomColors={false} |
| 225 |
/> |
| 226 |
</div> |
| 227 |
</PanelBody> |
| 228 |
</InspectorControls> |
| 229 |
|
| 230 |
<div {...blockProps}> |
| 231 |
<div className="wpvr-block-content"> |
| 232 |
{attributes.id !== "0" ? ( |
| 233 |
<div className="wpvr-tour-preview"> |
| 234 |
<div className="wpvr-tour-placeholder"> |
| 235 |
{__('WPVR Tour Preview', 'wpvr')} |
| 236 |
<p className="wpvr-tour-details"> |
| 237 |
ID: {attributes.id}<br/> |
| 238 |
{__('Width', 'wpvr')}: {attributes.width}{attributes.width_unit}<br/> |
| 239 |
{__('Height', 'wpvr')}: {attributes.height}{attributes.height_unit}<br/> |
| 240 |
{__('Mobile Height', 'wpvr')}: {attributes.mobile_height}{attributes.mobile_height_unit}<br/> |
| 241 |
{attributes.radius && `${__('Radius', 'wpvr')}: ${attributes.radius}${attributes.radius_unit}`} |
| 242 |
</p> |
| 243 |
</div> |
| 244 |
</div> |
| 245 |
) : ( |
| 246 |
<div className="wpvr-no-tour-selected"> |
| 247 |
{__('Please select a tour from the block settings panel.', 'wpvr')} |
| 248 |
</div> |
| 249 |
)} |
| 250 |
</div> |
| 251 |
</div> |
| 252 |
</> |
| 253 |
); |
| 254 |
} |
| 255 |
|
| 256 |
// Register the block |
| 257 |
registerBlockType('wpvr/wpvr-block', { |
| 258 |
title: __('WPVR Tour', 'wpvr'), |
| 259 |
description: __('Add a virtual reality tour to your content.', 'wpvr'), |
| 260 |
icon: blockIcon, |
| 261 |
category: 'media', |
| 262 |
keywords: [__('vr', 'wpvr'), __('tour', 'wpvr'), __('panorama', 'wpvr'), __('virtual reality', 'wpvr')], |
| 263 |
|
| 264 |
attributes: { |
| 265 |
id: { |
| 266 |
type: 'string', |
| 267 |
default: '0', |
| 268 |
}, |
| 269 |
width: { |
| 270 |
type: 'string', |
| 271 |
default: '600', |
| 272 |
}, |
| 273 |
width_unit: { |
| 274 |
type: 'string', |
| 275 |
default: 'px', |
| 276 |
}, |
| 277 |
height: { |
| 278 |
type: 'string', |
| 279 |
default: '400', |
| 280 |
}, |
| 281 |
height_unit: { |
| 282 |
type: 'string', |
| 283 |
default: 'px', |
| 284 |
}, |
| 285 |
mobile_height: { |
| 286 |
type: 'string', |
| 287 |
default: '300', |
| 288 |
}, |
| 289 |
mobile_height_unit: { |
| 290 |
type: 'string', |
| 291 |
default: 'px', |
| 292 |
}, |
| 293 |
radius: { |
| 294 |
type: 'string', |
| 295 |
default: '0', |
| 296 |
}, |
| 297 |
radius_unit: { |
| 298 |
type: 'string', |
| 299 |
default: 'px', |
| 300 |
}, |
| 301 |
border_width: { |
| 302 |
type: 'string', |
| 303 |
default: '0', |
| 304 |
}, |
| 305 |
border_style: { |
| 306 |
type: 'string', |
| 307 |
default: 'none', |
| 308 |
}, |
| 309 |
border_color: { |
| 310 |
type: 'string', |
| 311 |
default: '', |
| 312 |
}, |
| 313 |
}, |
| 314 |
|
| 315 |
edit: WpvrEdit, |
| 316 |
|
| 317 |
save: () => { |
| 318 |
// Return null to use PHP render callback |
| 319 |
return null; |
| 320 |
}, |
| 321 |
}); |