Files
OTSSigns-Website/theme/blocks/editor.js

1825 lines
101 KiB
JavaScript
Raw Normal View History

2026-02-20 21:28:00 -05:00
/**
* OTS Theme - Custom Block Editor Scripts (InnerBlocks Architecture)
2026-02-20 21:28:00 -05:00
*
* 13 blocks: 5 standalone + 4 parent/child pairs.
* Parent blocks use InnerBlocks for child items.
* Child blocks are dynamic (save -> null) with inline RichText editing.
*/
(function (wp) {
'use strict';
var el = wp.element.createElement;
var Frag = wp.element.Fragment;
var reg = wp.blocks.registerBlockType;
var RT = wp.blockEditor.RichText;
var IC = wp.blockEditor.InspectorControls;
var IB = wp.blockEditor.InnerBlocks;
var PB = wp.components.PanelBody;
var TC = wp.components.TextControl;
var TA = wp.components.TextareaControl;
var SC = wp.components.SelectControl;
var TG = wp.components.ToggleControl;
var RC = wp.components.RangeControl;
var Btn = wp.components.Button;
var MUC = wp.blockEditor.MediaUploadCheck;
var MU = wp.blockEditor.MediaUpload;
var useS = wp.element.useState;
/* ── Simple array helpers (for pricing-card features) ────────────────── */
function arrSet(arr, i, val) { var c = arr.slice(); c[i] = val; return c; }
function arrAdd(arr, val) { return arr.concat([val]); }
function arrRm(arr, i) { var c = arr.slice(); c.splice(i, 1); return c; }
/* ── Shared card image attributes ────────────────────────────────────── */
var CARD_IMAGE_ATTRS = {
imgId: { type: 'number', default: 0 },
imgUrl: { type: 'string', default: '' },
imgAlt: { type: 'string', default: '' },
imgWidth: { type: 'number', default: 80 },
imgHeight: { type: 'number', default: 0 },
imgFit: { type: 'string', default: 'contain' },
imgPosition: { type: 'string', default: 'top' }
};
/* ── Shared icon attributes ──────────────────────────────────────────── */
var CARD_ICON_ATTRS = {
icon: { type: 'string', default: '' },
iconType: { type: 'string', default: 'emoji' },
faIcon: { type: 'string', default: '' }
};
/**
* Build icon InspectorControls: a toggle that switches between emoji and
* Font Awesome mode, with the appropriate text input shown beneath it.
*/
/* ── Font Awesome icon catalogue ─────────────────────────────────────── */
// Format: ['s' = fas solid | 'b' = fab brand, 'icon-name']
var FA_ICONS = [
// Navigation
['s','angle-down'],['s','angle-left'],['s','angle-right'],['s','angle-up'],
['s','angles-down'],['s','angles-left'],['s','angles-right'],['s','angles-up'],
['s','arrow-down'],['s','arrow-left'],['s','arrow-right'],['s','arrow-up'],
['s','arrow-rotate-left'],['s','arrow-rotate-right'],['s','arrows-rotate'],
['s','arrows-up-down-left-right'],['s','caret-down'],['s','caret-left'],
['s','caret-right'],['s','caret-up'],['s','chevron-down'],['s','chevron-left'],
['s','chevron-right'],['s','chevron-up'],['s','circle-arrow-down'],
['s','circle-arrow-left'],['s','circle-arrow-right'],['s','circle-arrow-up'],
// Interface
['s','asterisk'],['s','at'],['s','ban'],['s','bars'],['s','bell'],['s','bell-slash'],
['s','bolt'],['s','check'],['s','check-double'],['s','circle-check'],
['s','circle-exclamation'],['s','circle-info'],['s','circle-minus'],
['s','circle-plus'],['s','circle-question'],['s','circle-xmark'],
['s','ellipsis'],['s','ellipsis-vertical'],['s','eye'],['s','eye-slash'],
['s','filter'],['s','grip'],['s','grip-vertical'],['s','hashtag'],['s','key'],
['s','lock'],['s','lock-open'],['s','magnifying-glass'],['s','minus'],['s','plus'],
['s','power-off'],['s','rotate'],['s','shield'],['s','shield-check'],
['s','shield-halved'],['s','sliders'],['s','sort'],['s','sort-down'],['s','sort-up'],
['s','spinner'],['s','star'],['s','star-half-stroke'],['s','toggle-off'],
['s','toggle-on'],['s','xmark'],
// Text editing
['s','align-center'],['s','align-justify'],['s','align-left'],['s','align-right'],
['s','bold'],['s','code'],['s','crop'],['s','eraser'],['s','highlight'],
['s','indent'],['s','italic'],['s','link'],['s','link-slash'],['s','list'],
['s','list-check'],['s','list-ol'],['s','list-ul'],['s','outdent'],['s','pen'],
['s','pen-to-square'],['s','pencil'],['s','quote-left'],['s','quote-right'],
['s','scissors'],['s','strikethrough'],['s','subscript'],['s','superscript'],
['s','table'],['s','table-list'],['s','underline'],
// Files and documents
['s','book'],['s','book-open'],['s','bookmark'],['s','box'],['s','box-archive'],
['s','boxes-stacked'],['s','clipboard'],['s','clipboard-check'],
['s','clipboard-list'],['s','copy'],['s','database'],['s','download'],
['s','envelope'],['s','envelope-open'],['s','file'],['s','file-arrow-down'],
['s','file-arrow-up'],['s','file-code'],['s','file-excel'],['s','file-image'],
['s','file-lines'],['s','file-pdf'],['s','file-powerpoint'],['s','file-word'],
['s','file-zipper'],['s','floppy-disk'],['s','folder'],['s','folder-minus'],
['s','folder-open'],['s','folder-plus'],['s','inbox'],['s','newspaper'],
['s','paper-plane'],['s','paperclip'],['s','print'],['s','share'],
['s','share-nodes'],['s','upload'],
// Technology
['s','barcode'],['s','bug'],['s','cloud'],['s','cloud-arrow-down'],
['s','cloud-arrow-up'],['s','code-branch'],['s','cpu'],['s','desktop'],
['s','display'],['s','ethernet'],['s','gear'],['s','gears'],['s','hard-drive'],
['s','keyboard'],['s','laptop'],['s','memory'],['s','microchip'],['s','mobile'],
['s','mobile-screen'],['s','network-wired'],['s','plug'],['s','qrcode'],
['s','robot'],['s','satellite'],['s','satellite-dish'],['s','server'],
['s','tablet'],['s','terminal'],['s','toolbox'],['s','wifi'],['s','wrench'],
// Media and communication
['s','backward'],['s','backward-fast'],['s','camera'],['s','camera-retro'],
['s','clapperboard'],['s','comment'],['s','comment-dots'],['s','comments'],
['s','film'],['s','forward'],['s','forward-fast'],['s','headphones'],
['s','message'],['s','microphone'],['s','microphone-slash'],['s','music'],
['s','pause'],['s','phone'],['s','phone-slash'],['s','phone-volume'],
['s','photo-film'],['s','play'],['s','radio'],['s','repeat'],['s','rss'],
['s','shuffle'],['s','stop'],['s','tv'],['s','video'],['s','video-slash'],
['s','voicemail'],['s','volume-high'],['s','volume-low'],['s','volume-xmark'],
// People
['s','address-book'],['s','address-card'],['s','baby'],['s','child'],
['s','circle-user'],['s','people-group'],['s','person'],['s','person-running'],
['s','person-walking'],['s','thumbs-down'],['s','thumbs-up'],['s','user'],
['s','user-check'],['s','user-group'],['s','user-minus'],['s','user-plus'],
['s','user-slash'],['s','user-tie'],['s','users'],
// Location and maps
['s','building'],['s','buildings'],['s','city'],['s','compass'],
['s','earth-americas'],['s','earth-asia'],['s','earth-europe'],['s','flag'],
['s','globe'],['s','house'],['s','location-dot'],['s','location-pin'],
['s','map'],['s','map-pin'],['s','mountain'],['s','road'],['s','route'],
['s','signs-post'],
// Business and finance
['s','award'],['s','briefcase'],['s','bullhorn'],['s','bullseye'],
['s','calendar'],['s','calendar-check'],['s','calendar-days'],
['s','calendar-minus'],['s','calendar-plus'],['s','certificate'],
['s','chart-bar'],['s','chart-column'],['s','chart-line'],['s','chart-pie'],
['s','chart-simple'],['s','clock'],['s','coins'],['s','credit-card'],
['s','crown'],['s','gem'],['s','gift'],['s','handshake'],['s','medal'],
['s','money-bill'],['s','money-bill-wave'],['s','piggy-bank'],['s','receipt'],
['s','stopwatch'],['s','suitcase'],['s','tag'],['s','tags'],['s','timer'],
['s','trophy'],['s','truck'],['s','wallet'],
// Nature and weather
['s','fire'],['s','fire-flame-curved'],['s','leaf'],['s','moon'],
['s','seedling'],['s','snowflake'],['s','sun'],['s','thermometer'],
['s','tree'],['s','water'],['s','wind'],
// Health and medical
['s','bandage'],['s','bone'],['s','brain'],['s','dna'],['s','heart'],
['s','heart-pulse'],['s','hospital'],['s','pills'],['s','stethoscope'],
['s','syringe'],['s','tooth'],['s','virus'],['s','wheelchair'],
// Brands
['b','android'],['b','angular'],['b','apple'],['b','aws'],['b','bitbucket'],
['b','bootstrap'],['b','chrome'],['b','css3'],['b','discord'],['b','docker'],
['b','dribbble'],['b','dropbox'],['b','facebook'],['b','facebook-f'],
['b','figma'],['b','firefox'],['b','git'],['b','github'],['b','gitlab'],
['b','google'],['b','html5'],['b','instagram'],['b','java'],['b','linkedin'],
['b','linux'],['b','microsoft'],['b','node-js'],['b','npm'],['b','php'],
['b','python'],['b','react'],['b','slack'],['b','square-js'],['b','tiktok'],
['b','vuejs'],['b','whatsapp'],['b','windows'],['b','wordpress'],
['b','x-twitter'],['b','youtube']
];
/* ── Icon picker component ───────────────────────────────────────────── */
function IconPicker(props) {
var value = props.value || '';
var onChange = props.onChange;
var qs = useS('');
var query = qs[0], setQuery = qs[1];
var lower = query.toLowerCase().replace(/\s+/g, '-');
var filtered = query
? FA_ICONS.filter(function(ic){ return ic[1].indexOf(lower) !== -1; })
: FA_ICONS;
return el('div', { className: 'oribi-icon-picker' },
// Current selection row
value
? el('div', { className: 'oribi-icon-current' },
el('i', { className: value, 'aria-hidden': 'true' }),
el('span', { className: 'oribi-icon-current-label' }, value),
el('button', {
className: 'oribi-icon-clear',
type: 'button',
onClick: function(){ onChange(''); }
}, '\u2715 Clear')
)
: null,
// Search input
el('input', {
type: 'search',
className: 'oribi-icon-search',
placeholder: 'Search ' + FA_ICONS.length + ' icons…',
value: query,
onChange: function(e){ setQuery(e.target.value); }
}),
// Result count badge (only when filtering)
query
? el('div', { className: 'oribi-icon-count' }, filtered.length + ' result' + (filtered.length === 1 ? '' : 's'))
: null,
// Icon grid
el('div', { className: 'oribi-icon-grid' },
filtered.length === 0
? el('div', { className: 'oribi-icon-empty' }, 'No icons found.')
: filtered.map(function(ic) {
var prefix = ic[0] === 'b' ? 'fab' : 'fas';
var cls = prefix + ' fa-' + ic[1];
var active = cls === value;
return el('button', {
key: cls,
type: 'button',
title: ic[1],
className: 'oribi-icon-cell' + (active ? ' is-active' : ''),
onClick: function(){ onChange(cls); }
},
el('i', { className: cls, 'aria-hidden': 'true' }),
el('span', null, ic[1])
);
})
)
);
}
function iconControls(a, s) {
var useFa = a.iconType === 'fontawesome';
return el(Frag, null,
el(TG, {
label: 'Use Font Awesome icon',
checked: useFa,
onChange: function(v){ s({ iconType: v ? 'fontawesome' : 'emoji' }); }
}),
useFa
? el(IconPicker, { value: a.faIcon || '', onChange: function(v){ s({ faIcon: v }); } })
: el(TC, { label: 'Icon (emoji)', value: a.icon || '', onChange: function(v){ s({ icon: v }); } })
);
}
/**
* Return the element to render in the card preview for the current icon state.
* cssClass - the wrapper class, e.g. 'feature-icon' or 'value-icon'
* extraStyle - optional inline style object for the wrapper
2026-02-20 21:28:00 -05:00
*/
function iconPreview(a, cssClass, extraStyle) {
var useFa = a.iconType === 'fontawesome';
var hasIcon = useFa ? !!a.faIcon : !!a.icon;
if (!hasIcon) return null;
var child = useFa
? el('i', { className: a.faIcon || '', 'aria-hidden': 'true' })
: a.icon;
return el('div', { className: cssClass, style: extraStyle || {} }, child);
}
/** Build shared Card Image InspectorControls panel. */
function cardImageControls(a, s) {
var imgW = a.imgWidth || 80;
var imgH = a.imgHeight || 0;
var imgPos = a.imgPosition || 'top';
var imgFit = a.imgFit || 'contain';
return el(PB, { title: 'Card Image', initialOpen: false },
el(MUC, null,
el(MU, {
onSelect: function(media){ s({ imgId: media.id, imgUrl: media.url, imgAlt: media.alt || '' }); },
allowedTypes: ['image'],
value: a.imgId || 0,
render: function(ref) {
return el(Frag, null,
a.imgUrl ? el('div', { style: { marginBottom: '8px' } },
el('img', { src: a.imgUrl, style: { maxWidth: '100%', height: 'auto', borderRadius: '4px', display: 'block', marginBottom: '6px' } }),
el(Btn, { variant: 'link', isDestructive: true, onClick: function(){ s({ imgId: 0, imgUrl: '', imgAlt: '' }); } }, 'Remove image')
) : null,
el(Btn, { onClick: ref.open, variant: 'secondary', __next40pxDefaultSize: true },
a.imgUrl ? 'Replace image' : 'Select from Media Library')
);
}
})
),
a.imgUrl ? el(Frag, null,
el(RC, { label: 'Width (px)', value: imgW, min: 20, max: 600, step: 4,
onChange: function(v){ s({ imgWidth: v }); } }),
el(RC, { label: 'Height (px) - 0 = auto', value: imgH, min: 0, max: 600, step: 4,
2026-02-20 21:28:00 -05:00
onChange: function(v){ s({ imgHeight: v }); } }),
el(TG, { label: 'Scale to fill (cover)', checked: imgFit === 'cover',
onChange: function(v){ s({ imgFit: v ? 'cover' : 'contain' }); } }),
el(SC, { label: 'Position', value: imgPos, options: [
{ label: 'Above content', value: 'top' },
{ label: 'Left of content', value: 'left' },
{ label: 'Replace icon', value: 'replace-icon' },
{ label: 'Background', value: 'background' }
], onChange: function(v){ s({ imgPosition: v }); } })
) : null
);
}
/** Build an image preview element for the editor. */
function cardImagePreview(a) {
if (!a.imgUrl) return null;
var imgW = a.imgWidth || 80;
var imgH = a.imgHeight || 0;
var imgFit = a.imgFit || 'contain';
var style = {
width: imgW + 'px', maxWidth: '100%',
height: imgH > 0 ? imgH + 'px' : 'auto',
borderRadius: '4px', objectFit: imgFit, display: 'block'
};
return el('div', { className: 'oribi-card-img-wrap', style: { marginBottom: '1.25rem' } },
el('img', { src: a.imgUrl, className: 'oribi-card-img oribi-card-img--' + imgFit, style: style })
);
}
/** Build a shared card section edit component. */
function createCardSectionEdit(allowedBlocks, defaultTemplate, label) {
return function(props) {
var a = props.attributes, s = props.setAttributes;
return el(Frag, null,
el(IC, null,
el(PB, { title: 'Section Settings' },
el(SC, { label: 'Background', value: a.variant, options: [
{ label: 'Normal', value: 'normal' }, { label: 'Alternate', value: 'alt' }
], onChange: function(v){s({variant:v});} }),
el(RC, { label: 'Columns', value: a.columns, min: 1, max: 4, onChange: function(v){s({columns:v});} }),
el(TC, { label: 'Label', value: a.label, onChange: function(v){s({label:v});} })
)
),
el('section', { className: a.variant === 'alt' ? 'section section-alt' : 'section' },
el('div', { className: 'container' },
el('div', { className: 'section-header' },
a.label ? el('span', { className: 'section-label' }, a.label) : null,
el(RT, { tagName: 'h2', value: a.heading, onChange: function(v){s({heading:v});}, placeholder: 'Section heading...' }),
el(RT, { tagName: 'p', className: 'lead', value: a.lead, onChange: function(v){s({lead:v});}, placeholder: 'Lead text...' })
),
el('div', { className: 'grid-' + a.columns },
el(IB, {
allowedBlocks: allowedBlocks,
template: defaultTemplate,
templateLock: false
})
)
)
)
);
};
}
/** Standard section attributes for JS. */
var SECTION_ATTRS = {
align: { type: 'string', default: 'full' },
variant: { type: 'string', default: 'normal' },
label: { type: 'string', default: '' },
heading: { type: 'string', default: '' },
lead: { type: 'string', default: '' },
columns: { type: 'number', default: 3 }
};
/*
STANDALONE BLOCKS (unchanged architecture)
*/
/* 1. HERO ─────────────────────────────────────────────────────────────── */
reg('oribi/hero', {
title: 'Oribi Hero',
icon: 'cover-image',
category: 'oribi',
supports: { align: ['full'], html: false },
attributes: {
align: { type: 'string', default: 'full' },
label: { type: 'string', default: '' },
title: { type: 'string', default: '' },
highlightWord: { type: 'string', default: '' },
description: { type: 'string', default: '' },
primaryBtnText: { type: 'string', default: 'Get in Touch' },
primaryBtnUrl: { type: 'string', default: '/contact' },
secondaryBtnText: { type: 'string', default: '' },
secondaryBtnUrl: { type: 'string', default: '' },
stat1Value: { type: 'string', default: '' },
stat1Label: { type: 'string', default: '' },
stat2Value: { type: 'string', default: '' },
stat2Label: { type: 'string', default: '' },
svcLaptop1: { type: 'string', default: 'Data Backup' },
svcLaptop2: { type: 'string', default: 'Endpoint Security' },
svcLaptop3: { type: 'string', default: 'Patch Management' },
svcCloud1: { type: 'string', default: 'Email Protection' },
svcCloud2: { type: 'string', default: 'License Management' },
svcCloud3: { type: 'string', default: 'Cloud Backup' },
svcDesktop1: { type: 'string', default: 'Network Monitoring' },
svcDesktop2: { type: 'string', default: 'Threat Detection' },
svcDesktop3: { type: 'string', default: 'Cloud Management' },
svcPhone1: { type: 'string', default: 'Mobile Security' },
svcPhone2: { type: 'string', default: 'Data Encryption' },
},
edit: function (props) {
var a = props.attributes, s = props.setAttributes;
return el(Frag, null,
el(IC, null,
el(PB, { title: 'Highlight' },
el(TC, { label: 'Word to highlight in title', value: a.highlightWord, onChange: function(v){s({highlightWord:v});} })
),
el(PB, { title: 'Primary Button' },
el(TC, { label: 'URL', value: a.primaryBtnUrl, onChange: function(v){s({primaryBtnUrl:v});} })
),
el(PB, { title: 'Secondary Button', initialOpen: false },
el(TC, { label: 'URL', value: a.secondaryBtnUrl, onChange: function(v){s({secondaryBtnUrl:v});} })
)
),
el('section', { className: 'hero' },
el('div', { className: 'container hero-inner' },
el('div', { className: 'hero-content' },
el(RT, { tagName: 'span', className: 'hero-label', value: a.label,
onChange: function(v){s({label:v});}, placeholder: '\u25CF Label text', allowedFormats: [] }),
el(RT, { tagName: 'h1', className: 'hero-title', value: a.title,
onChange: function(v){s({title:v});}, placeholder: 'Hero title...' }),
el(RT, { tagName: 'p', className: 'hero-description', value: a.description,
onChange: function(v){s({description:v});}, placeholder: 'Description...' }),
el('div', { className: 'btn-group' },
el(RT, { tagName: 'span', className: 'btn btn-primary btn-lg', value: a.primaryBtnText,
onChange: function(v){s({primaryBtnText:v});}, placeholder: 'Button', allowedFormats: [] }),
el(RT, { tagName: 'span', className: 'btn btn-ghost btn-lg', value: a.secondaryBtnText,
onChange: function(v){s({secondaryBtnText:v});}, placeholder: 'Secondary button', allowedFormats: [] })
),
el('div', { className: 'hero-stats' },
el('div', null,
el(RT, { tagName: 'div', className: 'hero-stat-value', value: a.stat1Value,
onChange: function(v){s({stat1Value:v});}, placeholder: '\u2014', allowedFormats: [] }),
el(RT, { tagName: 'div', className: 'hero-stat-label', value: a.stat1Label,
onChange: function(v){s({stat1Label:v});}, placeholder: 'Stat label', allowedFormats: [] })
),
el('div', null,
el(RT, { tagName: 'div', className: 'hero-stat-value', value: a.stat2Value,
onChange: function(v){s({stat2Value:v});}, placeholder: '\u2014', allowedFormats: [] }),
el(RT, { tagName: 'div', className: 'hero-stat-label', value: a.stat2Label,
onChange: function(v){s({stat2Label:v});}, placeholder: 'Stat label', allowedFormats: [] })
)
)
),
el('div', { className: 'hero-visual' },
el('div', { className: 'hero-devices' },
el('div', { className: 'hero-device hero-device--laptop', style: { opacity: 1 } },
el('div', { className: 'hero-device__frame' },
el('div', { className: 'hero-device__screen' },
el('div', { className: 'hero-device__screen-content' },
el('div', { className: 'hero-device__app-bars' }, el('div'),el('div'),el('div'),el('div'))
)
),
el('div', { className: 'hero-device__base' })
),
el('ul', { className: 'hero-device__services' },
el('li', { className: 'svc', style: { opacity: 1 } }, el('span',{className:'svc__dot'}), ' Data Backup'),
el('li', { className: 'svc', style: { opacity: 1 } }, el('span',{className:'svc__dot'}), ' Endpoint Security'),
el('li', { className: 'svc', style: { opacity: 1 } }, el('span',{className:'svc__dot'}), ' Patch Management')
)
),
el('div', { className: 'hero-device hero-device--cloud', style: { opacity: 1 } },
el('div', { className: 'hero-device__frame' },
el('div', { className: 'hero-device__cloud-icon' },
el('span', { className: 'hero-device__cloud-label' }, '365')
)
),
el('ul', { className: 'hero-device__services' },
el('li', { className: 'svc', style: { opacity: 1 } }, el('span',{className:'svc__dot'}), ' Email Protection'),
el('li', { className: 'svc', style: { opacity: 1 } }, el('span',{className:'svc__dot'}), ' License Management'),
el('li', { className: 'svc', style: { opacity: 1 } }, el('span',{className:'svc__dot'}), ' Cloud Backup')
)
),
el('div', { className: 'hero-device hero-device--desktop', style: { opacity: 1 } },
el('div', { className: 'hero-device__frame' },
el('div', { className: 'hero-device__screen' },
el('div', { className: 'hero-device__screen-content' },
el('div', { className: 'hero-device__dash-row' }, el('div',{className:'hero-device__dash-card'}), el('div',{className:'hero-device__dash-card'})),
el('div', { className: 'hero-device__dash-bar' }),
el('div', { className: 'hero-device__dash-bar hero-device__dash-bar--short' })
)
),
el('div', { className: 'hero-device__stand' }),
el('div', { className: 'hero-device__stand-base' })
),
el('ul', { className: 'hero-device__services' },
el('li', { className: 'svc', style: { opacity: 1 } }, el('span',{className:'svc__dot'}), ' Network Monitoring'),
el('li', { className: 'svc', style: { opacity: 1 } }, el('span',{className:'svc__dot'}), ' Threat Detection'),
el('li', { className: 'svc', style: { opacity: 1 } }, el('span',{className:'svc__dot'}), ' Cloud Management')
)
),
el('div', { className: 'hero-device hero-device--phone', style: { opacity: 1 } },
el('div', { className: 'hero-device__frame' },
el('div', { className: 'hero-device__screen' },
el('div', { className: 'hero-device__screen-content' },
el('div', { className: 'hero-device__notif' },
el('span', { className: 'hero-device__notif-icon' }, '\u2713'),
el('span', { className: 'hero-device__notif-text' }, 'Secure')
)
)
)
),
el('ul', { className: 'hero-device__services' },
el('li', { className: 'svc', style: { opacity: 1 } }, el('span',{className:'svc__dot'}), ' Mobile Security'),
el('li', { className: 'svc', style: { opacity: 1 } }, el('span',{className:'svc__dot'}), ' Data Encryption')
)
)
)
)
)
)
);
},
save: function () { return null; }
});
/* 2. PAGE HERO ────────────────────────────────────────────────────────── */
reg('oribi/page-hero', {
title: 'Oribi Page Hero',
icon: 'flag',
category: 'oribi',
supports: { align: ['full'], html: false },
attributes: {
align: { type: 'string', default: 'full' },
label: { type: 'string', default: '' },
title: { type: 'string', default: '' },
description: { type: 'string', default: '' },
},
edit: function (props) {
var a = props.attributes, s = props.setAttributes;
return el(Frag, null,
el(IC, null,
el(PB, { title: 'Settings' },
el(TC, { label: 'Label (optional)', value: a.label, onChange: function(v){s({label:v});} })
)
),
el('section', { className: 'page-hero' },
el('div', { className: 'container' },
a.label ? el('span', { className: 'hero-label' }, a.label) : null,
el(RT, { tagName: 'h1', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Page title...' }),
el(RT, { tagName: 'p', className: 'lead', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' })
)
)
);
},
save: function () { return null; }
});
/* 5. CTA BANNER ───────────────────────────────────────────────────────── */
reg('oribi/cta-banner', {
title: 'Oribi CTA Banner',
icon: 'megaphone',
category: 'oribi',
supports: { align: ['full'], html: false },
attributes: {
align: { type: 'string', default: 'full' },
heading: { type: 'string', default: '' },
text: { type: 'string', default: '' },
btnText: { type: 'string', default: '' },
btnUrl: { type: 'string', default: '' },
},
edit: function (props) {
var a = props.attributes, s = props.setAttributes;
return el(Frag, null,
el(IC, null,
el(PB, { title: 'Button' },
el(TC, { label: 'URL', value: a.btnUrl, onChange: function(v){s({btnUrl:v});} })
)
),
el('section', { className: 'cta-banner' },
el('div', { className: 'container text-center' },
el(RT, { tagName: 'h2', value: a.heading, onChange: function(v){s({heading:v});}, placeholder: 'CTA heading...' }),
el(RT, { tagName: 'p', value: a.text, onChange: function(v){s({text:v});}, placeholder: 'CTA text...' }),
el(RT, { tagName: 'span', className: 'btn btn-primary btn-lg', style: { background: '#fff', color: 'var(--color-primary)' }, value: a.btnText, onChange: function(v){s({btnText:v});}, placeholder: 'Button text...' })
)
)
);
},
save: function () { return null; }
});
/* 6. INTRO SECTION ────────────────────────────────────────────────────── */
reg('oribi/intro-section', {
title: 'Oribi Intro Section',
icon: 'id',
category: 'oribi',
supports: { align: ['full'], html: false },
attributes: {
align: { type: 'string', default: 'full' },
variant: { type: 'string', default: 'normal' },
label: { type: 'string', default: '' },
heading: { type: 'string', default: '' },
description: { type: 'string', default: '' },
visual: { type: 'string', default: '' },
reversed: { type: 'boolean', default: false },
imgId: { type: 'number', default: 0 },
imgUrl: { type: 'string', default: '' },
imgAlt: { type: 'string', default: '' },
imgWidth: { type: 'number', default: 280 },
},
edit: function (props) {
var a = props.attributes, s = props.setAttributes;
var imgW = a.imgWidth || 280;
var visualContent = a.imgUrl
? el('img', { src: a.imgUrl, style: { width: imgW + 'px', maxWidth: '100%', height: 'auto', borderRadius: '8px', objectFit: 'contain', display: 'block' } })
: (a.visual || '\uD83D\uDCBB');
return el(Frag, null,
el(IC, null,
el(PB, { title: 'Settings' },
el(SC, { label: 'Background', value: a.variant, options: [
{ label: 'Normal', value: 'normal' }, { label: 'Alternate', value: 'alt' }
], onChange: function(v){s({variant:v});} }),
el(TC, { label: 'Label', value: a.label, onChange: function(v){s({label:v});} }),
el(TC, { label: 'Visual (emoji or text)', value: a.visual, onChange: function(v){s({visual:v});} }),
el(TG, { label: 'Reversed layout', checked: a.reversed, onChange: function(v){s({reversed:v});} })
),
el(PB, { title: 'Visual Image', initialOpen: false },
el(MUC, null,
el(MU, {
onSelect: function(media){ s({ imgId: media.id, imgUrl: media.url, imgAlt: media.alt || '' }); },
allowedTypes: ['image'],
value: a.imgId || 0,
render: function(ref) {
return el(Frag, null,
a.imgUrl ? el('div', { style: { marginBottom: '8px' } },
el('img', { src: a.imgUrl, style: { maxWidth: '100%', height: 'auto', borderRadius: '4px', display: 'block', marginBottom: '6px' } }),
el(Btn, { variant: 'link', isDestructive: true, onClick: function(){ s({ imgId: 0, imgUrl: '', imgAlt: '' }); } }, 'Remove image')
) : null,
el(Btn, { onClick: ref.open, variant: 'secondary', __next40pxDefaultSize: true },
a.imgUrl ? 'Replace image' : 'Select from Media Library')
);
}
})
),
a.imgUrl ? el(RC, { label: 'Width (px)', value: imgW, min: 50, max: 420, step: 4,
onChange: function(v){ s({ imgWidth: v }); } }) : null
)
),
el('section', { className: a.variant === 'alt' ? 'section section-alt' : 'section' },
el('div', { className: 'container' },
el('div', { className: 'about-intro', style: a.reversed ? { direction: 'rtl' } : {} },
el('div', { style: a.reversed ? { direction: 'ltr' } : {} },
a.label ? el('span', { className: 'section-label' }, a.label) : null,
el(RT, { tagName: 'h2', style: { marginBottom: '1.5rem' }, value: a.heading, onChange: function(v){s({heading:v});}, placeholder: 'Heading...' }),
el(RT, { tagName: 'p', className: 'lead', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' })
),
el('div', { className: 'about-intro-visual' + (a.imgUrl ? ' has-img' : ''), style: a.reversed ? { direction: 'ltr' } : {} }, visualContent)
)
)
)
);
},
save: function () { return null; }
});
/* 8. CONTACT SECTION ──────────────────────────────────────────────────── */
reg('oribi/contact-section', {
title: 'Oribi Contact',
icon: 'email',
category: 'oribi',
supports: { align: ['full'], html: false },
attributes: {
align: { type: 'string', default: 'full' },
heading: { type: 'string', default: "Let's Talk" },
lead: { type: 'string', default: '' },
email: { type: 'string', default: 'solutions@oribi-tech.com' },
supportUrl: { type: 'string', default: '' },
portalUrl: { type: 'string', default: '' },
location: { type: 'string', default: 'Saratoga Springs, Upstate New York' },
formHeading: { type: 'string', default: 'Want to Learn More?' },
},
edit: function (props) {
var a = props.attributes, s = props.setAttributes;
return el(Frag, null,
el(IC, null,
el(PB, { title: 'Contact Settings' },
el(TC, { label: 'Email', value: a.email, onChange: function(v){s({email:v});} }),
el(TC, { label: 'Support URL', value: a.supportUrl, onChange: function(v){s({supportUrl:v});} }),
el(TC, { label: 'Portal URL', value: a.portalUrl, onChange: function(v){s({portalUrl:v});} }),
el(TC, { label: 'Location', value: a.location, onChange: function(v){s({location:v});} }),
el(TC, { label: 'Form Heading', value: a.formHeading, onChange: function(v){s({formHeading:v});} })
)
),
el('section', { className: 'section' },
el('div', { className: 'container' },
el('div', { className: 'contact-layout' },
el('div', { className: 'contact-info' },
el(RT, { tagName: 'h2', value: a.heading, onChange: function(v){s({heading:v});}, placeholder: 'Heading...' }),
el(RT, { tagName: 'p', className: 'lead', value: a.lead, onChange: function(v){s({lead:v});}, placeholder: 'Lead text...' }),
el('div', { className: 'contact-method' },
el('div',{className:'contact-method-icon'},'\uD83D\uDCE7'),
el('div',null, el('div',{className:'contact-method-label'},'Email Us'), el('div',{className:'contact-method-value'}, a.email))
),
el('div', { className: 'contact-method' },
el('div',{className:'contact-method-icon'},'\uD83C\uDFAB'),
el('div',null, el('div',{className:'contact-method-label'},'Support'), el('div',{className:'contact-method-value'}, 'Open a Support Ticket'))
),
el('div', { className: 'contact-method' },
el('div',{className:'contact-method-icon'},'\uD83C\uDF0E'),
el('div',null, el('div',{className:'contact-method-label'},'Client Portal'), el('div',{className:'contact-method-value'}, 'portal.oribi-tech.com'))
),
el('div', { className: 'contact-method' },
el('div',{className:'contact-method-icon'},'\uD83D\uDCCD'),
el('div',null, el('div',{className:'contact-method-label'},'Location'), el('div',{className:'contact-method-value'}, a.location))
)
),
el('div', { className: 'contact-form-wrap' },
el('h3', { style: { marginBottom: '1.5rem' } }, a.formHeading),
el('div', { style: { padding: '2rem', background: 'var(--color-bg-alt)', borderRadius: 'var(--radius-md)', textAlign: 'center', color: 'var(--color-text-muted)' } },
'Contact form renders on the live site'
)
)
)
)
)
);
},
save: function () { return null; }
});
/*
CHILD BLOCKS (each renders one item inside a parent)
*/
/* ── Feature Card ─────────────────────────────────────────────────────── */
reg('oribi/feature-card', {
title: 'Feature Card',
icon: 'screenoptions',
category: 'oribi',
parent: ['oribi/feature-section'],
supports: { html: false, reusable: false },
attributes: Object.assign({}, {
icon: { type: 'string', default: '' },
iconType: { type: 'string', default: 'emoji' },
faIcon: { type: 'string', default: '' },
title: { type: 'string', default: '' },
description: { type: 'string', default: '' },
url: { type: 'string', default: '' },
centered: { type: 'boolean', default: false }
}, CARD_IMAGE_ATTRS),
edit: function (props) {
var a = props.attributes, s = props.setAttributes;
var imgPos = a.imgPosition || 'top';
var imgPrev = cardImagePreview(a);
var centeredStyle = a.centered ? { marginInline: 'auto' } : {};
var cardPreview;
if ( a.imgUrl && imgPos === 'left' ) {
cardPreview = el('div', { className: 'oribi-card img-left' },
imgPrev,
el('div', { className: 'oribi-card-body' },
el(RT, { tagName: 'h3', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Card title...' }),
el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Card description...' })
)
);
} else if ( a.imgUrl && imgPos === 'background' ) {
cardPreview = el('div', { className: 'oribi-card img-bg', style: { backgroundImage: 'url(' + a.imgUrl + ')', backgroundSize: 'cover', backgroundPosition: 'center', color: '#fff' } },
el('div', { className: 'oribi-card-body' },
iconPreview(a, 'feature-icon', centeredStyle),
el(RT, { tagName: 'h3', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Card title...' }),
el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Card description...' })
)
);
} else {
var showIconPrev = !a.imgUrl || imgPos !== 'replace-icon';
cardPreview = el('div', { className: 'oribi-card' + (a.centered ? ' text-center' : '') + (a.imgUrl ? ' img-' + imgPos : '') },
a.imgUrl && (imgPos === 'top' || imgPos === 'replace-icon') ? imgPrev : null,
showIconPrev ? iconPreview(a, 'feature-icon', centeredStyle) : null,
el(RT, { tagName: 'h3', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Card title...' }),
el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Card description...' })
);
}
return el(Frag, null,
el(IC, null,
el(PB, { title: 'Card Settings' },
iconControls(a, s),
el(TC, { label: 'URL (optional)', value: a.url || '', onChange: function(v){s({url:v});} }),
el(TG, { label: 'Centered', checked: !!a.centered, onChange: function(v){s({centered:v});} })
),
cardImageControls(a, s)
),
cardPreview
);
},
save: function () { return null; }
});
/* ── Value Card ───────────────────────────────────────────────────────── */
reg('oribi/value-card', {
title: 'Value Card',
icon: 'heart',
category: 'oribi',
parent: ['oribi/value-section'],
supports: { html: false, reusable: false },
attributes: Object.assign({}, {
icon: { type: 'string', default: '' },
iconType: { type: 'string', default: 'emoji' },
faIcon: { type: 'string', default: '' },
title: { type: 'string', default: '' },
description: { type: 'string', default: '' }
}, CARD_IMAGE_ATTRS),
edit: function (props) {
var a = props.attributes, s = props.setAttributes;
var imgPos = a.imgPosition || 'top';
var imgPrev = cardImagePreview(a);
var showIconPrev = !a.imgUrl || imgPos !== 'replace-icon';
var body;
if ( a.imgUrl && imgPos === 'left' ) {
body = el('div', { className: 'oribi-card value-card img-left' },
imgPrev,
el('div', { className: 'oribi-card-body' },
el(RT, { tagName: 'h3', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Title...' }),
el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' })
)
);
} else if ( a.imgUrl && imgPos === 'background' ) {
body = el('div', { className: 'oribi-card value-card img-bg', style: { backgroundImage: 'url(' + a.imgUrl + ')', backgroundSize: 'cover', backgroundPosition: 'center', color: '#fff' } },
el('div', { className: 'oribi-card-body' },
showIconPrev ? iconPreview(a, 'value-icon') : null,
el(RT, { tagName: 'h3', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Title...' }),
el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' })
)
);
} else {
body = el('div', { className: 'oribi-card value-card' + (a.imgUrl ? ' img-' + imgPos : '') },
a.imgUrl && (imgPos === 'top' || imgPos === 'replace-icon') ? imgPrev : null,
showIconPrev ? iconPreview(a, 'value-icon') : null,
el(RT, { tagName: 'h3', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Title...' }),
el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' })
);
}
return el(Frag, null,
el(IC, null,
el(PB, { title: 'Card Settings' },
iconControls(a, s)
),
cardImageControls(a, s)
),
body
);
},
save: function () { return null; }
});
/* ── Addon Card ───────────────────────────────────────────────────────── */
reg('oribi/addon-card', {
title: 'Addon Card',
icon: 'plus-alt2',
category: 'oribi',
parent: ['oribi/addon-section'],
supports: { html: false, reusable: false },
attributes: Object.assign({}, {
icon: { type: 'string', default: '' },
iconType: { type: 'string', default: 'emoji' },
faIcon: { type: 'string', default: '' },
title: { type: 'string', default: '' },
description: { type: 'string', default: '' },
tag: { type: 'string', default: '' }
}, CARD_IMAGE_ATTRS),
edit: function (props) {
var a = props.attributes, s = props.setAttributes;
var imgPos = a.imgPosition || 'top';
var imgPrev = cardImagePreview(a);
var showIconPrev = !a.imgUrl || imgPos !== 'replace-icon';
var body;
if ( a.imgUrl && imgPos === 'left' ) {
body = el('div', { className: 'oribi-card img-left' },
imgPrev,
el('div', { className: 'oribi-card-body' },
a.tag ? el('span', { className: 'addon-tag' }, a.tag) : null,
el(RT, { tagName: 'h3', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Title...' }),
el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' })
)
);
} else if ( a.imgUrl && imgPos === 'background' ) {
body = el('div', { className: 'oribi-card img-bg', style: { backgroundImage: 'url(' + a.imgUrl + ')', backgroundSize: 'cover', backgroundPosition: 'center', color: '#fff' } },
el('div', { className: 'oribi-card-body' },
showIconPrev ? iconPreview(a, 'feature-icon') : null,
a.tag ? el('span', { className: 'addon-tag' }, a.tag) : null,
el(RT, { tagName: 'h3', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Title...' }),
el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' })
)
);
} else {
body = el('div', { className: 'oribi-card' + (a.imgUrl ? ' img-' + imgPos : '') },
a.imgUrl && (imgPos === 'top' || imgPos === 'replace-icon') ? imgPrev : null,
showIconPrev ? iconPreview(a, 'feature-icon') : null,
a.tag ? el('span', { className: 'addon-tag' }, a.tag) : null,
el(RT, { tagName: 'h3', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Title...' }),
el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' })
);
}
return el(Frag, null,
el(IC, null,
el(PB, { title: 'Card Settings' },
iconControls(a, s),
el(TC, { label: 'Tag / Badge (optional)', value: a.tag || '', onChange: function(v){s({tag:v});} })
),
cardImageControls(a, s)
),
body
);
},
save: function () { return null; }
});
/* ── Image Card ───────────────────────────────────────────────────────── */
reg('oribi/image-card', {
title: 'Image Card',
icon: 'format-image',
category: 'oribi',
parent: ['oribi/image-section'],
supports: { html: false, reusable: false },
attributes: Object.assign({}, {
icon: { type: 'string', default: '' },
iconType: { type: 'string', default: 'emoji' },
faIcon: { type: 'string', default: '' },
title: { type: 'string', default: '' },
description: { type: 'string', default: '' },
url: { type: 'string', default: '' }
}, CARD_IMAGE_ATTRS),
edit: function (props) {
var a = props.attributes, s = props.setAttributes;
var imgPos = a.imgPosition || 'top';
var imgPrev = cardImagePreview(a);
var showIconPrev = !a.imgUrl || imgPos !== 'replace-icon';
return el(Frag, null,
el(IC, null,
el(PB, { title: 'Card Settings' },
iconControls(a, s),
el(TC, { label: 'URL (optional)', value: a.url || '', onChange: function(v){s({url:v});} })
),
cardImageControls(a, s)
),
el('div', { className: 'oribi-card image-card' + (a.imgUrl ? ' img-' + imgPos : '') },
a.imgUrl && (imgPos === 'top' || imgPos === 'replace-icon') ? imgPrev : null,
showIconPrev ? iconPreview(a, 'feature-icon') : null,
el('div', { className: 'oribi-card-body' },
el(RT, { tagName: 'h3', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Title...' }),
el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' })
)
)
);
},
save: function () { return null; }
});
/* ── Stat Card ────────────────────────────────────────────────────────── */
reg('oribi/stat-card', {
title: 'Stat Card',
icon: 'chart-bar',
category: 'oribi',
parent: ['oribi/stat-section'],
supports: { html: false, reusable: false },
attributes: Object.assign({}, {
icon: { type: 'string', default: '' },
iconType: { type: 'string', default: 'emoji' },
faIcon: { type: 'string', default: '' },
value: { type: 'string', default: '' },
label: { type: 'string', default: '' },
description: { type: 'string', default: '' }
}, CARD_IMAGE_ATTRS),
edit: function (props) {
var a = props.attributes, s = props.setAttributes;
var imgPrev = cardImagePreview(a);
var imgPos = a.imgPosition || 'top';
var body;
if ( a.imgUrl && imgPos === 'background' ) {
body = el('div', { className: 'oribi-card stat-card img-bg', style: { backgroundImage: 'url(' + a.imgUrl + ')', backgroundSize: 'cover', backgroundPosition: 'center', color: '#fff' } },
el('div', { className: 'oribi-card-body' },
iconPreview(a, 'feature-icon'),
el(RT, { tagName: 'div', className: 'stat-value', value: a.value, onChange: function(v){s({value:v});}, placeholder: '99.9%' }),
el(RT, { tagName: 'div', className: 'stat-label', value: a.label, onChange: function(v){s({label:v});}, placeholder: 'Label...' }),
el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description (optional)...' })
)
);
} else {
body = el('div', { className: 'oribi-card stat-card' + (a.imgUrl ? ' img-' + imgPos : '') },
(a.imgUrl && imgPos !== 'replace-icon') ? imgPrev : null,
iconPreview(a, 'feature-icon'),
el(RT, { tagName: 'div', className: 'stat-value', value: a.value, onChange: function(v){s({value:v});}, placeholder: '99.9%' }),
el(RT, { tagName: 'div', className: 'stat-label', value: a.label, onChange: function(v){s({label:v});}, placeholder: 'Label...' }),
el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description (optional)...' })
);
}
return el(Frag, null,
el(IC, null,
el(PB, { title: 'Card Settings' },
iconControls(a, s)
),
cardImageControls(a, s)
),
body
);
},
save: function () { return null; }
});
/* ── Link Card ────────────────────────────────────────────────────────── */
reg('oribi/link-card', {
title: 'Link Card',
icon: 'admin-links',
category: 'oribi',
parent: ['oribi/link-section'],
supports: { html: false, reusable: false },
attributes: Object.assign({}, {
icon: { type: 'string', default: '' },
iconType: { type: 'string', default: 'emoji' },
faIcon: { type: 'string', default: '' },
title: { type: 'string', default: '' },
description: { type: 'string', default: '' },
linkText: { type: 'string', default: '' },
linkUrl: { type: 'string', default: '' }
}, CARD_IMAGE_ATTRS),
edit: function (props) {
var a = props.attributes, s = props.setAttributes;
var imgPos = a.imgPosition || 'top';
var imgPrev = cardImagePreview(a);
var showIconPrev = !a.imgUrl || imgPos !== 'replace-icon';
var body;
if ( a.imgUrl && imgPos === 'left' ) {
body = el('div', { className: 'oribi-card link-card img-left' },
imgPrev,
el('div', { className: 'oribi-card-body' },
showIconPrev ? iconPreview(a, 'feature-icon') : null,
el(RT, { tagName: 'h3', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Title...' }),
el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' }),
el(RT, { tagName: 'span', className: 'link-card-cta', value: a.linkText, onChange: function(v){s({linkText:v});}, placeholder: 'Link text → ...' })
)
);
} else if ( a.imgUrl && imgPos === 'background' ) {
body = el('div', { className: 'oribi-card link-card img-bg', style: { backgroundImage: 'url(' + a.imgUrl + ')', backgroundSize: 'cover', backgroundPosition: 'center', color: '#fff' } },
el('div', { className: 'oribi-card-body' },
showIconPrev ? iconPreview(a, 'feature-icon') : null,
el(RT, { tagName: 'h3', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Title...' }),
el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' }),
el(RT, { tagName: 'span', className: 'link-card-cta', value: a.linkText, onChange: function(v){s({linkText:v});}, placeholder: 'Link text → ...' })
)
);
} else {
body = el('div', { className: 'oribi-card link-card' + (a.imgUrl ? ' img-' + imgPos : '') },
(a.imgUrl && (imgPos === 'top' || imgPos === 'replace-icon')) ? imgPrev : null,
showIconPrev ? iconPreview(a, 'feature-icon') : null,
el(RT, { tagName: 'h3', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Title...' }),
el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' }),
el(RT, { tagName: 'span', className: 'link-card-cta', value: a.linkText, onChange: function(v){s({linkText:v});}, placeholder: 'Link text → ...' })
);
}
return el(Frag, null,
el(IC, null,
el(PB, { title: 'Card Settings' },
iconControls(a, s),
el(TC, { label: 'Link URL', value: a.linkUrl || '', onChange: function(v){s({linkUrl:v});} })
),
cardImageControls(a, s)
),
body
);
},
save: function () { return null; }
});
/* ── Pricing Card ─────────────────────────────────────────────────────── */
reg('oribi/pricing-card', {
title: 'Pricing Card',
icon: 'money-alt',
category: 'oribi',
parent: ['oribi/pricing-section'],
supports: { html: false, reusable: false },
attributes: {
icon: { type: 'string', default: '' },
iconType: { type: 'string', default: 'emoji' },
faIcon: { type: 'string', default: '' },
name: { type: 'string', default: '' },
tagline: { type: 'string', default: '' },
price: { type: 'string', default: '' },
pricePer: { type: 'string', default: '' },
2026-02-20 21:28:00 -05:00
features: { type: 'array', default: [] },
btnText: { type: 'string', default: 'Get Started' },
btnUrl: { type: 'string', default: '/contact' },
featured: { type: 'boolean', default: false },
badge: { type: 'string', default: '' },
imgId: { type: 'number', default: 0 },
imgUrl: { type: 'string', default: '' },
imgAlt: { type: 'string', default: '' },
imgWidth: { type: 'number', default: 80 },
},
edit: function (props) {
var a = props.attributes, s = props.setAttributes;
var features = a.features || [];
var imgW = a.imgWidth || 80;
return el(Frag, null,
el(IC, null,
el(PB, { title: 'Card Settings' },
iconControls(a, s),
el(TC, { label: 'Button URL', value: a.btnUrl, onChange: function(v){s({btnUrl:v});} }),
el(TG, { label: 'Featured', checked: !!a.featured, onChange: function(v){s({featured:v});} }),
a.featured ? el(TC, { label: 'Badge Text', value: a.badge, onChange: function(v){s({badge:v});} }) : null
),
el(PB, { title: 'Card Image', initialOpen: false },
el(MUC, null,
el(MU, {
onSelect: function(media){ s({ imgId: media.id, imgUrl: media.url, imgAlt: media.alt || '' }); },
allowedTypes: ['image'],
value: a.imgId || 0,
render: function(ref) {
return el(Frag, null,
a.imgUrl ? el('div', { style: { marginBottom: '8px' } },
el('img', { src: a.imgUrl, style: { maxWidth: '100%', height: 'auto', borderRadius: '4px', display: 'block', marginBottom: '6px' } }),
el(Btn, { variant: 'link', isDestructive: true, onClick: function(){ s({ imgId: 0, imgUrl: '', imgAlt: '' }); } }, 'Remove image')
) : null,
el(Btn, { onClick: ref.open, variant: 'secondary', __next40pxDefaultSize: true },
a.imgUrl ? 'Replace image' : 'Select from Media Library')
);
}
})
),
a.imgUrl ? el(RC, { label: 'Width (px)', value: imgW, min: 20, max: 400, step: 4,
onChange: function(v){ s({ imgWidth: v }); } }) : null
)
),
el('div', { className: 'pricing-card' + (a.featured ? ' featured' : '') },
a.featured && a.badge ? el(RT, { tagName: 'span', className: 'pricing-badge', value: a.badge,
onChange: function(v){s({badge:v});}, placeholder: 'Badge...' }) : null,
a.imgUrl ? el('div', { style: { textAlign: 'center', marginBottom: '1.25rem' } },
el('img', { src: a.imgUrl, style: { width: imgW + 'px', maxWidth: '100%', height: 'auto', borderRadius: '4px', objectFit: 'contain' } })
) : null,
iconPreview(a, 'feature-icon', { marginInline: 'auto' }),
el(RT, { tagName: 'div', className: 'pricing-name', value: a.name,
onChange: function(v){s({name:v});}, placeholder: 'Plan name...' }),
el(RT, { tagName: 'p', className: 'pricing-tagline', value: a.tagline,
onChange: function(v){s({tagline:v});}, placeholder: 'Tagline...' }),
a.price || a.pricePer ? el('div', { className: 'pricing-price' },
el(RT, { tagName: 'div', className: 'pricing-amount', value: a.price || '',
onChange: function(v){s({price:v});}, placeholder: '$0' }),
el(RT, { tagName: 'div', className: 'pricing-per', value: a.pricePer || '',
onChange: function(v){s({pricePer:v});}, placeholder: 'per screen / month' })
) : el('div', { className: 'pricing-price' },
el(RT, { tagName: 'div', className: 'pricing-amount', value: '',
onChange: function(v){s({price:v});}, placeholder: '$0' }),
el(RT, { tagName: 'div', className: 'pricing-per', value: '',
onChange: function(v){s({pricePer:v});}, placeholder: 'per screen / month' })
),
2026-02-20 21:28:00 -05:00
el('ul', { className: 'pricing-features' },
features.map(function (f, fi) {
return el('li', { key: fi, style: { display: 'flex', alignItems: 'center', gap: '4px' } },
el('span', { className: 'pricing-check' }, '\u2713'),
el(RT, { tagName: 'span', style: { flex: 1, minWidth: 0 }, value: f,
onChange: function(v){ s({features: arrSet(features, fi, v)}); }, placeholder: 'Feature...' }),
el(Btn, { isSmall: true, isDestructive: true,
style: { minWidth: '20px', padding: 0, height: '20px', flexShrink: 0 },
onClick: function(){ s({features: arrRm(features, fi)}); } }, '\u2715')
);
}),
el('li', { style: { listStyle: 'none', marginTop: '4px' } },
el(Btn, { isSmall: true, variant: 'secondary',
onClick: function(){ s({features: arrAdd(features, '')}); } }, '+ Feature')
)
),
el(RT, { tagName: 'span',
className: 'btn ' + (a.featured ? 'btn-primary' : 'btn-outline'),
style: { width: '100%', justifyContent: 'center', cursor: 'text' },
value: a.btnText || '',
onChange: function(v){s({btnText:v});}, placeholder: 'Button text...' })
)
);
},
save: function () { return null; }
});
/* ── Platform Row ─────────────────────────────────────────────────────── */
reg('oribi/platform-row', {
title: 'Platform Row',
icon: 'slides',
category: 'oribi',
parent: ['oribi/platform-section'],
supports: { html: false, reusable: false },
attributes: {
heading: { type: 'string', default: '' },
description: { type: 'string', default: '' },
btnText: { type: 'string', default: 'Learn More' },
btnUrl: { type: 'string', default: '' },
visual: { type: 'string', default: '' },
reversed: { type: 'boolean', default: false },
imgId: { type: 'number', default: 0 },
imgUrl: { type: 'string', default: '' },
imgAlt: { type: 'string', default: '' },
imgWidth: { type: 'number', default: 300 },
cameraAnim: { type: 'boolean', default: false },
2026-02-20 21:28:00 -05:00
},
edit: function (props) {
var a = props.attributes, s = props.setAttributes;
var imgW = a.imgWidth || 300;
return el(Frag, null,
el(IC, null,
el(PB, { title: 'Row Settings' },
el(TC, { label: 'Visual (emoji)', value: a.visual, onChange: function(v){s({visual:v});} }),
el(TC, { label: 'Button URL', value: a.btnUrl, onChange: function(v){s({btnUrl:v});} }),
el(TG, { label: 'Reversed', checked: !!a.reversed, onChange: function(v){s({reversed:v});} }),
el(TG, { label: 'Camera Animation', checked: !!a.cameraAnim, onChange: function(v){s({cameraAnim:v});} })
2026-02-20 21:28:00 -05:00
),
el(PB, { title: 'Visual Image', initialOpen: false },
el(MUC, null,
el(MU, {
onSelect: function(media){ s({ imgId: media.id, imgUrl: media.url, imgAlt: media.alt || '' }); },
allowedTypes: ['image'],
value: a.imgId || 0,
render: function(ref) {
return el(Frag, null,
a.imgUrl ? el('div', { style: { marginBottom: '8px' } },
el('img', { src: a.imgUrl, style: { maxWidth: '100%', height: 'auto', borderRadius: '4px', display: 'block', marginBottom: '6px' } }),
el(Btn, { variant: 'link', isDestructive: true, onClick: function(){ s({ imgId: 0, imgUrl: '', imgAlt: '' }); } }, 'Remove image')
) : null,
el(Btn, { onClick: ref.open, variant: 'secondary', __next40pxDefaultSize: true },
a.imgUrl ? 'Replace image' : 'Select from Media Library')
);
}
})
),
a.imgUrl ? el(RC, { label: 'Width (px)', value: imgW, min: 50, max: 600, step: 4,
onChange: function(v){ s({ imgWidth: v }); } }) : null
)
),
el('div', { className: 'platform-row' + (a.reversed ? ' reverse' : '') },
el('div', { className: 'platform-text' },
el(RT, { tagName: 'h3', value: a.heading, onChange: function(v){s({heading:v});}, placeholder: 'Service name...' }),
el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Service description...' }),
a.btnUrl ? el(RT, { tagName: 'span', className: 'btn btn-outline mt-3',
value: a.btnText, onChange: function(v){s({btnText:v});}, placeholder: 'Button...' }) : null
),
a.cameraAnim
? el('div', { className: 'platform-visual has-camera' },
el('div', { className: 'cam-stage', 'aria-hidden': 'true' },
el('div', { className: 'cam-scene' },
el('div', { className: 'cam-subject cam-subject--1' }),
el('div', { className: 'cam-flash-overlay' })
),
el('div', { className: 'cam-body' },
el('div', { className: 'cam-top' },
el('div', { className: 'cam-hump' }, el('div', { className: 'cam-dial' })),
el('div', { className: 'cam-shutter-btn' }, el('div', { className: 'cam-shutter-inner' }))
),
el('div', { className: 'cam-front' },
el('div', { className: 'cam-grip-ridge' }),
el('div', { className: 'cam-lens-assembly' },
el('div', { className: 'cam-lens-barrel' },
el('div', { className: 'cam-lens-glass' },
el('div', { className: 'cam-reflection' })
)
)
),
el('div', { className: 'cam-controls' },
el('div', { className: 'cam-led' }),
el('div', { className: 'cam-btn' }),
el('div', { className: 'cam-btn' })
)
)
),
el('div', { className: 'cam-prints' },
el('div', { className: 'cam-print cam-print--1', style: { opacity: '1', transform: 'rotate(-14deg) translateY(0)' } },
el('div', { className: 'cam-print__img' })
)
)
)
)
: a.imgUrl
2026-02-20 21:28:00 -05:00
? el('div', { className: 'platform-visual has-img' },
el('img', { src: a.imgUrl, style: { width: imgW + 'px', maxWidth: '100%', height: 'auto', borderRadius: '4px', objectFit: 'contain', display: 'block', marginInline: 'auto' } })
)
: el('div', { className: 'platform-visual' }, a.visual || '\uD83D\uDCBB')
)
);
},
save: function () { return null; }
});
/* ── Trust Item ───────────────────────────────────────────────────────── */
reg('oribi/trust-item', {
title: 'Trust Item',
icon: 'shield',
category: 'oribi',
parent: ['oribi/trust-section'],
supports: { html: false, reusable: false },
attributes: {
heading: { type: 'string', default: '' },
description: { type: 'string', default: '' },
},
edit: function (props) {
var a = props.attributes, s = props.setAttributes;
return el('div', { className: 'trust-item' },
el(RT, { tagName: 'h3', style: { marginBottom: '1rem' },
value: a.heading, onChange: function(v){s({heading:v});}, placeholder: 'Sub-heading...' }),
el(RT, { tagName: 'p', value: a.description,
onChange: function(v){s({description:v});}, placeholder: 'Description...' })
);
},
save: function () { return null; }
});
/*
PARENT BLOCKS (use InnerBlocks for child items)
*/
/* 3. FEATURE SECTION ──────────────────────────────────────────────────── */
reg('oribi/feature-section', {
title: 'Oribi Feature Section',
icon: 'grid-view',
category: 'oribi',
supports: { align: ['full'], html: false },
attributes: SECTION_ATTRS,
edit: createCardSectionEdit(['oribi/feature-card'], [['oribi/feature-card', {}]], 'Feature Card'),
save: function () { return el(IB.Content); }
});
/* VALUE SECTION ────────────────────────────────────────────────────────── */
reg('oribi/value-section', {
title: 'Oribi Value Section',
icon: 'heart',
category: 'oribi',
supports: { align: ['full'], html: false },
attributes: SECTION_ATTRS,
edit: createCardSectionEdit(['oribi/value-card'], [['oribi/value-card', {}]], 'Value Card'),
save: function () { return el(IB.Content); }
});
/* ADDON SECTION ────────────────────────────────────────────────────────── */
reg('oribi/addon-section', {
title: 'Oribi Addon Section',
icon: 'plus-alt2',
category: 'oribi',
supports: { align: ['full'], html: false },
attributes: SECTION_ATTRS,
edit: createCardSectionEdit(['oribi/addon-card'], [['oribi/addon-card', {}]], 'Addon Card'),
save: function () { return el(IB.Content); }
});
/* IMAGE SECTION ────────────────────────────────────────────────────────── */
reg('oribi/image-section', {
title: 'Oribi Image Section',
icon: 'format-image',
category: 'oribi',
supports: { align: ['full'], html: false },
attributes: SECTION_ATTRS,
edit: createCardSectionEdit(['oribi/image-card'], [['oribi/image-card', {}]], 'Image Card'),
save: function () { return el(IB.Content); }
});
/* STAT SECTION ─────────────────────────────────────────────────────────── */
reg('oribi/stat-section', {
title: 'Oribi Stat Section',
icon: 'chart-bar',
category: 'oribi',
supports: { align: ['full'], html: false },
attributes: SECTION_ATTRS,
edit: createCardSectionEdit(['oribi/stat-card'], [['oribi/stat-card', {}]], 'Stat Card'),
save: function () { return el(IB.Content); }
});
/* LINK SECTION ─────────────────────────────────────────────────────────── */
reg('oribi/link-section', {
title: 'Oribi Link Section',
icon: 'admin-links',
category: 'oribi',
supports: { align: ['full'], html: false },
attributes: SECTION_ATTRS,
edit: createCardSectionEdit(['oribi/link-card'], [['oribi/link-card', {}]], 'Link Card'),
save: function () { return el(IB.Content); }
});
/* 4. PRICING SECTION ──────────────────────────────────────────────────── */
reg('oribi/pricing-section', {
title: 'Oribi Pricing',
icon: 'money-alt',
category: 'oribi',
supports: { align: ['full'], html: false },
attributes: {
align: { type: 'string', default: 'full' },
variant: { type: 'string', default: 'normal' },
label: { type: 'string', default: '' },
heading: { type: 'string', default: '' },
lead: { type: 'string', default: '' },
},
edit: function (props) {
var a = props.attributes, s = props.setAttributes;
return el(Frag, null,
el(IC, null,
el(PB, { title: 'Section Settings' },
el(SC, { label: 'Background', value: a.variant, options: [
{ label: 'Normal', value: 'normal' }, { label: 'Alternate', value: 'alt' }
], onChange: function(v){s({variant:v});} }),
el(TC, { label: 'Label', value: a.label, onChange: function(v){s({label:v});} })
)
),
el('section', { className: a.variant === 'alt' ? 'section section-alt' : 'section' },
el('div', { className: 'container' },
el('div', { className: 'section-header' },
a.label ? el('span', { className: 'section-label' }, a.label) : null,
el(RT, { tagName: 'h2', value: a.heading, onChange: function(v){s({heading:v});}, placeholder: 'Pricing heading...' }),
el(RT, { tagName: 'p', className: 'lead', value: a.lead, onChange: function(v){s({lead:v});}, placeholder: 'Lead text...' })
),
el('div', { className: 'pricing-grid' },
el(IB, {
allowedBlocks: ['oribi/pricing-card'],
template: [
['oribi/pricing-card', { name: 'Essentials' }],
['oribi/pricing-card', { name: 'Pro', featured: true, badge: 'Most Popular' }],
['oribi/pricing-card', { name: 'Enterprise' }]
],
templateLock: false
})
)
)
)
);
},
save: function () { return el(IB.Content); }
});
/* 7. PLATFORM SECTION ─────────────────────────────────────────────────── */
reg('oribi/platform-section', {
title: 'Oribi Platform Section',
icon: 'slides',
category: 'oribi',
supports: { align: ['full'], html: false },
attributes: {
align: { type: 'string', default: 'full' },
label: { type: 'string', default: '' },
heading: { type: 'string', default: '' },
lead: { type: 'string', default: '' },
},
edit: function (props) {
var a = props.attributes, s = props.setAttributes;
return el(Frag, null,
el(IC, null,
el(PB, { title: 'Section' },
el(TC, { label: 'Label', value: a.label, onChange: function(v){s({label:v});} })
)
),
el('section', { className: 'section' },
el('div', { className: 'container' },
el('div', { className: 'section-header' },
a.label ? el('span', { className: 'section-label' }, a.label) : null,
el(RT, { tagName: 'h2', value: a.heading, onChange: function(v){s({heading:v});}, placeholder: 'Section heading...' }),
el(RT, { tagName: 'p', className: 'lead', value: a.lead, onChange: function(v){s({lead:v});}, placeholder: 'Lead text...' })
),
el(IB, {
allowedBlocks: ['oribi/platform-row'],
template: [['oribi/platform-row', {}]],
templateLock: false
})
)
)
);
},
save: function () { return el(IB.Content); }
});
/* 9. TRUST SECTION ────────────────────────────────────────────────────── */
reg('oribi/trust-section', {
title: 'Oribi Trust Section',
icon: 'shield',
category: 'oribi',
supports: { align: ['full'], html: false },
attributes: {
align: { type: 'string', default: 'full' },
label: { type: 'string', default: '' },
heading: { type: 'string', default: '' },
lead: { type: 'string', default: '' },
btnText: { type: 'string', default: '' },
btnUrl: { type: 'string', default: '' },
btnSub: { type: 'string', default: '' },
},
edit: function (props) {
var a = props.attributes, s = props.setAttributes;
return el(Frag, null,
el(IC, null,
el(PB, { title: 'Settings' },
el(TC, { label: 'Label', value: a.label, onChange: function(v){s({label:v});} }),
el(TC, { label: 'Button Text', value: a.btnText, onChange: function(v){s({btnText:v});} }),
el(TC, { label: 'Button URL', value: a.btnUrl, onChange: function(v){s({btnUrl:v});} }),
el(TC, { label: 'Button Subtext', value: a.btnSub, onChange: function(v){s({btnSub:v});} })
)
),
el('section', { className: 'section' },
el('div', { className: 'container' },
el('div', { className: 'section-header' },
a.label ? el('span', { className: 'section-label' }, a.label) : null,
el(RT, { tagName: 'h2', value: a.heading, onChange: function(v){s({heading:v});}, placeholder: 'Heading...' }),
el(RT, { tagName: 'p', className: 'lead', value: a.lead, onChange: function(v){s({lead:v});}, placeholder: 'Lead text...' })
),
el('div', { className: 'grid-2', style: { alignItems: 'center' } },
el('div', { style: { display: 'flex', flexDirection: 'column', gap: '1.5rem' } },
el(IB, {
allowedBlocks: ['oribi/trust-item'],
template: [['oribi/trust-item', {}]],
templateLock: false
})
),
el('div', { style: { textAlign: 'center' } },
el('span', { className: 'btn btn-primary btn-lg' }, a.btnText || 'Button'),
a.btnSub ? el('p', { className: 'lead mt-2', style: { fontSize: '.9rem' } }, a.btnSub) : null
)
)
)
)
);
},
save: function () { return el(IB.Content); }
});
/* 10. FAQ SECTION ─────────────────────────────────────────────────────── */
reg('oribi/faq-section', {
title: 'Oribi FAQ Section',
icon: 'editor-help',
category: 'oribi',
supports: { align: ['full'], html: false },
attributes: {
align: { type: 'string', default: 'full' },
variant: { type: 'string', default: 'normal' },
label: { type: 'string', default: '' },
heading: { type: 'string', default: '' },
lead: { type: 'string', default: '' },
},
edit: function (props) {
var a = props.attributes, s = props.setAttributes;
return el(Frag, null,
el(IC, null,
el(PB, { title: 'Section Settings' },
el(SC, { label: 'Background', value: a.variant, options: [
{ label: 'Normal', value: 'normal' }, { label: 'Alternate', value: 'alt' }
], onChange: function(v){s({variant:v});} }),
el(TC, { label: 'Label', value: a.label, onChange: function(v){s({label:v});} })
)
),
el('section', { className: a.variant === 'alt' ? 'section section-alt' : 'section' },
el('div', { className: 'container' },
el('div', { className: 'section-header' },
a.label ? el('span', { className: 'section-label' }, a.label) : null,
el(RT, { tagName: 'h2', value: a.heading, onChange: function(v){s({heading:v});}, placeholder: 'FAQ heading...' }),
el(RT, { tagName: 'p', className: 'lead', value: a.lead, onChange: function(v){s({lead:v});}, placeholder: 'Lead text...' })
),
el('div', { className: 'faq-list' },
el(IB, {
allowedBlocks: ['oribi/faq-item'],
template: [['oribi/faq-item', {}]],
templateLock: false
})
)
)
)
);
},
save: function () { return el(IB.Content); }
});
/* FAQ ITEM (child) ─────────────────────────────────────────────────────── */
reg('oribi/faq-item', {
title: 'Oribi FAQ Item',
icon: 'editor-help',
category: 'oribi',
parent: ['oribi/faq-section'],
supports: { html: false, reusable: false },
attributes: {
question: { type: 'string', default: '' },
answer: { type: 'string', default: '' },
},
edit: function (props) {
var a = props.attributes, s = props.setAttributes;
return el('details', { className: 'faq-item', open: true },
el('summary', { className: 'faq-question' },
el(RT, { tagName: 'span', value: a.question,
onChange: function(v){s({question:v});}, placeholder: 'Question...' })
),
el('div', { className: 'faq-answer' },
el(RT, { tagName: 'p', value: a.answer,
onChange: function(v){s({answer:v});}, placeholder: 'Answer...' })
)
);
},
save: function () { return null; }
});
/* 11. COMPARISON TABLE (standalone) ───────────────────────────────────── */
reg('oribi/comparison-table', {
title: 'Oribi Comparison Table',
icon: 'editor-table',
category: 'oribi',
supports: { align: ['full'], html: false },
attributes: {
align: { type: 'string', default: 'full' },
variant: { type: 'string', default: 'normal' },
label: { type: 'string', default: '' },
heading: { type: 'string', default: '' },
lead: { type: 'string', default: '' },
columns: { type: 'array', default: [] },
rows: { type: 'array', default: [] },
},
edit: function (props) {
var a = props.attributes, s = props.setAttributes;
var cols = a.columns || [];
var rows = a.rows || [];
return el(Frag, null,
el(IC, null,
el(PB, { title: 'Table Settings' },
el(SC, { label: 'Background', value: a.variant, options: [
{ label: 'Normal', value: 'normal' }, { label: 'Alternate', value: 'alt' }
], onChange: function(v){s({variant:v});} }),
el(TC, { label: 'Label', value: a.label, onChange: function(v){s({label:v});} })
)
),
el('section', { className: a.variant === 'alt' ? 'section section-alt' : 'section' },
el('div', { className: 'container' },
el('div', { className: 'section-header' },
a.label ? el('span', { className: 'section-label' }, a.label) : null,
el(RT, { tagName: 'h2', value: a.heading, onChange: function(v){s({heading:v});}, placeholder: 'Table heading...' }),
el(RT, { tagName: 'p', className: 'lead', value: a.lead, onChange: function(v){s({lead:v});}, placeholder: 'Lead text...' })
),
el('div', { className: 'comparison-table-wrap' },
el('table', { className: 'comparison-table' },
el('thead', null,
el('tr', null,
el('th', { className: 'comparison-feature-col' }, 'Feature'),
cols.map(function(col, i) { return el('th', { key: i }, col); })
)
),
el('tbody', null,
rows.map(function(row, i) {
if (row.group) {
return el('tr', { key: i, className: 'comparison-group-row' },
el('td', { colSpan: cols.length + 1 }, row.group)
);
}
return el('tr', { key: i },
el('td', { className: 'comparison-feature-name' }, row.feature || ''),
(row.values || []).map(function(val, j) {
return el('td', { key: j, className: 'comparison-cell' },
val === true ? '\u2713' : val === false ? '\u2014' : String(val)
);
})
);
})
)
)
)
)
)
);
},
save: function () { return null; }
});
/*
TEMPLATE-PART HELPER BLOCKS
*/
/*
ANIMATED HERO BLOCKS (OTS Signs)
*/
/* ANIMATED HERO ───────────────────────────────────────────────────────── */
reg('oribi/hero-animated', {
title: 'Animated Hero',
icon: 'admin-site-alt3',
category: 'oribi',
supports: { align: ['full'], html: false },
attributes: {
align: { type: 'string', default: 'full' },
label: { type: 'string', default: '' },
title: { type: 'string', default: '' },
highlightWord: { type: 'string', default: '' },
description: { type: 'string', default: '' },
primaryBtnText: { type: 'string', default: 'Get Started' },
primaryBtnUrl: { type: 'string', default: '/contact' },
secondaryBtnText: { type: 'string', default: '' },
secondaryBtnUrl: { type: 'string', default: '' },
stat1Value: { type: 'string', default: '' },
stat1Label: { type: 'string', default: '' },
stat2Value: { type: 'string', default: '' },
stat2Label: { type: 'string', default: '' },
stat3Value: { type: 'string', default: '' },
stat3Label: { type: 'string', default: '' },
},
edit: function (props) {
var a = props.attributes, s = props.setAttributes;
// Build particle elements for editor preview
var particles = [];
for (var i = 1; i <= 12; i++) {
particles.push(el('div', { key: 'p' + i, className: 'hero-particle hero-particle--' + i }));
}
return el(Frag, null,
el(IC, null,
el(PB, { title: 'Highlight' },
el(TC, { label: 'Word to highlight in title', value: a.highlightWord, onChange: function(v){s({highlightWord:v});} })
),
el(PB, { title: 'Primary Button' },
el(TC, { label: 'URL', value: a.primaryBtnUrl, onChange: function(v){s({primaryBtnUrl:v});} })
),
el(PB, { title: 'Secondary Button', initialOpen: false },
el(TC, { label: 'URL', value: a.secondaryBtnUrl, onChange: function(v){s({secondaryBtnUrl:v});} })
)
),
el('section', { className: 'hero hero-animated' },
el('div', { className: 'hero-particles', 'aria-hidden': 'true' }, particles),
el('div', { className: 'hero-animated__glow' }),
el('div', { className: 'container hero-animated__inner' },
el('div', { className: 'hero-animated__content' },
el(RT, { tagName: 'span', className: 'hero-label', value: a.label,
onChange: function(v){s({label:v});}, placeholder: '\u25CF Label text', allowedFormats: [] }),
el(RT, { tagName: 'h1', className: 'hero-title', value: a.title,
onChange: function(v){s({title:v});}, placeholder: 'Hero title...' }),
el(RT, { tagName: 'p', className: 'hero-description', value: a.description,
onChange: function(v){s({description:v});}, placeholder: 'Description...' }),
el('div', { className: 'btn-group' },
el(RT, { tagName: 'span', className: 'btn btn-primary btn-lg', value: a.primaryBtnText,
onChange: function(v){s({primaryBtnText:v});}, placeholder: 'Button', allowedFormats: [] }),
el(RT, { tagName: 'span', className: 'btn btn-ghost btn-lg', value: a.secondaryBtnText,
onChange: function(v){s({secondaryBtnText:v});}, placeholder: 'Secondary button', allowedFormats: [] })
),
el('div', { className: 'hero-stats hero-stats--three' },
el('div', null,
el(RT, { tagName: 'div', className: 'hero-stat-value', value: a.stat1Value,
onChange: function(v){s({stat1Value:v});}, placeholder: '\u2014', allowedFormats: [] }),
el(RT, { tagName: 'div', className: 'hero-stat-label', value: a.stat1Label,
onChange: function(v){s({stat1Label:v});}, placeholder: 'Stat label', allowedFormats: [] })
),
el('div', null,
el(RT, { tagName: 'div', className: 'hero-stat-value', value: a.stat2Value,
onChange: function(v){s({stat2Value:v});}, placeholder: '\u2014', allowedFormats: [] }),
el(RT, { tagName: 'div', className: 'hero-stat-label', value: a.stat2Label,
onChange: function(v){s({stat2Label:v});}, placeholder: 'Stat label', allowedFormats: [] })
),
el('div', null,
el(RT, { tagName: 'div', className: 'hero-stat-value', value: a.stat3Value,
onChange: function(v){s({stat3Value:v});}, placeholder: '\u2014', allowedFormats: [] }),
el(RT, { tagName: 'div', className: 'hero-stat-label', value: a.stat3Label,
onChange: function(v){s({stat3Label:v});}, placeholder: 'Stat label', allowedFormats: [] })
)
)
)
)
)
);
},
save: function () { return null; }
});
/* ANIMATED PAGE HERO ──────────────────────────────────────────────────── */
reg('oribi/page-hero-animated', {
title: 'Animated Page Hero',
icon: 'flag',
category: 'oribi',
supports: { align: ['full'], html: false },
attributes: {
align: { type: 'string', default: 'full' },
label: { type: 'string', default: '' },
title: { type: 'string', default: '' },
description: { type: 'string', default: '' },
},
edit: function (props) {
var a = props.attributes, s = props.setAttributes;
var particles = [];
for (var i = 1; i <= 8; i++) {
particles.push(el('div', { key: 'p' + i, className: 'hero-particle hero-particle--' + i }));
}
return el(Frag, null,
el(IC, null,
el(PB, { title: 'Settings' },
el(TC, { label: 'Label (optional)', value: a.label, onChange: function(v){s({label:v});} })
)
),
el('section', { className: 'page-hero page-hero-animated' },
el('div', { className: 'hero-particles', 'aria-hidden': 'true' }, particles),
el('div', { className: 'hero-animated__glow' }),
el('div', { className: 'hero-overlay' }),
el('div', { className: 'container' },
a.label ? el('span', { className: 'hero-label' }, a.label) : null,
el(RT, { tagName: 'h1', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Page title...' }),
el(RT, { tagName: 'p', className: 'lead', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' })
)
)
);
},
save: function () { return null; }
});
/*
TEMPLATE-PART HELPER BLOCKS
*/
reg('oribi/site-header', {
title: 'Oribi Site Header',
icon: 'admin-home',
category: 'oribi',
supports: { html: false, multiple: false, reusable: false },
edit: function () {
return el('div', {
style: { background: '#111111', color: '#fff', padding: '20px 24px', borderRadius: '8px', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }
2026-02-20 21:28:00 -05:00
},
el('div', { style: { display: 'flex', alignItems: 'center', gap: '8px' } },
el('strong', { style: { fontSize: '1.2rem' } }, 'Oribi'),
el('span', { style: { fontSize: '1.2rem', fontWeight: 300 } }, 'Tech')
),
el('div', { style: { display: 'flex', gap: '1.5rem', fontSize: '.9rem', opacity: 0.7 } },
el('span', null, 'Services'),
el('span', null, 'About'),
el('span', null, 'FAQ'),
el('span', null, 'Contact')
)
);
},
save: function () { return null; }
});
reg('oribi/site-footer', {
title: 'Oribi Site Footer',
icon: 'admin-home',
category: 'oribi',
supports: { html: false, multiple: false, reusable: false },
edit: function () {
return el('div', {
style: { background: '#111111', color: '#fff', padding: '24px', borderRadius: '8px', textAlign: 'center' }
2026-02-20 21:28:00 -05:00
},
el('strong', { style: { fontSize: '1.1rem' } }, 'OTS Theme - Site Footer'),
2026-02-20 21:28:00 -05:00
el('p', { style: { opacity: 0.5, margin: '8px 0 0', fontSize: '.85rem' } }, 'Brand · Service Links · Company Links · Connect · Copyright')
);
},
save: function () { return null; }
});
})(window.wp);