@themarioga/grid-editor 8.1.0 → 8.2.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 +13 -0
- package/README.md +11 -0
- package/dist/grideditor.bundle.min.js +3 -3
- package/dist/grideditor.css +9 -0
- package/dist/grideditor.d.ts +14 -0
- package/dist/grideditor.esm.js +110 -8
- 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 +110 -8
- 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/docs/events.md +7 -0
- package/package.json +1 -1
- package/src/js/grideditor.jquery.js +2 -0
- package/src/js/grideditor.js +108 -5
- package/src/less/grideditor.less +10 -0
- package/types/grideditor.d.ts +14 -0
package/docs/events.md
CHANGED
|
@@ -89,6 +89,7 @@ about every insertion can bind `grideditor:before-add` and switch on
|
|
|
89
89
|
| `grideditor:before-utility` | — | yes | before a utility class is written, from the panel, a plugin's tool or `setUtility` |
|
|
90
90
|
| `grideditor:after-utility` | — | no | after the class is written and the preview redrawn, only if something changed |
|
|
91
91
|
| `grideditor:view-change` | — | no | after the view changes, only if it actually changed |
|
|
92
|
+
| `grideditor:target-change` | — | no | with `active_target`, after the column or section the toolbar adds to changes, only if it actually changed |
|
|
92
93
|
| `grideditor:after-copy` | — | no | with the clipboard plugin, after a node is copied |
|
|
93
94
|
|
|
94
95
|
Two things here are not in the 3.0 specification's catalogue. The indent pair,
|
|
@@ -160,6 +161,12 @@ view), `from` and `to`. A utility's `breakpoint` is the view it was written
|
|
|
160
161
|
in, which `setUtility` can be asked to make a different one from the view on
|
|
161
162
|
screen, and its `source` is `panel`, `tool` or `api`.
|
|
162
163
|
|
|
164
|
+
`target-change` carries no node either: its payload is `canvas`, `target` - the
|
|
165
|
+
column or section the toolbar adds to now, or `null` for the end of the canvas
|
|
166
|
+
- and `from`, the one before. What a toolbar button then adds fires its add
|
|
167
|
+
events as any click does, with `source: 'tool'` (or `'paste'`) and the target,
|
|
168
|
+
or the canvas for a section, as the `parent`.
|
|
169
|
+
|
|
163
170
|
`source` matters to a host that both drives the editor from its own palette and
|
|
164
171
|
listens for what the user does: `api` is your own call coming back to you,
|
|
165
172
|
`tool` is a click in the editor, `dragdrop` is a gesture, and `paste` is a node
|
package/package.json
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"comment": "A scoped package publishes restricted unless it is told otherwise.",
|
|
34
34
|
"access": "public"
|
|
35
35
|
},
|
|
36
|
-
"version": "8.
|
|
36
|
+
"version": "8.2.0",
|
|
37
37
|
"main": "dist/grideditor.js",
|
|
38
38
|
"module": "dist/grideditor.esm.js",
|
|
39
39
|
"types": "dist/grideditor.d.ts",
|
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,7 @@ 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
|
+
'active_target' : false, // A click in a column makes it where the toolbar's buttons add
|
|
367
370
|
'element_tools' : [], // Host tools on element drawers, same shape as row_tools
|
|
368
371
|
'element_classes' : [], // Preset class toggles on an element's settings panel
|
|
369
372
|
// content_types, text_tools and text_classes are the text editor
|
|
@@ -416,6 +419,7 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
416
419
|
var sizePicker = null; // The open column size picker, if there is one
|
|
417
420
|
var sourceOpen = false; // Whether the canvas is being edited as html
|
|
418
421
|
var dropMarker = null; // The line showing where a dragged toolbar button would land
|
|
422
|
+
var activeTarget = null; // The column or region the toolbar adds to, with active_target
|
|
419
423
|
var warnedHere = {}; // Some warnings are worth saying once per instance, not once per call
|
|
420
424
|
var sortables = []; // Every list made sortable, so deinit destroys exactly those
|
|
421
425
|
var instanceId = ++editorCounter; // Scopes the sortable groups to this editor
|
|
@@ -979,6 +983,35 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
979
983
|
dom.delegate(canvas, 'click', '.ge-popup-trigger, [data-ge-popup-target]', function(e) {
|
|
980
984
|
if (dom.hasClass(canvas, 'ge-editing')) { e.preventDefault(); }
|
|
981
985
|
}, signal);
|
|
986
|
+
|
|
987
|
+
if (settings.active_target) {
|
|
988
|
+
// Whatever the click was for - a text, a tool - it also says
|
|
989
|
+
// where the toolbar adds next. A click on no column or region
|
|
990
|
+
// says nowhere, and the toolbar adds to the canvas again.
|
|
991
|
+
// The click a drag ends in - a resize, a move - is not one:
|
|
992
|
+
// it comes where the pointer let go, not where it pressed
|
|
993
|
+
var pressed = null;
|
|
994
|
+
canvas.addEventListener('pointerdown', function(e) {
|
|
995
|
+
pressed = { x: e.clientX, y: e.clientY };
|
|
996
|
+
}, signal);
|
|
997
|
+
canvas.addEventListener('click', function(e) {
|
|
998
|
+
var from = pressed;
|
|
999
|
+
pressed = null;
|
|
1000
|
+
if (!dom.hasClass(canvas, 'ge-editing')) { return; }
|
|
1001
|
+
if (from && Math.abs(e.clientX - from.x) + Math.abs(e.clientY - from.y) > settings.drag.threshold) { return; }
|
|
1002
|
+
var region = dom.closest(e.target, targetSelector());
|
|
1003
|
+
changeTarget(region && region !== canvas && canvas.contains(region) ? region : null);
|
|
1004
|
+
}, signal);
|
|
1005
|
+
|
|
1006
|
+
// Escape is the panel's, the dialog's, and a field's or a
|
|
1007
|
+
// text's while one has the focus; after those, it is this
|
|
1008
|
+
document.addEventListener('keydown', function(e) {
|
|
1009
|
+
if (e.key !== 'Escape' || !activeTarget || openSettingsState || dialogState) { return; }
|
|
1010
|
+
var focus = document.activeElement;
|
|
1011
|
+
if (focus && (dom.is(focus, 'input, textarea, select') || focus.isContentEditable)) { return; }
|
|
1012
|
+
changeTarget(null);
|
|
1013
|
+
}, signal);
|
|
1014
|
+
}
|
|
982
1015
|
}
|
|
983
1016
|
|
|
984
1017
|
/**
|
|
@@ -1019,6 +1052,8 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1019
1052
|
});
|
|
1020
1053
|
|
|
1021
1054
|
btn.addEventListener('click', function() {
|
|
1055
|
+
if (activeTarget) { addToTarget(btn, 'tool'); return; }
|
|
1056
|
+
|
|
1022
1057
|
var row = rowFromLayoutValue(layout);
|
|
1023
1058
|
|
|
1024
1059
|
var added = addNode('row', row, function() {
|
|
@@ -1055,6 +1090,8 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1055
1090
|
|
|
1056
1091
|
dom.attr(button, { 'data-ge-toolbar': 'container', 'data-ge-container-type': type });
|
|
1057
1092
|
button.addEventListener('click', function() {
|
|
1093
|
+
if (activeTarget) { addToTarget(button, 'tool'); return; }
|
|
1094
|
+
|
|
1058
1095
|
var row = createRow();
|
|
1059
1096
|
var column = row.appendChild(createColumn(MAX_COL_SIZE));
|
|
1060
1097
|
var container = column.appendChild(definition.create({}));
|
|
@@ -1184,6 +1221,8 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1184
1221
|
'data-ge-item': index,
|
|
1185
1222
|
});
|
|
1186
1223
|
button.addEventListener('click', function() {
|
|
1224
|
+
if (activeTarget) { addToTarget(button, item.source || 'tool'); return; }
|
|
1225
|
+
|
|
1187
1226
|
var made = item.create();
|
|
1188
1227
|
|
|
1189
1228
|
// One that belongs in a column brings a row and a column
|
|
@@ -1339,7 +1378,7 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1339
1378
|
* not have what it makes - a section dropped on a column - gives way
|
|
1340
1379
|
* to the canvas, just after the top level block the pointer was in.
|
|
1341
1380
|
*/
|
|
1342
|
-
function insertFeatureFromToolbar(button, where) {
|
|
1381
|
+
function insertFeatureFromToolbar(button, where, source) {
|
|
1343
1382
|
var item = FEATURES[button.getAttribute('data-ge-feature')].toolbar[parseInt(button.getAttribute('data-ge-item'), 10)];
|
|
1344
1383
|
var made = item.create();
|
|
1345
1384
|
var placed = made;
|
|
@@ -1360,7 +1399,7 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1360
1399
|
} else {
|
|
1361
1400
|
where.region.appendChild(placed);
|
|
1362
1401
|
}
|
|
1363
|
-
}, { parent: where.region, source: item.source || 'dragdrop' });
|
|
1402
|
+
}, { parent: where.region, source: item.source || source || 'dragdrop' });
|
|
1364
1403
|
}
|
|
1365
1404
|
|
|
1366
1405
|
/** A detached row with one full width column holding `node`. */
|
|
@@ -1396,9 +1435,9 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1396
1435
|
* The row or container a toolbar button stands for, made and put where
|
|
1397
1436
|
* the pointer left it.
|
|
1398
1437
|
*/
|
|
1399
|
-
function insertFromToolbar(button, where) {
|
|
1438
|
+
function insertFromToolbar(button, where, source) {
|
|
1400
1439
|
if (button.getAttribute('data-ge-toolbar') === 'feature') {
|
|
1401
|
-
return insertFeatureFromToolbar(button, where);
|
|
1440
|
+
return insertFeatureFromToolbar(button, where, source);
|
|
1402
1441
|
}
|
|
1403
1442
|
|
|
1404
1443
|
var container = button.getAttribute('data-ge-toolbar') === 'container';
|
|
@@ -1418,7 +1457,7 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1418
1457
|
} else {
|
|
1419
1458
|
where.region.appendChild(placed);
|
|
1420
1459
|
}
|
|
1421
|
-
}, { parent: where.region, source: 'dragdrop' });
|
|
1460
|
+
}, { parent: where.region, source: source || 'dragdrop' });
|
|
1422
1461
|
}
|
|
1423
1462
|
|
|
1424
1463
|
/**
|
|
@@ -1710,6 +1749,8 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1710
1749
|
*/
|
|
1711
1750
|
function openSource() {
|
|
1712
1751
|
deinit();
|
|
1752
|
+
// The canvas is made again from the html when it comes back
|
|
1753
|
+
changeTarget(null);
|
|
1713
1754
|
htmlTextArea.style.height = (0.8 * document.documentElement.clientHeight) + 'px';
|
|
1714
1755
|
htmlTextArea.value = canvas.innerHTML;
|
|
1715
1756
|
dom.show(htmlTextArea);
|
|
@@ -1732,6 +1773,39 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1732
1773
|
dom.hide(htmlTextArea);
|
|
1733
1774
|
}
|
|
1734
1775
|
|
|
1776
|
+
/**
|
|
1777
|
+
* What can be the active target: a column, or a plugin's region - a
|
|
1778
|
+
* section. Not the canvas, which is where the toolbar adds without one.
|
|
1779
|
+
*/
|
|
1780
|
+
function targetSelector() {
|
|
1781
|
+
return ['.column'].concat(pluginHooks('regions')).join(', ');
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
function isTarget(node) {
|
|
1785
|
+
return !!node && node !== canvas && canvas.contains(node) && dom.is(node, targetSelector());
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
/** The one way the active target changes, told only when it does. */
|
|
1789
|
+
function changeTarget(node) {
|
|
1790
|
+
if (node === activeTarget) { return; }
|
|
1791
|
+
|
|
1792
|
+
var from = activeTarget;
|
|
1793
|
+
if (from) { dom.removeClass(from, 'ge-active-target'); }
|
|
1794
|
+
activeTarget = node;
|
|
1795
|
+
if (node && dom.hasClass(canvas, 'ge-editing')) { dom.addClass(node, 'ge-active-target'); }
|
|
1796
|
+
|
|
1797
|
+
emit('target-change', { canvas: canvas, target: node, from: from });
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
/**
|
|
1801
|
+
* A toolbar button's click with an active target: what it makes goes
|
|
1802
|
+
* where a drop at the target's end would put it.
|
|
1803
|
+
*/
|
|
1804
|
+
function addToTarget(button, source) {
|
|
1805
|
+
var made = insertFromToolbar(button, { region: activeTarget, before: null }, source);
|
|
1806
|
+
if (made && made.scrollIntoView) { made.scrollIntoView({ behavior: 'smooth' }); }
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1735
1809
|
function init() {
|
|
1736
1810
|
// The node whose settings were open is gone - deleted, say - and
|
|
1737
1811
|
// its panel with it
|
|
@@ -1759,12 +1833,21 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1759
1833
|
makeResizable();
|
|
1760
1834
|
switchLayout(curView);
|
|
1761
1835
|
refreshPreviews(canvas);
|
|
1836
|
+
|
|
1837
|
+
// Deleted, or taken out some other way, it is a target no more
|
|
1838
|
+
if (activeTarget && !canvas.contains(activeTarget)) {
|
|
1839
|
+
changeTarget(null);
|
|
1840
|
+
} else if (activeTarget) {
|
|
1841
|
+
dom.addClass(activeTarget, 'ge-active-target');
|
|
1842
|
+
}
|
|
1762
1843
|
}
|
|
1763
1844
|
|
|
1764
1845
|
function deinit() {
|
|
1765
1846
|
// Its panel goes home to its drawer first, and both go together
|
|
1766
1847
|
closeSettings();
|
|
1767
1848
|
dom.removeClass(canvas, 'ge-editing ge-drag-drawer ge-dropping');
|
|
1849
|
+
// The mark goes, the target stays: getHtml comes straight back
|
|
1850
|
+
if (activeTarget) { dom.removeClass(activeTarget, 'ge-active-target'); }
|
|
1768
1851
|
// Before the drawers and the text blocks come off: an editor
|
|
1769
1852
|
// plugin closes its editors here, and finds them where it left them
|
|
1770
1853
|
plugins('onBeforeDeinit');
|
|
@@ -1826,6 +1909,7 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1826
1909
|
// The html being edited is what the canvas is left with
|
|
1827
1910
|
if (sourceOpen) { closeSource(); }
|
|
1828
1911
|
deinit();
|
|
1912
|
+
activeTarget = null;
|
|
1829
1913
|
removeConfirmModal();
|
|
1830
1914
|
removeSettingsPanels();
|
|
1831
1915
|
removeDialog();
|
|
@@ -5150,6 +5234,23 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
5150
5234
|
* finishes before the canvas is rebuilt. The ones that do something,
|
|
5151
5235
|
* rather than answer something, hand back the instance, to chain.
|
|
5152
5236
|
*/
|
|
5237
|
+
function apiSetActiveTarget(value) {
|
|
5238
|
+
if (!settings.active_target) {
|
|
5239
|
+
warnOnceHere('active_target', 'setActiveTarget needs the active_target setting: without it, it does nothing');
|
|
5240
|
+
return;
|
|
5241
|
+
}
|
|
5242
|
+
if (value === null || value === undefined) { changeTarget(null); return; }
|
|
5243
|
+
|
|
5244
|
+
var node = nodeFrom(value);
|
|
5245
|
+
if (!isTarget(node)) {
|
|
5246
|
+
var named = typeof value === 'string' ? value : (node ? '<' + node.tagName.toLowerCase() + '>' : String(value));
|
|
5247
|
+
warn('setActiveTarget: ' + named + ' is not a column or a region of the canvas');
|
|
5248
|
+
return;
|
|
5249
|
+
}
|
|
5250
|
+
|
|
5251
|
+
changeTarget(node);
|
|
5252
|
+
}
|
|
5253
|
+
|
|
5153
5254
|
var own = {
|
|
5154
5255
|
getHtml: getHtml,
|
|
5155
5256
|
getPlainHtml: getPlainHtml,
|
|
@@ -5180,6 +5281,8 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
5180
5281
|
setLocale: setLocale,
|
|
5181
5282
|
getUtility: getUtility,
|
|
5182
5283
|
setUtility: setUtility,
|
|
5284
|
+
getActiveTarget: function() { return settings.active_target ? activeTarget : null; },
|
|
5285
|
+
setActiveTarget: apiSetActiveTarget,
|
|
5183
5286
|
};
|
|
5184
5287
|
|
|
5185
5288
|
Object.keys(own).forEach(function(name) {
|
package/src/less/grideditor.less
CHANGED
|
@@ -516,6 +516,16 @@
|
|
|
516
516
|
outline-offset: 1px;
|
|
517
517
|
}
|
|
518
518
|
|
|
519
|
+
/* Where the toolbar's buttons add, with active_target. A node that is both
|
|
520
|
+
shows both: this one a little further out */
|
|
521
|
+
.ge-canvas.ge-editing .ge-active-target {
|
|
522
|
+
outline: 2px dashed var(--bs-success, #198754);
|
|
523
|
+
outline-offset: -2px;
|
|
524
|
+
}
|
|
525
|
+
.ge-canvas.ge-editing .ge-active-target.ge-settings-target {
|
|
526
|
+
box-shadow: 0 0 0 4px var(--bs-success, #198754);
|
|
527
|
+
}
|
|
528
|
+
|
|
519
529
|
/* While editing */
|
|
520
530
|
.ge-canvas.ge-editing {
|
|
521
531
|
|
package/types/grideditor.d.ts
CHANGED
|
@@ -107,6 +107,8 @@ export interface GridEditorOptions {
|
|
|
107
107
|
text_tools?: HostTool[];
|
|
108
108
|
drag_handle?: 'tool' | 'drawer';
|
|
109
109
|
toolbar_drag?: 'auto' | boolean;
|
|
110
|
+
/** A click in a column or a section makes it where the toolbar's buttons add. Default false. */
|
|
111
|
+
active_target?: boolean;
|
|
110
112
|
/** The plugins to use, of any kind; null is every one loaded. */
|
|
111
113
|
plugins?: string[] | null;
|
|
112
114
|
/** The text editors offered, in order. */
|
|
@@ -218,6 +220,13 @@ export interface ViewChangePayload {
|
|
|
218
220
|
to: View;
|
|
219
221
|
}
|
|
220
222
|
|
|
223
|
+
/** The active target changed: the column or region the toolbar adds to, or null for the canvas. */
|
|
224
|
+
export interface TargetChangePayload {
|
|
225
|
+
canvas: HTMLElement;
|
|
226
|
+
target: HTMLElement | null;
|
|
227
|
+
from: HTMLElement | null;
|
|
228
|
+
}
|
|
229
|
+
|
|
221
230
|
/** The events, by name, with their payloads. Adding fires a specific name and the generic one. */
|
|
222
231
|
export interface GridEditorEventMap {
|
|
223
232
|
'grideditor:before-add': Payload;
|
|
@@ -255,6 +264,7 @@ export interface GridEditorEventMap {
|
|
|
255
264
|
'grideditor:after-copy': Payload;
|
|
256
265
|
'grideditor:popup-orphan': PopupOrphanPayload;
|
|
257
266
|
'grideditor:view-change': ViewChangePayload;
|
|
267
|
+
'grideditor:target-change': TargetChangePayload;
|
|
258
268
|
}
|
|
259
269
|
|
|
260
270
|
/** An event the editor dispatches on its canvas: a CustomEvent whose detail is the payload. */
|
|
@@ -343,6 +353,10 @@ export declare class GridEditor {
|
|
|
343
353
|
/** Take the editor off: the markup stays, everything else goes. */
|
|
344
354
|
destroy(): this;
|
|
345
355
|
changeView(view: View): this;
|
|
356
|
+
/** The column or region the toolbar adds to, or null. Null without active_target. */
|
|
357
|
+
getActiveTarget(): HTMLElement | null;
|
|
358
|
+
/** Make a column or region, or the first a selector matches, where the toolbar adds; null for none. */
|
|
359
|
+
setActiveTarget(target: HTMLElement | string | null): this;
|
|
346
360
|
getView(): View;
|
|
347
361
|
setLocale(code: string): this;
|
|
348
362
|
createRow(layout?: RowLayout, options?: Placement): HTMLElement | null;
|