@themarioga/grid-editor 8.1.0 → 8.3.0
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/CHANGELOG.md +22 -0
- package/README.md +19 -0
- package/dist/grideditor.bundle.min.js +3 -3
- package/dist/grideditor.css +76 -0
- package/dist/grideditor.d.ts +16 -0
- package/dist/grideditor.esm.js +218 -12
- package/dist/grideditor.jquery.js +3 -1
- package/dist/grideditor.jquery.min.js +1 -1
- package/dist/grideditor.jquery.min.js.map +2 -2
- package/dist/grideditor.js +218 -12
- package/dist/grideditor.min.css +1 -1
- package/dist/grideditor.min.css.map +1 -1
- package/dist/grideditor.min.js +2 -2
- package/dist/grideditor.min.js.map +3 -3
- package/dist/locales/grideditor.es.esm.js +1 -0
- package/dist/locales/grideditor.es.js +1 -0
- package/dist/locales/grideditor.es.min.js +1 -1
- package/dist/locales/grideditor.es.min.js.map +2 -2
- package/docs/events.md +7 -0
- package/docs/locale-keys.md +1 -0
- package/package.json +1 -1
- package/src/js/grideditor.jquery.js +2 -0
- package/src/js/grideditor.js +229 -9
- package/src/js/locales/grideditor.es.js +1 -0
- package/src/less/grideditor.less +86 -0
- package/types/grideditor.d.ts +16 -0
package/src/js/grideditor.js
CHANGED
|
@@ -35,6 +35,8 @@ var METHODS = {
|
|
|
35
35
|
setLocale: {},
|
|
36
36
|
getUtility: { value: true },
|
|
37
37
|
setUtility: { value: true },
|
|
38
|
+
getActiveTarget: { value: true },
|
|
39
|
+
setActiveTarget: {},
|
|
38
40
|
};
|
|
39
41
|
|
|
40
42
|
/**
|
|
@@ -364,6 +366,8 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
364
366
|
'row_tools' : [],
|
|
365
367
|
'drag_handle' : 'tool', // 'tool' for the move tool, 'drawer' for the whole drawer
|
|
366
368
|
'toolbar_drag' : 'auto', // Drag the toolbar's buttons onto the canvas. 'auto' follows drag_handle
|
|
369
|
+
'toolbar_overflow' : 'menu', // What doesn't fit on one line: 'menu' behind a button, 'wrap' onto another line
|
|
370
|
+
'active_target' : false, // A click in a column makes it where the toolbar's buttons add
|
|
367
371
|
'element_tools' : [], // Host tools on element drawers, same shape as row_tools
|
|
368
372
|
'element_classes' : [], // Preset class toggles on an element's settings panel
|
|
369
373
|
// content_types, text_tools and text_classes are the text editor
|
|
@@ -416,6 +420,9 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
416
420
|
var sizePicker = null; // The open column size picker, if there is one
|
|
417
421
|
var sourceOpen = false; // Whether the canvas is being edited as html
|
|
418
422
|
var dropMarker = null; // The line showing where a dragged toolbar button would land
|
|
423
|
+
var activeTarget = null; // The column or region the toolbar adds to, with active_target
|
|
424
|
+
var toolbarLifetime = null; // Aborted when the toolbar is rebuilt or taken away
|
|
425
|
+
var closeOverflowMenu = function() {}; // Folds the toolbar's overflow menu away, when it has one
|
|
419
426
|
var warnedHere = {}; // Some warnings are worth saying once per instance, not once per call
|
|
420
427
|
var sortables = []; // Every list made sortable, so deinit destroys exactly those
|
|
421
428
|
var instanceId = ++editorCounter; // Scopes the sortable groups to this editor
|
|
@@ -460,7 +467,7 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
460
467
|
removeConfirmModal();
|
|
461
468
|
removeSettingsPanels();
|
|
462
469
|
removeDialog();
|
|
463
|
-
|
|
470
|
+
removeMainControls();
|
|
464
471
|
createMainControls();
|
|
465
472
|
reset();
|
|
466
473
|
}
|
|
@@ -979,6 +986,35 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
979
986
|
dom.delegate(canvas, 'click', '.ge-popup-trigger, [data-ge-popup-target]', function(e) {
|
|
980
987
|
if (dom.hasClass(canvas, 'ge-editing')) { e.preventDefault(); }
|
|
981
988
|
}, signal);
|
|
989
|
+
|
|
990
|
+
if (settings.active_target) {
|
|
991
|
+
// Whatever the click was for - a text, a tool - it also says
|
|
992
|
+
// where the toolbar adds next. A click on no column or region
|
|
993
|
+
// says nowhere, and the toolbar adds to the canvas again.
|
|
994
|
+
// The click a drag ends in - a resize, a move - is not one:
|
|
995
|
+
// it comes where the pointer let go, not where it pressed
|
|
996
|
+
var pressed = null;
|
|
997
|
+
canvas.addEventListener('pointerdown', function(e) {
|
|
998
|
+
pressed = { x: e.clientX, y: e.clientY };
|
|
999
|
+
}, signal);
|
|
1000
|
+
canvas.addEventListener('click', function(e) {
|
|
1001
|
+
var from = pressed;
|
|
1002
|
+
pressed = null;
|
|
1003
|
+
if (!dom.hasClass(canvas, 'ge-editing')) { return; }
|
|
1004
|
+
if (from && Math.abs(e.clientX - from.x) + Math.abs(e.clientY - from.y) > settings.drag.threshold) { return; }
|
|
1005
|
+
var region = dom.closest(e.target, targetSelector());
|
|
1006
|
+
changeTarget(region && region !== canvas && canvas.contains(region) ? region : null);
|
|
1007
|
+
}, signal);
|
|
1008
|
+
|
|
1009
|
+
// Escape is the panel's, the dialog's, and a field's or a
|
|
1010
|
+
// text's while one has the focus; after those, it is this
|
|
1011
|
+
document.addEventListener('keydown', function(e) {
|
|
1012
|
+
if (e.key !== 'Escape' || !activeTarget || openSettingsState || dialogState) { return; }
|
|
1013
|
+
var focus = document.activeElement;
|
|
1014
|
+
if (focus && (dom.is(focus, 'input, textarea, select') || focus.isContentEditable)) { return; }
|
|
1015
|
+
changeTarget(null);
|
|
1016
|
+
}, signal);
|
|
1017
|
+
}
|
|
982
1018
|
}
|
|
983
1019
|
|
|
984
1020
|
/**
|
|
@@ -1002,8 +1038,17 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1002
1038
|
htmlTextArea.parentNode.insertBefore(mainControls, htmlTextArea);
|
|
1003
1039
|
wrapper = mainControls.appendChild(dom.element('div', { 'class': 'ge-wrapper ge-top' }));
|
|
1004
1040
|
|
|
1041
|
+
// With the overflow menu, the add buttons stand in a line of
|
|
1042
|
+
// their own that keeps to one line, beside the menu's button
|
|
1043
|
+
var overflowMenu = settings.toolbar_overflow !== 'wrap';
|
|
1044
|
+
var start = wrapper;
|
|
1045
|
+
if (overflowMenu) {
|
|
1046
|
+
dom.addClass(wrapper, 'ge-toolbar-menu');
|
|
1047
|
+
start = wrapper.appendChild(dom.element('div', { 'class': 'ge-toolbar-start' }));
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1005
1050
|
// Add row
|
|
1006
|
-
addRowGroup =
|
|
1051
|
+
addRowGroup = start.appendChild(dom.element('div', { 'class': 'ge-addRowGroup btn-group' }));
|
|
1007
1052
|
addContainerGroup = dom.element('div', { 'class': 'ge-addContainerGroup btn-group ms-1' });
|
|
1008
1053
|
settings.new_row_layouts.forEach(function(layout) {
|
|
1009
1054
|
var grouped = !Array.isArray(layout);
|
|
@@ -1019,6 +1064,8 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1019
1064
|
});
|
|
1020
1065
|
|
|
1021
1066
|
btn.addEventListener('click', function() {
|
|
1067
|
+
if (activeTarget) { addToTarget(btn, 'tool'); return; }
|
|
1068
|
+
|
|
1022
1069
|
var row = rowFromLayoutValue(layout);
|
|
1023
1070
|
|
|
1024
1071
|
var added = addNode('row', row, function() {
|
|
@@ -1044,7 +1091,7 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1044
1091
|
btn.appendChild(dom.create(icon));
|
|
1045
1092
|
});
|
|
1046
1093
|
|
|
1047
|
-
|
|
1094
|
+
start.appendChild(addContainerGroup);
|
|
1048
1095
|
|
|
1049
1096
|
// A container starts in a row of its own, the way the add row
|
|
1050
1097
|
// buttons next to these ones do
|
|
@@ -1055,6 +1102,8 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1055
1102
|
|
|
1056
1103
|
dom.attr(button, { 'data-ge-toolbar': 'container', 'data-ge-container-type': type });
|
|
1057
1104
|
button.addEventListener('click', function() {
|
|
1105
|
+
if (activeTarget) { addToTarget(button, 'tool'); return; }
|
|
1106
|
+
|
|
1058
1107
|
var row = createRow();
|
|
1059
1108
|
var column = row.appendChild(createColumn(MAX_COL_SIZE));
|
|
1060
1109
|
var container = column.appendChild(definition.create({}));
|
|
@@ -1149,9 +1198,111 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1149
1198
|
endItems.forEach(function(button) { end.appendChild(button); });
|
|
1150
1199
|
}
|
|
1151
1200
|
|
|
1201
|
+
if (overflowMenu) { createOverflowMenu(start); }
|
|
1202
|
+
|
|
1152
1203
|
makeToolbarDraggable();
|
|
1153
1204
|
}
|
|
1154
1205
|
|
|
1206
|
+
/** The toolbar off the page, with what keeps its overflow menu up to date. */
|
|
1207
|
+
function removeMainControls() {
|
|
1208
|
+
if (toolbarLifetime) { toolbarLifetime.abort(); }
|
|
1209
|
+
toolbarLifetime = null;
|
|
1210
|
+
closeOverflowMenu = function() {};
|
|
1211
|
+
mainControls.remove();
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
/**
|
|
1215
|
+
* The add buttons that don't fit on the toolbar's line, behind a
|
|
1216
|
+
* button at its end. They are the same buttons, moved rather than
|
|
1217
|
+
* copied, so a click, a drag or a plugin finding them with
|
|
1218
|
+
* toolbarItems works as it does on the line. Which ones go is worked
|
|
1219
|
+
* out again whenever the toolbar or what is on it changes size.
|
|
1220
|
+
*/
|
|
1221
|
+
function createOverflowMenu(start) {
|
|
1222
|
+
var more = dom.create('<div class="ge-toolbar-more">' +
|
|
1223
|
+
'<button type="button" class="btn btn-sm btn-primary"><i class="bi bi-three-dots"></i></button>' +
|
|
1224
|
+
'<div class="ge-toolbar-overflow">' +
|
|
1225
|
+
'<div class="ge-addRowGroup btn-group"></div>' +
|
|
1226
|
+
'<div class="ge-addContainerGroup btn-group"></div>' +
|
|
1227
|
+
'</div>' +
|
|
1228
|
+
'</div>');
|
|
1229
|
+
start.parentNode.insertBefore(more, start.nextSibling);
|
|
1230
|
+
|
|
1231
|
+
var toggle = dom.one(more, 'button');
|
|
1232
|
+
var panel = dom.one(more, '.ge-toolbar-overflow');
|
|
1233
|
+
var spares = dom.all(panel, '.btn-group');
|
|
1234
|
+
// Every add button with the group it belongs in, in toolbar order
|
|
1235
|
+
var homes = [];
|
|
1236
|
+
[addRowGroup, addContainerGroup].forEach(function(group, index) {
|
|
1237
|
+
Array.prototype.slice.call(group.children).forEach(function(button) {
|
|
1238
|
+
homes.push({ button: button, home: group, spare: spares[index] });
|
|
1239
|
+
});
|
|
1240
|
+
});
|
|
1241
|
+
|
|
1242
|
+
toggle.setAttribute('title', t('tool.more'));
|
|
1243
|
+
toggle.setAttribute('aria-haspopup', 'true');
|
|
1244
|
+
toggle.setAttribute('aria-expanded', 'false');
|
|
1245
|
+
|
|
1246
|
+
var menuLifetime = toolbarLifetime = new AbortController();
|
|
1247
|
+
var signal = { signal: menuLifetime.signal };
|
|
1248
|
+
|
|
1249
|
+
toggle.addEventListener('click', function() { openOverflow(!dom.hasClass(more, 'ge-open')); });
|
|
1250
|
+
// A button in the menu has done its job once it is clicked
|
|
1251
|
+
panel.addEventListener('click', function(e) {
|
|
1252
|
+
if (dom.closest(e.target, '[data-ge-toolbar]')) { openOverflow(false); }
|
|
1253
|
+
});
|
|
1254
|
+
document.addEventListener('pointerdown', function(e) {
|
|
1255
|
+
if (!more.contains(e.target)) { openOverflow(false); }
|
|
1256
|
+
}, signal);
|
|
1257
|
+
document.addEventListener('keydown', function(e) {
|
|
1258
|
+
if (e.key === 'Escape') { openOverflow(false); }
|
|
1259
|
+
}, signal);
|
|
1260
|
+
closeOverflowMenu = function() { openOverflow(false); };
|
|
1261
|
+
|
|
1262
|
+
fit();
|
|
1263
|
+
|
|
1264
|
+
if (window.ResizeObserver) {
|
|
1265
|
+
// Not in the callback itself: moving buttons there would
|
|
1266
|
+
// resize what is being observed before the frame is drawn
|
|
1267
|
+
var pending = false;
|
|
1268
|
+
var observer = new window.ResizeObserver(function() {
|
|
1269
|
+
if (pending) { return; }
|
|
1270
|
+
pending = true;
|
|
1271
|
+
window.requestAnimationFrame(function() {
|
|
1272
|
+
pending = false;
|
|
1273
|
+
if (!menuLifetime.signal.aborted) { fit(); }
|
|
1274
|
+
});
|
|
1275
|
+
});
|
|
1276
|
+
// The toolbar's width, and the buttons on the right of it
|
|
1277
|
+
// showing and hiding, as the paste button does
|
|
1278
|
+
observer.observe(wrapper);
|
|
1279
|
+
observer.observe(start);
|
|
1280
|
+
menuLifetime.signal.addEventListener('abort', function() { observer.disconnect(); });
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
function openOverflow(open) {
|
|
1284
|
+
open = open && dom.hasClass(more, 'ge-needed');
|
|
1285
|
+
dom.toggleClass(more, 'ge-open', open);
|
|
1286
|
+
toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
/** Back on the line, then the last ones off it until the rest fit. */
|
|
1290
|
+
function fit() {
|
|
1291
|
+
homes.forEach(function(entry) { entry.home.appendChild(entry.button); });
|
|
1292
|
+
dom.removeClass(more, 'ge-needed');
|
|
1293
|
+
|
|
1294
|
+
if (start.scrollWidth <= start.clientWidth) {
|
|
1295
|
+
openOverflow(false);
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
dom.addClass(more, 'ge-needed');
|
|
1300
|
+
for (var i = homes.length - 1; i >= 0 && start.scrollWidth > start.clientWidth; i--) {
|
|
1301
|
+
homes[i].spare.insertBefore(homes[i].button, homes[i].spare.firstChild);
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1155
1306
|
/**
|
|
1156
1307
|
* A toolbar button's face: the plugin's icon alone when it has one,
|
|
1157
1308
|
* with its label as the title, or a plus and the label otherwise.
|
|
@@ -1184,6 +1335,8 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1184
1335
|
'data-ge-item': index,
|
|
1185
1336
|
});
|
|
1186
1337
|
button.addEventListener('click', function() {
|
|
1338
|
+
if (activeTarget) { addToTarget(button, item.source || 'tool'); return; }
|
|
1339
|
+
|
|
1187
1340
|
var made = item.create();
|
|
1188
1341
|
|
|
1189
1342
|
// One that belongs in a column brings a row and a column
|
|
@@ -1259,6 +1412,8 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1259
1412
|
|
|
1260
1413
|
helper = dom.addClass(button.cloneNode(true), 'ge-toolbar-helper');
|
|
1261
1414
|
document.body.appendChild(helper);
|
|
1415
|
+
// The menu a button came from would hide where it lands
|
|
1416
|
+
closeOverflowMenu();
|
|
1262
1417
|
dom.addClass(canvas, 'ge-dropping');
|
|
1263
1418
|
}
|
|
1264
1419
|
|
|
@@ -1339,7 +1494,7 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1339
1494
|
* not have what it makes - a section dropped on a column - gives way
|
|
1340
1495
|
* to the canvas, just after the top level block the pointer was in.
|
|
1341
1496
|
*/
|
|
1342
|
-
function insertFeatureFromToolbar(button, where) {
|
|
1497
|
+
function insertFeatureFromToolbar(button, where, source) {
|
|
1343
1498
|
var item = FEATURES[button.getAttribute('data-ge-feature')].toolbar[parseInt(button.getAttribute('data-ge-item'), 10)];
|
|
1344
1499
|
var made = item.create();
|
|
1345
1500
|
var placed = made;
|
|
@@ -1360,7 +1515,7 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1360
1515
|
} else {
|
|
1361
1516
|
where.region.appendChild(placed);
|
|
1362
1517
|
}
|
|
1363
|
-
}, { parent: where.region, source: item.source || 'dragdrop' });
|
|
1518
|
+
}, { parent: where.region, source: item.source || source || 'dragdrop' });
|
|
1364
1519
|
}
|
|
1365
1520
|
|
|
1366
1521
|
/** A detached row with one full width column holding `node`. */
|
|
@@ -1396,9 +1551,9 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1396
1551
|
* The row or container a toolbar button stands for, made and put where
|
|
1397
1552
|
* the pointer left it.
|
|
1398
1553
|
*/
|
|
1399
|
-
function insertFromToolbar(button, where) {
|
|
1554
|
+
function insertFromToolbar(button, where, source) {
|
|
1400
1555
|
if (button.getAttribute('data-ge-toolbar') === 'feature') {
|
|
1401
|
-
return insertFeatureFromToolbar(button, where);
|
|
1556
|
+
return insertFeatureFromToolbar(button, where, source);
|
|
1402
1557
|
}
|
|
1403
1558
|
|
|
1404
1559
|
var container = button.getAttribute('data-ge-toolbar') === 'container';
|
|
@@ -1418,7 +1573,7 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1418
1573
|
} else {
|
|
1419
1574
|
where.region.appendChild(placed);
|
|
1420
1575
|
}
|
|
1421
|
-
}, { parent: where.region, source: 'dragdrop' });
|
|
1576
|
+
}, { parent: where.region, source: source || 'dragdrop' });
|
|
1422
1577
|
}
|
|
1423
1578
|
|
|
1424
1579
|
/**
|
|
@@ -1710,6 +1865,8 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1710
1865
|
*/
|
|
1711
1866
|
function openSource() {
|
|
1712
1867
|
deinit();
|
|
1868
|
+
// The canvas is made again from the html when it comes back
|
|
1869
|
+
changeTarget(null);
|
|
1713
1870
|
htmlTextArea.style.height = (0.8 * document.documentElement.clientHeight) + 'px';
|
|
1714
1871
|
htmlTextArea.value = canvas.innerHTML;
|
|
1715
1872
|
dom.show(htmlTextArea);
|
|
@@ -1732,6 +1889,39 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1732
1889
|
dom.hide(htmlTextArea);
|
|
1733
1890
|
}
|
|
1734
1891
|
|
|
1892
|
+
/**
|
|
1893
|
+
* What can be the active target: a column, or a plugin's region - a
|
|
1894
|
+
* section. Not the canvas, which is where the toolbar adds without one.
|
|
1895
|
+
*/
|
|
1896
|
+
function targetSelector() {
|
|
1897
|
+
return ['.column'].concat(pluginHooks('regions')).join(', ');
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
function isTarget(node) {
|
|
1901
|
+
return !!node && node !== canvas && canvas.contains(node) && dom.is(node, targetSelector());
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
/** The one way the active target changes, told only when it does. */
|
|
1905
|
+
function changeTarget(node) {
|
|
1906
|
+
if (node === activeTarget) { return; }
|
|
1907
|
+
|
|
1908
|
+
var from = activeTarget;
|
|
1909
|
+
if (from) { dom.removeClass(from, 'ge-active-target'); }
|
|
1910
|
+
activeTarget = node;
|
|
1911
|
+
if (node && dom.hasClass(canvas, 'ge-editing')) { dom.addClass(node, 'ge-active-target'); }
|
|
1912
|
+
|
|
1913
|
+
emit('target-change', { canvas: canvas, target: node, from: from });
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
/**
|
|
1917
|
+
* A toolbar button's click with an active target: what it makes goes
|
|
1918
|
+
* where a drop at the target's end would put it.
|
|
1919
|
+
*/
|
|
1920
|
+
function addToTarget(button, source) {
|
|
1921
|
+
var made = insertFromToolbar(button, { region: activeTarget, before: null }, source);
|
|
1922
|
+
if (made && made.scrollIntoView) { made.scrollIntoView({ behavior: 'smooth' }); }
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1735
1925
|
function init() {
|
|
1736
1926
|
// The node whose settings were open is gone - deleted, say - and
|
|
1737
1927
|
// its panel with it
|
|
@@ -1759,12 +1949,21 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1759
1949
|
makeResizable();
|
|
1760
1950
|
switchLayout(curView);
|
|
1761
1951
|
refreshPreviews(canvas);
|
|
1952
|
+
|
|
1953
|
+
// Deleted, or taken out some other way, it is a target no more
|
|
1954
|
+
if (activeTarget && !canvas.contains(activeTarget)) {
|
|
1955
|
+
changeTarget(null);
|
|
1956
|
+
} else if (activeTarget) {
|
|
1957
|
+
dom.addClass(activeTarget, 'ge-active-target');
|
|
1958
|
+
}
|
|
1762
1959
|
}
|
|
1763
1960
|
|
|
1764
1961
|
function deinit() {
|
|
1765
1962
|
// Its panel goes home to its drawer first, and both go together
|
|
1766
1963
|
closeSettings();
|
|
1767
1964
|
dom.removeClass(canvas, 'ge-editing ge-drag-drawer ge-dropping');
|
|
1965
|
+
// The mark goes, the target stays: getHtml comes straight back
|
|
1966
|
+
if (activeTarget) { dom.removeClass(activeTarget, 'ge-active-target'); }
|
|
1768
1967
|
// Before the drawers and the text blocks come off: an editor
|
|
1769
1968
|
// plugin closes its editors here, and finds them where it left them
|
|
1770
1969
|
plugins('onBeforeDeinit');
|
|
@@ -1826,10 +2025,11 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1826
2025
|
// The html being edited is what the canvas is left with
|
|
1827
2026
|
if (sourceOpen) { closeSource(); }
|
|
1828
2027
|
deinit();
|
|
2028
|
+
activeTarget = null;
|
|
1829
2029
|
removeConfirmModal();
|
|
1830
2030
|
removeSettingsPanels();
|
|
1831
2031
|
removeDialog();
|
|
1832
|
-
|
|
2032
|
+
removeMainControls();
|
|
1833
2033
|
htmlTextArea.remove();
|
|
1834
2034
|
lifetime.abort();
|
|
1835
2035
|
instances.delete(canvas);
|
|
@@ -5150,6 +5350,23 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
5150
5350
|
* finishes before the canvas is rebuilt. The ones that do something,
|
|
5151
5351
|
* rather than answer something, hand back the instance, to chain.
|
|
5152
5352
|
*/
|
|
5353
|
+
function apiSetActiveTarget(value) {
|
|
5354
|
+
if (!settings.active_target) {
|
|
5355
|
+
warnOnceHere('active_target', 'setActiveTarget needs the active_target setting: without it, it does nothing');
|
|
5356
|
+
return;
|
|
5357
|
+
}
|
|
5358
|
+
if (value === null || value === undefined) { changeTarget(null); return; }
|
|
5359
|
+
|
|
5360
|
+
var node = nodeFrom(value);
|
|
5361
|
+
if (!isTarget(node)) {
|
|
5362
|
+
var named = typeof value === 'string' ? value : (node ? '<' + node.tagName.toLowerCase() + '>' : String(value));
|
|
5363
|
+
warn('setActiveTarget: ' + named + ' is not a column or a region of the canvas');
|
|
5364
|
+
return;
|
|
5365
|
+
}
|
|
5366
|
+
|
|
5367
|
+
changeTarget(node);
|
|
5368
|
+
}
|
|
5369
|
+
|
|
5153
5370
|
var own = {
|
|
5154
5371
|
getHtml: getHtml,
|
|
5155
5372
|
getPlainHtml: getPlainHtml,
|
|
@@ -5180,6 +5397,8 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
5180
5397
|
setLocale: setLocale,
|
|
5181
5398
|
getUtility: getUtility,
|
|
5182
5399
|
setUtility: setUtility,
|
|
5400
|
+
getActiveTarget: function() { return settings.active_target ? activeTarget : null; },
|
|
5401
|
+
setActiveTarget: apiSetActiveTarget,
|
|
5183
5402
|
};
|
|
5184
5403
|
|
|
5185
5404
|
Object.keys(own).forEach(function(name) {
|
|
@@ -5313,6 +5532,7 @@ GridEditor.locales = {
|
|
|
5313
5532
|
'tool.indent_increase': 'Increase indent\n(hold shift for max)',
|
|
5314
5533
|
'tool.edit_source': 'Edit Source Code',
|
|
5315
5534
|
'tool.preview': 'Preview',
|
|
5535
|
+
'tool.more': 'More',
|
|
5316
5536
|
'panel.title': '{kind} settings',
|
|
5317
5537
|
'panel.close': 'Close',
|
|
5318
5538
|
'panel.done': 'Done',
|
|
@@ -51,6 +51,7 @@ GridEditor.locales.es = {
|
|
|
51
51
|
'tool.indent_increase': 'Aumentar sangría\n(Mayús para el máximo)',
|
|
52
52
|
'tool.edit_source': 'Editar el código fuente',
|
|
53
53
|
'tool.preview': 'Vista previa',
|
|
54
|
+
'tool.more': 'Más',
|
|
54
55
|
'panel.title': 'Ajustes: {kind}',
|
|
55
56
|
'panel.close': 'Cerrar',
|
|
56
57
|
'panel.done': 'Hecho',
|
package/src/less/grideditor.less
CHANGED
|
@@ -67,6 +67,82 @@
|
|
|
67
67
|
margin-left: 5px;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
/* toolbar_overflow: 'menu'. The add buttons keep to one line, and the
|
|
71
|
+
ones that don't fit go behind a button at its end. The right-hand
|
|
72
|
+
buttons come before in the markup, so they are ordered back to how
|
|
73
|
+
floating them puts them. */
|
|
74
|
+
.ge-wrapper.ge-toolbar-menu {
|
|
75
|
+
display: flex;
|
|
76
|
+
align-items: flex-start;
|
|
77
|
+
|
|
78
|
+
> .pull-right {
|
|
79
|
+
float: none;
|
|
80
|
+
}
|
|
81
|
+
> .ge-toolbar-end {
|
|
82
|
+
order: 1;
|
|
83
|
+
}
|
|
84
|
+
> .btn-group.pull-right {
|
|
85
|
+
order: 2;
|
|
86
|
+
}
|
|
87
|
+
> .ge-layout-mode {
|
|
88
|
+
order: 3;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.ge-toolbar-start {
|
|
93
|
+
display: flex;
|
|
94
|
+
flex: 1 1 auto;
|
|
95
|
+
min-width: 0;
|
|
96
|
+
overflow: hidden;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.ge-toolbar-more {
|
|
100
|
+
display: none;
|
|
101
|
+
position: relative;
|
|
102
|
+
flex: none;
|
|
103
|
+
margin: 0 5px 0 4px;
|
|
104
|
+
|
|
105
|
+
&.ge-needed {
|
|
106
|
+
display: block;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.ge-toolbar-overflow {
|
|
111
|
+
display: none;
|
|
112
|
+
position: absolute;
|
|
113
|
+
z-index: 30;
|
|
114
|
+
top: ~"calc(100% + 4px)";
|
|
115
|
+
right: 0;
|
|
116
|
+
flex-direction: column;
|
|
117
|
+
gap: 6px;
|
|
118
|
+
width: max-content;
|
|
119
|
+
max-width: ~"min(420px, calc(100vw - 32px))";
|
|
120
|
+
padding: 6px;
|
|
121
|
+
background: #fff;
|
|
122
|
+
border: 1px solid rgba(0, 0, 0, .15);
|
|
123
|
+
border-radius: 6px;
|
|
124
|
+
box-shadow: 0 6px 16px rgba(0, 0, 0, .15);
|
|
125
|
+
|
|
126
|
+
> .btn-group {
|
|
127
|
+
flex-wrap: wrap;
|
|
128
|
+
gap: 4px;
|
|
129
|
+
margin: 0;
|
|
130
|
+
|
|
131
|
+
&:empty {
|
|
132
|
+
display: none;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/* Wrapped onto several lines, a group's buttons stand apart */
|
|
136
|
+
> .btn-group > .btn.btn {
|
|
137
|
+
margin-left: 0;
|
|
138
|
+
border-radius: 4px;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.ge-toolbar-more.ge-open > .ge-toolbar-overflow {
|
|
143
|
+
display: flex;
|
|
144
|
+
}
|
|
145
|
+
|
|
70
146
|
.ge-toolbar-end {
|
|
71
147
|
margin-right: 5px;
|
|
72
148
|
|
|
@@ -516,6 +592,16 @@
|
|
|
516
592
|
outline-offset: 1px;
|
|
517
593
|
}
|
|
518
594
|
|
|
595
|
+
/* Where the toolbar's buttons add, with active_target. A node that is both
|
|
596
|
+
shows both: this one a little further out */
|
|
597
|
+
.ge-canvas.ge-editing .ge-active-target {
|
|
598
|
+
outline: 2px dashed var(--bs-success, #198754);
|
|
599
|
+
outline-offset: -2px;
|
|
600
|
+
}
|
|
601
|
+
.ge-canvas.ge-editing .ge-active-target.ge-settings-target {
|
|
602
|
+
box-shadow: 0 0 0 4px var(--bs-success, #198754);
|
|
603
|
+
}
|
|
604
|
+
|
|
519
605
|
/* While editing */
|
|
520
606
|
.ge-canvas.ge-editing {
|
|
521
607
|
|
package/types/grideditor.d.ts
CHANGED
|
@@ -107,6 +107,10 @@ export interface GridEditorOptions {
|
|
|
107
107
|
text_tools?: HostTool[];
|
|
108
108
|
drag_handle?: 'tool' | 'drawer';
|
|
109
109
|
toolbar_drag?: 'auto' | boolean;
|
|
110
|
+
/** The add buttons that don't fit on the toolbar's line: behind a more button, or onto another line. Default 'menu'. */
|
|
111
|
+
toolbar_overflow?: 'menu' | 'wrap';
|
|
112
|
+
/** A click in a column or a section makes it where the toolbar's buttons add. Default false. */
|
|
113
|
+
active_target?: boolean;
|
|
110
114
|
/** The plugins to use, of any kind; null is every one loaded. */
|
|
111
115
|
plugins?: string[] | null;
|
|
112
116
|
/** The text editors offered, in order. */
|
|
@@ -218,6 +222,13 @@ export interface ViewChangePayload {
|
|
|
218
222
|
to: View;
|
|
219
223
|
}
|
|
220
224
|
|
|
225
|
+
/** The active target changed: the column or region the toolbar adds to, or null for the canvas. */
|
|
226
|
+
export interface TargetChangePayload {
|
|
227
|
+
canvas: HTMLElement;
|
|
228
|
+
target: HTMLElement | null;
|
|
229
|
+
from: HTMLElement | null;
|
|
230
|
+
}
|
|
231
|
+
|
|
221
232
|
/** The events, by name, with their payloads. Adding fires a specific name and the generic one. */
|
|
222
233
|
export interface GridEditorEventMap {
|
|
223
234
|
'grideditor:before-add': Payload;
|
|
@@ -255,6 +266,7 @@ export interface GridEditorEventMap {
|
|
|
255
266
|
'grideditor:after-copy': Payload;
|
|
256
267
|
'grideditor:popup-orphan': PopupOrphanPayload;
|
|
257
268
|
'grideditor:view-change': ViewChangePayload;
|
|
269
|
+
'grideditor:target-change': TargetChangePayload;
|
|
258
270
|
}
|
|
259
271
|
|
|
260
272
|
/** An event the editor dispatches on its canvas: a CustomEvent whose detail is the payload. */
|
|
@@ -343,6 +355,10 @@ export declare class GridEditor {
|
|
|
343
355
|
/** Take the editor off: the markup stays, everything else goes. */
|
|
344
356
|
destroy(): this;
|
|
345
357
|
changeView(view: View): this;
|
|
358
|
+
/** The column or region the toolbar adds to, or null. Null without active_target. */
|
|
359
|
+
getActiveTarget(): HTMLElement | null;
|
|
360
|
+
/** Make a column or region, or the first a selector matches, where the toolbar adds; null for none. */
|
|
361
|
+
setActiveTarget(target: HTMLElement | string | null): this;
|
|
346
362
|
getView(): View;
|
|
347
363
|
setLocale(code: string): this;
|
|
348
364
|
createRow(layout?: RowLayout, options?: Placement): HTMLElement | null;
|