2026-02-20 21:28:00 -05:00
|
|
|
/**
|
2026-02-20 22:06:53 -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) {
|
2026-02-27 17:57:59 -05:00
|
|
|
'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' }
|
|
|
|
|
};
|
2026-02-20 21:28:00 -05:00
|
|
|
|
2026-02-27 17:57:59 -05:00
|
|
|
/* ── Shared icon attributes ──────────────────────────────────────────── */
|
|
|
|
|
var CARD_ICON_ATTRS = {
|
|
|
|
|
icon: { type: 'string', default: '' },
|
|
|
|
|
iconType: { type: 'string', default: 'emoji' },
|
|
|
|
|
faIcon: { type: 'string', default: '' }
|
2026-02-20 21:28:00 -05:00
|
|
|
};
|
2026-02-27 17:57:59 -05:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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')
|
2026-02-20 21:28:00 -05:00
|
|
|
)
|
2026-02-27 17:57:59 -05:00
|
|
|
: 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])
|
|
|
|
|
);
|
|
|
|
|
})
|
2026-02-20 21:28:00 -05:00
|
|
|
)
|
|
|
|
|
);
|
2026-02-27 17:57:59 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function iconControls(a, s) {
|
|
|
|
|
var useFa = a.iconType === 'fontawesome';
|
2026-02-20 21:28:00 -05:00
|
|
|
return el(Frag, null,
|
2026-02-27 17:57:59 -05:00
|
|
|
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
|
|
|
|
|
*/
|
|
|
|
|
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')
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
})
|
2026-02-20 21:28:00 -05:00
|
|
|
),
|
2026-02-27 17:57:59 -05:00
|
|
|
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,
|
|
|
|
|
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...' })
|
2026-02-20 21:28:00 -05:00
|
|
|
),
|
2026-02-27 17:57:59 -05:00
|
|
|
el('div', { className: 'grid-' + a.columns },
|
|
|
|
|
el(IB, {
|
|
|
|
|
allowedBlocks: allowedBlocks,
|
|
|
|
|
template: defaultTemplate,
|
|
|
|
|
templateLock: false
|
|
|
|
|
})
|
2026-02-20 21:28:00 -05:00
|
|
|
)
|
2026-02-27 17:57:59 -05:00
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 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 }); } })
|
2026-02-20 21:28:00 -05:00
|
|
|
),
|
2026-02-27 17:57:59 -05:00
|
|
|
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: []
|
|
|
|
|
})
|
2026-02-20 21:28:00 -05:00
|
|
|
),
|
2026-02-27 17:57:59 -05:00
|
|
|
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: []
|
|
|
|
|
})
|
2026-02-20 21:28:00 -05:00
|
|
|
)
|
2026-02-27 17:57:59 -05:00
|
|
|
)
|
|
|
|
|
),
|
|
|
|
|
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')
|
2026-02-20 21:28:00 -05:00
|
|
|
)
|
|
|
|
|
),
|
2026-02-27 17:57:59 -05:00
|
|
|
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')
|
2026-02-20 21:28:00 -05:00
|
|
|
)
|
|
|
|
|
),
|
2026-02-27 17:57:59 -05:00
|
|
|
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')
|
|
|
|
|
)
|
2026-02-20 21:28:00 -05:00
|
|
|
),
|
2026-02-27 17:57:59 -05:00
|
|
|
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' })
|
2026-02-20 21:28:00 -05:00
|
|
|
)
|
2026-02-27 17:57:59 -05:00
|
|
|
),
|
|
|
|
|
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')
|
2026-02-20 21:28:00 -05:00
|
|
|
)
|
|
|
|
|
),
|
2026-02-27 17:57:59 -05:00
|
|
|
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')
|
|
|
|
|
)
|
2026-02-20 21:28:00 -05:00
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
2026-02-27 17:57:59 -05:00
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
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...' })
|
|
|
|
|
)
|
2026-02-20 21:28:00 -05:00
|
|
|
)
|
2026-02-27 17:57:59 -05:00
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
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 }); } })
|
|
|
|
|
)
|
2026-02-20 21:28:00 -05:00
|
|
|
),
|
2026-02-27 17:57:59 -05:00
|
|
|
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...' })
|
|
|
|
|
)
|
2026-02-20 21:28:00 -05:00
|
|
|
)
|
2026-02-27 17:57:59 -05:00
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
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 },
|
|
|
|
|
cloudAnim: { 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 animHtml = null;
|
|
|
|
|
if (a.cloudAnim) {
|
|
|
|
|
animHtml = el('div', { className: 'ds-anim-container' },
|
|
|
|
|
el('div', { className: 'ds-tv' },
|
|
|
|
|
el('i', { className: 'fas fa-tv', 'aria-hidden': 'true' }),
|
|
|
|
|
el('div', { className: 'ds-tv-screen' })
|
|
|
|
|
),
|
|
|
|
|
el('div', { className: 'ds-line' },
|
|
|
|
|
el('div', { className: 'ds-packet ds-packet-1' }),
|
|
|
|
|
el('div', { className: 'ds-packet ds-packet-2' }),
|
|
|
|
|
el('div', { className: 'ds-packet ds-packet-3' })
|
|
|
|
|
),
|
|
|
|
|
el('div', { className: 'ds-cloud' },
|
|
|
|
|
el('i', { className: 'fas fa-cloud', 'aria-hidden': 'true' })
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var visualContent = a.cloudAnim ? animHtml : (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(TG, { label: 'Cloud Server Animation', checked: a.cloudAnim, onChange: function (v) { s({ cloudAnim: 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')
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
})
|
2026-02-20 21:28:00 -05:00
|
|
|
),
|
2026-02-27 17:57:59 -05:00
|
|
|
a.imgUrl ? el(RC, {
|
|
|
|
|
label: 'Width (px)', value: imgW, min: 50, max: 420, step: 4,
|
|
|
|
|
onChange: function (v) { s({ imgWidth: v }); }
|
|
|
|
|
}) : null
|
2026-02-20 21:28:00 -05:00
|
|
|
)
|
2026-02-27 17:57:59 -05:00
|
|
|
),
|
|
|
|
|
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...' })
|
2026-02-20 21:28:00 -05:00
|
|
|
),
|
2026-02-27 17:57:59 -05:00
|
|
|
el('div', { className: 'about-intro-visual' + (a.cloudAnim ? ' has-cloud-anim' : (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))
|
|
|
|
|
)
|
2026-02-20 21:28:00 -05:00
|
|
|
),
|
2026-02-27 17:57:59 -05:00
|
|
|
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'
|
|
|
|
|
)
|
2026-02-20 21:28:00 -05:00
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
);
|
2026-02-27 17:57:59 -05:00
|
|
|
},
|
|
|
|
|
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
|
2026-02-20 21:28:00 -05:00
|
|
|
);
|
2026-02-27 17:57:59 -05:00
|
|
|
},
|
|
|
|
|
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);
|
2026-02-20 21:28:00 -05:00
|
|
|
var showIconPrev = !a.imgUrl || imgPos !== 'replace-icon';
|
|
|
|
|
|
2026-02-27 17:57:59 -05:00
|
|
|
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,
|
2026-02-20 21:28:00 -05:00
|
|
|
showIconPrev ? iconPreview(a, 'value-icon') : null,
|
2026-02-27 17:57:59 -05:00
|
|
|
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)
|
2026-02-20 21:28:00 -05:00
|
|
|
),
|
2026-02-27 17:57:59 -05:00
|
|
|
body
|
2026-02-20 21:28:00 -05:00
|
|
|
);
|
2026-02-27 17:57:59 -05:00
|
|
|
},
|
|
|
|
|
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,
|
2026-02-20 21:28:00 -05:00
|
|
|
showIconPrev ? iconPreview(a, 'feature-icon') : null,
|
|
|
|
|
a.tag ? el('span', { className: 'addon-tag' }, a.tag) : null,
|
2026-02-27 17:57:59 -05:00
|
|
|
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)
|
2026-02-20 21:28:00 -05:00
|
|
|
),
|
2026-02-27 17:57:59 -05:00
|
|
|
body
|
2026-02-20 21:28:00 -05:00
|
|
|
);
|
2026-02-27 17:57:59 -05:00
|
|
|
},
|
|
|
|
|
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';
|
2026-02-20 21:28:00 -05:00
|
|
|
|
2026-02-27 17:57:59 -05:00
|
|
|
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)
|
2026-02-20 21:28:00 -05:00
|
|
|
),
|
2026-02-27 17:57:59 -05:00
|
|
|
el('div', { className: 'oribi-card image-card' + (a.imgUrl ? ' img-' + imgPos : '') },
|
|
|
|
|
a.imgUrl && (imgPos === 'top' || imgPos === 'replace-icon') ? imgPrev : null,
|
2026-02-20 21:28:00 -05:00
|
|
|
showIconPrev ? iconPreview(a, 'feature-icon') : null,
|
2026-02-27 17:57:59 -05:00
|
|
|
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...' })
|
|
|
|
|
)
|
2026-02-20 21:28:00 -05:00
|
|
|
)
|
|
|
|
|
);
|
2026-02-27 17:57:59 -05:00
|
|
|
},
|
|
|
|
|
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
|
2026-02-20 21:28:00 -05:00
|
|
|
);
|
2026-02-27 17:57:59 -05:00
|
|
|
},
|
|
|
|
|
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';
|
2026-02-20 21:28:00 -05:00
|
|
|
|
2026-02-27 17:57:59 -05:00
|
|
|
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 }); } })
|
2026-02-20 21:28:00 -05:00
|
|
|
),
|
2026-02-27 17:57:59 -05:00
|
|
|
cardImageControls(a, s)
|
2026-02-21 00:53:13 -05:00
|
|
|
),
|
2026-02-27 17:57:59 -05:00
|
|
|
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: '' },
|
|
|
|
|
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
|
2026-02-20 21:28:00 -05:00
|
|
|
)
|
|
|
|
|
),
|
2026-02-27 17:57:59 -05:00
|
|
|
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'
|
2026-02-21 14:11:56 -05:00
|
|
|
})
|
2026-02-27 17:57:59 -05:00
|
|
|
) : 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
|
|
|
})
|
|
|
|
|
),
|
2026-02-27 17:57:59 -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...'
|
|
|
|
|
})
|
2026-02-20 21:28:00 -05:00
|
|
|
)
|
2026-02-27 17:57:59 -05:00
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
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 },
|
|
|
|
|
isDashboard: { type: 'boolean', default: false },
|
|
|
|
|
deviceAnim: { type: 'boolean', default: false },
|
|
|
|
|
tvStick: { type: 'boolean', default: false },
|
|
|
|
|
cameraAnim: { type: 'boolean', default: false },
|
|
|
|
|
neverGoesDark: { type: 'boolean', default: false },
|
|
|
|
|
brandedAnim: { type: 'boolean', default: false },
|
|
|
|
|
galleryIds: { type: 'array', default: [] },
|
|
|
|
|
},
|
|
|
|
|
edit: function (props) {
|
|
|
|
|
var a = props.attributes, s = props.setAttributes;
|
|
|
|
|
var imgW = a.imgWidth || 300;
|
|
|
|
|
|
|
|
|
|
/* ── Animation HTML strings (mirror PHP render, used via dangerouslySetInnerHTML) ── */
|
|
|
|
|
var DA_SCREEN = '<div class="da-screen"><div class="da-promo"><div class="da-promo__top"><span class="da-promo__dot"></span><span class="da-promo__brand"></span></div><div class="da-promo__hero"></div><div class="da-promo__row"><span class="da-promo__line da-promo__line--lg"></span><span class="da-promo__line da-promo__line--sm"></span></div><div class="da-promo__row"><span class="da-promo__line da-promo__line--md"></span><span class="da-promo__line da-promo__line--xs"></span></div><div class="da-promo__ticker"><span class="da-promo__chip"></span><span class="da-promo__chip"></span><span class="da-promo__chip"></span></div></div></div>';
|
|
|
|
|
var DA_HTML = '<div class="da-stage" aria-hidden="true">' +
|
|
|
|
|
'<div class="da-device da-tablet"><div class="da-body">' + DA_SCREEN + '</div><span class="da-label">Tablet</span></div>' +
|
|
|
|
|
'<div class="da-device da-monitor-sm"><div class="da-body">' + DA_SCREEN + '</div><div class="da-stand"><div class="da-stem"></div><div class="da-base"></div></div><span class="da-label">Small Monitor</span></div>' +
|
|
|
|
|
'<div class="da-device da-monitor-lg"><div class="da-body">' + DA_SCREEN + '</div><div class="da-stand"><div class="da-stem"></div><div class="da-base"></div></div><span class="da-label">Large Monitor</span></div>' +
|
|
|
|
|
'<div class="da-device da-tv"><div class="da-body">' + DA_SCREEN + '</div><div class="da-feet"><div class="da-foot"></div><div class="da-foot"></div></div><span class="da-label">TV</span></div>' +
|
|
|
|
|
'<div class="da-device da-projector"><div class="da-proj-layout"><div class="da-proj-body"><div class="da-lens"></div></div><div class="da-beam"></div><div class="da-proj-screen">' + DA_SCREEN + '</div></div><span class="da-label">Projector</span></div>' +
|
|
|
|
|
'<div class="da-device da-vwall"><div class="da-vwall-grid"><div class="da-panel">' + DA_SCREEN + '</div><div class="da-panel">' + DA_SCREEN + '</div><div class="da-panel">' + DA_SCREEN + '</div><div class="da-panel">' + DA_SCREEN + '</div></div><span class="da-label">Video Wall</span></div>' +
|
|
|
|
|
'</div>';
|
|
|
|
|
|
|
|
|
|
var TS_MI = '<div class="ts-menu__item"><span class="ts-menu__name"></span><span class="ts-menu__dots"></span><span class="ts-menu__price"></span></div>';
|
|
|
|
|
var TS_COL = '<div class="ts-menu__col"><div class="ts-menu__cat"></div>' + TS_MI + TS_MI + TS_MI + '</div>';
|
|
|
|
|
var TS_HTML = '<div class="ts-stage" data-tv-stick-anim aria-hidden="true">' +
|
|
|
|
|
'<div class="ts-tv"><div class="ts-tv__body"><div class="ts-tv__screen"><div class="ts-slides">' +
|
|
|
|
|
'<div class="ts-slide ts-slide--menu"><div class="ts-menu"><div class="ts-menu__header"><div class="ts-menu__logo"></div><div class="ts-menu__title"></div></div><div class="ts-menu__cols">' + TS_COL + TS_COL + '</div></div></div>' +
|
|
|
|
|
'<div class="ts-slide ts-slide--wayfind"><div class="ts-wf"><div class="ts-wf__header"><div class="ts-wf__building"></div></div><div class="ts-wf__rows"><div class="ts-wf__row"><span class="ts-wf__arrow ts-wf__arrow--left"></span><span class="ts-wf__label ts-wf__label--w1"></span><span class="ts-wf__floor"></span></div><div class="ts-wf__row"><span class="ts-wf__arrow ts-wf__arrow--right"></span><span class="ts-wf__label ts-wf__label--w2"></span><span class="ts-wf__floor"></span></div><div class="ts-wf__row"><span class="ts-wf__arrow ts-wf__arrow--up"></span><span class="ts-wf__label ts-wf__label--w3"></span><span class="ts-wf__floor"></span></div><div class="ts-wf__row"><span class="ts-wf__arrow ts-wf__arrow--left"></span><span class="ts-wf__label ts-wf__label--w4"></span><span class="ts-wf__floor"></span></div></div></div></div>' +
|
|
|
|
|
'<div class="ts-slide ts-slide--sched"><div class="ts-sched"><div class="ts-sched__header"><div class="ts-sched__title"></div><div class="ts-sched__date"></div></div><div class="ts-sched__table"><div class="ts-sched__hrow"><span class="ts-sched__hcell"></span><span class="ts-sched__hcell"></span><span class="ts-sched__hcell"></span><span class="ts-sched__hcell"></span></div><div class="ts-sched__row"><span class="ts-sched__time"></span><span class="ts-sched__event ts-sched__event--a"></span><span class="ts-sched__cell"></span><span class="ts-sched__cell"></span></div><div class="ts-sched__row"><span class="ts-sched__time"></span><span class="ts-sched__cell"></span><span class="ts-sched__event ts-sched__event--b"></span><span class="ts-sched__cell"></span></div><div class="ts-sched__row"><span class="ts-sched__time"></span><span class="ts-sched__cell"></span><span class="ts-sched__cell"></span><span class="ts-sched__event ts-sched__event--c"></span></div><div class="ts-sched__row"><span class="ts-sched__time"></span><span class="ts-sched__event ts-sched__event--a"></span><span class="ts-sched__event ts-sched__event--b"></span><span class="ts-sched__cell"></span></div></div></div></div>' +
|
|
|
|
|
'</div></div><div class="ts-tv__port"></div></div><div class="ts-tv__feet"><div class="ts-tv__foot"></div><div class="ts-tv__foot"></div></div></div>' +
|
|
|
|
|
'<div class="ts-stick"><div class="ts-stick__body"><div class="ts-stick__led"></div></div><div class="ts-stick__connector"></div></div>' +
|
|
|
|
|
'</div>';
|
|
|
|
|
|
|
|
|
|
var NGD_ROW = '<div class="ngd-menu__row"><span class="ngd-menu__name"></span><span class="ngd-menu__price"></span></div>';
|
|
|
|
|
var NGD_ROWH = '<div class="ngd-menu__row ngd-menu__row--hl"><span class="ngd-menu__name"></span><span class="ngd-menu__price"></span></div>';
|
|
|
|
|
var NGD_HTML = '<div class="ngd-stage" aria-hidden="true">' +
|
|
|
|
|
'<div class="ngd-tv"><div class="ngd-tv__body"><div class="ngd-tv__screen"><div class="ngd-menu"><div class="ngd-menu__hd"><div class="ngd-menu__logo"></div><div class="ngd-menu__ttl"></div></div><div class="ngd-menu__cols"><div class="ngd-menu__col"><div class="ngd-menu__cat"></div>' + NGD_ROW + NGD_ROWH + NGD_ROW + '</div><div class="ngd-menu__col"><div class="ngd-menu__cat"></div>' + NGD_ROW + NGD_ROW + NGD_ROWH + '</div></div><div class="ngd-menu__ticker"><div class="ngd-menu__ticker-inner"></div></div></div></div><div class="ngd-tv__port"></div></div><div class="ngd-tv__feet"><div class="ngd-tv__foot"></div><div class="ngd-tv__foot"></div></div></div>' +
|
|
|
|
|
'<div class="ngd-player"><div class="ngd-player__connector"></div><div class="ngd-player__body"><div class="ngd-player__led"></div></div><div class="ngd-player__check"><svg viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="11" cy="11" r="10" stroke="#4CAF50" stroke-width="1.5"/><polyline points="6,11 9.5,14.5 16,7.5" stroke="#4CAF50" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></div></div>' +
|
|
|
|
|
'<div class="ngd-signal-wrap"><div class="ngd-cloud"><svg class="ngd-cloud__svg" viewBox="0 0 64 46" fill="none" xmlns="http://www.w3.org/2000/svg"><circle class="ngd-cloud__path" cx="32" cy="23" r="14" stroke-width="2.2" fill="none"/><path class="ngd-cloud__path" d="M18.5 23C23 20 41 20 45.5 23" stroke-width="1.9" stroke-linecap="round" fill="none"/><path class="ngd-cloud__path" d="M20.5 29C25.5 31 38.5 31 43.5 29" stroke-width="1.9" stroke-linecap="round" fill="none"/><path class="ngd-cloud__path" d="M24.5 12C21.5 16.5 21.5 29.5 24.5 34" stroke-width="1.9" stroke-linecap="round" fill="none"/><path class="ngd-cloud__path" d="M39.5 12C42.5 16.5 42.5 29.5 39.5 34" stroke-width="1.9" stroke-linecap="round" fill="none"/></svg></div><div class="ngd-signal-line"><div class="ngd-signal__dots"><div class="ngd-signal__dot ngd-signal__dot--1"></div><div class="ngd-signal__dot ngd-signal__dot--2"></div><div class="ngd-signal__dot ngd-signal__dot--3"></div></div><div class="ngd-signal__break"><svg viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="9" cy="9" r="8" fill="rgba(239,68,68,0.12)" stroke="#ef4444" stroke-width="1.2"/><line x1="5.5" y1="5.5" x2="12.5" y2="12.5" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/><line x1="12.5" y1="5.5" x2="5.5" y2="12.5" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg></div></div></div>' +
|
|
|
|
|
'</div>';
|
|
|
|
|
|
|
|
|
|
var BD_SPLASH = '<div class="bd-splash"><div class="bd-splash__logo"></div></div>';
|
|
|
|
|
var BD_HDR = '<div class="bd-ui__header"><div class="bd-ui__logo"></div><div class="bd-ui__brand-bar"></div></div>';
|
|
|
|
|
var BD_HTML = '<div class="bd-stage" aria-hidden="true">' +
|
|
|
|
|
'<div class="bd-device bd-device--tablet"><span class="bd-device__label">Kiosk</span><div class="bd-device__body"><div class="bd-device__screen">' + BD_SPLASH + '<div class="bd-ui">' + BD_HDR + '<div class="bd-ui__content"><div class="bd-promo bd-promo--welcome"><div class="bd-promo__hero"></div><div class="bd-promo__heading"></div><div class="bd-promo__text"></div><div class="bd-promo__btn"></div></div></div></div></div></div></div>' +
|
|
|
|
|
'<div class="bd-device bd-device--wall"><span class="bd-device__label">Wall Display</span><div class="bd-device__body"><div class="bd-device__screen">' + BD_SPLASH + '<div class="bd-ui">' + BD_HDR + '<div class="bd-ui__content"><div class="bd-promo bd-promo--sale"><div class="bd-promo__cols"><div class="bd-promo__visual"></div><div class="bd-promo__info"><div class="bd-promo__badge"></div><div class="bd-promo__heading"></div><div class="bd-promo__text"></div><div class="bd-promo__text bd-promo__text--short"></div><div class="bd-promo__price"></div></div></div></div></div></div></div></div><div class="bd-mount"></div></div>' +
|
|
|
|
|
'<div class="bd-device bd-device--interactive"><span class="bd-device__label">Interactive</span><div class="bd-device__body"><div class="bd-device__screen">' + BD_SPLASH + '<div class="bd-ui">' + BD_HDR + '<div class="bd-ui__content"><div class="bd-promo bd-promo--menu"><div class="bd-promo__heading"></div><div class="bd-promo__grid"><div class="bd-promo__tile"><div class="bd-promo__tile-img"></div><div class="bd-promo__tile-lbl"></div></div><div class="bd-promo__tile"><div class="bd-promo__tile-img"></div><div class="bd-promo__tile-lbl"></div></div><div class="bd-promo__tile"><div class="bd-promo__tile-img"></div><div class="bd-promo__tile-lbl"></div></div><div class="bd-promo__tile"><div class="bd-promo__tile-img"></div><div class="bd-promo__tile-lbl"></div></div></div></div></div></div></div></div><div class="bd-table"></div></div>' +
|
|
|
|
|
'</div>';
|
|
|
|
|
|
|
|
|
|
var DB_HTML = '<div class="dashboard-tv" data-dashboard-container="true"><div class="dashboard-tv__body"><div class="dashboard-tv__screen"><svg viewBox="0 0 800 450" xmlns="http://www.w3.org/2000/svg" class="dashboard-chart" role="img" aria-label="Animated dashboard charts"><defs><linearGradient id="dbe-barGrad" x1="0%" y1="0%" x2="0%" y2="100%"><stop offset="0%" stop-color="#D83302" stop-opacity="1"/><stop offset="100%" stop-color="#4CAF50" stop-opacity=".8"/></linearGradient><linearGradient id="dbe-lineGrad" x1="0%" y1="0%" x2="0%" y2="100%"><stop offset="0%" stop-color="#4CAF50" stop-opacity=".3"/><stop offset="100%" stop-color="#4CAF50" stop-opacity="0"/></linearGradient></defs><g transform="translate(35,20)"><text class="ct" x="0" y="0" font-size="14" font-weight="600" fill="#888">Performance</text><g id="dbe-bars1" transform="translate(0,25)"><rect x="0" y="60" width="28" height="60" fill="url(#dbe-barGrad)"/><rect x="40" y="80" width="28" height="40" fill="url(#dbe-barGrad)"/><rect x="80" y="40" width="28" height="80" fill="url(#dbe-barGrad)"/><rect x="120" y="70" width="28" height="50" fill="url(#dbe-barGrad)"/><rect x="160" y="50" width="28" height="70" fill="url(#dbe-barGrad)"/></g><g transform="translate(0,152)"><text class="cl" x="14" y="0" font-size="10" text-anchor="middle" fill="#aaa">API</text><text class="cl" x="54" y="0" font-size="10" text-anchor="middle" fill="#aaa">Cache</text><text class="cl" x="94" y="0" font-size="10" text-anchor="middle" fill="#aaa">DB</text><text class="cl" x="134" y="0" font-size="10" text-anchor="middle" fill="#aaa">Queue</text><text class="cl" x="174" y="0" font-size="10" text-anchor="middle" fill="#aaa">Worker</text></g></g><g transform="translate(430,20)"><text class="ct" x="0" y="0" font-size="14" font-weight="600" fill="#888">Requests/sec</text><g transform="translate(0,25)"><rect x="0" y="50" width="28" height="70" fill="url(#dbe-barGrad)"/><rect x="40" y="30" width="28" height="90" fill="url(#dbe-barGrad)"/><rect x="80" y="65" width="28" height="55" fill="url(#dbe-barGrad)"/><rect x="120" y="45" width="28" height="75" fill="url(#dbe-barGrad)"/></g><g transform="translate(0,152)"><text class="cl" x="14" y="0" font-size="10" text-anchor="middle" fill="#aaa">Read</text><text class="cl" x="54" y="0" font-size="10" text-anchor="middle" fill="#aaa">Write</text><text class="cl" x="94" y="0" font-size="10" text-anchor="middle" fill="#aaa">Update</text><text class="cl" x="134" y="0" font-size="10" text-anchor="middle" fill="#aaa">Delete</text></g></g><g transform="translate(35,245)"><text class="ct" x="0" y="0" font-size="14" font-weight="600" fill="#888">Traffic Trend</text><g transform="translate(0,25)"><line class="grid-line" x1="0" y1="0" x2="340" y2="0" stroke="#ddd" stroke-width=".5"/><line class="grid-line" x1="0" y1="40" x2="340" y2="40" stroke="#ddd" stroke-width=".5"/><line class="grid-line" x1="0" y1="80" x2="340" y2="80" stroke="#ddd" stroke-width=".5"/><line class="grid-line" x1="0" y1="120" x2="340" y2="120" stroke="#ddd" stroke-width=".5"/><path d="M0,90 L42,70 L85,50 L127,80 L170,45 L212,65 L255,55 L297,75 L340,60 L340,145 L0,145 Z" fill="url(#dbe-lineGrad)"/><path d="M0,90 L42,70 L85,50 L127,80 L170,45 L212,65 L255,55 L297,75 L340,60" stroke="#4CAF50" stroke-width="2.5" fill="none" stroke-linecap="round"/></g></g><g transform="translate(490,245)"><text class="ct" x="100" y="0" font-size="14" font-weight="600" text-anchor="middle" fill="#888">Distribution</text><g transform="translate(100,90)"><path d="M0,0 L0,-55 A55,55 0 0,1 38.89,-38.89 Z" fill="#D83302" opacity=".9"/><path d="M0,0 L38.89,-38.89 A55,55 0 0,1 55,0 Z" fill="#4CAF50" opacity=".8"/><path d="M0,0 L55,0 A55,55 0 0,1 0,55 Z" fill="#f59e0b" opacity=".7"/><path d="M0,0 L0,55 A55,55 0 1,1 0,-55 Z" fill="#ef4444" opacity=".7"/><circle cx="0" cy="0" r="22" fill="#f5f5f5" stroke="#ddd" stroke-width="1"/></g></g></svg></div></div><div class="dashboard-tv__feet"><div class="dashboard-tv__foot"></div><div class="dashboard-tv__foot"></div></div></div>';
|
|
|
|
|
|
|
|
|
|
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: 'Dashboard Animation', checked: !!a.isDashboard, onChange: function (v) { s({ isDashboard: v }); } }),
|
|
|
|
|
el(TG, { label: 'Device Animation', checked: !!a.deviceAnim, onChange: function (v) { s({ deviceAnim: v }); } }),
|
|
|
|
|
el(TG, { label: 'TV Stick Animation', checked: !!a.tvStick, onChange: function (v) { s({ tvStick: v }); } }),
|
|
|
|
|
el(TG, { label: 'Camera Animation', checked: !!a.cameraAnim, onChange: function (v) { s({ cameraAnim: v }); } }),
|
|
|
|
|
el(TG, { label: 'Never Goes Dark', checked: !!a.neverGoesDark, onChange: function (v) { s({ neverGoesDark: v }); } }),
|
|
|
|
|
el(TG, { label: 'Branded Display', checked: !!a.brandedAnim, onChange: function (v) { s({ brandedAnim: v }); } })
|
|
|
|
|
),
|
|
|
|
|
el(PB, { title: 'Gallery TV Slideshow', initialOpen: false },
|
|
|
|
|
el(MUC, null,
|
|
|
|
|
el(MU, {
|
|
|
|
|
onSelect: function (media) {
|
|
|
|
|
s({ galleryIds: media.map(function (m) { return m.id; }) });
|
|
|
|
|
},
|
|
|
|
|
allowedTypes: ['image'],
|
|
|
|
|
gallery: true,
|
|
|
|
|
multiple: true,
|
|
|
|
|
value: a.galleryIds || [],
|
|
|
|
|
render: function (ref) {
|
|
|
|
|
return el(Frag, null,
|
|
|
|
|
a.galleryIds && a.galleryIds.length
|
|
|
|
|
? el('div', { style: { marginBottom: '8px' } },
|
|
|
|
|
el('p', { style: { margin: '0 0 4px' } }, a.galleryIds.length + ' image(s) selected'),
|
|
|
|
|
el(Btn, { variant: 'link', isDestructive: true, onClick: function () { s({ galleryIds: [] }); } }, 'Clear gallery')
|
|
|
|
|
)
|
|
|
|
|
: null,
|
|
|
|
|
el(Btn, { onClick: ref.open, variant: 'secondary', __next40pxDefaultSize: true },
|
|
|
|
|
a.galleryIds && a.galleryIds.length ? 'Edit gallery' : 'Select images for TV slideshow')
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
)
|
|
|
|
|
),
|
|
|
|
|
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
|
|
|
|
|
)
|
2026-02-20 21:28:00 -05:00
|
|
|
),
|
2026-02-27 17:57:59 -05:00
|
|
|
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.isDashboard
|
|
|
|
|
? el('div', { className: 'platform-visual has-dashboard', dangerouslySetInnerHTML: { __html: DB_HTML } })
|
|
|
|
|
: a.cameraAnim
|
|
|
|
|
? el('div', { className: 'platform-visual has-camera' },
|
|
|
|
|
el('div', { className: 'cam-stage', 'aria-hidden': 'true' },
|
|
|
|
|
// Photo camera (left)
|
|
|
|
|
el('div', { className: 'pc-wrap' },
|
|
|
|
|
el('div', { className: 'pc-body' },
|
|
|
|
|
el('div', { className: 'pc-flash-unit' }),
|
|
|
|
|
el('div', { className: 'pc-top' },
|
|
|
|
|
el('div', { className: 'pc-shutter-btn' }),
|
|
|
|
|
el('div', { className: 'pc-viewfinder' })
|
|
|
|
|
),
|
|
|
|
|
el('div', { className: 'pc-front' },
|
|
|
|
|
el('div', { className: 'pc-lens-ring' },
|
|
|
|
|
el('div', { className: 'pc-lens-glass' },
|
|
|
|
|
el('div', { className: 'pc-lens-reflex' })
|
|
|
|
|
)
|
|
|
|
|
),
|
|
|
|
|
el('div', { className: 'pc-grip' })
|
2026-02-21 10:12:23 -05:00
|
|
|
)
|
|
|
|
|
),
|
2026-02-27 17:57:59 -05:00
|
|
|
el('div', { className: 'pc-prints' },
|
|
|
|
|
el('div', { className: 'pc-print pc-print--1', style: { opacity: '1', transform: 'rotate(-12deg) translateY(0)' } },
|
|
|
|
|
el('div', { className: 'pc-print__img' })
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
),
|
|
|
|
|
// Centre scene
|
|
|
|
|
el('div', { className: 'cam-scene' },
|
|
|
|
|
el('div', { className: 'cam-subject cam-subject--1' }),
|
|
|
|
|
el('div', { className: 'cam-flash-overlay' }),
|
|
|
|
|
el('div', { className: 'cam-vid-overlay' })
|
|
|
|
|
),
|
|
|
|
|
// Video camera on tripod (right)
|
|
|
|
|
el('div', { className: 'vc-wrap' },
|
|
|
|
|
el('div', { className: 'vc-camera' },
|
|
|
|
|
el('div', { className: 'vc-handle' }),
|
|
|
|
|
el('div', { className: 'vc-body' },
|
|
|
|
|
el('div', { className: 'vc-lens-barrel' },
|
|
|
|
|
el('div', { className: 'vc-lens-tip' },
|
|
|
|
|
el('div', { className: 'vc-lens-glass' },
|
|
|
|
|
el('div', { className: 'vc-lens-reflex' })
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
),
|
|
|
|
|
el('div', { className: 'vc-top-rail' }),
|
|
|
|
|
el('div', { className: 'vc-rec-light' }),
|
|
|
|
|
el('div', { className: 'vc-eyepiece' })
|
2026-02-21 10:04:18 -05:00
|
|
|
)
|
2026-02-21 10:12:23 -05:00
|
|
|
),
|
2026-02-27 17:57:59 -05:00
|
|
|
el('div', { className: 'vc-tripod' },
|
|
|
|
|
el('div', { className: 'vc-stem' }),
|
|
|
|
|
el('div', { className: 'vc-legs' },
|
|
|
|
|
el('div', { className: 'vc-leg vc-leg--l' }),
|
|
|
|
|
el('div', { className: 'vc-leg vc-leg--c' }),
|
|
|
|
|
el('div', { className: 'vc-leg vc-leg--r' })
|
|
|
|
|
)
|
|
|
|
|
)
|
2026-02-21 10:04:18 -05:00
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
2026-02-27 17:57:59 -05:00
|
|
|
: a.deviceAnim
|
|
|
|
|
? el('div', { className: 'platform-visual has-anim', dangerouslySetInnerHTML: { __html: DA_HTML } })
|
|
|
|
|
: a.tvStick
|
|
|
|
|
? el('div', { className: 'platform-visual has-tv-stick', dangerouslySetInnerHTML: { __html: TS_HTML } })
|
|
|
|
|
: a.neverGoesDark
|
|
|
|
|
? el('div', { className: 'platform-visual has-ngd', dangerouslySetInnerHTML: { __html: NGD_HTML } })
|
|
|
|
|
: a.brandedAnim
|
|
|
|
|
? el('div', { className: 'platform-visual has-branded', dangerouslySetInnerHTML: { __html: BD_HTML } })
|
|
|
|
|
: a.imgUrl
|
|
|
|
|
? 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')
|
2026-02-20 21:28:00 -05:00
|
|
|
)
|
2026-02-27 17:57:59 -05:00
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
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 }); } })
|
2026-02-20 21:28:00 -05:00
|
|
|
)
|
2026-02-27 17:57:59 -05:00
|
|
|
),
|
|
|
|
|
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' },
|
2026-02-20 21:28:00 -05:00
|
|
|
el(IB, {
|
2026-02-27 17:57:59 -05:00
|
|
|
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' }]
|
|
|
|
|
],
|
2026-02-20 21:28:00 -05:00
|
|
|
templateLock: false
|
|
|
|
|
})
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
2026-02-27 17:57:59 -05:00
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
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...' })
|
|
|
|
|
),
|
2026-02-20 21:28:00 -05:00
|
|
|
el(IB, {
|
2026-02-27 17:57:59 -05:00
|
|
|
allowedBlocks: ['oribi/platform-row'],
|
|
|
|
|
template: [['oribi/platform-row', {}]],
|
2026-02-20 21:28:00 -05:00
|
|
|
templateLock: false
|
|
|
|
|
})
|
|
|
|
|
)
|
|
|
|
|
)
|
2026-02-27 17:57:59 -05:00
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
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 }); } })
|
|
|
|
|
)
|
2026-02-21 22:52:38 -05:00
|
|
|
),
|
2026-02-27 17:57:59 -05:00
|
|
|
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 }); } })
|
|
|
|
|
)
|
2026-02-21 22:52:38 -05:00
|
|
|
),
|
2026-02-27 17:57:59 -05:00
|
|
|
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
|
|
|
|
|
})
|
|
|
|
|
)
|
2026-02-21 22:52:38 -05:00
|
|
|
)
|
2026-02-20 21:28:00 -05:00
|
|
|
)
|
2026-02-27 17:57:59 -05:00
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
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 || [];
|
|
|
|
|
|
|
|
|
|
/* ── Column helpers ─────────────────────────────────────── */
|
|
|
|
|
function updateCol(idx, val) {
|
|
|
|
|
var c = cols.slice(); c[idx] = val; s({ columns: c });
|
|
|
|
|
}
|
|
|
|
|
function addCol() {
|
|
|
|
|
var newRows = rows.map(function (r) {
|
|
|
|
|
if (r.group) return r;
|
|
|
|
|
return Object.assign({}, r, { values: (r.values || []).concat([false]) });
|
|
|
|
|
});
|
|
|
|
|
s({ columns: cols.concat(['Plan']), rows: newRows });
|
|
|
|
|
}
|
|
|
|
|
function removeCol(idx) {
|
|
|
|
|
var c = cols.slice(); c.splice(idx, 1);
|
|
|
|
|
var newRows = rows.map(function (r) {
|
|
|
|
|
if (r.group) return r;
|
|
|
|
|
var v = (r.values || []).slice(); v.splice(idx, 1);
|
|
|
|
|
return Object.assign({}, r, { values: v });
|
|
|
|
|
});
|
|
|
|
|
s({ columns: c, rows: newRows });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Row helpers ────────────────────────────────────────── */
|
|
|
|
|
function updateRow(idx, key, val) {
|
|
|
|
|
var r = rows.slice();
|
|
|
|
|
r[idx] = Object.assign({}, r[idx]);
|
|
|
|
|
r[idx][key] = val;
|
|
|
|
|
s({ rows: r });
|
|
|
|
|
}
|
|
|
|
|
function updateCell(ri, ci, val) {
|
|
|
|
|
var r = rows.slice();
|
|
|
|
|
r[ri] = Object.assign({}, r[ri]);
|
|
|
|
|
var v = (r[ri].values || []).slice();
|
|
|
|
|
v[ci] = val;
|
|
|
|
|
r[ri].values = v;
|
|
|
|
|
s({ rows: r });
|
|
|
|
|
}
|
|
|
|
|
function toggleCell(ri, ci) {
|
|
|
|
|
var val = (rows[ri].values || [])[ci];
|
|
|
|
|
updateCell(ri, ci, val === true ? false : val === false ? true : true);
|
|
|
|
|
}
|
|
|
|
|
function switchCellToText(ri, ci) { updateCell(ri, ci, ''); }
|
|
|
|
|
function addFeatureRow() {
|
|
|
|
|
var vals = cols.map(function () { return false; });
|
|
|
|
|
s({ rows: rows.concat([{ feature: 'New feature', values: vals }]) });
|
|
|
|
|
}
|
|
|
|
|
function addGroupRow() {
|
|
|
|
|
s({ rows: rows.concat([{ group: 'New Group' }]) });
|
|
|
|
|
}
|
|
|
|
|
function removeRow(idx) {
|
|
|
|
|
var r = rows.slice(); r.splice(idx, 1); s({ rows: r });
|
|
|
|
|
}
|
|
|
|
|
function moveRow(idx, dir) {
|
|
|
|
|
var t = idx + dir;
|
|
|
|
|
if (t < 0 || t >= rows.length) return;
|
|
|
|
|
var r = rows.slice();
|
|
|
|
|
var tmp = r[idx]; r[idx] = r[t]; r[t] = tmp;
|
|
|
|
|
s({ rows: r });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Inline styles for editor controls ──────────────────── */
|
|
|
|
|
var inputStyle = { width: '100%', padding: '4px 6px', border: '1px solid #ddd', borderRadius: '3px', fontSize: '13px', background: 'transparent', boxSizing: 'border-box' };
|
|
|
|
|
var thInputStyle = Object.assign({}, inputStyle, { fontWeight: 600, textAlign: 'center' });
|
|
|
|
|
var smallBtnStyle = { background: 'none', border: 'none', cursor: 'pointer', padding: '2px 4px', fontSize: '11px', lineHeight: 1, opacity: 0.6, verticalAlign: 'middle' };
|
|
|
|
|
var rowCtrlStyle = { whiteSpace: 'nowrap', width: '1%', padding: '4px', verticalAlign: 'middle', border: 'none', background: 'transparent' };
|
|
|
|
|
var cellBtnStyle = { background: 'none', border: '1px solid #ddd', borderRadius: '3px', cursor: 'pointer', padding: '2px 6px', fontSize: '13px', lineHeight: '1.4', margin: '0 1px' };
|
|
|
|
|
|
|
|
|
|
/* ── Render ─────────────────────────────────────────────── */
|
|
|
|
|
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 }); } })
|
2026-02-20 21:28:00 -05:00
|
|
|
),
|
2026-02-27 17:57:59 -05:00
|
|
|
el(PB, { title: 'Columns', initialOpen: false },
|
|
|
|
|
cols.map(function (col, i) {
|
|
|
|
|
return el('div', { key: i, style: { display: 'flex', gap: '4px', marginBottom: '8px', alignItems: 'center' } },
|
|
|
|
|
el(TC, { label: '', value: col, onChange: function (v) { updateCol(i, v); }, style: { flex: 1 } }),
|
|
|
|
|
el(Btn, { isDestructive: true, isSmall: true, onClick: function () { removeCol(i); } }, '\u2715')
|
|
|
|
|
);
|
|
|
|
|
}),
|
|
|
|
|
el(Btn, { isSecondary: true, isSmall: true, onClick: addCol }, '+ Add Column')
|
|
|
|
|
),
|
|
|
|
|
el(PB, { title: 'Add Rows', initialOpen: false },
|
|
|
|
|
el('div', { style: { display: 'flex', gap: '8px' } },
|
|
|
|
|
el(Btn, { isSecondary: true, isSmall: true, onClick: addFeatureRow }, '+ Feature Row'),
|
|
|
|
|
el(Btn, { isSecondary: true, isSmall: true, onClick: addGroupRow }, '+ Group Row')
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
),
|
|
|
|
|
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 },
|
|
|
|
|
el('input', { type: 'text', value: col, style: thInputStyle, onChange: function (e) { updateCol(i, e.target.value); } })
|
|
|
|
|
);
|
|
|
|
|
}),
|
|
|
|
|
el('th', { style: rowCtrlStyle })
|
|
|
|
|
)
|
|
|
|
|
),
|
|
|
|
|
el('tbody', null,
|
|
|
|
|
rows.map(function (row, ri) {
|
|
|
|
|
if (row.group) {
|
|
|
|
|
return el('tr', { key: ri, className: 'comparison-group-row' },
|
|
|
|
|
el('td', { colSpan: cols.length + 1 },
|
|
|
|
|
el('input', { type: 'text', value: row.group, style: Object.assign({}, inputStyle, { fontWeight: 700 }), onChange: function (e) { updateRow(ri, 'group', e.target.value); } })
|
|
|
|
|
),
|
|
|
|
|
el('td', { style: rowCtrlStyle },
|
|
|
|
|
el('button', { style: smallBtnStyle, onClick: function () { moveRow(ri, -1); }, title: 'Move up' }, '\u25B2'),
|
|
|
|
|
el('button', { style: smallBtnStyle, onClick: function () { moveRow(ri, 1); }, title: 'Move down' }, '\u25BC'),
|
|
|
|
|
el('button', { style: Object.assign({}, smallBtnStyle, { color: '#b00' }), onClick: function () { removeRow(ri); }, title: 'Remove row' }, '\u2715')
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return el('tr', { key: ri },
|
|
|
|
|
el('td', { className: 'comparison-feature-name' },
|
|
|
|
|
el('input', { type: 'text', value: row.feature || '', style: inputStyle, placeholder: 'Feature name\u2026', onChange: function (e) { updateRow(ri, 'feature', e.target.value); } })
|
2026-02-21 22:52:38 -05:00
|
|
|
),
|
2026-02-27 17:57:59 -05:00
|
|
|
(row.values || []).map(function (val, ci) {
|
|
|
|
|
if (typeof val === 'boolean') {
|
|
|
|
|
return el('td', { key: ci, className: 'comparison-cell', style: { textAlign: 'center' } },
|
|
|
|
|
el('button', { style: Object.assign({}, cellBtnStyle, { color: val ? '#2e7d32' : '#c62828' }), onClick: function () { toggleCell(ri, ci); }, title: 'Toggle \u2713/\u2717' },
|
|
|
|
|
val ? '\u2713' : '\u2717'
|
|
|
|
|
),
|
|
|
|
|
el('button', { style: Object.assign({}, smallBtnStyle, { fontSize: '10px' }), onClick: function () { switchCellToText(ri, ci); }, title: 'Switch to text' }, 'Aa')
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return el('td', { key: ci, className: 'comparison-cell' },
|
|
|
|
|
el('div', { style: { display: 'flex', alignItems: 'center', gap: '2px' } },
|
|
|
|
|
el('input', { type: 'text', value: String(val), style: Object.assign({}, inputStyle, { flex: 1, minWidth: '60px' }), placeholder: 'Value\u2026', onChange: function (e) { updateCell(ri, ci, e.target.value); } }),
|
|
|
|
|
el('button', { style: Object.assign({}, smallBtnStyle, { color: '#2e7d32' }), onClick: function () { updateCell(ri, ci, true); }, title: 'Set as \u2713' }, '\u2713'),
|
|
|
|
|
el('button', { style: Object.assign({}, smallBtnStyle, { color: '#c62828' }), onClick: function () { updateCell(ri, ci, false); }, title: 'Set as \u2717' }, '\u2717')
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}),
|
2026-02-21 22:52:38 -05:00
|
|
|
el('td', { style: rowCtrlStyle },
|
2026-02-27 17:57:59 -05:00
|
|
|
el('button', { style: smallBtnStyle, onClick: function () { moveRow(ri, -1); }, title: 'Move up' }, '\u25B2'),
|
|
|
|
|
el('button', { style: smallBtnStyle, onClick: function () { moveRow(ri, 1); }, title: 'Move down' }, '\u25BC'),
|
|
|
|
|
el('button', { style: Object.assign({}, smallBtnStyle, { color: '#b00' }), onClick: function () { removeRow(ri); }, title: 'Remove row' }, '\u2715')
|
2026-02-21 22:52:38 -05:00
|
|
|
)
|
2026-02-20 21:28:00 -05:00
|
|
|
);
|
2026-02-27 17:57:59 -05:00
|
|
|
})
|
|
|
|
|
)
|
|
|
|
|
),
|
|
|
|
|
el('div', { style: { display: 'flex', gap: '8px', marginTop: '12px', justifyContent: 'center' } },
|
|
|
|
|
el(Btn, { isSecondary: true, isSmall: true, onClick: addFeatureRow }, '+ Feature Row'),
|
|
|
|
|
el(Btn, { isSecondary: true, isSmall: true, onClick: addGroupRow }, '+ Group Row'),
|
|
|
|
|
el(Btn, { isSecondary: true, isSmall: true, onClick: addCol }, '+ Column')
|
2026-02-20 21:28:00 -05:00
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
2026-02-27 17:57:59 -05:00
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
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 }); } })
|
|
|
|
|
)
|
2026-02-20 21:28:00 -05:00
|
|
|
),
|
2026-02-27 17:57:59 -05:00
|
|
|
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: []
|
|
|
|
|
})
|
2026-02-20 21:28:00 -05:00
|
|
|
),
|
2026-02-27 17:57:59 -05:00
|
|
|
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: []
|
|
|
|
|
})
|
|
|
|
|
)
|
2026-02-20 21:28:00 -05:00
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
2026-02-27 17:57:59 -05:00
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
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...' })
|
|
|
|
|
)
|
2026-02-20 21:28:00 -05:00
|
|
|
)
|
2026-02-27 17:57:59 -05:00
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
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' }
|
|
|
|
|
},
|
|
|
|
|
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')
|
2026-02-20 21:28:00 -05:00
|
|
|
)
|
2026-02-27 17:57:59 -05:00
|
|
|
);
|
2026-02-20 21:28:00 -05:00
|
|
|
},
|
2026-02-27 17:57:59 -05:00
|
|
|
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' }
|
|
|
|
|
},
|
|
|
|
|
el('strong', { style: { fontSize: '1.1rem' } }, 'OTS Theme - Site Footer'),
|
|
|
|
|
el('p', { style: { opacity: 0.5, margin: '8px 0 0', fontSize: '.85rem' } }, 'Brand · Service Links · Company Links · Connect · Copyright')
|
|
|
|
|
);
|
2026-02-20 21:28:00 -05:00
|
|
|
},
|
2026-02-27 17:57:59 -05:00
|
|
|
save: function () { return null; }
|
|
|
|
|
});
|
2026-02-20 21:28:00 -05:00
|
|
|
|
|
|
|
|
})(window.wp);
|