@innovastudio/contentbox 1.6.160 → 1.6.162
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 +81 -19
- package/public/contentbox/contentbox.esm.js +1679 -9
- package/public/contentbox/contentbox.min.js +14 -14
- package/readme.txt +1 -1
|
@@ -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;
|
|
@@ -40969,7 +41602,7 @@ class HtmlUtil {
|
|
|
40969
41602
|
this.builder.opts.onRender();
|
|
40970
41603
|
|
|
40971
41604
|
// Re-select
|
|
40972
|
-
if
|
|
41605
|
+
// if(row.firstChild) row.firstChild.click();
|
|
40973
41606
|
//Change to row selection
|
|
40974
41607
|
row.classList.remove('row-outline');
|
|
40975
41608
|
//Hide Column tool (new!)
|
|
@@ -41022,6 +41655,8 @@ class HtmlUtil {
|
|
|
41022
41655
|
}
|
|
41023
41656
|
util.clearControls(); // NEW
|
|
41024
41657
|
|
|
41658
|
+
// Re-init plugins
|
|
41659
|
+
if (this.builder.win.builderRuntime) this.builder.win.builderRuntime.reinitialize();
|
|
41025
41660
|
this.builder.hideModal(modal);
|
|
41026
41661
|
}
|
|
41027
41662
|
viewHtmlExternal() {
|
|
@@ -41568,6 +42203,35 @@ class HtmlUtil {
|
|
|
41568
42203
|
elm.innerHTML = '';
|
|
41569
42204
|
});
|
|
41570
42205
|
}
|
|
42206
|
+
|
|
42207
|
+
// Clean all interactive components
|
|
42208
|
+
const components = tmp.querySelectorAll('[data-cb-type]');
|
|
42209
|
+
components.forEach(element => {
|
|
42210
|
+
const original = element.getAttribute('data-cb-original-content');
|
|
42211
|
+
if (original) {
|
|
42212
|
+
// Restore original HTML
|
|
42213
|
+
element.innerHTML = original;
|
|
42214
|
+
|
|
42215
|
+
// Remove runtime-added attributes
|
|
42216
|
+
element.removeAttribute('data-cb-original-content');
|
|
42217
|
+
element.removeAttribute('data-cb-loaded');
|
|
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
|
+
element.removeAttribute('data-cb-editmode');
|
|
42229
|
+
|
|
42230
|
+
// Remove runtime-added classes
|
|
42231
|
+
const type = element.getAttribute('data-cb-type');
|
|
42232
|
+
element.classList.remove(`cb-${type}`);
|
|
42233
|
+
}
|
|
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>
|
|
@@ -77235,6 +77899,817 @@ class Spacer {
|
|
|
77235
77899
|
}
|
|
77236
77900
|
}
|
|
77237
77901
|
|
|
77902
|
+
/**
|
|
77903
|
+
* ContentBox Settings UI Generator
|
|
77904
|
+
* Automatically generates settings form based on plugin schema
|
|
77905
|
+
*
|
|
77906
|
+
* Usage in ContentBox Editor:
|
|
77907
|
+
* const generator = new SettingsUIGenerator(runtime);
|
|
77908
|
+
* const form = generator.generateForm('logo-loop', currentElement);
|
|
77909
|
+
* panel.appendChild(form);
|
|
77910
|
+
*/
|
|
77911
|
+
|
|
77912
|
+
class SettingsUIGenerator {
|
|
77913
|
+
constructor(runtime, builder) {
|
|
77914
|
+
this.runtime = runtime;
|
|
77915
|
+
this.builder = builder;
|
|
77916
|
+
}
|
|
77917
|
+
|
|
77918
|
+
/**
|
|
77919
|
+
* Generate settings form for a plugin
|
|
77920
|
+
* @param {string} pluginName - Plugin name
|
|
77921
|
+
* @param {HTMLElement} element - Current element being edited
|
|
77922
|
+
* @returns {HTMLElement} Form element
|
|
77923
|
+
*/
|
|
77924
|
+
generateForm(pluginName, element, onChange) {
|
|
77925
|
+
const plugin = this.runtime.getPlugin(pluginName);
|
|
77926
|
+
if (!plugin || !plugin.settings) {
|
|
77927
|
+
console.warn(`Plugin "${pluginName}" has no settings schema`);
|
|
77928
|
+
return this.createEmptyForm();
|
|
77929
|
+
}
|
|
77930
|
+
const form = document.createElement('div');
|
|
77931
|
+
form.className = 'cb-settings-form';
|
|
77932
|
+
|
|
77933
|
+
// Get current values from element
|
|
77934
|
+
const currentValues = this.getCurrentValues(element, plugin.settings);
|
|
77935
|
+
|
|
77936
|
+
// Check if plugin has grouped settings
|
|
77937
|
+
if (plugin.settings._groups) {
|
|
77938
|
+
this.generateGroupedForm(form, plugin.settings, currentValues);
|
|
77939
|
+
} else {
|
|
77940
|
+
this.generateFlatForm(form, plugin.settings, currentValues);
|
|
77941
|
+
}
|
|
77942
|
+
|
|
77943
|
+
// Attach listeners for auto-update
|
|
77944
|
+
if (typeof onChange === 'function') {
|
|
77945
|
+
form.addEventListener('input', () => {
|
|
77946
|
+
const values = this.getFormValues(form);
|
|
77947
|
+
onChange(values, element);
|
|
77948
|
+
});
|
|
77949
|
+
form.addEventListener('change', () => {
|
|
77950
|
+
const values = this.getFormValues(form);
|
|
77951
|
+
onChange(values, element);
|
|
77952
|
+
});
|
|
77953
|
+
}
|
|
77954
|
+
return form;
|
|
77955
|
+
}
|
|
77956
|
+
|
|
77957
|
+
/**
|
|
77958
|
+
* Generate flat form (no groups)
|
|
77959
|
+
*/
|
|
77960
|
+
generateFlatForm(container, settings, currentValues) {
|
|
77961
|
+
Object.keys(settings).forEach(key => {
|
|
77962
|
+
if (key.startsWith('_')) return; // Skip meta keys
|
|
77963
|
+
|
|
77964
|
+
const field = settings[key];
|
|
77965
|
+
const fieldElement = this.createField(key, field, currentValues[key]);
|
|
77966
|
+
container.appendChild(fieldElement);
|
|
77967
|
+
});
|
|
77968
|
+
}
|
|
77969
|
+
|
|
77970
|
+
/**
|
|
77971
|
+
* Generate grouped form
|
|
77972
|
+
*/
|
|
77973
|
+
generateGroupedForm(container, settings, currentValues) {
|
|
77974
|
+
const groups = settings._groups || [];
|
|
77975
|
+
groups.forEach(group => {
|
|
77976
|
+
const groupElement = document.createElement('div');
|
|
77977
|
+
groupElement.className = 'cb-settings-group';
|
|
77978
|
+
const groupTitle = document.createElement('h3');
|
|
77979
|
+
groupTitle.className = 'cb-settings-group-title';
|
|
77980
|
+
groupTitle.style.cssText = 'font-size: 17px;font-weight: 500;';
|
|
77981
|
+
groupTitle.textContent = group.label;
|
|
77982
|
+
groupElement.appendChild(groupTitle);
|
|
77983
|
+
group.fields.forEach(fieldKey => {
|
|
77984
|
+
const field = settings[fieldKey];
|
|
77985
|
+
if (!field) return;
|
|
77986
|
+
const fieldElement = this.createField(fieldKey, field, currentValues[fieldKey]);
|
|
77987
|
+
groupElement.appendChild(fieldElement);
|
|
77988
|
+
});
|
|
77989
|
+
container.appendChild(groupElement);
|
|
77990
|
+
});
|
|
77991
|
+
}
|
|
77992
|
+
|
|
77993
|
+
/**
|
|
77994
|
+
* Create a single form field
|
|
77995
|
+
*/
|
|
77996
|
+
createField(name, config, value) {
|
|
77997
|
+
const wrapper = document.createElement('div');
|
|
77998
|
+
wrapper.className = 'cb-settings-field';
|
|
77999
|
+
wrapper.style.marginBottom = '20px';
|
|
78000
|
+
wrapper.setAttribute('data-field', name);
|
|
78001
|
+
|
|
78002
|
+
// Add conditional display logic
|
|
78003
|
+
if (config.dependsOn) {
|
|
78004
|
+
wrapper.setAttribute('data-depends-on', JSON.stringify(config.dependsOn));
|
|
78005
|
+
wrapper.style.display = 'none'; // Hidden by default, shown by JS
|
|
78006
|
+
}
|
|
78007
|
+
|
|
78008
|
+
// Label
|
|
78009
|
+
const label = document.createElement('label');
|
|
78010
|
+
label.className = 'cb-settings-label';
|
|
78011
|
+
label.style.fontWeight = 500;
|
|
78012
|
+
label.textContent = config.label || name;
|
|
78013
|
+
// wrapper.appendChild(label);
|
|
78014
|
+
|
|
78015
|
+
// Input based on type
|
|
78016
|
+
const input = this.createInput(name, config, value);
|
|
78017
|
+
wrapper.appendChild(input);
|
|
78018
|
+
|
|
78019
|
+
// Description
|
|
78020
|
+
// if (config.description) {
|
|
78021
|
+
// const desc = document.createElement('p');
|
|
78022
|
+
// desc.className = 'cb-settings-description';
|
|
78023
|
+
// desc.textContent = config.description;
|
|
78024
|
+
// wrapper.appendChild(desc);
|
|
78025
|
+
// }
|
|
78026
|
+
|
|
78027
|
+
return wrapper;
|
|
78028
|
+
}
|
|
78029
|
+
|
|
78030
|
+
/**
|
|
78031
|
+
* Create input element based on field type
|
|
78032
|
+
*/
|
|
78033
|
+
createInput(name, config, value) {
|
|
78034
|
+
const currentValue = value !== undefined ? value : config.default;
|
|
78035
|
+
switch (config.type) {
|
|
78036
|
+
case 'number':
|
|
78037
|
+
return this.createNumberInput(name, config, currentValue);
|
|
78038
|
+
case 'boolean':
|
|
78039
|
+
return this.createCheckboxInput(name, config, currentValue);
|
|
78040
|
+
case 'select':
|
|
78041
|
+
return this.createSelectInput(name, config, currentValue);
|
|
78042
|
+
case 'radio':
|
|
78043
|
+
return this.createRadioInput(name, config, currentValue);
|
|
78044
|
+
case 'textarea':
|
|
78045
|
+
return this.createTextareaInput(name, config, currentValue);
|
|
78046
|
+
case 'color':
|
|
78047
|
+
return this.createColorInput(name, config, currentValue);
|
|
78048
|
+
case 'range':
|
|
78049
|
+
return this.createRangeInput(name, config, currentValue);
|
|
78050
|
+
case 'text':
|
|
78051
|
+
default:
|
|
78052
|
+
return this.createTextInput(name, config, currentValue);
|
|
78053
|
+
}
|
|
78054
|
+
}
|
|
78055
|
+
createTextInput(name, config, value) {
|
|
78056
|
+
const label = document.createElement('label');
|
|
78057
|
+
label.className = 'label';
|
|
78058
|
+
label.style.fontWeight = 500;
|
|
78059
|
+
const labelText = document.createElement('div');
|
|
78060
|
+
labelText.textContent = config.label || name;
|
|
78061
|
+
label.appendChild(labelText);
|
|
78062
|
+
const input = document.createElement('input');
|
|
78063
|
+
input.type = 'text';
|
|
78064
|
+
input.name = name;
|
|
78065
|
+
input.value = value || '';
|
|
78066
|
+
if (config.placeholder) input.placeholder = config.placeholder;
|
|
78067
|
+
label.appendChild(input);
|
|
78068
|
+
|
|
78069
|
+
// Add description if exists
|
|
78070
|
+
if (config.description) {
|
|
78071
|
+
const desc = document.createElement('small');
|
|
78072
|
+
desc.style.cssText = 'display: block;margin-top: 4px;';
|
|
78073
|
+
desc.textContent = config.description;
|
|
78074
|
+
label.appendChild(desc);
|
|
78075
|
+
}
|
|
78076
|
+
return label;
|
|
78077
|
+
}
|
|
78078
|
+
createNumberInput(name, config, value) {
|
|
78079
|
+
const label = document.createElement('label');
|
|
78080
|
+
label.className = 'label';
|
|
78081
|
+
label.style.fontWeight = 500;
|
|
78082
|
+
const labelText = document.createElement('div');
|
|
78083
|
+
labelText.textContent = config.label || name;
|
|
78084
|
+
if (config.unit) {
|
|
78085
|
+
labelText.textContent += ` (${config.unit})`;
|
|
78086
|
+
}
|
|
78087
|
+
label.appendChild(labelText);
|
|
78088
|
+
const input = document.createElement('input');
|
|
78089
|
+
input.type = 'number';
|
|
78090
|
+
input.style.cssText = 'width: 100px;';
|
|
78091
|
+
input.name = name;
|
|
78092
|
+
input.value = value !== undefined ? value : config.default;
|
|
78093
|
+
if (config.min !== undefined) input.min = config.min;
|
|
78094
|
+
if (config.max !== undefined) input.max = config.max;
|
|
78095
|
+
if (config.step !== undefined) input.step = config.step;
|
|
78096
|
+
label.appendChild(input);
|
|
78097
|
+
if (config.description) {
|
|
78098
|
+
const desc = document.createElement('small');
|
|
78099
|
+
desc.style.cssText = 'display: block;margin-top: 4px;';
|
|
78100
|
+
desc.textContent = config.description;
|
|
78101
|
+
label.appendChild(desc);
|
|
78102
|
+
}
|
|
78103
|
+
return label;
|
|
78104
|
+
}
|
|
78105
|
+
createCheckboxInput(name, config, value) {
|
|
78106
|
+
const label = document.createElement('label');
|
|
78107
|
+
label.className = 'label checkbox';
|
|
78108
|
+
const div = document.createElement('div');
|
|
78109
|
+
const input = document.createElement('input');
|
|
78110
|
+
input.type = 'checkbox';
|
|
78111
|
+
input.name = name;
|
|
78112
|
+
input.checked = value !== undefined ? value : config.default;
|
|
78113
|
+
const span = document.createElement('span');
|
|
78114
|
+
span.textContent = config.label || name;
|
|
78115
|
+
label.appendChild(input);
|
|
78116
|
+
label.appendChild(span);
|
|
78117
|
+
div.appendChild(label);
|
|
78118
|
+
if (config.description) {
|
|
78119
|
+
const desc = document.createElement('small');
|
|
78120
|
+
desc.style.cssText = 'display: block;margin-top: 4px;';
|
|
78121
|
+
desc.textContent = config.description;
|
|
78122
|
+
div.appendChild(desc);
|
|
78123
|
+
}
|
|
78124
|
+
return label;
|
|
78125
|
+
}
|
|
78126
|
+
createSelectInput(name, config, value) {
|
|
78127
|
+
const label = document.createElement('label');
|
|
78128
|
+
label.className = 'label';
|
|
78129
|
+
label.style.fontWeight = 500;
|
|
78130
|
+
const labelText = document.createTextNode((config.label || name) + ':');
|
|
78131
|
+
label.appendChild(labelText);
|
|
78132
|
+
const select = document.createElement('select');
|
|
78133
|
+
select.name = name;
|
|
78134
|
+
config.options.forEach(option => {
|
|
78135
|
+
const opt = document.createElement('option');
|
|
78136
|
+
opt.value = option.value;
|
|
78137
|
+
opt.textContent = option.label;
|
|
78138
|
+
if (option.value === value) opt.selected = true;
|
|
78139
|
+
select.appendChild(opt);
|
|
78140
|
+
});
|
|
78141
|
+
label.appendChild(select);
|
|
78142
|
+
if (config.description) {
|
|
78143
|
+
const desc = document.createElement('small');
|
|
78144
|
+
desc.style.cssText = 'display: block;margin-top: 4px;';
|
|
78145
|
+
desc.textContent = config.description;
|
|
78146
|
+
label.appendChild(desc);
|
|
78147
|
+
}
|
|
78148
|
+
return label;
|
|
78149
|
+
}
|
|
78150
|
+
createRadioInput(name, config, value) {
|
|
78151
|
+
const wrapper = document.createElement('div');
|
|
78152
|
+
const title = document.createElement('div');
|
|
78153
|
+
title.textContent = config.label || name;
|
|
78154
|
+
title.style.cssText = 'margin-bottom: 8px;font-weight: 590;';
|
|
78155
|
+
wrapper.appendChild(title);
|
|
78156
|
+
config.options.forEach(option => {
|
|
78157
|
+
const label = document.createElement('label');
|
|
78158
|
+
label.className = 'label checkbox';
|
|
78159
|
+
const input = document.createElement('input');
|
|
78160
|
+
input.type = 'radio';
|
|
78161
|
+
input.name = name;
|
|
78162
|
+
input.value = option.value;
|
|
78163
|
+
input.checked = option.value === value;
|
|
78164
|
+
const span = document.createElement('span');
|
|
78165
|
+
span.textContent = option.label;
|
|
78166
|
+
label.appendChild(input);
|
|
78167
|
+
label.appendChild(span);
|
|
78168
|
+
wrapper.appendChild(label);
|
|
78169
|
+
});
|
|
78170
|
+
if (config.description) {
|
|
78171
|
+
const desc = document.createElement('small');
|
|
78172
|
+
desc.style.cssText = 'display: block;margin-top: 4px;';
|
|
78173
|
+
desc.textContent = config.description;
|
|
78174
|
+
wrapper.appendChild(desc);
|
|
78175
|
+
}
|
|
78176
|
+
return wrapper;
|
|
78177
|
+
}
|
|
78178
|
+
createTextareaInput(name, config, value) {
|
|
78179
|
+
const label = document.createElement('label');
|
|
78180
|
+
label.className = 'label';
|
|
78181
|
+
label.style.fontWeight = 500;
|
|
78182
|
+
const labelText = document.createElement('div');
|
|
78183
|
+
labelText.textContent = config.label || name;
|
|
78184
|
+
label.appendChild(labelText);
|
|
78185
|
+
const textarea = document.createElement('textarea');
|
|
78186
|
+
textarea.name = name;
|
|
78187
|
+
textarea.value = value || '';
|
|
78188
|
+
textarea.rows = config.rows || 4;
|
|
78189
|
+
if (config.placeholder) textarea.placeholder = config.placeholder;
|
|
78190
|
+
label.appendChild(textarea);
|
|
78191
|
+
if (config.description) {
|
|
78192
|
+
const desc = document.createElement('small');
|
|
78193
|
+
desc.style.cssText = 'display: block;margin-top: 4px;';
|
|
78194
|
+
desc.textContent = config.description;
|
|
78195
|
+
label.appendChild(desc);
|
|
78196
|
+
}
|
|
78197
|
+
return label;
|
|
78198
|
+
}
|
|
78199
|
+
createColorInput(name, config, value) {
|
|
78200
|
+
const container = document.createElement('div');
|
|
78201
|
+
const label = document.createElement('label');
|
|
78202
|
+
label.className = 'label';
|
|
78203
|
+
label.style.fontWeight = 500;
|
|
78204
|
+
label.style.cursor = 'pointer';
|
|
78205
|
+
const labelText = document.createElement('div');
|
|
78206
|
+
labelText.textContent = config.label || name;
|
|
78207
|
+
label.appendChild(labelText);
|
|
78208
|
+
const input = document.createElement('input');
|
|
78209
|
+
input.type = 'hidden';
|
|
78210
|
+
input.name = name;
|
|
78211
|
+
input.value = value || config.default || '#000000';
|
|
78212
|
+
const group = document.createElement('div');
|
|
78213
|
+
group.className = 'group';
|
|
78214
|
+
group.style.cssText = 'width: 52px; margin:0; overflow: visible;';
|
|
78215
|
+
const colorBtn = document.createElement('button');
|
|
78216
|
+
colorBtn.type = 'button';
|
|
78217
|
+
colorBtn.title = config.label || 'Color';
|
|
78218
|
+
colorBtn.className = 'btn-color is-btn-color';
|
|
78219
|
+
colorBtn.style.backgroundColor = input.value;
|
|
78220
|
+
colorBtn.setAttribute('aria-label', `Choose color for ${config.label || name}`);
|
|
78221
|
+
const openPicker = e => {
|
|
78222
|
+
e.preventDefault();
|
|
78223
|
+
this.builder.openColorPicker(input.value, color => {
|
|
78224
|
+
input.value = color;
|
|
78225
|
+
colorBtn.style.backgroundColor = color;
|
|
78226
|
+
input.dispatchEvent(new Event('change', {
|
|
78227
|
+
bubbles: true
|
|
78228
|
+
}));
|
|
78229
|
+
}, colorBtn);
|
|
78230
|
+
};
|
|
78231
|
+
colorBtn.addEventListener('click', openPicker);
|
|
78232
|
+
// Make label click trigger the button
|
|
78233
|
+
label.addEventListener('click', e => {
|
|
78234
|
+
if (e.target === label || e.target === labelText) {
|
|
78235
|
+
colorBtn.click();
|
|
78236
|
+
}
|
|
78237
|
+
});
|
|
78238
|
+
group.appendChild(colorBtn);
|
|
78239
|
+
label.appendChild(input);
|
|
78240
|
+
if (config.description) {
|
|
78241
|
+
const desc = document.createElement('small');
|
|
78242
|
+
desc.style.cssText = 'display: block;margin-top: 4px;';
|
|
78243
|
+
desc.textContent = config.description;
|
|
78244
|
+
label.appendChild(desc);
|
|
78245
|
+
}
|
|
78246
|
+
container.appendChild(label);
|
|
78247
|
+
container.appendChild(group);
|
|
78248
|
+
return container;
|
|
78249
|
+
}
|
|
78250
|
+
createColorInput2(name, config, value) {
|
|
78251
|
+
const label = document.createElement('label');
|
|
78252
|
+
label.className = 'label';
|
|
78253
|
+
label.style.fontWeight = 500;
|
|
78254
|
+
const labelText = document.createElement('div');
|
|
78255
|
+
labelText.textContent = config.label || name;
|
|
78256
|
+
label.appendChild(labelText);
|
|
78257
|
+
const input = document.createElement('input');
|
|
78258
|
+
input.type = 'color';
|
|
78259
|
+
input.name = name;
|
|
78260
|
+
input.style.cssText = 'width:40px;height:40px';
|
|
78261
|
+
input.value = value || config.default || '#000000';
|
|
78262
|
+
label.appendChild(input);
|
|
78263
|
+
if (config.description) {
|
|
78264
|
+
const desc = document.createElement('small');
|
|
78265
|
+
desc.style.cssText = 'display: block;margin-top: 4px;';
|
|
78266
|
+
desc.textContent = config.description;
|
|
78267
|
+
label.appendChild(desc);
|
|
78268
|
+
}
|
|
78269
|
+
return label;
|
|
78270
|
+
}
|
|
78271
|
+
createRangeInput(name, config, value) {
|
|
78272
|
+
const label = document.createElement('label');
|
|
78273
|
+
label.className = 'label';
|
|
78274
|
+
label.style.fontWeight = 500;
|
|
78275
|
+
const currentVal = value !== undefined ? value : config.default;
|
|
78276
|
+
const labelText = document.createElement('div');
|
|
78277
|
+
labelText.textContent = (config.label || name) + ': ' + currentVal + (config.unit || '');
|
|
78278
|
+
label.appendChild(labelText);
|
|
78279
|
+
const input = document.createElement('input');
|
|
78280
|
+
input.type = 'range';
|
|
78281
|
+
input.className = 'is-rangeslider';
|
|
78282
|
+
input.name = name;
|
|
78283
|
+
input.value = currentVal;
|
|
78284
|
+
if (config.min !== undefined) input.min = config.min;
|
|
78285
|
+
if (config.max !== undefined) input.max = config.max;
|
|
78286
|
+
if (config.step !== undefined) input.step = config.step;
|
|
78287
|
+
input.addEventListener('input', () => {
|
|
78288
|
+
labelText.textContent = (config.label || name) + ': ' + input.value + (config.unit || '');
|
|
78289
|
+
});
|
|
78290
|
+
label.appendChild(input);
|
|
78291
|
+
if (config.description) {
|
|
78292
|
+
const desc = document.createElement('small');
|
|
78293
|
+
desc.style.cssText = 'display: block;margin-top: 4px;';
|
|
78294
|
+
desc.textContent = config.description;
|
|
78295
|
+
label.appendChild(desc);
|
|
78296
|
+
}
|
|
78297
|
+
return label;
|
|
78298
|
+
}
|
|
78299
|
+
|
|
78300
|
+
/**
|
|
78301
|
+
* Get current values from element attributes
|
|
78302
|
+
*/
|
|
78303
|
+
getCurrentValues(element, settings) {
|
|
78304
|
+
const values = {};
|
|
78305
|
+
Object.keys(settings).forEach(key => {
|
|
78306
|
+
if (key.startsWith('_')) return;
|
|
78307
|
+
const attrName = 'data-cb-' + key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
78308
|
+
const attrValue = element.getAttribute(attrName);
|
|
78309
|
+
if (attrValue !== null) {
|
|
78310
|
+
values[key] = this.parseValue(attrValue, settings[key].type);
|
|
78311
|
+
}
|
|
78312
|
+
});
|
|
78313
|
+
return values;
|
|
78314
|
+
}
|
|
78315
|
+
|
|
78316
|
+
/**
|
|
78317
|
+
* Parse attribute value based on type
|
|
78318
|
+
*/
|
|
78319
|
+
parseValue(value, type) {
|
|
78320
|
+
switch (type) {
|
|
78321
|
+
case 'number':
|
|
78322
|
+
return parseFloat(value);
|
|
78323
|
+
case 'boolean':
|
|
78324
|
+
return value === 'true';
|
|
78325
|
+
default:
|
|
78326
|
+
return value;
|
|
78327
|
+
}
|
|
78328
|
+
}
|
|
78329
|
+
|
|
78330
|
+
/**
|
|
78331
|
+
* Get form values
|
|
78332
|
+
*/
|
|
78333
|
+
getFormValues(form) {
|
|
78334
|
+
const values = {};
|
|
78335
|
+
const inputs = form.querySelectorAll('input, select, textarea');
|
|
78336
|
+
inputs.forEach(input => {
|
|
78337
|
+
const name = input.name;
|
|
78338
|
+
if (!name) return;
|
|
78339
|
+
if (input.type === 'checkbox') {
|
|
78340
|
+
values[name] = input.checked;
|
|
78341
|
+
} else if (input.type === 'radio') {
|
|
78342
|
+
if (input.checked) {
|
|
78343
|
+
values[name] = input.value;
|
|
78344
|
+
}
|
|
78345
|
+
} else {
|
|
78346
|
+
values[name] = input.value;
|
|
78347
|
+
}
|
|
78348
|
+
});
|
|
78349
|
+
return values;
|
|
78350
|
+
}
|
|
78351
|
+
|
|
78352
|
+
/**
|
|
78353
|
+
* Apply form values to element
|
|
78354
|
+
*/
|
|
78355
|
+
applyValues(element, values) {
|
|
78356
|
+
Object.keys(values).forEach(key => {
|
|
78357
|
+
const attrName = 'data-cb-' + key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
78358
|
+
element.setAttribute(attrName, values[key]);
|
|
78359
|
+
});
|
|
78360
|
+
}
|
|
78361
|
+
createEmptyForm() {
|
|
78362
|
+
const form = document.createElement('div');
|
|
78363
|
+
form.className = 'cb-settings-form';
|
|
78364
|
+
form.innerHTML = '<p>This plugin has no configurable settings.</p>';
|
|
78365
|
+
return form;
|
|
78366
|
+
}
|
|
78367
|
+
}
|
|
78368
|
+
|
|
78369
|
+
class Plugin {
|
|
78370
|
+
constructor(builder) {
|
|
78371
|
+
this.builder = builder;
|
|
78372
|
+
}
|
|
78373
|
+
renderTool() {
|
|
78374
|
+
const builderStuff = this.builder.builderStuff;
|
|
78375
|
+
const contentStuff = this.builder.contentStuff;
|
|
78376
|
+
const util = this.builder.util;
|
|
78377
|
+
const dom = this.builder.dom;
|
|
78378
|
+
let pluginTool = builderStuff.querySelector('.is-plugin-tool');
|
|
78379
|
+
if (!pluginTool) {
|
|
78380
|
+
let html = `
|
|
78381
|
+
<div class="is-tool is-plugin-tool">
|
|
78382
|
+
<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>
|
|
78383
|
+
</div>
|
|
78384
|
+
`;
|
|
78385
|
+
dom.appendHtml(builderStuff, html);
|
|
78386
|
+
if (!this.builder.iframe) {
|
|
78387
|
+
pluginTool = builderStuff.querySelector('.is-plugin-tool');
|
|
78388
|
+
} else {
|
|
78389
|
+
pluginTool = contentStuff.querySelector('.is-plugin-tool');
|
|
78390
|
+
}
|
|
78391
|
+
this.pluginTool = pluginTool;
|
|
78392
|
+
let btn = pluginTool.querySelector('button');
|
|
78393
|
+
dom.addEventListener(btn, 'click', () => {
|
|
78394
|
+
// old 10317
|
|
78395
|
+
|
|
78396
|
+
if (this.pluginModal && this.pluginModal.classList.contains('active')) {
|
|
78397
|
+
this.hidePluginEditor();
|
|
78398
|
+
} else {
|
|
78399
|
+
this.renderPanel();
|
|
78400
|
+
this.showPluginEditor();
|
|
78401
|
+
}
|
|
78402
|
+
btn.setAttribute('data-focus', true);
|
|
78403
|
+
});
|
|
78404
|
+
}
|
|
78405
|
+
}
|
|
78406
|
+
renderPanel() {
|
|
78407
|
+
const builderStuff = this.builder.builderStuff;
|
|
78408
|
+
const util = this.builder.util;
|
|
78409
|
+
const dom = this.builder.dom;
|
|
78410
|
+
let pluginModal = builderStuff.querySelector('.is-modal.pluginsettings');
|
|
78411
|
+
if (!pluginModal) {
|
|
78412
|
+
let html = `
|
|
78413
|
+
<div class="is-modal is-modal-content pluginsettings" tabindex="-1" role="dialog" aria-modal="true" aria-hidden="true">
|
|
78414
|
+
<div class="is-modal-bar is-draggable">
|
|
78415
|
+
<span>${util.out('Plugin Settings')}</span>
|
|
78416
|
+
<button class="is-modal-close" tabindex="-1" title="${util.out('Close')}">✕</button>
|
|
78417
|
+
</div>
|
|
78418
|
+
<div class="is-modal-controls" style="box-sizing:border-box;padding:30px;overflow-y: auto;height: calc(100% - 30px);">
|
|
78419
|
+
<div class="is-tabs" data-group="table">
|
|
78420
|
+
<a title="${util.out('Style')}" id="tabTableGeneral" href="" data-content="divTableGeneral" class="active">${util.out('Style')}</a>
|
|
78421
|
+
<a title="${util.out('Layout')}" id="tabTableLayout" href="" data-content="divTableLayout">${util.out('Layout')}</a>
|
|
78422
|
+
</div>
|
|
78423
|
+
<div id="divTableGeneral" class="is-tab-content active" tabindex="-1" data-group="table" style="display:block">
|
|
78424
|
+
|
|
78425
|
+
<div style="display:flex;padding-bottom:12px">
|
|
78426
|
+
<div style="padding-right:15px">
|
|
78427
|
+
<div>${util.out('Background')}:</div>
|
|
78428
|
+
<div>
|
|
78429
|
+
<button title="${util.out('Background Color')}" class="input-table-bgcolor is-btn-color"></button>
|
|
78430
|
+
</div>
|
|
78431
|
+
</div>
|
|
78432
|
+
<div>
|
|
78433
|
+
<div>${util.out('Text Color')}:</div>
|
|
78434
|
+
<div>
|
|
78435
|
+
<button title="${util.out('Text Color')}" class="input-table-textcolor is-btn-color"></button>
|
|
78436
|
+
</div>
|
|
78437
|
+
</div>
|
|
78438
|
+
</div>
|
|
78439
|
+
|
|
78440
|
+
<div style="padding-bottom:12px;">
|
|
78441
|
+
<div>${util.out('Border Thickness')}:</div>
|
|
78442
|
+
<div>
|
|
78443
|
+
<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>
|
|
78444
|
+
</div>
|
|
78445
|
+
</div>
|
|
78446
|
+
|
|
78447
|
+
<div style="padding-bottom:12px;">
|
|
78448
|
+
<div>${util.out('Border Color')}:</div>
|
|
78449
|
+
<div>
|
|
78450
|
+
<button title="${util.out('Border Color')}" class="input-table-bordercolor is-btn-color"></button>
|
|
78451
|
+
</div>
|
|
78452
|
+
</div>
|
|
78453
|
+
|
|
78454
|
+
<div style="padding-bottom:12px;">
|
|
78455
|
+
<div>${util.out('Apply To')}:</div>
|
|
78456
|
+
<div>
|
|
78457
|
+
<select id="selTableApplyTo" style="width:120px;">
|
|
78458
|
+
<option value="table">${util.out('Table')}</option>
|
|
78459
|
+
<option value="currentrow">${util.out('Current Row')}</option>
|
|
78460
|
+
<option value="currentcol">${util.out('Current Column')}</option>
|
|
78461
|
+
<option value="evenrows">${util.out('Even Rows')}</option>
|
|
78462
|
+
<option value="oddrows">${util.out('Odd Rows')}</option>
|
|
78463
|
+
<option value="currentcell">${util.out('Current Cell')}</option>
|
|
78464
|
+
</select>
|
|
78465
|
+
</div>
|
|
78466
|
+
</div>
|
|
78467
|
+
|
|
78468
|
+
</div>
|
|
78469
|
+
|
|
78470
|
+
<div id="divTableLayout" class="is-tab-content" tabindex="-1" data-group="table">
|
|
78471
|
+
|
|
78472
|
+
<div style="padding-bottom:12px;">
|
|
78473
|
+
<div>${util.out('Insert Row')}:</div>
|
|
78474
|
+
<div style="display:flex">
|
|
78475
|
+
<button class="classic" title="${util.out('Above')}" data-table-cmd="rowabove" title="${util.out('Above')}" style="margin-right:15px"> ${util.out('Above')} </button>
|
|
78476
|
+
<button class="classic" title="${util.out('Below')}" data-table-cmd="rowbelow" title="${util.out('Below')}" style=""> ${util.out('Below')} </button>
|
|
78477
|
+
</div>
|
|
78478
|
+
</div>
|
|
78479
|
+
|
|
78480
|
+
<div style="padding-bottom:15px;">
|
|
78481
|
+
<div>${util.out('Insert Column')}:</div>
|
|
78482
|
+
<div style="display:flex">
|
|
78483
|
+
<button class="classic" title="${util.out('Left')}" data-table-cmd="columnleft" title="${util.out('Left')}" style="margin-right:15px"> ${util.out('Left')} </button>
|
|
78484
|
+
<button class="classic" title="${util.out('Right')}" data-table-cmd="columnright" title="${util.out('Right')}" style=""> ${util.out('Right')} </button>
|
|
78485
|
+
</div>
|
|
78486
|
+
</div>
|
|
78487
|
+
|
|
78488
|
+
<div style="padding-bottom:15px;">
|
|
78489
|
+
<button class="classic" title="${util.out('Delete Row')}" data-table-cmd="delrow" title="Delete Row" style=""> ${util.out('Delete Row')} </button>
|
|
78490
|
+
</div>
|
|
78491
|
+
|
|
78492
|
+
<div style="padding-bottom:15px;">
|
|
78493
|
+
<button class="classic" title="${util.out('Delete Column')}" data-table-cmd="delcolumn" title="Delete Column" style=""> ${util.out('Delete Column')} </button>
|
|
78494
|
+
</div>
|
|
78495
|
+
|
|
78496
|
+
<div>
|
|
78497
|
+
<button class="classic" title="${util.out('Merge Cell')}" data-table-cmd="mergecell" style="">${util.out('Merge Cell')}</button>
|
|
78498
|
+
</div>
|
|
78499
|
+
</div>
|
|
78500
|
+
</div>
|
|
78501
|
+
</div>
|
|
78502
|
+
`;
|
|
78503
|
+
dom.appendHtml(builderStuff, html);
|
|
78504
|
+
pluginModal = builderStuff.querySelector('.is-modal.pluginsettings');
|
|
78505
|
+
this.pluginModal = pluginModal;
|
|
78506
|
+
let btnOk = pluginModal.querySelector('.input-ok');
|
|
78507
|
+
dom.addEventListener(btnOk, 'click', () => {
|
|
78508
|
+
this.builder.uo.saveForUndo();
|
|
78509
|
+
this.builder.opts.onChange();
|
|
78510
|
+
util.hideModal(pluginModal);
|
|
78511
|
+
});
|
|
78512
|
+
let btnCancel = pluginModal.querySelector('.input-cancel');
|
|
78513
|
+
dom.addEventListener(btnCancel, 'click', () => {
|
|
78514
|
+
util.hideModal(pluginModal);
|
|
78515
|
+
});
|
|
78516
|
+
new Tabs({
|
|
78517
|
+
element: pluginModal
|
|
78518
|
+
});
|
|
78519
|
+
new Draggable$2({
|
|
78520
|
+
selector: '.is-modal.pluginsettings .is-draggable'
|
|
78521
|
+
});
|
|
78522
|
+
}
|
|
78523
|
+
}
|
|
78524
|
+
showPluginEditor() {
|
|
78525
|
+
const pluginModal = this.pluginModal;
|
|
78526
|
+
this.builder.util.showModal(pluginModal);
|
|
78527
|
+
this.realtime();
|
|
78528
|
+
const handlePluginClick = e => {
|
|
78529
|
+
const clrPicker = document.querySelector('.pop-picker.active') || document.querySelector('.pickgradientcolor.active');
|
|
78530
|
+
let elm = e.target;
|
|
78531
|
+
let elmRemoved = !document.contains(elm); // e.g. when deleting a card in card list plugin
|
|
78532
|
+
|
|
78533
|
+
if (this.builder.doc.activeElement) {
|
|
78534
|
+
if (this.builder.doc.activeElement.closest('.is-modal')) {
|
|
78535
|
+
// prevent modal close when mouseup outside the modal
|
|
78536
|
+
return;
|
|
78537
|
+
}
|
|
78538
|
+
}
|
|
78539
|
+
if (!elm) return;
|
|
78540
|
+
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) {
|
|
78541
|
+
// click outside
|
|
78542
|
+
|
|
78543
|
+
// hide
|
|
78544
|
+
this.hidePluginEditor();
|
|
78545
|
+
// console.log('HIDE');
|
|
78546
|
+
|
|
78547
|
+
document.removeEventListener('click', handlePluginClick);
|
|
78548
|
+
if (this.builder.iframeDocument) {
|
|
78549
|
+
this.builder.doc.removeEventListener('click', handlePluginClick);
|
|
78550
|
+
}
|
|
78551
|
+
this.builder.handlePluginClick_ = false;
|
|
78552
|
+
}
|
|
78553
|
+
if (elm.closest('[data-cb-type]')) {
|
|
78554
|
+
this.realtime();
|
|
78555
|
+
}
|
|
78556
|
+
};
|
|
78557
|
+
if (!this.builder.handlePluginClick_) {
|
|
78558
|
+
document.addEventListener('click', handlePluginClick);
|
|
78559
|
+
if (this.builder.iframeDocument) {
|
|
78560
|
+
this.builder.doc.addEventListener('click', handlePluginClick);
|
|
78561
|
+
}
|
|
78562
|
+
this.builder.handlePluginClick_ = true;
|
|
78563
|
+
}
|
|
78564
|
+
}
|
|
78565
|
+
hidePluginEditor() {
|
|
78566
|
+
const pluginModal = this.pluginModal;
|
|
78567
|
+
if (pluginModal) this.builder.util.hideModal(pluginModal);
|
|
78568
|
+
}
|
|
78569
|
+
realtime() {
|
|
78570
|
+
const util = this.builder.util;
|
|
78571
|
+
const pluginModal = this.pluginModal;
|
|
78572
|
+
let currentElement = this.builder.activePlugin;
|
|
78573
|
+
const runtime = this.builder.win.builderRuntime;
|
|
78574
|
+
if (currentElement && runtime) {
|
|
78575
|
+
const pluginName = currentElement.getAttribute('data-cb-type');
|
|
78576
|
+
const plugin = runtime.getPlugin(pluginName);
|
|
78577
|
+
let titleHtml = util.out(plugin.displayName || plugin.name);
|
|
78578
|
+
const modalBar = pluginModal.querySelector('.is-modal-bar > span');
|
|
78579
|
+
modalBar.innerHTML = titleHtml;
|
|
78580
|
+
const container = pluginModal.querySelector('.is-modal-controls');
|
|
78581
|
+
|
|
78582
|
+
// Clear panel
|
|
78583
|
+
container.innerHTML = '';
|
|
78584
|
+
const div = document.createElement('div');
|
|
78585
|
+
div.classList.add('submain');
|
|
78586
|
+
container.appendChild(div);
|
|
78587
|
+
|
|
78588
|
+
// Store references
|
|
78589
|
+
this.currentElement = currentElement;
|
|
78590
|
+
this.generator = new SettingsUIGenerator(runtime, this.builder);
|
|
78591
|
+
|
|
78592
|
+
// Check if plugin has custom content editor
|
|
78593
|
+
const hasContentEditor = plugin && plugin.editor && plugin.editor.openContentEditor;
|
|
78594
|
+
if (hasContentEditor) {
|
|
78595
|
+
// Get original content for the editor to work with
|
|
78596
|
+
const originalContent = currentElement.getAttribute('data-cb-original-content');
|
|
78597
|
+
|
|
78598
|
+
// Create a temporary element for editing (so editor can manipulate it)
|
|
78599
|
+
let editableClone = document.querySelector('.editable-clone');
|
|
78600
|
+
if (editableClone) editableClone.remove();
|
|
78601
|
+
// editableClone = document.createElement('div');
|
|
78602
|
+
editableClone = currentElement.cloneNode(false);
|
|
78603
|
+
editableClone.innerHTML = originalContent;
|
|
78604
|
+
editableClone.className = 'editable-clone';
|
|
78605
|
+
editableClone.style.display = 'none'; // Hidden, just for editing
|
|
78606
|
+
document.body.appendChild(editableClone);
|
|
78607
|
+
const originalElement = editableClone.cloneNode(false);
|
|
78608
|
+
|
|
78609
|
+
// Let plugin handle everything - pass the editable clone
|
|
78610
|
+
const editorUI = plugin.editor.openContentEditor(editableClone, this.builder, async () => {
|
|
78611
|
+
this.builder.uo.saveForUndo();
|
|
78612
|
+
|
|
78613
|
+
// Store edited content from clone
|
|
78614
|
+
currentElement.setAttribute('data-cb-original-content', editableClone.innerHTML);
|
|
78615
|
+
|
|
78616
|
+
// Also performs similar value updates like the applyChanges()
|
|
78617
|
+
// currentElement.setAttribute('data-cb-content', editableClone.getAttribute('data-cb-content'));
|
|
78618
|
+
const getChangedDataAttributes = (el1, el2) => {
|
|
78619
|
+
const changed = [];
|
|
78620
|
+
for (const key of Object.keys(el1.dataset)) {
|
|
78621
|
+
if (key in el2.dataset && el1.dataset[key] !== el2.dataset[key]) {
|
|
78622
|
+
changed.push(key);
|
|
78623
|
+
}
|
|
78624
|
+
}
|
|
78625
|
+
return changed;
|
|
78626
|
+
};
|
|
78627
|
+
const changedAttributes = getChangedDataAttributes(originalElement, editableClone);
|
|
78628
|
+
// let hasChange = changedAttributes.length > 0;
|
|
78629
|
+
for (const attrName of changedAttributes) {
|
|
78630
|
+
// console.log(attrName);
|
|
78631
|
+
currentElement.dataset[attrName] = editableClone.dataset[attrName];
|
|
78632
|
+
// update form (not working)
|
|
78633
|
+
// const convertCbKey = (str) => {
|
|
78634
|
+
// return str.replace(/^cb/, '').replace(/^./, c => c.toLowerCase());
|
|
78635
|
+
// }
|
|
78636
|
+
// const name = convertCbKey(attrName);
|
|
78637
|
+
// this.currentForm.querySelector(`[name="${name}"]`).value = editableClone.dataset[attrName];
|
|
78638
|
+
}
|
|
78639
|
+
// console.log(hasChange)
|
|
78640
|
+
// -------
|
|
78641
|
+
|
|
78642
|
+
currentElement.innerHTML = editableClone.innerHTML; // Update current content
|
|
78643
|
+
|
|
78644
|
+
// Remove temporary clone
|
|
78645
|
+
editableClone.remove();
|
|
78646
|
+
|
|
78647
|
+
// update form (working)
|
|
78648
|
+
// currentElement.click();
|
|
78649
|
+
// return;
|
|
78650
|
+
|
|
78651
|
+
// Reinitialize plugin with new content
|
|
78652
|
+
await runtime.reinitialize(currentElement.parentElement);
|
|
78653
|
+
this.builder.onChange();
|
|
78654
|
+
});
|
|
78655
|
+
div.appendChild(editorUI);
|
|
78656
|
+
}
|
|
78657
|
+
|
|
78658
|
+
// Generate form
|
|
78659
|
+
this.currentForm = this.generator.generateForm(pluginName, currentElement, () => {
|
|
78660
|
+
this.builder.uo.saveForUndo();
|
|
78661
|
+
this.applyChanges(runtime);
|
|
78662
|
+
});
|
|
78663
|
+
this.currentForm.style.marginTop = '20px';
|
|
78664
|
+
div.appendChild(this.currentForm);
|
|
78665
|
+
}
|
|
78666
|
+
}
|
|
78667
|
+
async applyChanges(runtime) {
|
|
78668
|
+
if (!this.currentElement || !this.currentForm) return;
|
|
78669
|
+
|
|
78670
|
+
// 1. Get form values
|
|
78671
|
+
const values = this.generator.getFormValues(this.currentForm);
|
|
78672
|
+
|
|
78673
|
+
// 2. Apply to element attributes
|
|
78674
|
+
this.generator.applyValues(this.currentElement, values);
|
|
78675
|
+
|
|
78676
|
+
// 3. Reinitialize component
|
|
78677
|
+
const container = this.currentElement.parentElement || this.currentElement.closest('.is-wrapper');
|
|
78678
|
+
await runtime.reinitialize(container);
|
|
78679
|
+
|
|
78680
|
+
//Trigger Change event
|
|
78681
|
+
this.builder.opts.onChange();
|
|
78682
|
+
|
|
78683
|
+
// console.log('Settings applied and component reinitialized');
|
|
78684
|
+
}
|
|
78685
|
+
|
|
78686
|
+
click(e) {
|
|
78687
|
+
const plugin = e.target.closest('[data-cb-type]');
|
|
78688
|
+
if (plugin) {
|
|
78689
|
+
this.builder.activePlugin = plugin;
|
|
78690
|
+
if (!this.builder.isContentBox) {
|
|
78691
|
+
this.renderTool();
|
|
78692
|
+
this.pluginTool.style.display = 'flex';
|
|
78693
|
+
let _toolwidth = this.pluginTool.offsetWidth; //to get value, element must not hidden (display:none). So set display:flex before this.
|
|
78694
|
+
|
|
78695
|
+
let w = plugin.offsetWidth * this.builder.opts.zoom;
|
|
78696
|
+
let top = plugin.getBoundingClientRect().top + this.builder.win.pageYOffset;
|
|
78697
|
+
let left = plugin.getBoundingClientRect().left - 2;
|
|
78698
|
+
left = left + (w - _toolwidth);
|
|
78699
|
+
|
|
78700
|
+
//Adjust left in case an element is outside the screen
|
|
78701
|
+
const _screenwidth = window.innerWidth;
|
|
78702
|
+
if (_toolwidth + left > _screenwidth) left = plugin.getBoundingClientRect().left;
|
|
78703
|
+
this.pluginTool.style.top = top + 'px';
|
|
78704
|
+
this.pluginTool.style.left = left + 'px';
|
|
78705
|
+
}
|
|
78706
|
+
} else {
|
|
78707
|
+
this.builder.activePlugin = null;
|
|
78708
|
+
if (this.pluginTool) this.pluginTool.style.display = '';
|
|
78709
|
+
}
|
|
78710
|
+
}
|
|
78711
|
+
}
|
|
78712
|
+
|
|
77238
78713
|
class Module {
|
|
77239
78714
|
constructor(builder) {
|
|
77240
78715
|
this.builder = builder;
|
|
@@ -79076,6 +80551,7 @@ class Element$1 {
|
|
|
79076
80551
|
this.button = new Button(builder);
|
|
79077
80552
|
this.image = new Image$1(builder);
|
|
79078
80553
|
this.spacer = new Spacer(builder);
|
|
80554
|
+
this.plugin = new Plugin(builder);
|
|
79079
80555
|
this.module = new Module(builder);
|
|
79080
80556
|
this.code = new Code(builder);
|
|
79081
80557
|
this.iframe = new Iframe(builder);
|
|
@@ -79114,6 +80590,10 @@ class Element$1 {
|
|
|
79114
80590
|
|
|
79115
80591
|
// Set contentEditable FALSE on special elements
|
|
79116
80592
|
|
|
80593
|
+
const plugins = col.querySelectorAll('[data-cb-type]'); // plugins
|
|
80594
|
+
Array.prototype.forEach.call(plugins, plugin => {
|
|
80595
|
+
plugin.contentEditable = false;
|
|
80596
|
+
});
|
|
79117
80597
|
let sociallinks = col.querySelectorAll('.is-social');
|
|
79118
80598
|
Array.prototype.forEach.call(sociallinks, sociallink => {
|
|
79119
80599
|
sociallink.contentEditable = false;
|
|
@@ -79205,6 +80685,9 @@ class Element$1 {
|
|
|
79205
80685
|
subblock = true;
|
|
79206
80686
|
}
|
|
79207
80687
|
}
|
|
80688
|
+
if (elm.closest('[data-cb-type]')) {
|
|
80689
|
+
customcode = true;
|
|
80690
|
+
}
|
|
79208
80691
|
if (!customcode && !noedit && !_protected || subblock) {
|
|
79209
80692
|
//previously this is commented: && !noedit && !_protected
|
|
79210
80693
|
|
|
@@ -79275,6 +80758,9 @@ class Element$1 {
|
|
|
79275
80758
|
|
|
79276
80759
|
// Module
|
|
79277
80760
|
this.module.click(col, e);
|
|
80761
|
+
|
|
80762
|
+
// Plugin
|
|
80763
|
+
this.plugin.click(e);
|
|
79278
80764
|
}
|
|
79279
80765
|
}
|
|
79280
80766
|
|
|
@@ -88430,6 +89916,12 @@ class ElementTool {
|
|
|
88430
89916
|
const btnMore = elementTool.querySelector('.elm-more');
|
|
88431
89917
|
dom.addEventListener(btnMore, 'click', () => {
|
|
88432
89918
|
const viewportHeight = window.innerHeight;
|
|
89919
|
+
const elmSettings = elementMore.querySelector('.elm-settings');
|
|
89920
|
+
if (this.builder.activeElement.hasAttribute('data-cb-type')) {
|
|
89921
|
+
elmSettings.style.display = 'none';
|
|
89922
|
+
} else {
|
|
89923
|
+
elmSettings.style.display = '';
|
|
89924
|
+
}
|
|
88433
89925
|
|
|
88434
89926
|
/*
|
|
88435
89927
|
let top, left;
|
|
@@ -88695,7 +90187,10 @@ class ElementTool {
|
|
|
88695
90187
|
if (dom.parentsHasClass(elm, 'is-subblock')) {
|
|
88696
90188
|
subblock = true;
|
|
88697
90189
|
}
|
|
88698
|
-
|
|
90190
|
+
const plugin = elm.closest('[data-cb-type]');
|
|
90191
|
+
if ((customcode || noedit || _protected) && !subblock) ; else if (plugin) {
|
|
90192
|
+
activeElement = plugin;
|
|
90193
|
+
} else {
|
|
88699
90194
|
const tagName = elm.tagName.toLowerCase();
|
|
88700
90195
|
// LATER: label, code, figcaption ?
|
|
88701
90196
|
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 +90342,8 @@ class ElementTool {
|
|
|
88847
90342
|
let elm = this.builder.activeElement;
|
|
88848
90343
|
if (!elm) return;
|
|
88849
90344
|
if (elm.closest('.is-dock')) return;
|
|
90345
|
+
// if(elm.closest('[data-cb-type]')) return;
|
|
90346
|
+
|
|
88850
90347
|
let top, left;
|
|
88851
90348
|
if (!this.builder.iframe) {
|
|
88852
90349
|
top = elm.getBoundingClientRect().top + window.pageYOffset;
|
|
@@ -106495,6 +107992,9 @@ class ContentStuff$1 {
|
|
|
106495
107992
|
<div class="is-tool is-iframe-tool">
|
|
106496
107993
|
<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
107994
|
</div>
|
|
107995
|
+
<div class="is-tool is-plugin-tool">
|
|
107996
|
+
<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>
|
|
107997
|
+
</div>
|
|
106498
107998
|
<div class="is-tool is-module-tool">
|
|
106499
107999
|
<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
108000
|
<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 +108400,24 @@ class ContentStuff$1 {
|
|
|
106900
108400
|
/*
|
|
106901
108401
|
.is-tool.is-video-tool,
|
|
106902
108402
|
.is-tool.is-audio-tool,
|
|
108403
|
+
.is-tool.is-plugin-tool,
|
|
106903
108404
|
.is-tool.is-iframe-tool {
|
|
106904
108405
|
background: rgba(0, 0, 0, 0.15);
|
|
106905
108406
|
border: transparent 1px solid;
|
|
106906
108407
|
}
|
|
106907
108408
|
.is-tool.is-video-tool > div,
|
|
106908
108409
|
.is-tool.is-audio-tool > div,
|
|
108410
|
+
.is-tool.is-plugin-tool > div,
|
|
106909
108411
|
.is-tool.is-iframe-tool > div,
|
|
106910
108412
|
.is-tool.is-video-tool > button,
|
|
106911
108413
|
.is-tool.is-audio-tool > button,
|
|
108414
|
+
.is-tool.is-plugin-tool > button,
|
|
106912
108415
|
.is-tool.is-iframe-tool > button {
|
|
106913
108416
|
background: transparent;
|
|
106914
108417
|
}
|
|
106915
108418
|
.is-tool.is-video-tool svg,
|
|
106916
108419
|
.is-tool.is-audio-tool svg,
|
|
108420
|
+
.is-tool.is-plugin-tool svg,
|
|
106917
108421
|
.is-tool.is-iframe-tool svg {
|
|
106918
108422
|
fill: #fff;
|
|
106919
108423
|
}
|
|
@@ -106923,6 +108427,7 @@ class ContentStuff$1 {
|
|
|
106923
108427
|
.is-tool.is-table-tool,
|
|
106924
108428
|
.is-tool.is-code-tool,
|
|
106925
108429
|
.is-tool.is-module-tool,
|
|
108430
|
+
.is-tool.is-plugin-tool,
|
|
106926
108431
|
.is-tool#divLinkTool,
|
|
106927
108432
|
.is-tool#divButtonTool,
|
|
106928
108433
|
.is-tool.is-svg-tool {
|
|
@@ -106936,6 +108441,7 @@ class ContentStuff$1 {
|
|
|
106936
108441
|
.is-tool.is-table-tool > button,
|
|
106937
108442
|
.is-tool.is-code-tool > button,
|
|
106938
108443
|
.is-tool.is-module-tool > button,
|
|
108444
|
+
.is-tool.is-plugin-tool > button,
|
|
106939
108445
|
.is-tool#divLinkTool > button,
|
|
106940
108446
|
.is-tool#divButtonTool > button,
|
|
106941
108447
|
.is-tool.is-svg-tool > button {
|
|
@@ -106948,6 +108454,7 @@ class ContentStuff$1 {
|
|
|
106948
108454
|
.is-tool.is-table-tool > button svg,
|
|
106949
108455
|
.is-tool.is-code-tool > button svg,
|
|
106950
108456
|
.is-tool.is-module-tool > button svg,
|
|
108457
|
+
.is-tool.is-plugin-tool > button svg,
|
|
106951
108458
|
.is-tool#divLinkTool > button svg,
|
|
106952
108459
|
.is-tool#divButtonTool > button svg,
|
|
106953
108460
|
.is-tool.is-svg-tool > button svg {
|
|
@@ -106962,6 +108469,7 @@ class ContentStuff$1 {
|
|
|
106962
108469
|
|
|
106963
108470
|
.is-tool.is-video-tool,
|
|
106964
108471
|
.is-tool.is-audio-tool,
|
|
108472
|
+
.is-tool.is-plugin-tool,
|
|
106965
108473
|
.is-tool.is-iframe-tool {
|
|
106966
108474
|
background: rgba(255, 255, 255, 0.97) !important;
|
|
106967
108475
|
border: transparent 1px solid;
|
|
@@ -106973,6 +108481,7 @@ class ContentStuff$1 {
|
|
|
106973
108481
|
.is-tool.is-video-tool > button,
|
|
106974
108482
|
.is-tool.is-audio-tool > div,
|
|
106975
108483
|
.is-tool.is-audio-tool > button,
|
|
108484
|
+
.is-tool.is-plugin-tool > div,
|
|
106976
108485
|
.is-tool.is-iframe-tool > div,
|
|
106977
108486
|
.is-tool.is-iframe-tool > button {
|
|
106978
108487
|
width: 35px !important;
|
|
@@ -106982,6 +108491,7 @@ class ContentStuff$1 {
|
|
|
106982
108491
|
|
|
106983
108492
|
.is-tool.is-video-tool svg,
|
|
106984
108493
|
.is-tool.is-audio-tool svg,
|
|
108494
|
+
.is-tool.is-plugin-tool svg,
|
|
106985
108495
|
.is-tool.is-iframe-tool svg {
|
|
106986
108496
|
width: 17px;
|
|
106987
108497
|
height: 17px;
|
|
@@ -123687,6 +125197,143 @@ Add an image for each feature.`, 'Create a new content showcasing a photo galler
|
|
|
123687
125197
|
fromViewToActual(html) {
|
|
123688
125198
|
return this.htmlutil.fromViewToActual(html);
|
|
123689
125199
|
}
|
|
125200
|
+
openColorPicker(currentColor, callback, btn) {
|
|
125201
|
+
this.colorPicker.open(callback, currentColor, () => {}, btn);
|
|
125202
|
+
}
|
|
125203
|
+
openIconPicker(callback) {
|
|
125204
|
+
let iconModal = this.builderStuff.querySelector('.is-modal.iconselect');
|
|
125205
|
+
if (!iconModal) {
|
|
125206
|
+
// Create modal with search input and event listeners once
|
|
125207
|
+
let html = `
|
|
125208
|
+
<style>
|
|
125209
|
+
.is-modal.iconselect .icon-search-wrapper {
|
|
125210
|
+
position: sticky;
|
|
125211
|
+
top: 0;
|
|
125212
|
+
background: white;
|
|
125213
|
+
padding: 20px;
|
|
125214
|
+
border-bottom: 1px solid #ddd;
|
|
125215
|
+
z-index: 10;
|
|
125216
|
+
}
|
|
125217
|
+
.is-modal.iconselect .icon-search {
|
|
125218
|
+
width: 100%;
|
|
125219
|
+
padding: 10px 15px;
|
|
125220
|
+
font-size: 16px;
|
|
125221
|
+
border: 1px solid #ccc;
|
|
125222
|
+
border-radius: 4px;
|
|
125223
|
+
box-sizing: border-box;
|
|
125224
|
+
}
|
|
125225
|
+
.is-modal.iconselect .icon-search:focus {
|
|
125226
|
+
outline: none;
|
|
125227
|
+
border-color: #007bff;
|
|
125228
|
+
}
|
|
125229
|
+
.is-modal.iconselect .icon-list {
|
|
125230
|
+
display: flex;
|
|
125231
|
+
gap: 10px;
|
|
125232
|
+
flex-flow: wrap;
|
|
125233
|
+
padding: 20px;
|
|
125234
|
+
}
|
|
125235
|
+
.is-modal.iconselect .icon-list > button {
|
|
125236
|
+
display: flex !important;
|
|
125237
|
+
width: 80px !important;
|
|
125238
|
+
height: 80px !important;
|
|
125239
|
+
flex: none;
|
|
125240
|
+
font-size: 30px !important;
|
|
125241
|
+
box-shadow: none !important;
|
|
125242
|
+
align-items: center;
|
|
125243
|
+
justify-content: center;
|
|
125244
|
+
}
|
|
125245
|
+
.is-modal.iconselect .icon-list > button.hidden {
|
|
125246
|
+
display: none !important;
|
|
125247
|
+
}
|
|
125248
|
+
.is-modal.iconselect .no-results {
|
|
125249
|
+
padding: 40px 20px;
|
|
125250
|
+
text-align: center;
|
|
125251
|
+
color: #666;
|
|
125252
|
+
display: none;
|
|
125253
|
+
}
|
|
125254
|
+
.is-modal.iconselect .no-results.show {
|
|
125255
|
+
display: block;
|
|
125256
|
+
}
|
|
125257
|
+
</style>
|
|
125258
|
+
<div class="is-modal iconselect" tabindex="-1" role="dialog" aria-modal="true" aria-hidden="true">
|
|
125259
|
+
<div class="is-modal-content scroll" style="width: 90vw; max-width: 908px; height: 70vh; min-height: 525px; padding: 0;">
|
|
125260
|
+
<div style="height: 100%; overflow-y: auto; display: flex; flex-direction: column;">
|
|
125261
|
+
<div class="icon-search-wrapper">
|
|
125262
|
+
<input type="text" class="icon-search" placeholder="Search icons... (e.g., alarm, wrench)" />
|
|
125263
|
+
</div>
|
|
125264
|
+
<div class="icon-list"></div>
|
|
125265
|
+
<div class="no-results">No icons found matching your search.</div>
|
|
125266
|
+
</div>
|
|
125267
|
+
</div>
|
|
125268
|
+
</div>
|
|
125269
|
+
`;
|
|
125270
|
+
this.dom.appendHtml(this.builderStuff, html);
|
|
125271
|
+
iconModal = this.builderStuff.querySelector('.is-modal.iconselect');
|
|
125272
|
+
this.iconModal = iconModal;
|
|
125273
|
+
const iconList = this.builderStuff.querySelector('.icon-list');
|
|
125274
|
+
const searchInput = this.builderStuff.querySelector('.icon-search');
|
|
125275
|
+
const noResults = this.builderStuff.querySelector('.no-results');
|
|
125276
|
+
iconList.innerHTML = this.rte.getIcons2() + this.rte.getIcons();
|
|
125277
|
+
let icons = iconList.querySelectorAll('button');
|
|
125278
|
+
|
|
125279
|
+
// Add click handlers for icons
|
|
125280
|
+
icons.forEach(icon => {
|
|
125281
|
+
icon.addEventListener('click', () => {
|
|
125282
|
+
// Get the current callback from the modal's data
|
|
125283
|
+
const currentCallback = iconModal._currentCallback;
|
|
125284
|
+
if (currentCallback) {
|
|
125285
|
+
currentCallback(icon.innerHTML);
|
|
125286
|
+
}
|
|
125287
|
+
this.util.hideModal(iconModal);
|
|
125288
|
+
});
|
|
125289
|
+
});
|
|
125290
|
+
|
|
125291
|
+
// Add search/filter functionality
|
|
125292
|
+
searchInput.addEventListener('input', e => {
|
|
125293
|
+
const searchTerm = e.target.value.toLowerCase().trim();
|
|
125294
|
+
let visibleCount = 0;
|
|
125295
|
+
icons.forEach(icon => {
|
|
125296
|
+
const iconElement = icon.querySelector('i');
|
|
125297
|
+
if (!iconElement) return;
|
|
125298
|
+
|
|
125299
|
+
// Get class names from the icon
|
|
125300
|
+
const iconClasses = iconElement.className.toLowerCase();
|
|
125301
|
+
|
|
125302
|
+
// Check if search term matches any part of the icon classes
|
|
125303
|
+
if (searchTerm === '' || iconClasses.includes(searchTerm)) {
|
|
125304
|
+
icon.classList.remove('hidden');
|
|
125305
|
+
visibleCount++;
|
|
125306
|
+
} else {
|
|
125307
|
+
icon.classList.add('hidden');
|
|
125308
|
+
}
|
|
125309
|
+
});
|
|
125310
|
+
|
|
125311
|
+
// Show/hide no results message
|
|
125312
|
+
if (visibleCount === 0 && searchTerm !== '') {
|
|
125313
|
+
noResults.classList.add('show');
|
|
125314
|
+
} else {
|
|
125315
|
+
noResults.classList.remove('show');
|
|
125316
|
+
}
|
|
125317
|
+
});
|
|
125318
|
+
|
|
125319
|
+
// Clear search when modal is opened
|
|
125320
|
+
iconModal.addEventListener('modalshow', () => {
|
|
125321
|
+
searchInput.value = '';
|
|
125322
|
+
searchInput.dispatchEvent(new Event('input'));
|
|
125323
|
+
});
|
|
125324
|
+
}
|
|
125325
|
+
|
|
125326
|
+
// Store the current callback and reset search
|
|
125327
|
+
iconModal._currentCallback = callback;
|
|
125328
|
+
const searchInput = iconModal.querySelector('.icon-search');
|
|
125329
|
+
if (searchInput) {
|
|
125330
|
+
searchInput.value = '';
|
|
125331
|
+
searchInput.dispatchEvent(new Event('input'));
|
|
125332
|
+
// Focus search input after a brief delay to ensure modal is visible
|
|
125333
|
+
setTimeout(() => searchInput.focus(), 100);
|
|
125334
|
+
}
|
|
125335
|
+
this.util.showModal(iconModal);
|
|
125336
|
+
}
|
|
123690
125337
|
colorpicker(onPick, defaultcolor) {
|
|
123691
125338
|
// return new ColorPicker({
|
|
123692
125339
|
// onPick: onPick,
|
|
@@ -124338,6 +125985,9 @@ Add an image for each feature.`, 'Create a new content showcasing a photo galler
|
|
|
124338
125985
|
}
|
|
124339
125986
|
});
|
|
124340
125987
|
}
|
|
125988
|
+
openFilePicker(type, callback) {
|
|
125989
|
+
this.openAssetSelect(type, callback);
|
|
125990
|
+
}
|
|
124341
125991
|
openAssetSelect(targetAssetType, callback, defaultValue) {
|
|
124342
125992
|
const inpUrl = document.createElement('input');
|
|
124343
125993
|
|
|
@@ -162390,7 +164040,8 @@ Add an image for each feature.`, 'Create a new block showcasing a photo gallery
|
|
|
162390
164040
|
blockContainer: '.is-box',
|
|
162391
164041
|
slider: this.settings.slider,
|
|
162392
164042
|
onRender: () => {
|
|
162393
|
-
this.settings.onRender();
|
|
164043
|
+
this.settings.onRender(); // Re-init plugins
|
|
164044
|
+
// if(this.win.builderRuntime) this.win.builderRuntime.reinitialize();
|
|
162394
164045
|
},
|
|
162395
164046
|
onChange: () => {
|
|
162396
164047
|
this.settings.onChange();
|
|
@@ -164137,6 +165788,18 @@ Add an image for each feature.`, 'Create a new block showcasing a photo gallery
|
|
|
164137
165788
|
} // init
|
|
164138
165789
|
|
|
164139
165790
|
|
|
165791
|
+
openFilePicker(type, callback) {
|
|
165792
|
+
this.editor.openAssetSelect(type, callback);
|
|
165793
|
+
}
|
|
165794
|
+
|
|
165795
|
+
openColorPicker(currentColor, callback, btn) {
|
|
165796
|
+
this.editor.openColorPicker(currentColor, callback, btn);
|
|
165797
|
+
}
|
|
165798
|
+
|
|
165799
|
+
openIconPicker(callback) {
|
|
165800
|
+
this.editor.openIconPicker(callback);
|
|
165801
|
+
}
|
|
165802
|
+
|
|
164140
165803
|
openPreview() {
|
|
164141
165804
|
this.animateScroll.openPreview();
|
|
164142
165805
|
}
|
|
@@ -164738,7 +166401,7 @@ Add an image for each feature.`, 'Create a new block showcasing a photo gallery
|
|
|
164738
166401
|
this.markScrollTarget(this.activeSection); // adding class 'scroll-target'
|
|
164739
166402
|
}
|
|
164740
166403
|
|
|
164741
|
-
doUndoRedo() {
|
|
166404
|
+
async doUndoRedo() {
|
|
164742
166405
|
/*
|
|
164743
166406
|
// Clean
|
|
164744
166407
|
const elms = this.wrapperEl.querySelectorAll(`[data-center],[data-center-top],[data--50-bottom],
|
|
@@ -164807,6 +166470,9 @@ Add an image for each feature.`, 'Create a new block showcasing a photo gallery
|
|
|
164807
166470
|
if (this.controlPanel) {
|
|
164808
166471
|
this.controlpanel.select('');
|
|
164809
166472
|
}
|
|
166473
|
+
|
|
166474
|
+
const runtime = this.win.builderRuntime;
|
|
166475
|
+
if (runtime) runtime.reinitialize();
|
|
164810
166476
|
}
|
|
164811
166477
|
|
|
164812
166478
|
refreshAnim() {
|
|
@@ -165051,7 +166717,9 @@ Add an image for each feature.`, 'Create a new block showcasing a photo gallery
|
|
|
165051
166717
|
});
|
|
165052
166718
|
this.editor.applyBehavior(); // Re-apply
|
|
165053
166719
|
|
|
165054
|
-
this.settings.onRender();
|
|
166720
|
+
this.settings.onRender(); // Re-init plugins
|
|
166721
|
+
|
|
166722
|
+
if (this.win.builderRuntime) this.win.builderRuntime.reinitialize();
|
|
165055
166723
|
const scrollButton = this.wrapperEl.querySelectorAll('.is-arrow-down a');
|
|
165056
166724
|
scrollButton.forEach(btn => {
|
|
165057
166725
|
btn.addEventListener('click', e => {
|
|
@@ -165838,7 +167506,9 @@ Add an image for each feature.`, 'Create a new block showcasing a photo gallery
|
|
|
165838
167506
|
this.pageSetupDone = true; //prevent duplicate for traditional pageSetup on init
|
|
165839
167507
|
// Re-apply
|
|
165840
167508
|
|
|
165841
|
-
this.settings.onRender();
|
|
167509
|
+
this.settings.onRender(); // Re-init plugins
|
|
167510
|
+
|
|
167511
|
+
if (this.win.builderRuntime) this.win.builderRuntime.reinitialize();
|
|
165842
167512
|
const scrollButton = wrapper.querySelectorAll('.is-arrow-down a');
|
|
165843
167513
|
scrollButton.forEach(btn => {
|
|
165844
167514
|
btn.addEventListener('click', e => {
|