@innovastudio/contentbox 1.6.161 → 1.6.163
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +7 -0
- package/package.json +2 -2
- package/public/contentbox/contentbox.css +78 -7
- package/public/contentbox/contentbox.esm.js +1698 -13
- package/public/contentbox/contentbox.min.js +14 -14
|
@@ -12300,6 +12300,616 @@ class PanelSpacer {
|
|
|
12300
12300
|
|
|
12301
12301
|
}
|
|
12302
12302
|
|
|
12303
|
+
/**
|
|
12304
|
+
* ContentBox Settings UI Generator
|
|
12305
|
+
* Automatically generates settings form based on plugin schema
|
|
12306
|
+
*
|
|
12307
|
+
* Usage in ContentBox Editor:
|
|
12308
|
+
* const generator = new SettingsUIGenerator(runtime);
|
|
12309
|
+
* const form = generator.generateForm('logo-loop', currentElement);
|
|
12310
|
+
* panel.appendChild(form);
|
|
12311
|
+
*/
|
|
12312
|
+
class SettingsUIGenerator$1 {
|
|
12313
|
+
constructor(runtime, builder) {
|
|
12314
|
+
this.runtime = runtime;
|
|
12315
|
+
this.builder = builder;
|
|
12316
|
+
}
|
|
12317
|
+
/**
|
|
12318
|
+
* Generate settings form for a plugin
|
|
12319
|
+
* @param {string} pluginName - Plugin name
|
|
12320
|
+
* @param {HTMLElement} element - Current element being edited
|
|
12321
|
+
* @returns {HTMLElement} Form element
|
|
12322
|
+
*/
|
|
12323
|
+
|
|
12324
|
+
|
|
12325
|
+
generateForm(pluginName, element, onChange) {
|
|
12326
|
+
const plugin = this.runtime.getPlugin(pluginName);
|
|
12327
|
+
|
|
12328
|
+
if (!plugin || !plugin.settings) {
|
|
12329
|
+
console.warn(`Plugin "${pluginName}" has no settings schema`);
|
|
12330
|
+
return this.createEmptyForm();
|
|
12331
|
+
}
|
|
12332
|
+
|
|
12333
|
+
const form = document.createElement('div');
|
|
12334
|
+
form.className = 'cb-settings-form'; // Get current values from element
|
|
12335
|
+
|
|
12336
|
+
const currentValues = this.getCurrentValues(element, plugin.settings); // Check if plugin has grouped settings
|
|
12337
|
+
|
|
12338
|
+
if (plugin.settings._groups) {
|
|
12339
|
+
this.generateGroupedForm(form, plugin.settings, currentValues);
|
|
12340
|
+
} else {
|
|
12341
|
+
this.generateFlatForm(form, plugin.settings, currentValues);
|
|
12342
|
+
} // Attach listeners for auto-update
|
|
12343
|
+
|
|
12344
|
+
|
|
12345
|
+
if (typeof onChange === 'function') {
|
|
12346
|
+
form.addEventListener('input', () => {
|
|
12347
|
+
const values = this.getFormValues(form);
|
|
12348
|
+
onChange(values, element);
|
|
12349
|
+
});
|
|
12350
|
+
form.addEventListener('change', () => {
|
|
12351
|
+
const values = this.getFormValues(form);
|
|
12352
|
+
onChange(values, element);
|
|
12353
|
+
});
|
|
12354
|
+
}
|
|
12355
|
+
|
|
12356
|
+
return form;
|
|
12357
|
+
}
|
|
12358
|
+
/**
|
|
12359
|
+
* Generate flat form (no groups)
|
|
12360
|
+
*/
|
|
12361
|
+
|
|
12362
|
+
|
|
12363
|
+
generateFlatForm(container, settings, currentValues) {
|
|
12364
|
+
Object.keys(settings).forEach(key => {
|
|
12365
|
+
if (key.startsWith('_')) return; // Skip meta keys
|
|
12366
|
+
|
|
12367
|
+
const field = settings[key];
|
|
12368
|
+
const fieldElement = this.createField(key, field, currentValues[key]);
|
|
12369
|
+
container.appendChild(fieldElement);
|
|
12370
|
+
});
|
|
12371
|
+
}
|
|
12372
|
+
/**
|
|
12373
|
+
* Generate grouped form
|
|
12374
|
+
*/
|
|
12375
|
+
|
|
12376
|
+
|
|
12377
|
+
generateGroupedForm(container, settings, currentValues) {
|
|
12378
|
+
const groups = settings._groups || [];
|
|
12379
|
+
groups.forEach(group => {
|
|
12380
|
+
const groupElement = document.createElement('div');
|
|
12381
|
+
groupElement.className = 'cb-settings-group';
|
|
12382
|
+
const groupTitle = document.createElement('h3');
|
|
12383
|
+
groupTitle.className = 'cb-settings-group-title';
|
|
12384
|
+
groupTitle.style.cssText = 'font-size: 17px;font-weight: 500;';
|
|
12385
|
+
groupTitle.textContent = group.label;
|
|
12386
|
+
groupElement.appendChild(groupTitle);
|
|
12387
|
+
group.fields.forEach(fieldKey => {
|
|
12388
|
+
const field = settings[fieldKey];
|
|
12389
|
+
if (!field) return;
|
|
12390
|
+
const fieldElement = this.createField(fieldKey, field, currentValues[fieldKey]);
|
|
12391
|
+
groupElement.appendChild(fieldElement);
|
|
12392
|
+
});
|
|
12393
|
+
container.appendChild(groupElement);
|
|
12394
|
+
});
|
|
12395
|
+
}
|
|
12396
|
+
/**
|
|
12397
|
+
* Create a single form field
|
|
12398
|
+
*/
|
|
12399
|
+
|
|
12400
|
+
|
|
12401
|
+
createField(name, config, value) {
|
|
12402
|
+
const wrapper = document.createElement('div');
|
|
12403
|
+
wrapper.className = 'cb-settings-field';
|
|
12404
|
+
wrapper.style.marginBottom = '20px';
|
|
12405
|
+
wrapper.setAttribute('data-field', name); // Add conditional display logic
|
|
12406
|
+
|
|
12407
|
+
if (config.dependsOn) {
|
|
12408
|
+
wrapper.setAttribute('data-depends-on', JSON.stringify(config.dependsOn));
|
|
12409
|
+
wrapper.style.display = 'none'; // Hidden by default, shown by JS
|
|
12410
|
+
} // Label
|
|
12411
|
+
|
|
12412
|
+
|
|
12413
|
+
const label = document.createElement('label');
|
|
12414
|
+
label.className = 'cb-settings-label';
|
|
12415
|
+
label.style.fontWeight = 500;
|
|
12416
|
+
label.textContent = config.label || name; // wrapper.appendChild(label);
|
|
12417
|
+
// Input based on type
|
|
12418
|
+
|
|
12419
|
+
const input = this.createInput(name, config, value);
|
|
12420
|
+
wrapper.appendChild(input); // Description
|
|
12421
|
+
// if (config.description) {
|
|
12422
|
+
// const desc = document.createElement('p');
|
|
12423
|
+
// desc.className = 'cb-settings-description';
|
|
12424
|
+
// desc.textContent = config.description;
|
|
12425
|
+
// wrapper.appendChild(desc);
|
|
12426
|
+
// }
|
|
12427
|
+
|
|
12428
|
+
return wrapper;
|
|
12429
|
+
}
|
|
12430
|
+
/**
|
|
12431
|
+
* Create input element based on field type
|
|
12432
|
+
*/
|
|
12433
|
+
|
|
12434
|
+
|
|
12435
|
+
createInput(name, config, value) {
|
|
12436
|
+
const currentValue = value !== undefined ? value : config.default;
|
|
12437
|
+
|
|
12438
|
+
switch (config.type) {
|
|
12439
|
+
case 'number':
|
|
12440
|
+
return this.createNumberInput(name, config, currentValue);
|
|
12441
|
+
|
|
12442
|
+
case 'boolean':
|
|
12443
|
+
return this.createCheckboxInput(name, config, currentValue);
|
|
12444
|
+
|
|
12445
|
+
case 'select':
|
|
12446
|
+
return this.createSelectInput(name, config, currentValue);
|
|
12447
|
+
|
|
12448
|
+
case 'radio':
|
|
12449
|
+
return this.createRadioInput(name, config, currentValue);
|
|
12450
|
+
|
|
12451
|
+
case 'textarea':
|
|
12452
|
+
return this.createTextareaInput(name, config, currentValue);
|
|
12453
|
+
|
|
12454
|
+
case 'color':
|
|
12455
|
+
return this.createColorInput(name, config, currentValue);
|
|
12456
|
+
|
|
12457
|
+
case 'range':
|
|
12458
|
+
return this.createRangeInput(name, config, currentValue);
|
|
12459
|
+
|
|
12460
|
+
case 'text':
|
|
12461
|
+
default:
|
|
12462
|
+
return this.createTextInput(name, config, currentValue);
|
|
12463
|
+
}
|
|
12464
|
+
}
|
|
12465
|
+
|
|
12466
|
+
createTextInput(name, config, value) {
|
|
12467
|
+
const label = document.createElement('label');
|
|
12468
|
+
label.className = 'label';
|
|
12469
|
+
label.style.fontWeight = 500;
|
|
12470
|
+
const labelText = document.createElement('div');
|
|
12471
|
+
labelText.textContent = config.label || name;
|
|
12472
|
+
label.appendChild(labelText);
|
|
12473
|
+
const input = document.createElement('input');
|
|
12474
|
+
input.type = 'text';
|
|
12475
|
+
input.name = name;
|
|
12476
|
+
input.value = value || '';
|
|
12477
|
+
if (config.placeholder) input.placeholder = config.placeholder;
|
|
12478
|
+
label.appendChild(input); // Add description if exists
|
|
12479
|
+
|
|
12480
|
+
if (config.description) {
|
|
12481
|
+
const desc = document.createElement('small');
|
|
12482
|
+
desc.style.cssText = 'display: block;margin-top: 4px;';
|
|
12483
|
+
desc.textContent = config.description;
|
|
12484
|
+
label.appendChild(desc);
|
|
12485
|
+
}
|
|
12486
|
+
|
|
12487
|
+
return label;
|
|
12488
|
+
}
|
|
12489
|
+
|
|
12490
|
+
createNumberInput(name, config, value) {
|
|
12491
|
+
const label = document.createElement('label');
|
|
12492
|
+
label.className = 'label';
|
|
12493
|
+
label.style.fontWeight = 500;
|
|
12494
|
+
const labelText = document.createElement('div');
|
|
12495
|
+
labelText.textContent = config.label || name;
|
|
12496
|
+
|
|
12497
|
+
if (config.unit) {
|
|
12498
|
+
labelText.textContent += ` (${config.unit})`;
|
|
12499
|
+
}
|
|
12500
|
+
|
|
12501
|
+
label.appendChild(labelText);
|
|
12502
|
+
const input = document.createElement('input');
|
|
12503
|
+
input.type = 'number';
|
|
12504
|
+
input.style.cssText = 'width: 100px;';
|
|
12505
|
+
input.name = name;
|
|
12506
|
+
input.value = value !== undefined ? value : config.default;
|
|
12507
|
+
if (config.min !== undefined) input.min = config.min;
|
|
12508
|
+
if (config.max !== undefined) input.max = config.max;
|
|
12509
|
+
if (config.step !== undefined) input.step = config.step;
|
|
12510
|
+
label.appendChild(input);
|
|
12511
|
+
|
|
12512
|
+
if (config.description) {
|
|
12513
|
+
const desc = document.createElement('small');
|
|
12514
|
+
desc.style.cssText = 'display: block;margin-top: 4px;';
|
|
12515
|
+
desc.textContent = config.description;
|
|
12516
|
+
label.appendChild(desc);
|
|
12517
|
+
}
|
|
12518
|
+
|
|
12519
|
+
return label;
|
|
12520
|
+
}
|
|
12521
|
+
|
|
12522
|
+
createCheckboxInput(name, config, value) {
|
|
12523
|
+
const label = document.createElement('label');
|
|
12524
|
+
label.className = 'label checkbox';
|
|
12525
|
+
const div = document.createElement('div');
|
|
12526
|
+
const input = document.createElement('input');
|
|
12527
|
+
input.type = 'checkbox';
|
|
12528
|
+
input.name = name;
|
|
12529
|
+
input.checked = value !== undefined ? value : config.default;
|
|
12530
|
+
const span = document.createElement('span');
|
|
12531
|
+
span.textContent = config.label || name;
|
|
12532
|
+
label.appendChild(input);
|
|
12533
|
+
label.appendChild(span);
|
|
12534
|
+
div.appendChild(label);
|
|
12535
|
+
|
|
12536
|
+
if (config.description) {
|
|
12537
|
+
const desc = document.createElement('small');
|
|
12538
|
+
desc.style.cssText = 'display: block;margin-top: 4px;';
|
|
12539
|
+
desc.textContent = config.description;
|
|
12540
|
+
div.appendChild(desc);
|
|
12541
|
+
}
|
|
12542
|
+
|
|
12543
|
+
return label;
|
|
12544
|
+
}
|
|
12545
|
+
|
|
12546
|
+
createSelectInput(name, config, value) {
|
|
12547
|
+
const label = document.createElement('label');
|
|
12548
|
+
label.className = 'label';
|
|
12549
|
+
label.style.fontWeight = 500;
|
|
12550
|
+
const labelText = document.createTextNode((config.label || name) + ':');
|
|
12551
|
+
label.appendChild(labelText);
|
|
12552
|
+
const select = document.createElement('select');
|
|
12553
|
+
select.name = name;
|
|
12554
|
+
config.options.forEach(option => {
|
|
12555
|
+
const opt = document.createElement('option');
|
|
12556
|
+
opt.value = option.value;
|
|
12557
|
+
opt.textContent = option.label;
|
|
12558
|
+
if (option.value === value) opt.selected = true;
|
|
12559
|
+
select.appendChild(opt);
|
|
12560
|
+
});
|
|
12561
|
+
label.appendChild(select);
|
|
12562
|
+
|
|
12563
|
+
if (config.description) {
|
|
12564
|
+
const desc = document.createElement('small');
|
|
12565
|
+
desc.style.cssText = 'display: block;margin-top: 4px;';
|
|
12566
|
+
desc.textContent = config.description;
|
|
12567
|
+
label.appendChild(desc);
|
|
12568
|
+
}
|
|
12569
|
+
|
|
12570
|
+
return label;
|
|
12571
|
+
}
|
|
12572
|
+
|
|
12573
|
+
createRadioInput(name, config, value) {
|
|
12574
|
+
const wrapper = document.createElement('div');
|
|
12575
|
+
const title = document.createElement('div');
|
|
12576
|
+
title.textContent = config.label || name;
|
|
12577
|
+
title.style.cssText = 'margin-bottom: 8px;font-weight: 590;';
|
|
12578
|
+
wrapper.appendChild(title);
|
|
12579
|
+
config.options.forEach(option => {
|
|
12580
|
+
const label = document.createElement('label');
|
|
12581
|
+
label.className = 'label checkbox';
|
|
12582
|
+
const input = document.createElement('input');
|
|
12583
|
+
input.type = 'radio';
|
|
12584
|
+
input.name = name;
|
|
12585
|
+
input.value = option.value;
|
|
12586
|
+
input.checked = option.value === value;
|
|
12587
|
+
const span = document.createElement('span');
|
|
12588
|
+
span.textContent = option.label;
|
|
12589
|
+
label.appendChild(input);
|
|
12590
|
+
label.appendChild(span);
|
|
12591
|
+
wrapper.appendChild(label);
|
|
12592
|
+
});
|
|
12593
|
+
|
|
12594
|
+
if (config.description) {
|
|
12595
|
+
const desc = document.createElement('small');
|
|
12596
|
+
desc.style.cssText = 'display: block;margin-top: 4px;';
|
|
12597
|
+
desc.textContent = config.description;
|
|
12598
|
+
wrapper.appendChild(desc);
|
|
12599
|
+
}
|
|
12600
|
+
|
|
12601
|
+
return wrapper;
|
|
12602
|
+
}
|
|
12603
|
+
|
|
12604
|
+
createTextareaInput(name, config, value) {
|
|
12605
|
+
const label = document.createElement('label');
|
|
12606
|
+
label.className = 'label';
|
|
12607
|
+
label.style.fontWeight = 500;
|
|
12608
|
+
const labelText = document.createElement('div');
|
|
12609
|
+
labelText.textContent = config.label || name;
|
|
12610
|
+
label.appendChild(labelText);
|
|
12611
|
+
const textarea = document.createElement('textarea');
|
|
12612
|
+
textarea.name = name;
|
|
12613
|
+
textarea.value = value || '';
|
|
12614
|
+
textarea.rows = config.rows || 4;
|
|
12615
|
+
if (config.placeholder) textarea.placeholder = config.placeholder;
|
|
12616
|
+
label.appendChild(textarea);
|
|
12617
|
+
|
|
12618
|
+
if (config.description) {
|
|
12619
|
+
const desc = document.createElement('small');
|
|
12620
|
+
desc.style.cssText = 'display: block;margin-top: 4px;';
|
|
12621
|
+
desc.textContent = config.description;
|
|
12622
|
+
label.appendChild(desc);
|
|
12623
|
+
}
|
|
12624
|
+
|
|
12625
|
+
return label;
|
|
12626
|
+
}
|
|
12627
|
+
|
|
12628
|
+
createColorInput(name, config, value) {
|
|
12629
|
+
const container = document.createElement('div');
|
|
12630
|
+
const label = document.createElement('label');
|
|
12631
|
+
label.className = 'label';
|
|
12632
|
+
label.style.fontWeight = 500;
|
|
12633
|
+
label.style.cursor = 'pointer';
|
|
12634
|
+
const labelText = document.createElement('div');
|
|
12635
|
+
labelText.textContent = config.label || name;
|
|
12636
|
+
label.appendChild(labelText);
|
|
12637
|
+
const input = document.createElement('input');
|
|
12638
|
+
input.type = 'hidden';
|
|
12639
|
+
input.name = name;
|
|
12640
|
+
input.value = value || config.default || '#000000';
|
|
12641
|
+
const group = document.createElement('div');
|
|
12642
|
+
group.className = 'group';
|
|
12643
|
+
group.style.cssText = 'width: 52px; margin:0; overflow: visible;';
|
|
12644
|
+
const colorBtn = document.createElement('button');
|
|
12645
|
+
colorBtn.type = 'button';
|
|
12646
|
+
colorBtn.title = config.label || 'Color';
|
|
12647
|
+
colorBtn.className = 'btn-color is-btn-color';
|
|
12648
|
+
colorBtn.style.backgroundColor = input.value;
|
|
12649
|
+
colorBtn.setAttribute('aria-label', `Choose color for ${config.label || name}`);
|
|
12650
|
+
|
|
12651
|
+
const openPicker = e => {
|
|
12652
|
+
e.preventDefault();
|
|
12653
|
+
this.builder.openColorPicker(input.value, color => {
|
|
12654
|
+
input.value = color;
|
|
12655
|
+
colorBtn.style.backgroundColor = color;
|
|
12656
|
+
input.dispatchEvent(new Event('change', {
|
|
12657
|
+
bubbles: true
|
|
12658
|
+
}));
|
|
12659
|
+
}, colorBtn);
|
|
12660
|
+
};
|
|
12661
|
+
|
|
12662
|
+
colorBtn.addEventListener('click', openPicker); // Make label click trigger the button
|
|
12663
|
+
|
|
12664
|
+
label.addEventListener('click', e => {
|
|
12665
|
+
if (e.target === label || e.target === labelText) {
|
|
12666
|
+
colorBtn.click();
|
|
12667
|
+
}
|
|
12668
|
+
});
|
|
12669
|
+
group.appendChild(colorBtn);
|
|
12670
|
+
label.appendChild(input);
|
|
12671
|
+
|
|
12672
|
+
if (config.description) {
|
|
12673
|
+
const desc = document.createElement('small');
|
|
12674
|
+
desc.style.cssText = 'display: block;margin-top: 4px;';
|
|
12675
|
+
desc.textContent = config.description;
|
|
12676
|
+
label.appendChild(desc);
|
|
12677
|
+
}
|
|
12678
|
+
|
|
12679
|
+
container.appendChild(label);
|
|
12680
|
+
container.appendChild(group);
|
|
12681
|
+
return container;
|
|
12682
|
+
}
|
|
12683
|
+
|
|
12684
|
+
createColorInput2(name, config, value) {
|
|
12685
|
+
const label = document.createElement('label');
|
|
12686
|
+
label.className = 'label';
|
|
12687
|
+
label.style.fontWeight = 500;
|
|
12688
|
+
const labelText = document.createElement('div');
|
|
12689
|
+
labelText.textContent = config.label || name;
|
|
12690
|
+
label.appendChild(labelText);
|
|
12691
|
+
const input = document.createElement('input');
|
|
12692
|
+
input.type = 'color';
|
|
12693
|
+
input.name = name;
|
|
12694
|
+
input.style.cssText = 'width:40px;height:40px';
|
|
12695
|
+
input.value = value || config.default || '#000000';
|
|
12696
|
+
label.appendChild(input);
|
|
12697
|
+
|
|
12698
|
+
if (config.description) {
|
|
12699
|
+
const desc = document.createElement('small');
|
|
12700
|
+
desc.style.cssText = 'display: block;margin-top: 4px;';
|
|
12701
|
+
desc.textContent = config.description;
|
|
12702
|
+
label.appendChild(desc);
|
|
12703
|
+
}
|
|
12704
|
+
|
|
12705
|
+
return label;
|
|
12706
|
+
}
|
|
12707
|
+
|
|
12708
|
+
createRangeInput(name, config, value) {
|
|
12709
|
+
const label = document.createElement('label');
|
|
12710
|
+
label.className = 'label';
|
|
12711
|
+
label.style.fontWeight = 500;
|
|
12712
|
+
const currentVal = value !== undefined ? value : config.default;
|
|
12713
|
+
const labelText = document.createElement('div');
|
|
12714
|
+
labelText.textContent = (config.label || name) + ': ' + currentVal + (config.unit || '');
|
|
12715
|
+
label.appendChild(labelText);
|
|
12716
|
+
const input = document.createElement('input');
|
|
12717
|
+
input.type = 'range';
|
|
12718
|
+
input.className = 'is-rangeslider';
|
|
12719
|
+
input.name = name;
|
|
12720
|
+
input.value = currentVal;
|
|
12721
|
+
if (config.min !== undefined) input.min = config.min;
|
|
12722
|
+
if (config.max !== undefined) input.max = config.max;
|
|
12723
|
+
if (config.step !== undefined) input.step = config.step;
|
|
12724
|
+
input.addEventListener('input', () => {
|
|
12725
|
+
labelText.textContent = (config.label || name) + ': ' + input.value + (config.unit || '');
|
|
12726
|
+
});
|
|
12727
|
+
label.appendChild(input);
|
|
12728
|
+
|
|
12729
|
+
if (config.description) {
|
|
12730
|
+
const desc = document.createElement('small');
|
|
12731
|
+
desc.style.cssText = 'display: block;margin-top: 4px;';
|
|
12732
|
+
desc.textContent = config.description;
|
|
12733
|
+
label.appendChild(desc);
|
|
12734
|
+
}
|
|
12735
|
+
|
|
12736
|
+
return label;
|
|
12737
|
+
}
|
|
12738
|
+
/**
|
|
12739
|
+
* Get current values from element attributes
|
|
12740
|
+
*/
|
|
12741
|
+
|
|
12742
|
+
|
|
12743
|
+
getCurrentValues(element, settings) {
|
|
12744
|
+
const values = {};
|
|
12745
|
+
Object.keys(settings).forEach(key => {
|
|
12746
|
+
if (key.startsWith('_')) return;
|
|
12747
|
+
const attrName = 'data-cb-' + key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
12748
|
+
const attrValue = element.getAttribute(attrName);
|
|
12749
|
+
|
|
12750
|
+
if (attrValue !== null) {
|
|
12751
|
+
values[key] = this.parseValue(attrValue, settings[key].type);
|
|
12752
|
+
}
|
|
12753
|
+
});
|
|
12754
|
+
return values;
|
|
12755
|
+
}
|
|
12756
|
+
/**
|
|
12757
|
+
* Parse attribute value based on type
|
|
12758
|
+
*/
|
|
12759
|
+
|
|
12760
|
+
|
|
12761
|
+
parseValue(value, type) {
|
|
12762
|
+
switch (type) {
|
|
12763
|
+
case 'number':
|
|
12764
|
+
return parseFloat(value);
|
|
12765
|
+
|
|
12766
|
+
case 'boolean':
|
|
12767
|
+
return value === 'true';
|
|
12768
|
+
|
|
12769
|
+
default:
|
|
12770
|
+
return value;
|
|
12771
|
+
}
|
|
12772
|
+
}
|
|
12773
|
+
/**
|
|
12774
|
+
* Get form values
|
|
12775
|
+
*/
|
|
12776
|
+
|
|
12777
|
+
|
|
12778
|
+
getFormValues(form) {
|
|
12779
|
+
const values = {};
|
|
12780
|
+
const inputs = form.querySelectorAll('input, select, textarea');
|
|
12781
|
+
inputs.forEach(input => {
|
|
12782
|
+
const name = input.name;
|
|
12783
|
+
if (!name) return;
|
|
12784
|
+
|
|
12785
|
+
if (input.type === 'checkbox') {
|
|
12786
|
+
values[name] = input.checked;
|
|
12787
|
+
} else if (input.type === 'radio') {
|
|
12788
|
+
if (input.checked) {
|
|
12789
|
+
values[name] = input.value;
|
|
12790
|
+
}
|
|
12791
|
+
} else {
|
|
12792
|
+
values[name] = input.value;
|
|
12793
|
+
}
|
|
12794
|
+
});
|
|
12795
|
+
return values;
|
|
12796
|
+
}
|
|
12797
|
+
/**
|
|
12798
|
+
* Apply form values to element
|
|
12799
|
+
*/
|
|
12800
|
+
|
|
12801
|
+
|
|
12802
|
+
applyValues(element, values) {
|
|
12803
|
+
Object.keys(values).forEach(key => {
|
|
12804
|
+
const attrName = 'data-cb-' + key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
12805
|
+
element.setAttribute(attrName, values[key]);
|
|
12806
|
+
});
|
|
12807
|
+
}
|
|
12808
|
+
|
|
12809
|
+
createEmptyForm() {
|
|
12810
|
+
const form = document.createElement('div');
|
|
12811
|
+
form.className = 'cb-settings-form';
|
|
12812
|
+
form.innerHTML = '<p>This plugin has no configurable settings.</p>';
|
|
12813
|
+
return form;
|
|
12814
|
+
}
|
|
12815
|
+
|
|
12816
|
+
}
|
|
12817
|
+
|
|
12818
|
+
class PanelPlugin {
|
|
12819
|
+
constructor(panel, builder) {
|
|
12820
|
+
this.builder = builder;
|
|
12821
|
+
this.panel = panel;
|
|
12822
|
+
this.generator = null;
|
|
12823
|
+
this.currentElement = null;
|
|
12824
|
+
this.currentForm = null;
|
|
12825
|
+
}
|
|
12826
|
+
|
|
12827
|
+
render(pluginName, currentElement, runtime) {
|
|
12828
|
+
// Clear panel
|
|
12829
|
+
this.panel.innerHTML = '';
|
|
12830
|
+
const div = document.createElement('div');
|
|
12831
|
+
div.classList.add('submain');
|
|
12832
|
+
this.panel.appendChild(div); // Store references
|
|
12833
|
+
|
|
12834
|
+
this.currentElement = currentElement;
|
|
12835
|
+
this.generator = new SettingsUIGenerator$1(runtime, this.builder); // Check if plugin has custom content editor
|
|
12836
|
+
|
|
12837
|
+
const plugin = runtime.getPlugin(pluginName);
|
|
12838
|
+
const hasContentEditor = plugin && plugin.editor && plugin.editor.openContentEditor;
|
|
12839
|
+
|
|
12840
|
+
if (hasContentEditor) {
|
|
12841
|
+
// Get original content for the editor to work with
|
|
12842
|
+
const originalContent = currentElement.getAttribute('data-cb-original-content'); // Create a temporary element for editing (so editor can manipulate it)
|
|
12843
|
+
|
|
12844
|
+
let editableClone = document.querySelector('.editable-clone');
|
|
12845
|
+
if (editableClone) editableClone.remove(); // editableClone = document.createElement('div');
|
|
12846
|
+
|
|
12847
|
+
editableClone = currentElement.cloneNode(false);
|
|
12848
|
+
editableClone.innerHTML = originalContent;
|
|
12849
|
+
editableClone.className = 'editable-clone';
|
|
12850
|
+
editableClone.style.display = 'none'; // Hidden, just for editing
|
|
12851
|
+
|
|
12852
|
+
document.body.appendChild(editableClone);
|
|
12853
|
+
const originalElement = editableClone.cloneNode(false); // Let plugin handle everything - pass the editable clone
|
|
12854
|
+
|
|
12855
|
+
const editorUI = plugin.editor.openContentEditor(editableClone, this.builder, async () => {
|
|
12856
|
+
this.builder.editor.saveForUndo(); // Store edited content from clone
|
|
12857
|
+
|
|
12858
|
+
currentElement.setAttribute('data-cb-original-content', editableClone.innerHTML); // Also performs similar value updates like the applyChanges()
|
|
12859
|
+
// currentElement.setAttribute('data-cb-content', editableClone.getAttribute('data-cb-content'));
|
|
12860
|
+
|
|
12861
|
+
const getChangedDataAttributes = (el1, el2) => {
|
|
12862
|
+
const changed = [];
|
|
12863
|
+
|
|
12864
|
+
for (const key of Object.keys(el1.dataset)) {
|
|
12865
|
+
if (key in el2.dataset && el1.dataset[key] !== el2.dataset[key]) {
|
|
12866
|
+
changed.push(key);
|
|
12867
|
+
}
|
|
12868
|
+
}
|
|
12869
|
+
|
|
12870
|
+
return changed;
|
|
12871
|
+
};
|
|
12872
|
+
|
|
12873
|
+
const changedAttributes = getChangedDataAttributes(originalElement, editableClone);
|
|
12874
|
+
changedAttributes.forEach(attrName => {
|
|
12875
|
+
// console.log(attrName)
|
|
12876
|
+
// console.log(editableClone.dataset[attrName])
|
|
12877
|
+
currentElement.dataset[attrName] = editableClone.dataset[attrName];
|
|
12878
|
+
}); // -------
|
|
12879
|
+
|
|
12880
|
+
currentElement.innerHTML = editableClone.innerHTML; // Update current content
|
|
12881
|
+
// Remove temporary clone
|
|
12882
|
+
|
|
12883
|
+
editableClone.remove(); // Reinitialize plugin with new content
|
|
12884
|
+
|
|
12885
|
+
await runtime.reinitialize(currentElement.parentElement);
|
|
12886
|
+
this.builder.onChange();
|
|
12887
|
+
});
|
|
12888
|
+
div.appendChild(editorUI);
|
|
12889
|
+
} // Generate form
|
|
12890
|
+
|
|
12891
|
+
|
|
12892
|
+
this.currentForm = this.generator.generateForm(pluginName, currentElement, () => {
|
|
12893
|
+
this.builder.editor.saveForUndo();
|
|
12894
|
+
this.applyChanges(runtime);
|
|
12895
|
+
});
|
|
12896
|
+
div.appendChild(this.currentForm);
|
|
12897
|
+
}
|
|
12898
|
+
|
|
12899
|
+
async applyChanges(runtime) {
|
|
12900
|
+
if (!this.currentElement || !this.currentForm) return; // 1. Get form values
|
|
12901
|
+
|
|
12902
|
+
const values = this.generator.getFormValues(this.currentForm); // 2. Apply to element attributes
|
|
12903
|
+
|
|
12904
|
+
this.generator.applyValues(this.currentElement, values); // 3. Reinitialize component
|
|
12905
|
+
|
|
12906
|
+
const container = this.currentElement.parentElement || this.currentElement.closest('.is-wrapper');
|
|
12907
|
+
await runtime.reinitialize(container);
|
|
12908
|
+
this.builder.onChange(); // console.log('Settings applied and component reinitialized');
|
|
12909
|
+
}
|
|
12910
|
+
|
|
12911
|
+
}
|
|
12912
|
+
|
|
12303
12913
|
class PanelIcon {
|
|
12304
12914
|
constructor(panel, builder) {
|
|
12305
12915
|
this.builder = builder;
|
|
@@ -25494,6 +26104,7 @@ class ControlPanel {
|
|
|
25494
26104
|
<div class="panel-audio"></div>
|
|
25495
26105
|
<div class="panel-iframe"></div>
|
|
25496
26106
|
<div class="panel-spacer"></div>
|
|
26107
|
+
<div class="panel-plugin"></div>
|
|
25497
26108
|
<div class="panel-column"></div>
|
|
25498
26109
|
<div class="panel-row"></div>
|
|
25499
26110
|
<div class="panel-container"></div>
|
|
@@ -25544,6 +26155,7 @@ class ControlPanel {
|
|
|
25544
26155
|
this.panelAudio = controlPanel.querySelector('.panel-audio');
|
|
25545
26156
|
this.panelIframe = controlPanel.querySelector('.panel-iframe');
|
|
25546
26157
|
this.panelSpacer = controlPanel.querySelector('.panel-spacer');
|
|
26158
|
+
this.panelPlugin = controlPanel.querySelector('.panel-plugin');
|
|
25547
26159
|
this.panelIcon = controlPanel.querySelector('.panel-icon');
|
|
25548
26160
|
this.panelSvg = controlPanel.querySelector('.panel-svg');
|
|
25549
26161
|
this.panelCode = controlPanel.querySelector('.panel-code');
|
|
@@ -25584,6 +26196,7 @@ class ControlPanel {
|
|
|
25584
26196
|
this.objPanelAudio = new PanelAudio(this.panelAudio, this.builder);
|
|
25585
26197
|
this.objPanelIframe = new PanelIframe(this.panelIframe, this.builder);
|
|
25586
26198
|
this.objPanelSpacer = new PanelSpacer(this.panelSpacer, this.builder);
|
|
26199
|
+
this.objPanelPlugin = new PanelPlugin(this.panelPlugin, this.builder);
|
|
25587
26200
|
this.objPanelIcon = new PanelIcon(this.panelIcon, this.builder);
|
|
25588
26201
|
this.objPanelSvg = new PanelSvg(this.panelSvg, this.builder);
|
|
25589
26202
|
this.objPanelCode = new PanelCode(this.panelCode, this.builder);
|
|
@@ -26240,6 +26853,7 @@ class ControlPanel {
|
|
|
26240
26853
|
this.panelAudio.classList.remove('active');
|
|
26241
26854
|
this.panelIframe.classList.remove('active');
|
|
26242
26855
|
this.panelSpacer.classList.remove('active');
|
|
26856
|
+
this.panelPlugin.classList.remove('active');
|
|
26243
26857
|
this.panelIcon.classList.remove('active');
|
|
26244
26858
|
this.panelSvg.classList.remove('active');
|
|
26245
26859
|
this.panelCode.classList.remove('active');
|
|
@@ -26302,13 +26916,32 @@ class ControlPanel {
|
|
|
26302
26916
|
this.title.innerHTML = titleHtml;
|
|
26303
26917
|
this.panelCode.classList.add('active');
|
|
26304
26918
|
this.objPanelCode.getState();
|
|
26305
|
-
} else if (element.closest('[data-noedit]') && !element.closest('[data-html]')) {
|
|
26919
|
+
} else if (element.closest('[data-noedit]') && !element.closest('[data-html]') && !element.closest('[data-cb-type]')) {
|
|
26306
26920
|
let titleHtml = out('Locked');
|
|
26307
26921
|
this.title.innerHTML = titleHtml;
|
|
26308
26922
|
} else {
|
|
26309
26923
|
if (element.querySelector('.icon') && element.childElementCount === 1) ;
|
|
26310
26924
|
|
|
26311
|
-
if (element.
|
|
26925
|
+
if (element.closest('[data-cb-type]')) {
|
|
26926
|
+
const runtime = this.builder.win.builderRuntime;
|
|
26927
|
+
|
|
26928
|
+
if (runtime) {
|
|
26929
|
+
const pluginElement = element.closest('[data-cb-type]');
|
|
26930
|
+
const pluginName = pluginElement.getAttribute('data-cb-type');
|
|
26931
|
+
|
|
26932
|
+
if (!pluginElement.hasAttribute('data-cb-editmode')) {
|
|
26933
|
+
pluginElement.setAttribute('contentEditable', false);
|
|
26934
|
+
}
|
|
26935
|
+
|
|
26936
|
+
const plugin = runtime.getPlugin(pluginName);
|
|
26937
|
+
let titleHtml = out(plugin.displayName || plugin.name);
|
|
26938
|
+
this.title.innerHTML = titleHtml; // this.title.style.cssText = 'margin-bottom: 0';
|
|
26939
|
+
|
|
26940
|
+
this.objPanelPlugin.render(pluginName, pluginElement, runtime);
|
|
26941
|
+
}
|
|
26942
|
+
|
|
26943
|
+
this.panelPlugin.classList.add('active');
|
|
26944
|
+
} else if (element.tagName.toLowerCase() === 'img') {
|
|
26312
26945
|
// Image
|
|
26313
26946
|
let titleHtml = out('Image');
|
|
26314
26947
|
this.title.innerHTML = titleHtml;
|
|
@@ -32700,15 +33333,16 @@ class Util$1 {
|
|
|
32700
33333
|
rtepop.style.display = '';
|
|
32701
33334
|
dom.removeClass(rtepop, 'active');
|
|
32702
33335
|
dom.removeClass(rtepop, 'deactive');
|
|
32703
|
-
// dom.addClass(rtepop, 'deactive');
|
|
32704
33336
|
});
|
|
32705
33337
|
}
|
|
32706
|
-
|
|
32707
33338
|
let pops = builderStuff.querySelectorAll('.is-pop');
|
|
32708
33339
|
Array.prototype.forEach.call(pops, pop => {
|
|
32709
33340
|
pop.style.display = '';
|
|
32710
33341
|
dom.removeClass(pop, 'active');
|
|
32711
|
-
|
|
33342
|
+
this.builder.doc.body.focus();
|
|
33343
|
+
setTimeout(() => {
|
|
33344
|
+
pop.setAttribute('aria-hidden', true);
|
|
33345
|
+
}, 0);
|
|
32712
33346
|
});
|
|
32713
33347
|
this.builder.colTool.lockIndicator.style.display = '';
|
|
32714
33348
|
if (this.builder.resize) {
|
|
@@ -40969,7 +41603,7 @@ class HtmlUtil {
|
|
|
40969
41603
|
this.builder.opts.onRender();
|
|
40970
41604
|
|
|
40971
41605
|
// Re-select
|
|
40972
|
-
if
|
|
41606
|
+
// if(row.firstChild) row.firstChild.click();
|
|
40973
41607
|
//Change to row selection
|
|
40974
41608
|
row.classList.remove('row-outline');
|
|
40975
41609
|
//Hide Column tool (new!)
|
|
@@ -41022,6 +41656,8 @@ class HtmlUtil {
|
|
|
41022
41656
|
}
|
|
41023
41657
|
util.clearControls(); // NEW
|
|
41024
41658
|
|
|
41659
|
+
// Re-init plugins
|
|
41660
|
+
if (this.builder.win.builderRuntime) this.builder.win.builderRuntime.reinitialize();
|
|
41025
41661
|
this.builder.hideModal(modal);
|
|
41026
41662
|
}
|
|
41027
41663
|
viewHtmlExternal() {
|
|
@@ -41568,6 +42204,34 @@ class HtmlUtil {
|
|
|
41568
42204
|
elm.innerHTML = '';
|
|
41569
42205
|
});
|
|
41570
42206
|
}
|
|
42207
|
+
|
|
42208
|
+
// Clean all interactive components
|
|
42209
|
+
const components = tmp.querySelectorAll('[data-cb-type]');
|
|
42210
|
+
components.forEach(element => {
|
|
42211
|
+
const original = element.getAttribute('data-cb-original-content');
|
|
42212
|
+
if (original) {
|
|
42213
|
+
// Restore original HTML
|
|
42214
|
+
element.innerHTML = original;
|
|
42215
|
+
|
|
42216
|
+
// Remove runtime-added attributes
|
|
42217
|
+
element.removeAttribute('data-cb-original-content');
|
|
42218
|
+
|
|
42219
|
+
// element.removeAttribute('data-cb-logo-loop-initialized');
|
|
42220
|
+
|
|
42221
|
+
Array.from(element.attributes).forEach(attr => {
|
|
42222
|
+
const name = attr.name;
|
|
42223
|
+
// Remove any attribute that starts with "data-cb-" and ends with "-initialized"
|
|
42224
|
+
if (name.startsWith('data-cb-') && name.endsWith('-initialized')) {
|
|
42225
|
+
element.removeAttribute(name);
|
|
42226
|
+
}
|
|
42227
|
+
});
|
|
42228
|
+
|
|
42229
|
+
// Remove runtime-added classes
|
|
42230
|
+
const type = element.getAttribute('data-cb-type');
|
|
42231
|
+
element.classList.remove(`cb-${type}`);
|
|
42232
|
+
}
|
|
42233
|
+
element.removeAttribute('data-cb-loaded');
|
|
42234
|
+
});
|
|
41571
42235
|
html = '';
|
|
41572
42236
|
if (multiple) {
|
|
41573
42237
|
//ContentBox
|
|
@@ -42423,7 +43087,7 @@ const prepareSvgIcons = builder => {
|
|
|
42423
43087
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
|
42424
43088
|
<path d="M5 4h4l3 3h7a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2v-11a2 2 0 0 1 2 -2"></path>
|
|
42425
43089
|
</symbol>
|
|
42426
|
-
<symbol id="icon-folder" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
|
43090
|
+
<symbol id="icon-folder" viewBox="0 0 24 24" stroke-width="1.4" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
|
42427
43091
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
|
42428
43092
|
<path d="M11 19h-6a2 2 0 0 1 -2 -2v-11a2 2 0 0 1 2 -2h4l3 3h7a2 2 0 0 1 2 2v2.5"></path>
|
|
42429
43093
|
<path d="M18 18m-3 0a3 3 0 1 0 6 0a3 3 0 1 0 -6 0"></path>
|
|
@@ -42908,6 +43572,9 @@ class Grid {
|
|
|
42908
43572
|
cell.click(); //refresh active cell/row
|
|
42909
43573
|
}, 30);
|
|
42910
43574
|
this.builder.opts.onChange();
|
|
43575
|
+
|
|
43576
|
+
// Re-init plugins
|
|
43577
|
+
if (this.builder.win.builderRuntime) this.builder.win.builderRuntime.reinitialize(cellElement);
|
|
42911
43578
|
}
|
|
42912
43579
|
removeColumn() {
|
|
42913
43580
|
let util = this.util;
|
|
@@ -43681,6 +44348,9 @@ class Grid {
|
|
|
43681
44348
|
builder.applyBehaviorOn(builderActive);
|
|
43682
44349
|
this.rowTool.position(rowElement);
|
|
43683
44350
|
this.builder.opts.onChange();
|
|
44351
|
+
|
|
44352
|
+
// Re-init plugins
|
|
44353
|
+
if (this.builder.win.builderRuntime) this.builder.win.builderRuntime.reinitialize(rowElement);
|
|
43684
44354
|
}
|
|
43685
44355
|
}
|
|
43686
44356
|
class RowTool$1 {
|
|
@@ -77235,6 +77905,817 @@ class Spacer {
|
|
|
77235
77905
|
}
|
|
77236
77906
|
}
|
|
77237
77907
|
|
|
77908
|
+
/**
|
|
77909
|
+
* ContentBox Settings UI Generator
|
|
77910
|
+
* Automatically generates settings form based on plugin schema
|
|
77911
|
+
*
|
|
77912
|
+
* Usage in ContentBox Editor:
|
|
77913
|
+
* const generator = new SettingsUIGenerator(runtime);
|
|
77914
|
+
* const form = generator.generateForm('logo-loop', currentElement);
|
|
77915
|
+
* panel.appendChild(form);
|
|
77916
|
+
*/
|
|
77917
|
+
|
|
77918
|
+
class SettingsUIGenerator {
|
|
77919
|
+
constructor(runtime, builder) {
|
|
77920
|
+
this.runtime = runtime;
|
|
77921
|
+
this.builder = builder;
|
|
77922
|
+
}
|
|
77923
|
+
|
|
77924
|
+
/**
|
|
77925
|
+
* Generate settings form for a plugin
|
|
77926
|
+
* @param {string} pluginName - Plugin name
|
|
77927
|
+
* @param {HTMLElement} element - Current element being edited
|
|
77928
|
+
* @returns {HTMLElement} Form element
|
|
77929
|
+
*/
|
|
77930
|
+
generateForm(pluginName, element, onChange) {
|
|
77931
|
+
const plugin = this.runtime.getPlugin(pluginName);
|
|
77932
|
+
if (!plugin || !plugin.settings) {
|
|
77933
|
+
console.warn(`Plugin "${pluginName}" has no settings schema`);
|
|
77934
|
+
return this.createEmptyForm();
|
|
77935
|
+
}
|
|
77936
|
+
const form = document.createElement('div');
|
|
77937
|
+
form.className = 'cb-settings-form';
|
|
77938
|
+
|
|
77939
|
+
// Get current values from element
|
|
77940
|
+
const currentValues = this.getCurrentValues(element, plugin.settings);
|
|
77941
|
+
|
|
77942
|
+
// Check if plugin has grouped settings
|
|
77943
|
+
if (plugin.settings._groups) {
|
|
77944
|
+
this.generateGroupedForm(form, plugin.settings, currentValues);
|
|
77945
|
+
} else {
|
|
77946
|
+
this.generateFlatForm(form, plugin.settings, currentValues);
|
|
77947
|
+
}
|
|
77948
|
+
|
|
77949
|
+
// Attach listeners for auto-update
|
|
77950
|
+
if (typeof onChange === 'function') {
|
|
77951
|
+
form.addEventListener('input', () => {
|
|
77952
|
+
const values = this.getFormValues(form);
|
|
77953
|
+
onChange(values, element);
|
|
77954
|
+
});
|
|
77955
|
+
form.addEventListener('change', () => {
|
|
77956
|
+
const values = this.getFormValues(form);
|
|
77957
|
+
onChange(values, element);
|
|
77958
|
+
});
|
|
77959
|
+
}
|
|
77960
|
+
return form;
|
|
77961
|
+
}
|
|
77962
|
+
|
|
77963
|
+
/**
|
|
77964
|
+
* Generate flat form (no groups)
|
|
77965
|
+
*/
|
|
77966
|
+
generateFlatForm(container, settings, currentValues) {
|
|
77967
|
+
Object.keys(settings).forEach(key => {
|
|
77968
|
+
if (key.startsWith('_')) return; // Skip meta keys
|
|
77969
|
+
|
|
77970
|
+
const field = settings[key];
|
|
77971
|
+
const fieldElement = this.createField(key, field, currentValues[key]);
|
|
77972
|
+
container.appendChild(fieldElement);
|
|
77973
|
+
});
|
|
77974
|
+
}
|
|
77975
|
+
|
|
77976
|
+
/**
|
|
77977
|
+
* Generate grouped form
|
|
77978
|
+
*/
|
|
77979
|
+
generateGroupedForm(container, settings, currentValues) {
|
|
77980
|
+
const groups = settings._groups || [];
|
|
77981
|
+
groups.forEach(group => {
|
|
77982
|
+
const groupElement = document.createElement('div');
|
|
77983
|
+
groupElement.className = 'cb-settings-group';
|
|
77984
|
+
const groupTitle = document.createElement('h3');
|
|
77985
|
+
groupTitle.className = 'cb-settings-group-title';
|
|
77986
|
+
groupTitle.style.cssText = 'font-size: 17px;font-weight: 500;';
|
|
77987
|
+
groupTitle.textContent = group.label;
|
|
77988
|
+
groupElement.appendChild(groupTitle);
|
|
77989
|
+
group.fields.forEach(fieldKey => {
|
|
77990
|
+
const field = settings[fieldKey];
|
|
77991
|
+
if (!field) return;
|
|
77992
|
+
const fieldElement = this.createField(fieldKey, field, currentValues[fieldKey]);
|
|
77993
|
+
groupElement.appendChild(fieldElement);
|
|
77994
|
+
});
|
|
77995
|
+
container.appendChild(groupElement);
|
|
77996
|
+
});
|
|
77997
|
+
}
|
|
77998
|
+
|
|
77999
|
+
/**
|
|
78000
|
+
* Create a single form field
|
|
78001
|
+
*/
|
|
78002
|
+
createField(name, config, value) {
|
|
78003
|
+
const wrapper = document.createElement('div');
|
|
78004
|
+
wrapper.className = 'cb-settings-field';
|
|
78005
|
+
wrapper.style.marginBottom = '20px';
|
|
78006
|
+
wrapper.setAttribute('data-field', name);
|
|
78007
|
+
|
|
78008
|
+
// Add conditional display logic
|
|
78009
|
+
if (config.dependsOn) {
|
|
78010
|
+
wrapper.setAttribute('data-depends-on', JSON.stringify(config.dependsOn));
|
|
78011
|
+
wrapper.style.display = 'none'; // Hidden by default, shown by JS
|
|
78012
|
+
}
|
|
78013
|
+
|
|
78014
|
+
// Label
|
|
78015
|
+
const label = document.createElement('label');
|
|
78016
|
+
label.className = 'cb-settings-label';
|
|
78017
|
+
label.style.fontWeight = 500;
|
|
78018
|
+
label.textContent = config.label || name;
|
|
78019
|
+
// wrapper.appendChild(label);
|
|
78020
|
+
|
|
78021
|
+
// Input based on type
|
|
78022
|
+
const input = this.createInput(name, config, value);
|
|
78023
|
+
wrapper.appendChild(input);
|
|
78024
|
+
|
|
78025
|
+
// Description
|
|
78026
|
+
// if (config.description) {
|
|
78027
|
+
// const desc = document.createElement('p');
|
|
78028
|
+
// desc.className = 'cb-settings-description';
|
|
78029
|
+
// desc.textContent = config.description;
|
|
78030
|
+
// wrapper.appendChild(desc);
|
|
78031
|
+
// }
|
|
78032
|
+
|
|
78033
|
+
return wrapper;
|
|
78034
|
+
}
|
|
78035
|
+
|
|
78036
|
+
/**
|
|
78037
|
+
* Create input element based on field type
|
|
78038
|
+
*/
|
|
78039
|
+
createInput(name, config, value) {
|
|
78040
|
+
const currentValue = value !== undefined ? value : config.default;
|
|
78041
|
+
switch (config.type) {
|
|
78042
|
+
case 'number':
|
|
78043
|
+
return this.createNumberInput(name, config, currentValue);
|
|
78044
|
+
case 'boolean':
|
|
78045
|
+
return this.createCheckboxInput(name, config, currentValue);
|
|
78046
|
+
case 'select':
|
|
78047
|
+
return this.createSelectInput(name, config, currentValue);
|
|
78048
|
+
case 'radio':
|
|
78049
|
+
return this.createRadioInput(name, config, currentValue);
|
|
78050
|
+
case 'textarea':
|
|
78051
|
+
return this.createTextareaInput(name, config, currentValue);
|
|
78052
|
+
case 'color':
|
|
78053
|
+
return this.createColorInput(name, config, currentValue);
|
|
78054
|
+
case 'range':
|
|
78055
|
+
return this.createRangeInput(name, config, currentValue);
|
|
78056
|
+
case 'text':
|
|
78057
|
+
default:
|
|
78058
|
+
return this.createTextInput(name, config, currentValue);
|
|
78059
|
+
}
|
|
78060
|
+
}
|
|
78061
|
+
createTextInput(name, config, value) {
|
|
78062
|
+
const label = document.createElement('label');
|
|
78063
|
+
label.className = 'label';
|
|
78064
|
+
label.style.fontWeight = 500;
|
|
78065
|
+
const labelText = document.createElement('div');
|
|
78066
|
+
labelText.textContent = config.label || name;
|
|
78067
|
+
label.appendChild(labelText);
|
|
78068
|
+
const input = document.createElement('input');
|
|
78069
|
+
input.type = 'text';
|
|
78070
|
+
input.name = name;
|
|
78071
|
+
input.value = value || '';
|
|
78072
|
+
if (config.placeholder) input.placeholder = config.placeholder;
|
|
78073
|
+
label.appendChild(input);
|
|
78074
|
+
|
|
78075
|
+
// Add description if exists
|
|
78076
|
+
if (config.description) {
|
|
78077
|
+
const desc = document.createElement('small');
|
|
78078
|
+
desc.style.cssText = 'display: block;margin-top: 4px;';
|
|
78079
|
+
desc.textContent = config.description;
|
|
78080
|
+
label.appendChild(desc);
|
|
78081
|
+
}
|
|
78082
|
+
return label;
|
|
78083
|
+
}
|
|
78084
|
+
createNumberInput(name, config, value) {
|
|
78085
|
+
const label = document.createElement('label');
|
|
78086
|
+
label.className = 'label';
|
|
78087
|
+
label.style.fontWeight = 500;
|
|
78088
|
+
const labelText = document.createElement('div');
|
|
78089
|
+
labelText.textContent = config.label || name;
|
|
78090
|
+
if (config.unit) {
|
|
78091
|
+
labelText.textContent += ` (${config.unit})`;
|
|
78092
|
+
}
|
|
78093
|
+
label.appendChild(labelText);
|
|
78094
|
+
const input = document.createElement('input');
|
|
78095
|
+
input.type = 'number';
|
|
78096
|
+
input.style.cssText = 'width: 100px;';
|
|
78097
|
+
input.name = name;
|
|
78098
|
+
input.value = value !== undefined ? value : config.default;
|
|
78099
|
+
if (config.min !== undefined) input.min = config.min;
|
|
78100
|
+
if (config.max !== undefined) input.max = config.max;
|
|
78101
|
+
if (config.step !== undefined) input.step = config.step;
|
|
78102
|
+
label.appendChild(input);
|
|
78103
|
+
if (config.description) {
|
|
78104
|
+
const desc = document.createElement('small');
|
|
78105
|
+
desc.style.cssText = 'display: block;margin-top: 4px;';
|
|
78106
|
+
desc.textContent = config.description;
|
|
78107
|
+
label.appendChild(desc);
|
|
78108
|
+
}
|
|
78109
|
+
return label;
|
|
78110
|
+
}
|
|
78111
|
+
createCheckboxInput(name, config, value) {
|
|
78112
|
+
const label = document.createElement('label');
|
|
78113
|
+
label.className = 'label checkbox';
|
|
78114
|
+
const div = document.createElement('div');
|
|
78115
|
+
const input = document.createElement('input');
|
|
78116
|
+
input.type = 'checkbox';
|
|
78117
|
+
input.name = name;
|
|
78118
|
+
input.checked = value !== undefined ? value : config.default;
|
|
78119
|
+
const span = document.createElement('span');
|
|
78120
|
+
span.textContent = config.label || name;
|
|
78121
|
+
label.appendChild(input);
|
|
78122
|
+
label.appendChild(span);
|
|
78123
|
+
div.appendChild(label);
|
|
78124
|
+
if (config.description) {
|
|
78125
|
+
const desc = document.createElement('small');
|
|
78126
|
+
desc.style.cssText = 'display: block;margin-top: 4px;';
|
|
78127
|
+
desc.textContent = config.description;
|
|
78128
|
+
div.appendChild(desc);
|
|
78129
|
+
}
|
|
78130
|
+
return label;
|
|
78131
|
+
}
|
|
78132
|
+
createSelectInput(name, config, value) {
|
|
78133
|
+
const label = document.createElement('label');
|
|
78134
|
+
label.className = 'label';
|
|
78135
|
+
label.style.fontWeight = 500;
|
|
78136
|
+
const labelText = document.createTextNode((config.label || name) + ':');
|
|
78137
|
+
label.appendChild(labelText);
|
|
78138
|
+
const select = document.createElement('select');
|
|
78139
|
+
select.name = name;
|
|
78140
|
+
config.options.forEach(option => {
|
|
78141
|
+
const opt = document.createElement('option');
|
|
78142
|
+
opt.value = option.value;
|
|
78143
|
+
opt.textContent = option.label;
|
|
78144
|
+
if (option.value === value) opt.selected = true;
|
|
78145
|
+
select.appendChild(opt);
|
|
78146
|
+
});
|
|
78147
|
+
label.appendChild(select);
|
|
78148
|
+
if (config.description) {
|
|
78149
|
+
const desc = document.createElement('small');
|
|
78150
|
+
desc.style.cssText = 'display: block;margin-top: 4px;';
|
|
78151
|
+
desc.textContent = config.description;
|
|
78152
|
+
label.appendChild(desc);
|
|
78153
|
+
}
|
|
78154
|
+
return label;
|
|
78155
|
+
}
|
|
78156
|
+
createRadioInput(name, config, value) {
|
|
78157
|
+
const wrapper = document.createElement('div');
|
|
78158
|
+
const title = document.createElement('div');
|
|
78159
|
+
title.textContent = config.label || name;
|
|
78160
|
+
title.style.cssText = 'margin-bottom: 8px;font-weight: 590;';
|
|
78161
|
+
wrapper.appendChild(title);
|
|
78162
|
+
config.options.forEach(option => {
|
|
78163
|
+
const label = document.createElement('label');
|
|
78164
|
+
label.className = 'label checkbox';
|
|
78165
|
+
const input = document.createElement('input');
|
|
78166
|
+
input.type = 'radio';
|
|
78167
|
+
input.name = name;
|
|
78168
|
+
input.value = option.value;
|
|
78169
|
+
input.checked = option.value === value;
|
|
78170
|
+
const span = document.createElement('span');
|
|
78171
|
+
span.textContent = option.label;
|
|
78172
|
+
label.appendChild(input);
|
|
78173
|
+
label.appendChild(span);
|
|
78174
|
+
wrapper.appendChild(label);
|
|
78175
|
+
});
|
|
78176
|
+
if (config.description) {
|
|
78177
|
+
const desc = document.createElement('small');
|
|
78178
|
+
desc.style.cssText = 'display: block;margin-top: 4px;';
|
|
78179
|
+
desc.textContent = config.description;
|
|
78180
|
+
wrapper.appendChild(desc);
|
|
78181
|
+
}
|
|
78182
|
+
return wrapper;
|
|
78183
|
+
}
|
|
78184
|
+
createTextareaInput(name, config, value) {
|
|
78185
|
+
const label = document.createElement('label');
|
|
78186
|
+
label.className = 'label';
|
|
78187
|
+
label.style.fontWeight = 500;
|
|
78188
|
+
const labelText = document.createElement('div');
|
|
78189
|
+
labelText.textContent = config.label || name;
|
|
78190
|
+
label.appendChild(labelText);
|
|
78191
|
+
const textarea = document.createElement('textarea');
|
|
78192
|
+
textarea.name = name;
|
|
78193
|
+
textarea.value = value || '';
|
|
78194
|
+
textarea.rows = config.rows || 4;
|
|
78195
|
+
if (config.placeholder) textarea.placeholder = config.placeholder;
|
|
78196
|
+
label.appendChild(textarea);
|
|
78197
|
+
if (config.description) {
|
|
78198
|
+
const desc = document.createElement('small');
|
|
78199
|
+
desc.style.cssText = 'display: block;margin-top: 4px;';
|
|
78200
|
+
desc.textContent = config.description;
|
|
78201
|
+
label.appendChild(desc);
|
|
78202
|
+
}
|
|
78203
|
+
return label;
|
|
78204
|
+
}
|
|
78205
|
+
createColorInput(name, config, value) {
|
|
78206
|
+
const container = document.createElement('div');
|
|
78207
|
+
const label = document.createElement('label');
|
|
78208
|
+
label.className = 'label';
|
|
78209
|
+
label.style.fontWeight = 500;
|
|
78210
|
+
label.style.cursor = 'pointer';
|
|
78211
|
+
const labelText = document.createElement('div');
|
|
78212
|
+
labelText.textContent = config.label || name;
|
|
78213
|
+
label.appendChild(labelText);
|
|
78214
|
+
const input = document.createElement('input');
|
|
78215
|
+
input.type = 'hidden';
|
|
78216
|
+
input.name = name;
|
|
78217
|
+
input.value = value || config.default || '#000000';
|
|
78218
|
+
const group = document.createElement('div');
|
|
78219
|
+
group.className = 'group';
|
|
78220
|
+
group.style.cssText = 'width: 52px; margin:0; overflow: visible;';
|
|
78221
|
+
const colorBtn = document.createElement('button');
|
|
78222
|
+
colorBtn.type = 'button';
|
|
78223
|
+
colorBtn.title = config.label || 'Color';
|
|
78224
|
+
colorBtn.className = 'btn-color is-btn-color';
|
|
78225
|
+
colorBtn.style.backgroundColor = input.value;
|
|
78226
|
+
colorBtn.setAttribute('aria-label', `Choose color for ${config.label || name}`);
|
|
78227
|
+
const openPicker = e => {
|
|
78228
|
+
e.preventDefault();
|
|
78229
|
+
this.builder.openColorPicker(input.value, color => {
|
|
78230
|
+
input.value = color;
|
|
78231
|
+
colorBtn.style.backgroundColor = color;
|
|
78232
|
+
input.dispatchEvent(new Event('change', {
|
|
78233
|
+
bubbles: true
|
|
78234
|
+
}));
|
|
78235
|
+
}, colorBtn);
|
|
78236
|
+
};
|
|
78237
|
+
colorBtn.addEventListener('click', openPicker);
|
|
78238
|
+
// Make label click trigger the button
|
|
78239
|
+
label.addEventListener('click', e => {
|
|
78240
|
+
if (e.target === label || e.target === labelText) {
|
|
78241
|
+
colorBtn.click();
|
|
78242
|
+
}
|
|
78243
|
+
});
|
|
78244
|
+
group.appendChild(colorBtn);
|
|
78245
|
+
label.appendChild(input);
|
|
78246
|
+
if (config.description) {
|
|
78247
|
+
const desc = document.createElement('small');
|
|
78248
|
+
desc.style.cssText = 'display: block;margin-top: 4px;';
|
|
78249
|
+
desc.textContent = config.description;
|
|
78250
|
+
label.appendChild(desc);
|
|
78251
|
+
}
|
|
78252
|
+
container.appendChild(label);
|
|
78253
|
+
container.appendChild(group);
|
|
78254
|
+
return container;
|
|
78255
|
+
}
|
|
78256
|
+
createColorInput2(name, config, value) {
|
|
78257
|
+
const label = document.createElement('label');
|
|
78258
|
+
label.className = 'label';
|
|
78259
|
+
label.style.fontWeight = 500;
|
|
78260
|
+
const labelText = document.createElement('div');
|
|
78261
|
+
labelText.textContent = config.label || name;
|
|
78262
|
+
label.appendChild(labelText);
|
|
78263
|
+
const input = document.createElement('input');
|
|
78264
|
+
input.type = 'color';
|
|
78265
|
+
input.name = name;
|
|
78266
|
+
input.style.cssText = 'width:40px;height:40px';
|
|
78267
|
+
input.value = value || config.default || '#000000';
|
|
78268
|
+
label.appendChild(input);
|
|
78269
|
+
if (config.description) {
|
|
78270
|
+
const desc = document.createElement('small');
|
|
78271
|
+
desc.style.cssText = 'display: block;margin-top: 4px;';
|
|
78272
|
+
desc.textContent = config.description;
|
|
78273
|
+
label.appendChild(desc);
|
|
78274
|
+
}
|
|
78275
|
+
return label;
|
|
78276
|
+
}
|
|
78277
|
+
createRangeInput(name, config, value) {
|
|
78278
|
+
const label = document.createElement('label');
|
|
78279
|
+
label.className = 'label';
|
|
78280
|
+
label.style.fontWeight = 500;
|
|
78281
|
+
const currentVal = value !== undefined ? value : config.default;
|
|
78282
|
+
const labelText = document.createElement('div');
|
|
78283
|
+
labelText.textContent = (config.label || name) + ': ' + currentVal + (config.unit || '');
|
|
78284
|
+
label.appendChild(labelText);
|
|
78285
|
+
const input = document.createElement('input');
|
|
78286
|
+
input.type = 'range';
|
|
78287
|
+
input.className = 'is-rangeslider';
|
|
78288
|
+
input.name = name;
|
|
78289
|
+
input.value = currentVal;
|
|
78290
|
+
if (config.min !== undefined) input.min = config.min;
|
|
78291
|
+
if (config.max !== undefined) input.max = config.max;
|
|
78292
|
+
if (config.step !== undefined) input.step = config.step;
|
|
78293
|
+
input.addEventListener('input', () => {
|
|
78294
|
+
labelText.textContent = (config.label || name) + ': ' + input.value + (config.unit || '');
|
|
78295
|
+
});
|
|
78296
|
+
label.appendChild(input);
|
|
78297
|
+
if (config.description) {
|
|
78298
|
+
const desc = document.createElement('small');
|
|
78299
|
+
desc.style.cssText = 'display: block;margin-top: 4px;';
|
|
78300
|
+
desc.textContent = config.description;
|
|
78301
|
+
label.appendChild(desc);
|
|
78302
|
+
}
|
|
78303
|
+
return label;
|
|
78304
|
+
}
|
|
78305
|
+
|
|
78306
|
+
/**
|
|
78307
|
+
* Get current values from element attributes
|
|
78308
|
+
*/
|
|
78309
|
+
getCurrentValues(element, settings) {
|
|
78310
|
+
const values = {};
|
|
78311
|
+
Object.keys(settings).forEach(key => {
|
|
78312
|
+
if (key.startsWith('_')) return;
|
|
78313
|
+
const attrName = 'data-cb-' + key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
78314
|
+
const attrValue = element.getAttribute(attrName);
|
|
78315
|
+
if (attrValue !== null) {
|
|
78316
|
+
values[key] = this.parseValue(attrValue, settings[key].type);
|
|
78317
|
+
}
|
|
78318
|
+
});
|
|
78319
|
+
return values;
|
|
78320
|
+
}
|
|
78321
|
+
|
|
78322
|
+
/**
|
|
78323
|
+
* Parse attribute value based on type
|
|
78324
|
+
*/
|
|
78325
|
+
parseValue(value, type) {
|
|
78326
|
+
switch (type) {
|
|
78327
|
+
case 'number':
|
|
78328
|
+
return parseFloat(value);
|
|
78329
|
+
case 'boolean':
|
|
78330
|
+
return value === 'true';
|
|
78331
|
+
default:
|
|
78332
|
+
return value;
|
|
78333
|
+
}
|
|
78334
|
+
}
|
|
78335
|
+
|
|
78336
|
+
/**
|
|
78337
|
+
* Get form values
|
|
78338
|
+
*/
|
|
78339
|
+
getFormValues(form) {
|
|
78340
|
+
const values = {};
|
|
78341
|
+
const inputs = form.querySelectorAll('input, select, textarea');
|
|
78342
|
+
inputs.forEach(input => {
|
|
78343
|
+
const name = input.name;
|
|
78344
|
+
if (!name) return;
|
|
78345
|
+
if (input.type === 'checkbox') {
|
|
78346
|
+
values[name] = input.checked;
|
|
78347
|
+
} else if (input.type === 'radio') {
|
|
78348
|
+
if (input.checked) {
|
|
78349
|
+
values[name] = input.value;
|
|
78350
|
+
}
|
|
78351
|
+
} else {
|
|
78352
|
+
values[name] = input.value;
|
|
78353
|
+
}
|
|
78354
|
+
});
|
|
78355
|
+
return values;
|
|
78356
|
+
}
|
|
78357
|
+
|
|
78358
|
+
/**
|
|
78359
|
+
* Apply form values to element
|
|
78360
|
+
*/
|
|
78361
|
+
applyValues(element, values) {
|
|
78362
|
+
Object.keys(values).forEach(key => {
|
|
78363
|
+
const attrName = 'data-cb-' + key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
78364
|
+
element.setAttribute(attrName, values[key]);
|
|
78365
|
+
});
|
|
78366
|
+
}
|
|
78367
|
+
createEmptyForm() {
|
|
78368
|
+
const form = document.createElement('div');
|
|
78369
|
+
form.className = 'cb-settings-form';
|
|
78370
|
+
form.innerHTML = '<p>This plugin has no configurable settings.</p>';
|
|
78371
|
+
return form;
|
|
78372
|
+
}
|
|
78373
|
+
}
|
|
78374
|
+
|
|
78375
|
+
class Plugin {
|
|
78376
|
+
constructor(builder) {
|
|
78377
|
+
this.builder = builder;
|
|
78378
|
+
}
|
|
78379
|
+
renderTool() {
|
|
78380
|
+
const builderStuff = this.builder.builderStuff;
|
|
78381
|
+
const contentStuff = this.builder.contentStuff;
|
|
78382
|
+
const util = this.builder.util;
|
|
78383
|
+
const dom = this.builder.dom;
|
|
78384
|
+
let pluginTool = builderStuff.querySelector('.is-plugin-tool');
|
|
78385
|
+
if (!pluginTool) {
|
|
78386
|
+
let html = `
|
|
78387
|
+
<div class="is-tool is-plugin-tool">
|
|
78388
|
+
<button title="${util.out('Settings')}" data-title="${util.out('Settings')}" style="width:40px;height:40px;background:none;"><svg class="is-icon-flex"><use xlink:href="#icon-cog"></use></svg></button>
|
|
78389
|
+
</div>
|
|
78390
|
+
`;
|
|
78391
|
+
dom.appendHtml(builderStuff, html);
|
|
78392
|
+
if (!this.builder.iframe) {
|
|
78393
|
+
pluginTool = builderStuff.querySelector('.is-plugin-tool');
|
|
78394
|
+
} else {
|
|
78395
|
+
pluginTool = contentStuff.querySelector('.is-plugin-tool');
|
|
78396
|
+
}
|
|
78397
|
+
this.pluginTool = pluginTool;
|
|
78398
|
+
let btn = pluginTool.querySelector('button');
|
|
78399
|
+
dom.addEventListener(btn, 'click', () => {
|
|
78400
|
+
// old 10317
|
|
78401
|
+
|
|
78402
|
+
if (this.pluginModal && this.pluginModal.classList.contains('active')) {
|
|
78403
|
+
this.hidePluginEditor();
|
|
78404
|
+
} else {
|
|
78405
|
+
this.renderPanel();
|
|
78406
|
+
this.showPluginEditor();
|
|
78407
|
+
}
|
|
78408
|
+
btn.setAttribute('data-focus', true);
|
|
78409
|
+
});
|
|
78410
|
+
}
|
|
78411
|
+
}
|
|
78412
|
+
renderPanel() {
|
|
78413
|
+
const builderStuff = this.builder.builderStuff;
|
|
78414
|
+
const util = this.builder.util;
|
|
78415
|
+
const dom = this.builder.dom;
|
|
78416
|
+
let pluginModal = builderStuff.querySelector('.is-modal.pluginsettings');
|
|
78417
|
+
if (!pluginModal) {
|
|
78418
|
+
let html = `
|
|
78419
|
+
<div class="is-modal is-modal-content pluginsettings" tabindex="-1" role="dialog" aria-modal="true" aria-hidden="true">
|
|
78420
|
+
<div class="is-modal-bar is-draggable">
|
|
78421
|
+
<span>${util.out('Plugin Settings')}</span>
|
|
78422
|
+
<button class="is-modal-close" tabindex="-1" title="${util.out('Close')}">✕</button>
|
|
78423
|
+
</div>
|
|
78424
|
+
<div class="is-modal-controls" style="box-sizing:border-box;padding:30px;overflow-y: auto;height: calc(100% - 30px);">
|
|
78425
|
+
<div class="is-tabs" data-group="table">
|
|
78426
|
+
<a title="${util.out('Style')}" id="tabTableGeneral" href="" data-content="divTableGeneral" class="active">${util.out('Style')}</a>
|
|
78427
|
+
<a title="${util.out('Layout')}" id="tabTableLayout" href="" data-content="divTableLayout">${util.out('Layout')}</a>
|
|
78428
|
+
</div>
|
|
78429
|
+
<div id="divTableGeneral" class="is-tab-content active" tabindex="-1" data-group="table" style="display:block">
|
|
78430
|
+
|
|
78431
|
+
<div style="display:flex;padding-bottom:12px">
|
|
78432
|
+
<div style="padding-right:15px">
|
|
78433
|
+
<div>${util.out('Background')}:</div>
|
|
78434
|
+
<div>
|
|
78435
|
+
<button title="${util.out('Background Color')}" class="input-table-bgcolor is-btn-color"></button>
|
|
78436
|
+
</div>
|
|
78437
|
+
</div>
|
|
78438
|
+
<div>
|
|
78439
|
+
<div>${util.out('Text Color')}:</div>
|
|
78440
|
+
<div>
|
|
78441
|
+
<button title="${util.out('Text Color')}" class="input-table-textcolor is-btn-color"></button>
|
|
78442
|
+
</div>
|
|
78443
|
+
</div>
|
|
78444
|
+
</div>
|
|
78445
|
+
|
|
78446
|
+
<div style="padding-bottom:12px;">
|
|
78447
|
+
<div>${util.out('Border Thickness')}:</div>
|
|
78448
|
+
<div>
|
|
78449
|
+
<select id="selCellBorderWidth" style="width:120px;"><option value="0">No Border</option><option value="1">1</option><option value="2">2</option><option value="3">3</option></select>
|
|
78450
|
+
</div>
|
|
78451
|
+
</div>
|
|
78452
|
+
|
|
78453
|
+
<div style="padding-bottom:12px;">
|
|
78454
|
+
<div>${util.out('Border Color')}:</div>
|
|
78455
|
+
<div>
|
|
78456
|
+
<button title="${util.out('Border Color')}" class="input-table-bordercolor is-btn-color"></button>
|
|
78457
|
+
</div>
|
|
78458
|
+
</div>
|
|
78459
|
+
|
|
78460
|
+
<div style="padding-bottom:12px;">
|
|
78461
|
+
<div>${util.out('Apply To')}:</div>
|
|
78462
|
+
<div>
|
|
78463
|
+
<select id="selTableApplyTo" style="width:120px;">
|
|
78464
|
+
<option value="table">${util.out('Table')}</option>
|
|
78465
|
+
<option value="currentrow">${util.out('Current Row')}</option>
|
|
78466
|
+
<option value="currentcol">${util.out('Current Column')}</option>
|
|
78467
|
+
<option value="evenrows">${util.out('Even Rows')}</option>
|
|
78468
|
+
<option value="oddrows">${util.out('Odd Rows')}</option>
|
|
78469
|
+
<option value="currentcell">${util.out('Current Cell')}</option>
|
|
78470
|
+
</select>
|
|
78471
|
+
</div>
|
|
78472
|
+
</div>
|
|
78473
|
+
|
|
78474
|
+
</div>
|
|
78475
|
+
|
|
78476
|
+
<div id="divTableLayout" class="is-tab-content" tabindex="-1" data-group="table">
|
|
78477
|
+
|
|
78478
|
+
<div style="padding-bottom:12px;">
|
|
78479
|
+
<div>${util.out('Insert Row')}:</div>
|
|
78480
|
+
<div style="display:flex">
|
|
78481
|
+
<button class="classic" title="${util.out('Above')}" data-table-cmd="rowabove" title="${util.out('Above')}" style="margin-right:15px"> ${util.out('Above')} </button>
|
|
78482
|
+
<button class="classic" title="${util.out('Below')}" data-table-cmd="rowbelow" title="${util.out('Below')}" style=""> ${util.out('Below')} </button>
|
|
78483
|
+
</div>
|
|
78484
|
+
</div>
|
|
78485
|
+
|
|
78486
|
+
<div style="padding-bottom:15px;">
|
|
78487
|
+
<div>${util.out('Insert Column')}:</div>
|
|
78488
|
+
<div style="display:flex">
|
|
78489
|
+
<button class="classic" title="${util.out('Left')}" data-table-cmd="columnleft" title="${util.out('Left')}" style="margin-right:15px"> ${util.out('Left')} </button>
|
|
78490
|
+
<button class="classic" title="${util.out('Right')}" data-table-cmd="columnright" title="${util.out('Right')}" style=""> ${util.out('Right')} </button>
|
|
78491
|
+
</div>
|
|
78492
|
+
</div>
|
|
78493
|
+
|
|
78494
|
+
<div style="padding-bottom:15px;">
|
|
78495
|
+
<button class="classic" title="${util.out('Delete Row')}" data-table-cmd="delrow" title="Delete Row" style=""> ${util.out('Delete Row')} </button>
|
|
78496
|
+
</div>
|
|
78497
|
+
|
|
78498
|
+
<div style="padding-bottom:15px;">
|
|
78499
|
+
<button class="classic" title="${util.out('Delete Column')}" data-table-cmd="delcolumn" title="Delete Column" style=""> ${util.out('Delete Column')} </button>
|
|
78500
|
+
</div>
|
|
78501
|
+
|
|
78502
|
+
<div>
|
|
78503
|
+
<button class="classic" title="${util.out('Merge Cell')}" data-table-cmd="mergecell" style="">${util.out('Merge Cell')}</button>
|
|
78504
|
+
</div>
|
|
78505
|
+
</div>
|
|
78506
|
+
</div>
|
|
78507
|
+
</div>
|
|
78508
|
+
`;
|
|
78509
|
+
dom.appendHtml(builderStuff, html);
|
|
78510
|
+
pluginModal = builderStuff.querySelector('.is-modal.pluginsettings');
|
|
78511
|
+
this.pluginModal = pluginModal;
|
|
78512
|
+
let btnOk = pluginModal.querySelector('.input-ok');
|
|
78513
|
+
dom.addEventListener(btnOk, 'click', () => {
|
|
78514
|
+
this.builder.uo.saveForUndo();
|
|
78515
|
+
this.builder.opts.onChange();
|
|
78516
|
+
util.hideModal(pluginModal);
|
|
78517
|
+
});
|
|
78518
|
+
let btnCancel = pluginModal.querySelector('.input-cancel');
|
|
78519
|
+
dom.addEventListener(btnCancel, 'click', () => {
|
|
78520
|
+
util.hideModal(pluginModal);
|
|
78521
|
+
});
|
|
78522
|
+
new Tabs({
|
|
78523
|
+
element: pluginModal
|
|
78524
|
+
});
|
|
78525
|
+
new Draggable$2({
|
|
78526
|
+
selector: '.is-modal.pluginsettings .is-draggable'
|
|
78527
|
+
});
|
|
78528
|
+
}
|
|
78529
|
+
}
|
|
78530
|
+
showPluginEditor() {
|
|
78531
|
+
const pluginModal = this.pluginModal;
|
|
78532
|
+
this.builder.util.showModal(pluginModal);
|
|
78533
|
+
this.realtime();
|
|
78534
|
+
const handlePluginClick = e => {
|
|
78535
|
+
const clrPicker = document.querySelector('.pop-picker.active') || document.querySelector('.pickgradientcolor.active');
|
|
78536
|
+
let elm = e.target;
|
|
78537
|
+
let elmRemoved = !document.contains(elm); // e.g. when deleting a card in card list plugin
|
|
78538
|
+
|
|
78539
|
+
if (this.builder.doc.activeElement) {
|
|
78540
|
+
if (this.builder.doc.activeElement.closest('.is-modal')) {
|
|
78541
|
+
// prevent modal close when mouseup outside the modal
|
|
78542
|
+
return;
|
|
78543
|
+
}
|
|
78544
|
+
}
|
|
78545
|
+
if (!elm) return;
|
|
78546
|
+
if (!elm.closest('.is-plugin-tool') && !elm.closest('.is-sidebar') && !elm.closest('.is-modal') && !elm.closest('.keep-selection') && !elm.closest('[data-cb-type]') && !clrPicker && !elmRemoved) {
|
|
78547
|
+
// click outside
|
|
78548
|
+
|
|
78549
|
+
// hide
|
|
78550
|
+
this.hidePluginEditor();
|
|
78551
|
+
// console.log('HIDE');
|
|
78552
|
+
|
|
78553
|
+
document.removeEventListener('click', handlePluginClick);
|
|
78554
|
+
if (this.builder.iframeDocument) {
|
|
78555
|
+
this.builder.doc.removeEventListener('click', handlePluginClick);
|
|
78556
|
+
}
|
|
78557
|
+
this.builder.handlePluginClick_ = false;
|
|
78558
|
+
}
|
|
78559
|
+
if (elm.closest('[data-cb-type]')) {
|
|
78560
|
+
this.realtime();
|
|
78561
|
+
}
|
|
78562
|
+
};
|
|
78563
|
+
if (!this.builder.handlePluginClick_) {
|
|
78564
|
+
document.addEventListener('click', handlePluginClick);
|
|
78565
|
+
if (this.builder.iframeDocument) {
|
|
78566
|
+
this.builder.doc.addEventListener('click', handlePluginClick);
|
|
78567
|
+
}
|
|
78568
|
+
this.builder.handlePluginClick_ = true;
|
|
78569
|
+
}
|
|
78570
|
+
}
|
|
78571
|
+
hidePluginEditor() {
|
|
78572
|
+
const pluginModal = this.pluginModal;
|
|
78573
|
+
if (pluginModal) this.builder.util.hideModal(pluginModal);
|
|
78574
|
+
}
|
|
78575
|
+
realtime() {
|
|
78576
|
+
const util = this.builder.util;
|
|
78577
|
+
const pluginModal = this.pluginModal;
|
|
78578
|
+
let currentElement = this.builder.activePlugin;
|
|
78579
|
+
const runtime = this.builder.win.builderRuntime;
|
|
78580
|
+
if (currentElement && runtime) {
|
|
78581
|
+
const pluginName = currentElement.getAttribute('data-cb-type');
|
|
78582
|
+
const plugin = runtime.getPlugin(pluginName);
|
|
78583
|
+
let titleHtml = util.out(plugin.displayName || plugin.name);
|
|
78584
|
+
const modalBar = pluginModal.querySelector('.is-modal-bar > span');
|
|
78585
|
+
modalBar.innerHTML = titleHtml;
|
|
78586
|
+
const container = pluginModal.querySelector('.is-modal-controls');
|
|
78587
|
+
|
|
78588
|
+
// Clear panel
|
|
78589
|
+
container.innerHTML = '';
|
|
78590
|
+
const div = document.createElement('div');
|
|
78591
|
+
div.classList.add('submain');
|
|
78592
|
+
container.appendChild(div);
|
|
78593
|
+
|
|
78594
|
+
// Store references
|
|
78595
|
+
this.currentElement = currentElement;
|
|
78596
|
+
this.generator = new SettingsUIGenerator(runtime, this.builder);
|
|
78597
|
+
|
|
78598
|
+
// Check if plugin has custom content editor
|
|
78599
|
+
const hasContentEditor = plugin && plugin.editor && plugin.editor.openContentEditor;
|
|
78600
|
+
if (hasContentEditor) {
|
|
78601
|
+
// Get original content for the editor to work with
|
|
78602
|
+
const originalContent = currentElement.getAttribute('data-cb-original-content');
|
|
78603
|
+
|
|
78604
|
+
// Create a temporary element for editing (so editor can manipulate it)
|
|
78605
|
+
let editableClone = document.querySelector('.editable-clone');
|
|
78606
|
+
if (editableClone) editableClone.remove();
|
|
78607
|
+
// editableClone = document.createElement('div');
|
|
78608
|
+
editableClone = currentElement.cloneNode(false);
|
|
78609
|
+
editableClone.innerHTML = originalContent;
|
|
78610
|
+
editableClone.className = 'editable-clone';
|
|
78611
|
+
editableClone.style.display = 'none'; // Hidden, just for editing
|
|
78612
|
+
document.body.appendChild(editableClone);
|
|
78613
|
+
const originalElement = editableClone.cloneNode(false);
|
|
78614
|
+
|
|
78615
|
+
// Let plugin handle everything - pass the editable clone
|
|
78616
|
+
const editorUI = plugin.editor.openContentEditor(editableClone, this.builder, async () => {
|
|
78617
|
+
this.builder.uo.saveForUndo();
|
|
78618
|
+
|
|
78619
|
+
// Store edited content from clone
|
|
78620
|
+
currentElement.setAttribute('data-cb-original-content', editableClone.innerHTML);
|
|
78621
|
+
|
|
78622
|
+
// Also performs similar value updates like the applyChanges()
|
|
78623
|
+
// currentElement.setAttribute('data-cb-content', editableClone.getAttribute('data-cb-content'));
|
|
78624
|
+
const getChangedDataAttributes = (el1, el2) => {
|
|
78625
|
+
const changed = [];
|
|
78626
|
+
for (const key of Object.keys(el1.dataset)) {
|
|
78627
|
+
if (key in el2.dataset && el1.dataset[key] !== el2.dataset[key]) {
|
|
78628
|
+
changed.push(key);
|
|
78629
|
+
}
|
|
78630
|
+
}
|
|
78631
|
+
return changed;
|
|
78632
|
+
};
|
|
78633
|
+
const changedAttributes = getChangedDataAttributes(originalElement, editableClone);
|
|
78634
|
+
// let hasChange = changedAttributes.length > 0;
|
|
78635
|
+
for (const attrName of changedAttributes) {
|
|
78636
|
+
// console.log(attrName);
|
|
78637
|
+
currentElement.dataset[attrName] = editableClone.dataset[attrName];
|
|
78638
|
+
// update form (not working)
|
|
78639
|
+
// const convertCbKey = (str) => {
|
|
78640
|
+
// return str.replace(/^cb/, '').replace(/^./, c => c.toLowerCase());
|
|
78641
|
+
// }
|
|
78642
|
+
// const name = convertCbKey(attrName);
|
|
78643
|
+
// this.currentForm.querySelector(`[name="${name}"]`).value = editableClone.dataset[attrName];
|
|
78644
|
+
}
|
|
78645
|
+
// console.log(hasChange)
|
|
78646
|
+
// -------
|
|
78647
|
+
|
|
78648
|
+
currentElement.innerHTML = editableClone.innerHTML; // Update current content
|
|
78649
|
+
|
|
78650
|
+
// Remove temporary clone
|
|
78651
|
+
editableClone.remove();
|
|
78652
|
+
|
|
78653
|
+
// update form (working)
|
|
78654
|
+
// currentElement.click();
|
|
78655
|
+
// return;
|
|
78656
|
+
|
|
78657
|
+
// Reinitialize plugin with new content
|
|
78658
|
+
await runtime.reinitialize(currentElement.parentElement);
|
|
78659
|
+
this.builder.onChange();
|
|
78660
|
+
});
|
|
78661
|
+
div.appendChild(editorUI);
|
|
78662
|
+
}
|
|
78663
|
+
|
|
78664
|
+
// Generate form
|
|
78665
|
+
this.currentForm = this.generator.generateForm(pluginName, currentElement, () => {
|
|
78666
|
+
this.builder.uo.saveForUndo();
|
|
78667
|
+
this.applyChanges(runtime);
|
|
78668
|
+
});
|
|
78669
|
+
this.currentForm.style.marginTop = '20px';
|
|
78670
|
+
div.appendChild(this.currentForm);
|
|
78671
|
+
}
|
|
78672
|
+
}
|
|
78673
|
+
async applyChanges(runtime) {
|
|
78674
|
+
if (!this.currentElement || !this.currentForm) return;
|
|
78675
|
+
|
|
78676
|
+
// 1. Get form values
|
|
78677
|
+
const values = this.generator.getFormValues(this.currentForm);
|
|
78678
|
+
|
|
78679
|
+
// 2. Apply to element attributes
|
|
78680
|
+
this.generator.applyValues(this.currentElement, values);
|
|
78681
|
+
|
|
78682
|
+
// 3. Reinitialize component
|
|
78683
|
+
const container = this.currentElement.parentElement || this.currentElement.closest('.is-wrapper');
|
|
78684
|
+
await runtime.reinitialize(container);
|
|
78685
|
+
|
|
78686
|
+
//Trigger Change event
|
|
78687
|
+
this.builder.opts.onChange();
|
|
78688
|
+
|
|
78689
|
+
// console.log('Settings applied and component reinitialized');
|
|
78690
|
+
}
|
|
78691
|
+
|
|
78692
|
+
click(e) {
|
|
78693
|
+
const plugin = e.target.closest('[data-cb-type]');
|
|
78694
|
+
if (plugin) {
|
|
78695
|
+
this.builder.activePlugin = plugin;
|
|
78696
|
+
if (!this.builder.isContentBox) {
|
|
78697
|
+
this.renderTool();
|
|
78698
|
+
this.pluginTool.style.display = 'flex';
|
|
78699
|
+
let _toolwidth = this.pluginTool.offsetWidth; //to get value, element must not hidden (display:none). So set display:flex before this.
|
|
78700
|
+
|
|
78701
|
+
let w = plugin.offsetWidth * this.builder.opts.zoom;
|
|
78702
|
+
let top = plugin.getBoundingClientRect().top + this.builder.win.pageYOffset;
|
|
78703
|
+
let left = plugin.getBoundingClientRect().left - 2;
|
|
78704
|
+
left = left + (w - _toolwidth);
|
|
78705
|
+
|
|
78706
|
+
//Adjust left in case an element is outside the screen
|
|
78707
|
+
const _screenwidth = window.innerWidth;
|
|
78708
|
+
if (_toolwidth + left > _screenwidth) left = plugin.getBoundingClientRect().left;
|
|
78709
|
+
this.pluginTool.style.top = top + 'px';
|
|
78710
|
+
this.pluginTool.style.left = left + 'px';
|
|
78711
|
+
}
|
|
78712
|
+
} else {
|
|
78713
|
+
this.builder.activePlugin = null;
|
|
78714
|
+
if (this.pluginTool) this.pluginTool.style.display = '';
|
|
78715
|
+
}
|
|
78716
|
+
}
|
|
78717
|
+
}
|
|
78718
|
+
|
|
77238
78719
|
class Module {
|
|
77239
78720
|
constructor(builder) {
|
|
77240
78721
|
this.builder = builder;
|
|
@@ -79076,6 +80557,7 @@ class Element$1 {
|
|
|
79076
80557
|
this.button = new Button(builder);
|
|
79077
80558
|
this.image = new Image$1(builder);
|
|
79078
80559
|
this.spacer = new Spacer(builder);
|
|
80560
|
+
this.plugin = new Plugin(builder);
|
|
79079
80561
|
this.module = new Module(builder);
|
|
79080
80562
|
this.code = new Code(builder);
|
|
79081
80563
|
this.iframe = new Iframe(builder);
|
|
@@ -79114,6 +80596,10 @@ class Element$1 {
|
|
|
79114
80596
|
|
|
79115
80597
|
// Set contentEditable FALSE on special elements
|
|
79116
80598
|
|
|
80599
|
+
const plugins = col.querySelectorAll('[data-cb-type]'); // plugins
|
|
80600
|
+
Array.prototype.forEach.call(plugins, plugin => {
|
|
80601
|
+
plugin.contentEditable = false;
|
|
80602
|
+
});
|
|
79117
80603
|
let sociallinks = col.querySelectorAll('.is-social');
|
|
79118
80604
|
Array.prototype.forEach.call(sociallinks, sociallink => {
|
|
79119
80605
|
sociallink.contentEditable = false;
|
|
@@ -79205,6 +80691,9 @@ class Element$1 {
|
|
|
79205
80691
|
subblock = true;
|
|
79206
80692
|
}
|
|
79207
80693
|
}
|
|
80694
|
+
if (elm.closest('[data-cb-type]')) {
|
|
80695
|
+
customcode = true;
|
|
80696
|
+
}
|
|
79208
80697
|
if (!customcode && !noedit && !_protected || subblock) {
|
|
79209
80698
|
//previously this is commented: && !noedit && !_protected
|
|
79210
80699
|
|
|
@@ -79275,6 +80764,9 @@ class Element$1 {
|
|
|
79275
80764
|
|
|
79276
80765
|
// Module
|
|
79277
80766
|
this.module.click(col, e);
|
|
80767
|
+
|
|
80768
|
+
// Plugin
|
|
80769
|
+
this.plugin.click(e);
|
|
79278
80770
|
}
|
|
79279
80771
|
}
|
|
79280
80772
|
|
|
@@ -88430,6 +89922,12 @@ class ElementTool {
|
|
|
88430
89922
|
const btnMore = elementTool.querySelector('.elm-more');
|
|
88431
89923
|
dom.addEventListener(btnMore, 'click', () => {
|
|
88432
89924
|
const viewportHeight = window.innerHeight;
|
|
89925
|
+
const elmSettings = elementMore.querySelector('.elm-settings');
|
|
89926
|
+
if (this.builder.activeElement.hasAttribute('data-cb-type')) {
|
|
89927
|
+
elmSettings.style.display = 'none';
|
|
89928
|
+
} else {
|
|
89929
|
+
elmSettings.style.display = '';
|
|
89930
|
+
}
|
|
88433
89931
|
|
|
88434
89932
|
/*
|
|
88435
89933
|
let top, left;
|
|
@@ -88562,6 +90060,9 @@ class ElementTool {
|
|
|
88562
90060
|
|
|
88563
90061
|
//Trigger Change event
|
|
88564
90062
|
this.builder.opts.onChange();
|
|
90063
|
+
|
|
90064
|
+
// Re-init plugins
|
|
90065
|
+
if (this.builder.win.builderRuntime) this.builder.win.builderRuntime.reinitialize(newelm.parentNode);
|
|
88565
90066
|
}, 100); //Timeout needed by Safari
|
|
88566
90067
|
});
|
|
88567
90068
|
|
|
@@ -88695,7 +90196,10 @@ class ElementTool {
|
|
|
88695
90196
|
if (dom.parentsHasClass(elm, 'is-subblock')) {
|
|
88696
90197
|
subblock = true;
|
|
88697
90198
|
}
|
|
88698
|
-
|
|
90199
|
+
const plugin = elm.closest('[data-cb-type]');
|
|
90200
|
+
if ((customcode || noedit || _protected) && !subblock) ; else if (plugin) {
|
|
90201
|
+
activeElement = plugin;
|
|
90202
|
+
} else {
|
|
88699
90203
|
const tagName = elm.tagName.toLowerCase();
|
|
88700
90204
|
// LATER: label, code, figcaption ?
|
|
88701
90205
|
if (!elm.classList.contains('cell-active') && (tagName === 'h1' || tagName === 'h2' || tagName === 'h3' || tagName === 'h4' || tagName === 'h5' || tagName === 'h6' || tagName === 'p' || tagName === 'div' || tagName === 'pre' || tagName === 'blockquote' || tagName === 'li' || tagName === 'img' || tagName === 'iframe')) {
|
|
@@ -88847,6 +90351,8 @@ class ElementTool {
|
|
|
88847
90351
|
let elm = this.builder.activeElement;
|
|
88848
90352
|
if (!elm) return;
|
|
88849
90353
|
if (elm.closest('.is-dock')) return;
|
|
90354
|
+
// if(elm.closest('[data-cb-type]')) return;
|
|
90355
|
+
|
|
88850
90356
|
let top, left;
|
|
88851
90357
|
if (!this.builder.iframe) {
|
|
88852
90358
|
top = elm.getBoundingClientRect().top + window.pageYOffset;
|
|
@@ -106495,6 +108001,9 @@ class ContentStuff$1 {
|
|
|
106495
108001
|
<div class="is-tool is-iframe-tool">
|
|
106496
108002
|
<button title="${util.out('Settings')}" data-title="${util.out('Settings')}" style="width:40px;height:40px;background:none;"><svg class="is-icon-flex"><use xlink:href="#icon-cog"></use></svg></button>
|
|
106497
108003
|
</div>
|
|
108004
|
+
<div class="is-tool is-plugin-tool">
|
|
108005
|
+
<button title="${util.out('Settings')}" data-title="${util.out('Settings')}" style="width:40px;height:40px;background:none;"><svg class="is-icon-flex"><use xlink:href="#icon-cog"></use></svg></button>
|
|
108006
|
+
</div>
|
|
106498
108007
|
<div class="is-tool is-module-tool">
|
|
106499
108008
|
<button class="btn-module-refresh" title="${util.out('Refresh')}" data-title="${util.out('Refresh')}" style="width:40px;height:40px;"><svg class="is-icon-flex"><use xlink:href="#icon-reload"></use></svg></button>
|
|
106500
108009
|
<button class="btn-module-settings" title="${util.out('Settings')}" data-title="${util.out('Settings')}" style="width:40px;height:40px;"><svg class="is-icon-flex"><use xlink:href="#icon-cog"></use></svg></button>
|
|
@@ -106900,20 +108409,24 @@ class ContentStuff$1 {
|
|
|
106900
108409
|
/*
|
|
106901
108410
|
.is-tool.is-video-tool,
|
|
106902
108411
|
.is-tool.is-audio-tool,
|
|
108412
|
+
.is-tool.is-plugin-tool,
|
|
106903
108413
|
.is-tool.is-iframe-tool {
|
|
106904
108414
|
background: rgba(0, 0, 0, 0.15);
|
|
106905
108415
|
border: transparent 1px solid;
|
|
106906
108416
|
}
|
|
106907
108417
|
.is-tool.is-video-tool > div,
|
|
106908
108418
|
.is-tool.is-audio-tool > div,
|
|
108419
|
+
.is-tool.is-plugin-tool > div,
|
|
106909
108420
|
.is-tool.is-iframe-tool > div,
|
|
106910
108421
|
.is-tool.is-video-tool > button,
|
|
106911
108422
|
.is-tool.is-audio-tool > button,
|
|
108423
|
+
.is-tool.is-plugin-tool > button,
|
|
106912
108424
|
.is-tool.is-iframe-tool > button {
|
|
106913
108425
|
background: transparent;
|
|
106914
108426
|
}
|
|
106915
108427
|
.is-tool.is-video-tool svg,
|
|
106916
108428
|
.is-tool.is-audio-tool svg,
|
|
108429
|
+
.is-tool.is-plugin-tool svg,
|
|
106917
108430
|
.is-tool.is-iframe-tool svg {
|
|
106918
108431
|
fill: #fff;
|
|
106919
108432
|
}
|
|
@@ -106923,6 +108436,7 @@ class ContentStuff$1 {
|
|
|
106923
108436
|
.is-tool.is-table-tool,
|
|
106924
108437
|
.is-tool.is-code-tool,
|
|
106925
108438
|
.is-tool.is-module-tool,
|
|
108439
|
+
.is-tool.is-plugin-tool,
|
|
106926
108440
|
.is-tool#divLinkTool,
|
|
106927
108441
|
.is-tool#divButtonTool,
|
|
106928
108442
|
.is-tool.is-svg-tool {
|
|
@@ -106936,6 +108450,7 @@ class ContentStuff$1 {
|
|
|
106936
108450
|
.is-tool.is-table-tool > button,
|
|
106937
108451
|
.is-tool.is-code-tool > button,
|
|
106938
108452
|
.is-tool.is-module-tool > button,
|
|
108453
|
+
.is-tool.is-plugin-tool > button,
|
|
106939
108454
|
.is-tool#divLinkTool > button,
|
|
106940
108455
|
.is-tool#divButtonTool > button,
|
|
106941
108456
|
.is-tool.is-svg-tool > button {
|
|
@@ -106948,6 +108463,7 @@ class ContentStuff$1 {
|
|
|
106948
108463
|
.is-tool.is-table-tool > button svg,
|
|
106949
108464
|
.is-tool.is-code-tool > button svg,
|
|
106950
108465
|
.is-tool.is-module-tool > button svg,
|
|
108466
|
+
.is-tool.is-plugin-tool > button svg,
|
|
106951
108467
|
.is-tool#divLinkTool > button svg,
|
|
106952
108468
|
.is-tool#divButtonTool > button svg,
|
|
106953
108469
|
.is-tool.is-svg-tool > button svg {
|
|
@@ -106962,6 +108478,7 @@ class ContentStuff$1 {
|
|
|
106962
108478
|
|
|
106963
108479
|
.is-tool.is-video-tool,
|
|
106964
108480
|
.is-tool.is-audio-tool,
|
|
108481
|
+
.is-tool.is-plugin-tool,
|
|
106965
108482
|
.is-tool.is-iframe-tool {
|
|
106966
108483
|
background: rgba(255, 255, 255, 0.97) !important;
|
|
106967
108484
|
border: transparent 1px solid;
|
|
@@ -106973,6 +108490,7 @@ class ContentStuff$1 {
|
|
|
106973
108490
|
.is-tool.is-video-tool > button,
|
|
106974
108491
|
.is-tool.is-audio-tool > div,
|
|
106975
108492
|
.is-tool.is-audio-tool > button,
|
|
108493
|
+
.is-tool.is-plugin-tool > div,
|
|
106976
108494
|
.is-tool.is-iframe-tool > div,
|
|
106977
108495
|
.is-tool.is-iframe-tool > button {
|
|
106978
108496
|
width: 35px !important;
|
|
@@ -106982,6 +108500,7 @@ class ContentStuff$1 {
|
|
|
106982
108500
|
|
|
106983
108501
|
.is-tool.is-video-tool svg,
|
|
106984
108502
|
.is-tool.is-audio-tool svg,
|
|
108503
|
+
.is-tool.is-plugin-tool svg,
|
|
106985
108504
|
.is-tool.is-iframe-tool svg {
|
|
106986
108505
|
width: 17px;
|
|
106987
108506
|
height: 17px;
|
|
@@ -123603,6 +125122,9 @@ Add an image for each feature.`, 'Create a new content showcasing a photo galler
|
|
|
123603
125122
|
|
|
123604
125123
|
// Zoom
|
|
123605
125124
|
this.setZoomOnControl(builder);
|
|
125125
|
+
|
|
125126
|
+
// Re-init plugins
|
|
125127
|
+
if (this.win.builderRuntime) this.win.builderRuntime.reinitialize(builder);
|
|
123606
125128
|
}
|
|
123607
125129
|
applySortableGrid() {
|
|
123608
125130
|
if (this.iframe) {
|
|
@@ -123687,6 +125209,143 @@ Add an image for each feature.`, 'Create a new content showcasing a photo galler
|
|
|
123687
125209
|
fromViewToActual(html) {
|
|
123688
125210
|
return this.htmlutil.fromViewToActual(html);
|
|
123689
125211
|
}
|
|
125212
|
+
openColorPicker(currentColor, callback, btn) {
|
|
125213
|
+
this.colorPicker.open(callback, currentColor, () => {}, btn);
|
|
125214
|
+
}
|
|
125215
|
+
openIconPicker(callback) {
|
|
125216
|
+
let iconModal = this.builderStuff.querySelector('.is-modal.iconselect');
|
|
125217
|
+
if (!iconModal) {
|
|
125218
|
+
// Create modal with search input and event listeners once
|
|
125219
|
+
let html = `
|
|
125220
|
+
<style>
|
|
125221
|
+
.is-modal.iconselect .icon-search-wrapper {
|
|
125222
|
+
position: sticky;
|
|
125223
|
+
top: 0;
|
|
125224
|
+
background: white;
|
|
125225
|
+
padding: 20px;
|
|
125226
|
+
border-bottom: 1px solid #ddd;
|
|
125227
|
+
z-index: 10;
|
|
125228
|
+
}
|
|
125229
|
+
.is-modal.iconselect .icon-search {
|
|
125230
|
+
width: 100%;
|
|
125231
|
+
padding: 10px 15px;
|
|
125232
|
+
font-size: 16px;
|
|
125233
|
+
border: 1px solid #ccc;
|
|
125234
|
+
border-radius: 4px;
|
|
125235
|
+
box-sizing: border-box;
|
|
125236
|
+
}
|
|
125237
|
+
.is-modal.iconselect .icon-search:focus {
|
|
125238
|
+
outline: none;
|
|
125239
|
+
border-color: #007bff;
|
|
125240
|
+
}
|
|
125241
|
+
.is-modal.iconselect .icon-list {
|
|
125242
|
+
display: flex;
|
|
125243
|
+
gap: 10px;
|
|
125244
|
+
flex-flow: wrap;
|
|
125245
|
+
padding: 20px;
|
|
125246
|
+
}
|
|
125247
|
+
.is-modal.iconselect .icon-list > button {
|
|
125248
|
+
display: flex !important;
|
|
125249
|
+
width: 80px !important;
|
|
125250
|
+
height: 80px !important;
|
|
125251
|
+
flex: none;
|
|
125252
|
+
font-size: 30px !important;
|
|
125253
|
+
box-shadow: none !important;
|
|
125254
|
+
align-items: center;
|
|
125255
|
+
justify-content: center;
|
|
125256
|
+
}
|
|
125257
|
+
.is-modal.iconselect .icon-list > button.hidden {
|
|
125258
|
+
display: none !important;
|
|
125259
|
+
}
|
|
125260
|
+
.is-modal.iconselect .no-results {
|
|
125261
|
+
padding: 40px 20px;
|
|
125262
|
+
text-align: center;
|
|
125263
|
+
color: #666;
|
|
125264
|
+
display: none;
|
|
125265
|
+
}
|
|
125266
|
+
.is-modal.iconselect .no-results.show {
|
|
125267
|
+
display: block;
|
|
125268
|
+
}
|
|
125269
|
+
</style>
|
|
125270
|
+
<div class="is-modal iconselect" tabindex="-1" role="dialog" aria-modal="true" aria-hidden="true">
|
|
125271
|
+
<div class="is-modal-content scroll" style="width: 90vw; max-width: 908px; height: 70vh; min-height: 525px; padding: 0;">
|
|
125272
|
+
<div style="height: 100%; overflow-y: auto; display: flex; flex-direction: column;">
|
|
125273
|
+
<div class="icon-search-wrapper">
|
|
125274
|
+
<input type="text" class="icon-search" placeholder="Search icons... (e.g., alarm, wrench)" />
|
|
125275
|
+
</div>
|
|
125276
|
+
<div class="icon-list"></div>
|
|
125277
|
+
<div class="no-results">No icons found matching your search.</div>
|
|
125278
|
+
</div>
|
|
125279
|
+
</div>
|
|
125280
|
+
</div>
|
|
125281
|
+
`;
|
|
125282
|
+
this.dom.appendHtml(this.builderStuff, html);
|
|
125283
|
+
iconModal = this.builderStuff.querySelector('.is-modal.iconselect');
|
|
125284
|
+
this.iconModal = iconModal;
|
|
125285
|
+
const iconList = this.builderStuff.querySelector('.icon-list');
|
|
125286
|
+
const searchInput = this.builderStuff.querySelector('.icon-search');
|
|
125287
|
+
const noResults = this.builderStuff.querySelector('.no-results');
|
|
125288
|
+
iconList.innerHTML = this.rte.getIcons2() + this.rte.getIcons();
|
|
125289
|
+
let icons = iconList.querySelectorAll('button');
|
|
125290
|
+
|
|
125291
|
+
// Add click handlers for icons
|
|
125292
|
+
icons.forEach(icon => {
|
|
125293
|
+
icon.addEventListener('click', () => {
|
|
125294
|
+
// Get the current callback from the modal's data
|
|
125295
|
+
const currentCallback = iconModal._currentCallback;
|
|
125296
|
+
if (currentCallback) {
|
|
125297
|
+
currentCallback(icon.innerHTML);
|
|
125298
|
+
}
|
|
125299
|
+
this.util.hideModal(iconModal);
|
|
125300
|
+
});
|
|
125301
|
+
});
|
|
125302
|
+
|
|
125303
|
+
// Add search/filter functionality
|
|
125304
|
+
searchInput.addEventListener('input', e => {
|
|
125305
|
+
const searchTerm = e.target.value.toLowerCase().trim();
|
|
125306
|
+
let visibleCount = 0;
|
|
125307
|
+
icons.forEach(icon => {
|
|
125308
|
+
const iconElement = icon.querySelector('i');
|
|
125309
|
+
if (!iconElement) return;
|
|
125310
|
+
|
|
125311
|
+
// Get class names from the icon
|
|
125312
|
+
const iconClasses = iconElement.className.toLowerCase();
|
|
125313
|
+
|
|
125314
|
+
// Check if search term matches any part of the icon classes
|
|
125315
|
+
if (searchTerm === '' || iconClasses.includes(searchTerm)) {
|
|
125316
|
+
icon.classList.remove('hidden');
|
|
125317
|
+
visibleCount++;
|
|
125318
|
+
} else {
|
|
125319
|
+
icon.classList.add('hidden');
|
|
125320
|
+
}
|
|
125321
|
+
});
|
|
125322
|
+
|
|
125323
|
+
// Show/hide no results message
|
|
125324
|
+
if (visibleCount === 0 && searchTerm !== '') {
|
|
125325
|
+
noResults.classList.add('show');
|
|
125326
|
+
} else {
|
|
125327
|
+
noResults.classList.remove('show');
|
|
125328
|
+
}
|
|
125329
|
+
});
|
|
125330
|
+
|
|
125331
|
+
// Clear search when modal is opened
|
|
125332
|
+
iconModal.addEventListener('modalshow', () => {
|
|
125333
|
+
searchInput.value = '';
|
|
125334
|
+
searchInput.dispatchEvent(new Event('input'));
|
|
125335
|
+
});
|
|
125336
|
+
}
|
|
125337
|
+
|
|
125338
|
+
// Store the current callback and reset search
|
|
125339
|
+
iconModal._currentCallback = callback;
|
|
125340
|
+
const searchInput = iconModal.querySelector('.icon-search');
|
|
125341
|
+
if (searchInput) {
|
|
125342
|
+
searchInput.value = '';
|
|
125343
|
+
searchInput.dispatchEvent(new Event('input'));
|
|
125344
|
+
// Focus search input after a brief delay to ensure modal is visible
|
|
125345
|
+
setTimeout(() => searchInput.focus(), 100);
|
|
125346
|
+
}
|
|
125347
|
+
this.util.showModal(iconModal);
|
|
125348
|
+
}
|
|
123690
125349
|
colorpicker(onPick, defaultcolor) {
|
|
123691
125350
|
// return new ColorPicker({
|
|
123692
125351
|
// onPick: onPick,
|
|
@@ -124338,6 +125997,9 @@ Add an image for each feature.`, 'Create a new content showcasing a photo galler
|
|
|
124338
125997
|
}
|
|
124339
125998
|
});
|
|
124340
125999
|
}
|
|
126000
|
+
openFilePicker(type, callback) {
|
|
126001
|
+
this.openAssetSelect(type, callback);
|
|
126002
|
+
}
|
|
124341
126003
|
openAssetSelect(targetAssetType, callback, defaultValue) {
|
|
124342
126004
|
const inpUrl = document.createElement('input');
|
|
124343
126005
|
|
|
@@ -143456,7 +145118,10 @@ class Section {
|
|
|
143456
145118
|
if (this.builder.eb) this.builder.eb.refresh(); // freeform
|
|
143457
145119
|
|
|
143458
145120
|
this.builder.onRender();
|
|
143459
|
-
this.builder.onChange();
|
|
145121
|
+
this.builder.onChange(); // Re-init plugins
|
|
145122
|
+
|
|
145123
|
+
const runtime = this.builder.win.builderRuntime;
|
|
145124
|
+
if (runtime) runtime.reinitialize(section);
|
|
143460
145125
|
}
|
|
143461
145126
|
|
|
143462
145127
|
sectionDelete() {
|
|
@@ -162390,7 +164055,8 @@ Add an image for each feature.`, 'Create a new block showcasing a photo gallery
|
|
|
162390
164055
|
blockContainer: '.is-box',
|
|
162391
164056
|
slider: this.settings.slider,
|
|
162392
164057
|
onRender: () => {
|
|
162393
|
-
this.settings.onRender();
|
|
164058
|
+
this.settings.onRender(); // Re-init plugins
|
|
164059
|
+
// if(this.win.builderRuntime) this.win.builderRuntime.reinitialize();
|
|
162394
164060
|
},
|
|
162395
164061
|
onChange: () => {
|
|
162396
164062
|
this.settings.onChange();
|
|
@@ -164137,6 +165803,18 @@ Add an image for each feature.`, 'Create a new block showcasing a photo gallery
|
|
|
164137
165803
|
} // init
|
|
164138
165804
|
|
|
164139
165805
|
|
|
165806
|
+
openFilePicker(type, callback) {
|
|
165807
|
+
this.editor.openAssetSelect(type, callback);
|
|
165808
|
+
}
|
|
165809
|
+
|
|
165810
|
+
openColorPicker(currentColor, callback, btn) {
|
|
165811
|
+
this.editor.openColorPicker(currentColor, callback, btn);
|
|
165812
|
+
}
|
|
165813
|
+
|
|
165814
|
+
openIconPicker(callback) {
|
|
165815
|
+
this.editor.openIconPicker(callback);
|
|
165816
|
+
}
|
|
165817
|
+
|
|
164140
165818
|
openPreview() {
|
|
164141
165819
|
this.animateScroll.openPreview();
|
|
164142
165820
|
}
|
|
@@ -164738,7 +166416,7 @@ Add an image for each feature.`, 'Create a new block showcasing a photo gallery
|
|
|
164738
166416
|
this.markScrollTarget(this.activeSection); // adding class 'scroll-target'
|
|
164739
166417
|
}
|
|
164740
166418
|
|
|
164741
|
-
doUndoRedo() {
|
|
166419
|
+
async doUndoRedo() {
|
|
164742
166420
|
/*
|
|
164743
166421
|
// Clean
|
|
164744
166422
|
const elms = this.wrapperEl.querySelectorAll(`[data-center],[data-center-top],[data--50-bottom],
|
|
@@ -164807,6 +166485,9 @@ Add an image for each feature.`, 'Create a new block showcasing a photo gallery
|
|
|
164807
166485
|
if (this.controlPanel) {
|
|
164808
166486
|
this.controlpanel.select('');
|
|
164809
166487
|
}
|
|
166488
|
+
|
|
166489
|
+
const runtime = this.win.builderRuntime;
|
|
166490
|
+
if (runtime) runtime.reinitialize();
|
|
164810
166491
|
}
|
|
164811
166492
|
|
|
164812
166493
|
refreshAnim() {
|
|
@@ -165051,7 +166732,9 @@ Add an image for each feature.`, 'Create a new block showcasing a photo gallery
|
|
|
165051
166732
|
});
|
|
165052
166733
|
this.editor.applyBehavior(); // Re-apply
|
|
165053
166734
|
|
|
165054
|
-
this.settings.onRender();
|
|
166735
|
+
this.settings.onRender(); // Re-init plugins
|
|
166736
|
+
|
|
166737
|
+
if (this.win.builderRuntime) this.win.builderRuntime.reinitialize();
|
|
165055
166738
|
const scrollButton = this.wrapperEl.querySelectorAll('.is-arrow-down a');
|
|
165056
166739
|
scrollButton.forEach(btn => {
|
|
165057
166740
|
btn.addEventListener('click', e => {
|
|
@@ -165838,7 +167521,9 @@ Add an image for each feature.`, 'Create a new block showcasing a photo gallery
|
|
|
165838
167521
|
this.pageSetupDone = true; //prevent duplicate for traditional pageSetup on init
|
|
165839
167522
|
// Re-apply
|
|
165840
167523
|
|
|
165841
|
-
this.settings.onRender();
|
|
167524
|
+
this.settings.onRender(); // Re-init plugins
|
|
167525
|
+
|
|
167526
|
+
if (this.win.builderRuntime) this.win.builderRuntime.reinitialize();
|
|
165842
167527
|
const scrollButton = wrapper.querySelectorAll('.is-arrow-down a');
|
|
165843
167528
|
scrollButton.forEach(btn => {
|
|
165844
167529
|
btn.addEventListener('click', e => {
|