Enhance platform row block with new animation options and gallery support

- Added new attributes for dashboard, device, TV stick, camera, never goes dark, and branded animations in the platform row block.
- Implemented HTML structures for various animations including dashboard, device, TV stick, and branded displays.
- Integrated a media uploader for selecting images for a TV slideshow.
- Updated the editor controls to manage new animation settings and gallery images.

Update enqueue scripts for new animation functionalities

- Enqueued scripts for gallery TV slideshow and video editor timeline animator.

Revise theme color palette for improved aesthetics

- Changed primary color scheme to a new vibrant palette for light and dark modes.

Adjust button styles to reflect new primary color

- Updated button background color to match the new primary color in theme settings.

Modify theme JSON to align with new color definitions

- Updated color definitions in theme.json to reflect the new primary color scheme for both light and dark modes.
This commit is contained in:
Matt Batchelder
2026-02-22 11:14:51 -05:00
21 changed files with 3425 additions and 361 deletions

View File

@@ -1170,28 +1170,105 @@ reg('oribi/platform-row', {
parent: ['oribi/platform-section'],
supports: { html: false, reusable: false },
attributes: {
heading: { type: 'string', default: '' },
description: { type: 'string', default: '' },
btnText: { type: 'string', default: 'Learn More' },
btnUrl: { type: 'string', default: '' },
visual: { type: 'string', default: '' },
reversed: { type: 'boolean', default: false },
imgId: { type: 'number', default: 0 },
imgUrl: { type: 'string', default: '' },
imgAlt: { type: 'string', default: '' },
imgWidth: { type: 'number', default: 300 },
cameraAnim: { type: 'boolean', default: false },
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: 'Camera Animation', checked: !!a.cameraAnim, onChange: function(v){s({cameraAnim: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,
@@ -1222,7 +1299,9 @@ reg('oribi/platform-row', {
a.btnUrl ? el(RT, { tagName: 'span', className: 'btn btn-outline mt-3',
value: a.btnText, onChange: function(v){s({btnText:v});}, placeholder: 'Button...' }) : null
),
a.cameraAnim
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)
@@ -1282,6 +1361,14 @@ reg('oribi/platform-row', {
)
)
)
: 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' } })
@@ -1618,6 +1705,74 @@ reg('oribi/comparison-table', {
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' },
@@ -1625,6 +1780,21 @@ reg('oribi/comparison-table', {
{ 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(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' },
@@ -1639,26 +1809,62 @@ reg('oribi/comparison-table', {
el('thead', null,
el('tr', null,
el('th', { className: 'comparison-feature-col' }, 'Feature'),
cols.map(function(col, i) { return el('th', { key: i }, col); })
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, i) {
rows.map(function(row, ri) {
if (row.group) {
return el('tr', { key: i, className: 'comparison-group-row' },
el('td', { colSpan: cols.length + 1 }, 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: i },
el('td', { className: 'comparison-feature-name' }, row.feature || ''),
(row.values || []).map(function(val, j) {
return el('td', { key: j, className: 'comparison-cell' },
val === true ? '\u2713' : val === false ? '\u2014' : String(val)
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); } })
),
(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')
)
);
})
}),
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')
)
);
})
)
),
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')
)
)
)