@innovastudio/contentbox 1.6.165 → 1.6.166

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@innovastudio/contentbox",
3
3
  "type": "module",
4
- "version": "1.6.165",
4
+ "version": "1.6.166",
5
5
  "description": "",
6
6
  "main": "public/contentbox/contentbox.esm.js",
7
7
  "types": "index.d.ts",
@@ -59,7 +59,7 @@
59
59
  "ws": "^8.13.0"
60
60
  },
61
61
  "dependencies": {
62
- "@innovastudio/contentbuilder": "^1.5.163",
62
+ "@innovastudio/contentbuilder": "^1.5.164",
63
63
  "js-beautify": "^1.14.0",
64
64
  "sortablejs": "^1.15.2"
65
65
  }
@@ -12625,6 +12625,45 @@ class SettingsUIGenerator$1 {
12625
12625
  return label;
12626
12626
  }
12627
12627
 
12628
+ hexToRgba(color) {
12629
+ // If it's already rgba() or rgb(), just return as-is
12630
+ if (/^rgba?\(/i.test(color)) {
12631
+ return color;
12632
+ } // Otherwise assume hex
12633
+
12634
+
12635
+ color = color.replace(/^#/, ''); // Expand shorthand hex (#rgb or #rgba)
12636
+
12637
+ if (color.length === 3 || color.length === 4) {
12638
+ color = color.split('').map(ch => ch + ch).join('');
12639
+ } // Parse r, g, b, a
12640
+
12641
+
12642
+ const r = parseInt(color.substring(0, 2), 16);
12643
+ const g = parseInt(color.substring(2, 4), 16);
12644
+ const b = parseInt(color.substring(4, 6), 16);
12645
+ const a = color.length === 8 ? parseInt(color.substring(6, 8), 16) / 255 : 1;
12646
+ return `rgba(${r}, ${g}, ${b}, ${a})`;
12647
+ }
12648
+
12649
+ toHex(colorStr) {
12650
+ const ctx = document.createElement('canvas').getContext('2d');
12651
+ ctx.fillStyle = colorStr;
12652
+ const computed = ctx.fillStyle; // browser-normalized color
12653
+ // If already hex and 7 or 9 chars long (#rrggbb or #rrggbbaa), return as-is
12654
+
12655
+ if (computed.startsWith('#')) return computed; // Parse rgba(...) or rgb(...)
12656
+
12657
+ const rgba = computed.match(/\d+(\.\d+)?/g).map(Number);
12658
+ const [r, g, b, a = 1] = rgba; // default alpha = 1
12659
+ // Convert RGB and Alpha to hex (2 digits each)
12660
+
12661
+ const rgbHex = [r, g, b].map(v => v.toString(16).padStart(2, '0')).join('');
12662
+ const alphaHex = Math.round(a * 255).toString(16).padStart(2, '0'); // Only include alpha if it's not fully opaque
12663
+
12664
+ return a < 1 ? `#${rgbHex}${alphaHex}` : `#${rgbHex}`;
12665
+ }
12666
+
12628
12667
  createColorInput(name, config, value) {
12629
12668
  const container = document.createElement('div');
12630
12669
  const label = document.createElement('label');
@@ -12645,13 +12684,15 @@ class SettingsUIGenerator$1 {
12645
12684
  colorBtn.type = 'button';
12646
12685
  colorBtn.title = config.label || 'Color';
12647
12686
  colorBtn.className = 'btn-color is-btn-color';
12648
- colorBtn.style.backgroundColor = input.value;
12687
+ colorBtn.style.backgroundColor = this.hexToRgba(input.value);
12649
12688
  colorBtn.setAttribute('aria-label', `Choose color for ${config.label || name}`);
12650
12689
 
12651
12690
  const openPicker = e => {
12652
12691
  e.preventDefault();
12653
12692
  this.builder.openColorPicker(input.value, color => {
12654
- input.value = color;
12693
+ // input.value = color;
12694
+ const hex = this.toHex(color);
12695
+ input.value = hex;
12655
12696
  colorBtn.style.backgroundColor = color;
12656
12697
  input.dispatchEvent(new Event('change', {
12657
12698
  bubbles: true
@@ -12717,10 +12758,10 @@ class SettingsUIGenerator$1 {
12717
12758
  input.type = 'range';
12718
12759
  input.className = 'is-rangeslider';
12719
12760
  input.name = name;
12720
- input.value = currentVal;
12721
12761
  if (config.min !== undefined) input.min = config.min;
12722
12762
  if (config.max !== undefined) input.max = config.max;
12723
12763
  if (config.step !== undefined) input.step = config.step;
12764
+ input.value = currentVal;
12724
12765
  input.addEventListener('input', () => {
12725
12766
  labelText.textContent = (config.label || name) + ': ' + input.value + (config.unit || '');
12726
12767
  });
@@ -12853,7 +12894,23 @@ class PanelPlugin {
12853
12894
  const originalElement = editableClone.cloneNode(false); // Let plugin handle everything - pass the editable clone
12854
12895
 
12855
12896
  const editorUI = plugin.editor.openContentEditor(editableClone, this.builder, async () => {
12856
- this.builder.editor.saveForUndo(); // Store edited content from clone
12897
+ this.builder.editor.saveForUndo(); // Add index for sortable grid (for easy reorder)
12898
+
12899
+ let grid = editableClone.querySelector('.grid-sortable');
12900
+
12901
+ if (!grid) {
12902
+ if (currentElement.classList.contains('grid-sortable')) {
12903
+ grid = editableClone;
12904
+ }
12905
+ }
12906
+
12907
+ if (grid) {
12908
+ Array.from(grid.children).forEach((child, index) => {
12909
+ if (child.nodeType === 1 && child.tagName !== 'STYLE' && child.tagName !== 'SCRIPT') child.setAttribute('data-index', index);
12910
+ });
12911
+ } //----
12912
+ // Store edited content from clone
12913
+
12857
12914
 
12858
12915
  currentElement.setAttribute('data-cb-original-content', editableClone.innerHTML); // Also performs similar value updates like the applyChanges()
12859
12916
  // currentElement.setAttribute('data-cb-content', editableClone.getAttribute('data-cb-content'));
@@ -12884,6 +12941,8 @@ class PanelPlugin {
12884
12941
 
12885
12942
  await runtime.reinitialize(currentElement.parentElement);
12886
12943
  this.builder.onChange();
12944
+ this.builder.editor.hideElementTools();
12945
+ this.builder.makeSortable(currentElement.parentElement);
12887
12946
  });
12888
12947
  div.appendChild(editorUI);
12889
12948
  } // Generate form
@@ -12906,6 +12965,8 @@ class PanelPlugin {
12906
12965
  const container = this.currentElement.parentElement || this.currentElement.closest('.is-wrapper');
12907
12966
  await runtime.reinitialize(container);
12908
12967
  this.builder.onChange(); // console.log('Settings applied and component reinitialized');
12968
+
12969
+ this.builder.makeSortable(container);
12909
12970
  }
12910
12971
 
12911
12972
  }
@@ -26934,6 +26995,7 @@ class ControlPanel {
26934
26995
  }
26935
26996
 
26936
26997
  const plugin = runtime.getPlugin(pluginName);
26998
+ if (!plugin) return;
26937
26999
  let titleHtml = out(plugin.displayName || plugin.name);
26938
27000
  this.title.innerHTML = titleHtml; // this.title.style.cssText = 'margin-bottom: 0';
26939
27001
 
@@ -32643,18 +32705,21 @@ class Util$1 {
32643
32705
  let builderActive = this.builder.doc.querySelector('.builder-active');
32644
32706
  if (builderActive) this.builder.applyBehaviorOn(builderActive);
32645
32707
  this.fixLayout(row);
32646
-
32647
- // cellElement.click(); //change active block to the newly created
32648
- if (element && element.tagName.toLowerCase() === 'img') {
32649
- element.onload = () => {
32708
+ try {
32709
+ // cellElement.click(); //change active block to the newly created
32710
+ if (element && element.tagName.toLowerCase() === 'img') {
32711
+ element.onload = () => {
32712
+ element.click();
32713
+ element.onload = null;
32714
+ setTimeout(() => {
32715
+ this.builder.element.image.repositionImageTool();
32716
+ }, 100);
32717
+ };
32718
+ } else if (element) {
32650
32719
  element.click();
32651
- element.onload = null;
32652
- setTimeout(() => {
32653
- this.builder.element.image.repositionImageTool();
32654
- }, 100);
32655
- };
32656
- } else if (element) {
32657
- element.click();
32720
+ }
32721
+ } catch (e) {
32722
+ // Do Nothing
32658
32723
  }
32659
32724
  }
32660
32725
  if (mode === 'row') {
@@ -32772,6 +32837,10 @@ class Util$1 {
32772
32837
 
32773
32838
  //Trigger Render event
32774
32839
  this.builder.opts.onRender();
32840
+
32841
+ // Reinit after drag drop block
32842
+ if (this.builder.win.builderRuntime) this.builder.win.builderRuntime.reinitialize();
32843
+ this.builder.makeSortable(this.builder.doc);
32775
32844
  }
32776
32845
 
32777
32846
  // https://stackoverflow.com/questions/494143/creating-a-new-dom-element-from-an-html-string-using-built-in-dom-methods-or-pro
@@ -32864,6 +32933,7 @@ class Util$1 {
32864
32933
 
32865
32934
  //Hide Column tool (new!)
32866
32935
  this.builder.util.hideColumnTool();
32936
+ this.builder.makeSortable(rowElement);
32867
32937
  } else if (bSnippet) {
32868
32938
  if (noedit) {
32869
32939
  this.addContent(html, mode, 'data-noedit');
@@ -33037,6 +33107,7 @@ class Util$1 {
33037
33107
  this.builder.doc.querySelectorAll('.dummy-module').forEach(module => {
33038
33108
  module.classList.remove('dummy-module');
33039
33109
  });
33110
+ this.builder.makeSortable(this.builder.doc);
33040
33111
 
33041
33112
  // Change to row selection
33042
33113
  rowElement.className = rowElement.className.replace('row-outline', '');
@@ -41663,6 +41734,7 @@ class HtmlUtil {
41663
41734
  // Re-init plugins
41664
41735
  if (this.builder.win.builderRuntime) await this.builder.win.builderRuntime.reinitialize();
41665
41736
  this.builder.hideModal(modal);
41737
+ this.builder.makeSortable(this.builder.doc);
41666
41738
  }
41667
41739
  viewHtmlExternal() {
41668
41740
  const util = this.builder.util;
@@ -42235,6 +42307,25 @@ class HtmlUtil {
42235
42307
  element.classList.remove(`cb-${type}`);
42236
42308
  }
42237
42309
  element.removeAttribute('data-cb-loaded');
42310
+ let grid;
42311
+ if (element.classList.contains('grid-sortable')) {
42312
+ grid = element;
42313
+ }
42314
+ if (element.querySelector('.grid-sortable')) {
42315
+ grid = element.querySelector('.grid-sortable');
42316
+ }
42317
+ element.removeAttribute('id');
42318
+ if (grid) {
42319
+ Array.from(grid.children).forEach(elm => {
42320
+ elm.removeAttribute('data-index');
42321
+ elm.removeAttribute('data-item-id');
42322
+ });
42323
+ }
42324
+ const elms = element.querySelectorAll('[data-scroll], [data-scroll-once]');
42325
+ elms.forEach(elm => {
42326
+ elm.removeAttribute('data-scroll');
42327
+ elm.removeAttribute('data-scroll-once');
42328
+ });
42238
42329
  });
42239
42330
  html = '';
42240
42331
  if (multiple) {
@@ -43367,6 +43458,9 @@ class Grid {
43367
43458
  this.builder.hideTools();
43368
43459
  cell.parentElement.insertBefore(cell, cell.previousElementSibling);
43369
43460
  this.builder.opts.onChange(true);
43461
+
43462
+ // Re-init plugins
43463
+ if (this.builder.win.builderRuntime) this.builder.win.builderRuntime.reinitialize(cell);
43370
43464
  }
43371
43465
  }
43372
43466
  moveColumnNext() {
@@ -43379,6 +43473,9 @@ class Grid {
43379
43473
  this.builder.hideTools();
43380
43474
  cell.parentElement.insertBefore(cellnext, cell);
43381
43475
  this.builder.opts.onChange(true);
43476
+
43477
+ // Re-init plugins
43478
+ if (this.builder.win.builderRuntime) this.builder.win.builderRuntime.reinitialize(cell);
43382
43479
  }
43383
43480
  }
43384
43481
  moveColumnUp() {
@@ -43408,6 +43505,9 @@ class Grid {
43408
43505
  //Move row up
43409
43506
  row.parentNode.insertBefore(row, row.previousElementSibling);
43410
43507
  this.builder.opts.onChange(true);
43508
+
43509
+ // Re-init plugins
43510
+ if (this.builder.win.builderRuntime) this.builder.win.builderRuntime.reinitialize(row);
43411
43511
  return;
43412
43512
  } else {
43413
43513
  this.builder.uo.saveForUndo();
@@ -43458,6 +43558,9 @@ class Grid {
43458
43558
  row = cell.parentNode; //update active row
43459
43559
  util.fixLayout(row);
43460
43560
  if (row.nextElementSibling) util.fixLayout(row.nextElementSibling);
43561
+
43562
+ // Re-init plugins
43563
+ if (this.builder.win.builderRuntime) this.builder.win.builderRuntime.reinitialize(row);
43461
43564
  }
43462
43565
  moveColumnDown() {
43463
43566
  let builder = this.builder;
@@ -43488,6 +43591,9 @@ class Grid {
43488
43591
  //Move row down
43489
43592
  row.parentNode.insertBefore(row.nextElementSibling, row);
43490
43593
  this.builder.opts.onChange(true);
43594
+
43595
+ // Re-init plugins
43596
+ if (this.builder.win.builderRuntime) this.builder.win.builderRuntime.reinitialize(row);
43491
43597
  return;
43492
43598
  } else {
43493
43599
  this.builder.uo.saveForUndo();
@@ -43538,6 +43644,9 @@ class Grid {
43538
43644
  row = cell.parentNode; //update active row
43539
43645
  util.fixLayout(row);
43540
43646
  if (row.previousElementSibling) util.fixLayout(row.previousElementSibling);
43647
+
43648
+ // Re-init plugins
43649
+ if (this.builder.win.builderRuntime) this.builder.win.builderRuntime.reinitialize(row);
43541
43650
  }
43542
43651
  duplicateColumn() {
43543
43652
  let builder = this.builder;
@@ -44241,6 +44350,9 @@ class Grid {
44241
44350
  row.parentNode.insertBefore(row, row.previousElementSibling);
44242
44351
  this.rowTool.position(row);
44243
44352
  this.builder.opts.onChange();
44353
+
44354
+ // Re-init plugins
44355
+ if (this.builder.win.builderRuntime) this.builder.win.builderRuntime.reinitialize(row);
44244
44356
  } else {
44245
44357
  // Move to previous container
44246
44358
 
@@ -44285,6 +44397,9 @@ class Grid {
44285
44397
  row.parentNode.insertBefore(row.nextElementSibling, row);
44286
44398
  this.rowTool.position(row);
44287
44399
  this.builder.opts.onChange();
44400
+
44401
+ // Re-init plugins
44402
+ if (this.builder.win.builderRuntime) this.builder.win.builderRuntime.reinitialize(row);
44288
44403
  } else {
44289
44404
  // Move to next container
44290
44405
 
@@ -123188,6 +123303,7 @@ Add an image for each feature.`, 'Create a new content showcasing a photo galler
123188
123303
  }
123189
123304
  });
123190
123305
  });
123306
+ this.makeSortable(this.doc);
123191
123307
  }
123192
123308
 
123193
123309
  // Load plugins
@@ -125165,6 +125281,7 @@ Add an image for each feature.`, 'Create a new content showcasing a photo galler
125165
125281
  });
125166
125282
  });
125167
125283
  });
125284
+ this.makeSortable(this.doc);
125168
125285
  this.doc.querySelectorAll('.dummy-module').forEach(module => {
125169
125286
  module.classList.remove('dummy-module');
125170
125287
  });
@@ -126952,24 +127069,7 @@ Please obtain a license at: https://innovastudio.com/contentbox`);
126952
127069
  });
126953
127070
 
126954
127071
  // On Load HTML
126955
- const grids = this.doc.querySelectorAll('.grid-sortable');
126956
- grids.forEach(grid => {
126957
- if (grid.closest('.is-builder')) new Sortable$1(grid, {
126958
- animation: 600,
126959
- dragClass: 'hide-drag-class',
126960
- onStart: () => {
126961
- this.uo.saveForUndo(true);
126962
- },
126963
- onSort: () => {
126964
- if (grid.closest('[data-html]')) {
126965
- const moduleElm = grid.closest('[data-html]');
126966
- const encodedHtml = encodeURIComponent(moduleElm.innerHTML);
126967
- moduleElm.setAttribute('data-html', encodedHtml);
126968
- this.opts.onChange();
126969
- }
126970
- }
126971
- });
126972
- });
127072
+ this.makeSortable(this.doc, true);
126973
127073
  this.refresh();
126974
127074
  if (this.win.Block) {
126975
127075
  this.win.Block.render();
@@ -127009,24 +127109,7 @@ Please obtain a license at: https://innovastudio.com/contentbox`);
127009
127109
  this.applyBehavior();
127010
127110
 
127011
127111
  // On Load HTML
127012
- const grids = this.doc.querySelectorAll('.grid-sortable');
127013
- grids.forEach(grid => {
127014
- if (grid.closest('.is-builder')) new Sortable$1(grid, {
127015
- animation: 600,
127016
- dragClass: 'hide-drag-class',
127017
- onStart: () => {
127018
- this.uo.saveForUndo(true);
127019
- },
127020
- onSort: () => {
127021
- if (grid.closest('[data-html]')) {
127022
- const moduleElm = grid.closest('[data-html]');
127023
- const encodedHtml = encodeURIComponent(moduleElm.innerHTML);
127024
- moduleElm.setAttribute('data-html', encodedHtml);
127025
- this.opts.onChange();
127026
- }
127027
- }
127028
- });
127029
- });
127112
+ this.makeSortable(this.doc, true);
127030
127113
 
127031
127114
  // this.uo.saveForUndo(); //First time
127032
127115
 
@@ -129625,6 +129708,86 @@ Please obtain a license at: https://innovastudio.com/contentbox`);
129625
129708
  // this.doc.querySelectorAll('.elm-inspected').forEach(elm => elm.classList.remove('elm-inspected'));
129626
129709
  // this.doc.querySelectorAll('.elm-active').forEach(elm => elm.classList.remove('elm-active'));
129627
129710
  }
129711
+
129712
+ // Make list inside plugin element sortable
129713
+ makeSortable(container, module) {
129714
+ const grids = container.querySelectorAll('.grid-sortable');
129715
+ grids.forEach(grid => {
129716
+ const sortable = new Sortable$1(grid, {
129717
+ animation: 600,
129718
+ onStart: () => {
129719
+ this.uo.saveForUndo(true);
129720
+ },
129721
+ onSort: async () => {
129722
+ if (module) if (grid.closest('[data-html]')) {
129723
+ const moduleElm = grid.closest('[data-html]');
129724
+ const encodedHtml = encodeURIComponent(moduleElm.innerHTML);
129725
+ moduleElm.setAttribute('data-html', encodedHtml);
129726
+ this.onChange();
129727
+ }
129728
+ let plugin = grid.closest('[data-cb-original-content]');
129729
+ if (plugin) {
129730
+ // grid.setAttribute('data-cb-original-content', plugin.innerHTML); // don't do this!
129731
+ this.updateCleanContentOrder(grid); // use this!
129732
+
129733
+ this.onChange();
129734
+ grid.click();
129735
+ }
129736
+ }
129737
+ });
129738
+ // Store instance for later access
129739
+ grid._sortable = sortable;
129740
+ });
129741
+ }
129742
+ async updateCleanContentOrder(grid) {
129743
+ if (!grid) return;
129744
+ const plugin = grid.closest('[data-cb-original-content]');
129745
+ if (!plugin) return;
129746
+
129747
+ // 1. Get the current visual order of data-index from the actual DOM
129748
+ const newIndexOrder = Array.from(grid.children).map(child => child.dataset.index).filter(index => index !== undefined); // in case some lack data-index
129749
+
129750
+ // 2. Parse the *original clean* HTML
129751
+ const cleanHtml = plugin.dataset.cbOriginalContent;
129752
+ const parser = new DOMParser();
129753
+ const doc = parser.parseFromString(cleanHtml, 'text/html');
129754
+ let cleanGrid = doc.querySelector('.grid-sortable');
129755
+ let pluginIsSortableRoot = false;
129756
+ if (!cleanGrid) {
129757
+ // console.log('[step] .grid-sortable not found inside parsed clean HTML — creating wrapper from body children');
129758
+ cleanGrid = doc.createElement('div');
129759
+ cleanGrid.classList.add('grid-sortable');
129760
+ Array.from(doc.body.children).forEach(child => {
129761
+ if (child.nodeType === 1) cleanGrid.appendChild(child);
129762
+ });
129763
+ doc.body.innerHTML = '';
129764
+ doc.body.appendChild(cleanGrid);
129765
+ pluginIsSortableRoot = true;
129766
+ }
129767
+ const cleanItems = Array.from(cleanGrid.children).filter(el => el.nodeType === 1);
129768
+
129769
+ // 3. Build a map: data-index → clean node
129770
+ const cleanItemMap = new Map();
129771
+ cleanItems.forEach(item => {
129772
+ const idx = item.dataset.index;
129773
+ if (idx !== undefined) cleanItemMap.set(idx, item);
129774
+ });
129775
+
129776
+ // 4. Reorder clean items to match newIndexOrder
129777
+ const reorderedCleanItems = newIndexOrder.map(idx => cleanItemMap.get(idx)).filter(Boolean); // skip missing
129778
+
129779
+ // 5. Serialize back to HTML string
129780
+ const newCleanHtml = reorderedCleanItems.map(item => item.outerHTML).join('');
129781
+
129782
+ // 6. Update the clean source
129783
+ if (pluginIsSortableRoot) {
129784
+ plugin.setAttribute('data-cb-original-content', newCleanHtml);
129785
+ } else {
129786
+ cleanGrid.innerHTML = newCleanHtml;
129787
+ const newHTML = doc.body.innerHTML;
129788
+ plugin.setAttribute('data-cb-original-content', newHTML);
129789
+ }
129790
+ }
129628
129791
  }
129629
129792
 
129630
129793
  class ContentStuff {
@@ -165286,23 +165449,7 @@ Add an image for each feature.`, 'Create a new block showcasing a photo gallery
165286
165449
  activeModule.appendChild(range.createContextualFragment(html)); // We use createContextualFragment so that embedded javascript code (code block) will be executed
165287
165450
 
165288
165451
  this.settings.onRender();
165289
- const grids = activeModule.querySelectorAll('.grid-sortable');
165290
- grids.forEach(grid => {
165291
- new Sortable(grid, {
165292
- animation: 600,
165293
- onStart: () => {
165294
- this.editor.saveForUndo(true);
165295
- },
165296
- onSort: () => {
165297
- if (grid.closest('[data-html]')) {
165298
- const moduleElm = grid.closest('[data-html]');
165299
- const encodedHtml = encodeURIComponent(moduleElm.innerHTML);
165300
- moduleElm.setAttribute('data-html', encodedHtml);
165301
- this.onChange();
165302
- }
165303
- }
165304
- });
165305
- });
165452
+ this.makeSortable(activeModule);
165306
165453
  this.settings.onChange();
165307
165454
  this.hideModal(modalEditModule);
165308
165455
  });
@@ -166369,27 +166516,89 @@ Add an image for each feature.`, 'Create a new block showcasing a photo gallery
166369
166516
  const typePanel = document.querySelector('#divSidebarTypography');
166370
166517
  if (typePanel.classList.contains('active')) this.sidebar.selectType();
166371
166518
  });
166372
- const grids = box.querySelectorAll('.grid-sortable'); // apply sortable to all grids, including any in is-container,
166373
- // since ContentBuilder loadHTML is not used here in ContentBox
166519
+ this.makeSortable(box, true);
166520
+ } // boxSetup
166521
+ // Make list inside plugin element sortable
166522
+
166374
166523
 
166524
+ makeSortable(container, module) {
166525
+ const grids = container.querySelectorAll('.grid-sortable');
166375
166526
  grids.forEach(grid => {
166376
- new Sortable(grid, {
166527
+ const sortable = new Sortable(grid, {
166377
166528
  animation: 600,
166378
166529
  onStart: () => {
166379
166530
  this.editor.saveForUndo(true);
166380
166531
  },
166381
- onSort: () => {
166382
- if (grid.closest('[data-html]')) {
166532
+ onSort: async () => {
166533
+ if (module) if (grid.closest('[data-html]')) {
166383
166534
  const moduleElm = grid.closest('[data-html]');
166384
166535
  const encodedHtml = encodeURIComponent(moduleElm.innerHTML);
166385
166536
  moduleElm.setAttribute('data-html', encodedHtml);
166386
166537
  this.onChange();
166387
166538
  }
166539
+ let plugin = grid.closest('[data-cb-original-content]');
166540
+
166541
+ if (plugin) {
166542
+ // grid.setAttribute('data-cb-original-content', plugin.innerHTML); // don't do this!
166543
+ this.updateCleanContentOrder(grid); // use this!
166544
+
166545
+ this.onChange();
166546
+ grid.click();
166547
+ }
166388
166548
  }
166389
- });
166549
+ }); // Store instance for later access
166550
+
166551
+ grid._sortable = sortable;
166390
166552
  });
166391
- } // boxSetup
166553
+ }
166554
+
166555
+ async updateCleanContentOrder(grid) {
166556
+ if (!grid) return;
166557
+ const plugin = grid.closest('[data-cb-original-content]');
166558
+ if (!plugin) return; // 1. Get the current visual order of data-index from the actual DOM
166559
+
166560
+ const newIndexOrder = Array.from(grid.children).map(child => child.dataset.index).filter(index => index !== undefined); // in case some lack data-index
166561
+ // 2. Parse the *original clean* HTML
166562
+
166563
+ const cleanHtml = plugin.dataset.cbOriginalContent;
166564
+ const parser = new DOMParser();
166565
+ const doc = parser.parseFromString(cleanHtml, 'text/html');
166566
+ let cleanGrid = doc.querySelector('.grid-sortable');
166567
+ let pluginIsSortableRoot = false;
166392
166568
 
166569
+ if (!cleanGrid) {
166570
+ // console.log('[step] .grid-sortable not found inside parsed clean HTML — creating wrapper from body children');
166571
+ cleanGrid = doc.createElement('div');
166572
+ cleanGrid.classList.add('grid-sortable');
166573
+ Array.from(doc.body.children).forEach(child => {
166574
+ if (child.nodeType === 1) cleanGrid.appendChild(child);
166575
+ });
166576
+ doc.body.innerHTML = '';
166577
+ doc.body.appendChild(cleanGrid);
166578
+ pluginIsSortableRoot = true;
166579
+ }
166580
+
166581
+ const cleanItems = Array.from(cleanGrid.children).filter(el => el.nodeType === 1); // 3. Build a map: data-index → clean node
166582
+
166583
+ const cleanItemMap = new Map();
166584
+ cleanItems.forEach(item => {
166585
+ const idx = item.dataset.index;
166586
+ if (idx !== undefined) cleanItemMap.set(idx, item);
166587
+ }); // 4. Reorder clean items to match newIndexOrder
166588
+
166589
+ const reorderedCleanItems = newIndexOrder.map(idx => cleanItemMap.get(idx)).filter(Boolean); // skip missing
166590
+ // 5. Serialize back to HTML string
166591
+
166592
+ const newCleanHtml = reorderedCleanItems.map(item => item.outerHTML).join(''); // 6. Update the clean source
166593
+
166594
+ if (pluginIsSortableRoot) {
166595
+ plugin.setAttribute('data-cb-original-content', newCleanHtml);
166596
+ } else {
166597
+ cleanGrid.innerHTML = newCleanHtml;
166598
+ const newHTML = doc.body.innerHTML;
166599
+ plugin.setAttribute('data-cb-original-content', newHTML);
166600
+ }
166601
+ }
166393
166602
 
166394
166603
  addSection(html, contentCss) {
166395
166604
  let newSection = this.addIdea(html);