@themarioga/grid-editor 8.3.0 → 8.4.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.
Files changed (40) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +17 -1
  3. package/dist/grideditor.bundle.min.js +3 -3
  4. package/dist/grideditor.css +49 -0
  5. package/dist/grideditor.d.ts +4 -0
  6. package/dist/grideditor.esm.js +200 -8
  7. package/dist/grideditor.js +200 -8
  8. package/dist/grideditor.min.css +1 -1
  9. package/dist/grideditor.min.css.map +1 -1
  10. package/dist/grideditor.min.js +2 -2
  11. package/dist/grideditor.min.js.map +3 -3
  12. package/dist/locales/grideditor.es.esm.js +3 -0
  13. package/dist/locales/grideditor.es.js +3 -0
  14. package/dist/locales/grideditor.es.min.js +1 -1
  15. package/dist/locales/grideditor.es.min.js.map +2 -2
  16. package/dist/plugins/grideditor.ckeditor.esm.js +1 -0
  17. package/dist/plugins/grideditor.ckeditor.js +1 -0
  18. package/dist/plugins/grideditor.ckeditor.min.js +1 -1
  19. package/dist/plugins/grideditor.ckeditor.min.js.map +2 -2
  20. package/dist/plugins/grideditor.sections.esm.js +1 -0
  21. package/dist/plugins/grideditor.sections.js +1 -0
  22. package/dist/plugins/grideditor.sections.min.js +1 -1
  23. package/dist/plugins/grideditor.sections.min.js.map +2 -2
  24. package/dist/plugins/grideditor.summernote.esm.js +1 -0
  25. package/dist/plugins/grideditor.summernote.js +1 -0
  26. package/dist/plugins/grideditor.summernote.min.js +1 -1
  27. package/dist/plugins/grideditor.summernote.min.js.map +2 -2
  28. package/dist/plugins/grideditor.tinymce.esm.js +1 -0
  29. package/dist/plugins/grideditor.tinymce.js +1 -0
  30. package/dist/plugins/grideditor.tinymce.min.js +1 -1
  31. package/dist/plugins/grideditor.tinymce.min.js.map +2 -2
  32. package/docs/locale-keys.md +4 -1
  33. package/docs/plugins.md +22 -0
  34. package/package.json +1 -1
  35. package/src/js/grideditor.js +246 -8
  36. package/src/js/locales/grideditor.es.js +3 -0
  37. package/src/js/plugins/grideditor.sections.js +1 -0
  38. package/src/js/text/grideditor.text.js +1 -0
  39. package/src/less/grideditor.less +58 -0
  40. package/types/grideditor.d.ts +4 -0
@@ -198,6 +198,15 @@ function translate(settings, key, params) {
198
198
  });
199
199
  }
200
200
 
201
+ /** Whether some locale has a string for the key, asked without the warning a missing one gets. */
202
+ function hasString(settings, key) {
203
+ var locales = GridEditor.locales;
204
+
205
+ return (settings.locale_strings || {})[key] !== undefined ||
206
+ (locales[settings.locale] || {})[key] !== undefined ||
207
+ locales.en[key] !== undefined;
208
+ }
209
+
201
210
  function warn(message) {
202
211
  if (window.console && window.console.warn) {
203
212
  window.console.warn('grid-editor: ' + message);
@@ -367,6 +376,8 @@ function build(instance, baseElem, optionsOrMethod) {
367
376
  'drag_handle' : 'tool', // 'tool' for the move tool, 'drawer' for the whole drawer
368
377
  'toolbar_drag' : 'auto', // Drag the toolbar's buttons onto the canvas. 'auto' follows drag_handle
369
378
  'toolbar_overflow' : 'menu', // What doesn't fit on one line: 'menu' behind a button, 'wrap' onto another line
379
+ 'drawer_overflow' : 'menu', // The same for the drawers' tools: 'menu' behind a button, 'wrap' onto another line
380
+ 'toolbar_groups' : false, // The add buttons in categories, one shown at a time, picked with tabs
370
381
  'active_target' : false, // A click in a column makes it where the toolbar's buttons add
371
382
  'element_tools' : [], // Host tools on element drawers, same shape as row_tools
372
383
  'element_classes' : [], // Preset class toggles on an element's settings panel
@@ -423,6 +434,9 @@ function build(instance, baseElem, optionsOrMethod) {
423
434
  var activeTarget = null; // The column or region the toolbar adds to, with active_target
424
435
  var toolbarLifetime = null; // Aborted when the toolbar is rebuilt or taken away
425
436
  var closeOverflowMenu = function() {}; // Folds the toolbar's overflow menu away, when it has one
437
+ var refitToolbar = function() {}; // Works out again what the toolbar's overflow menu holds, when it has one
438
+ var addButtons = []; // The toolbar's add buttons, in toolbar order: { button, home, group }
439
+ var toolbarGroup = null; // The category shown, with toolbar_groups. Kept when the toolbar is rebuilt
426
440
  var warnedHere = {}; // Some warnings are worth saying once per instance, not once per call
427
441
  var sortables = []; // Every list made sortable, so deinit destroys exactly those
428
442
  var instanceId = ++editorCounter; // Scopes the sortable groups to this editor
@@ -972,6 +986,7 @@ function build(instance, baseElem, optionsOrMethod) {
972
986
  canvas.parentNode.insertBefore(htmlTextArea, canvas);
973
987
 
974
988
  createMainControls();
989
+ watchDrawers();
975
990
 
976
991
  var signal = { signal: lifetime.signal };
977
992
 
@@ -1003,6 +1018,16 @@ function build(instance, baseElem, optionsOrMethod) {
1003
1018
  if (!dom.hasClass(canvas, 'ge-editing')) { return; }
1004
1019
  if (from && Math.abs(e.clientX - from.x) + Math.abs(e.clientY - from.y) > settings.drag.threshold) { return; }
1005
1020
  var region = dom.closest(e.target, targetSelector());
1021
+
1022
+ // The target's own background, or its drawer's, clicked
1023
+ // again takes it back. What is in it - a text, a tool, a
1024
+ // nested column - keeps it, or makes that the target
1025
+ if (region && region === activeTarget &&
1026
+ (e.target === region || e.target === dom.child(region, '.ge-tools-drawer'))) {
1027
+ changeTarget(null);
1028
+ return;
1029
+ }
1030
+
1006
1031
  changeTarget(region && region !== canvas && canvas.contains(region) ? region : null);
1007
1032
  }, signal);
1008
1033
 
@@ -1047,6 +1072,8 @@ function build(instance, baseElem, optionsOrMethod) {
1047
1072
  start = wrapper.appendChild(dom.element('div', { 'class': 'ge-toolbar-start' }));
1048
1073
  }
1049
1074
 
1075
+ addButtons = [];
1076
+
1050
1077
  // Add row
1051
1078
  addRowGroup = start.appendChild(dom.element('div', { 'class': 'ge-addRowGroup btn-group' }));
1052
1079
  addContainerGroup = dom.element('div', { 'class': 'ge-addContainerGroup btn-group ms-1' });
@@ -1077,6 +1104,7 @@ function build(instance, baseElem, optionsOrMethod) {
1077
1104
  }
1078
1105
  });
1079
1106
  addRowGroup.appendChild(btn);
1107
+ addButtons.push({ button: btn, home: addRowGroup, group: 'rows' });
1080
1108
 
1081
1109
  btn.appendChild(dom.create('<i class="bi bi-plus"></i>'));
1082
1110
 
@@ -1113,6 +1141,7 @@ function build(instance, baseElem, optionsOrMethod) {
1113
1141
  }, { parent: canvas, source: 'tool' });
1114
1142
  });
1115
1143
  addContainerGroup.appendChild(button);
1144
+ addButtons.push({ button: button, home: addContainerGroup, group: toolbarGroupOf(definition.group) || 'content' });
1116
1145
  });
1117
1146
 
1118
1147
  // A feature plugin's own buttons, beside the containers': what
@@ -1128,6 +1157,8 @@ function build(instance, baseElem, optionsOrMethod) {
1128
1157
  endItems.push(button);
1129
1158
  } else {
1130
1159
  addContainerGroup.appendChild(button);
1160
+ // One that names no category of its own has its plugin's
1161
+ addButtons.push({ button: button, home: addContainerGroup, group: toolbarGroupOf(item.group) || name });
1131
1162
  }
1132
1163
  });
1133
1164
  });
@@ -1198,16 +1229,96 @@ function build(instance, baseElem, optionsOrMethod) {
1198
1229
  endItems.forEach(function(button) { end.appendChild(button); });
1199
1230
  }
1200
1231
 
1232
+ if (settings.toolbar_groups) { createGroupTabs(start); }
1201
1233
  if (overflowMenu) { createOverflowMenu(start); }
1202
1234
 
1203
1235
  makeToolbarDraggable();
1204
1236
  }
1205
1237
 
1238
+ /** The core's categories, ahead of the plugins' own. */
1239
+ var CORE_GROUPS = [
1240
+ { name: 'rows', labelKey: 'group.rows' },
1241
+ { name: 'content', labelKey: 'group.content' },
1242
+ ];
1243
+
1244
+ /** A plugin's category, when it names one. */
1245
+ function toolbarGroupOf(value) {
1246
+ return typeof value === 'string' && value.trim() !== '' ? value : null;
1247
+ }
1248
+
1249
+ /**
1250
+ * toolbar_groups: tabs at the start of the toolbar, one per category
1251
+ * of add buttons, and only the chosen one's buttons on it. The rest
1252
+ * wait in a hidden stash inside the toolbar, so toolbarItems still
1253
+ * finds them and the btn-groups round the corners of what shows. With
1254
+ * a single category there is nothing to choose, and no tabs.
1255
+ */
1256
+ function createGroupTabs(start) {
1257
+ // The core's two first, then the rest as their first button comes
1258
+ var names = CORE_GROUPS.map(function(group) { return group.name; });
1259
+ addButtons.forEach(function(entry) {
1260
+ if (names.indexOf(entry.group) < 0) { names.push(entry.group); }
1261
+ });
1262
+ names = names.filter(function(name) {
1263
+ return addButtons.some(function(entry) { return entry.group === name; });
1264
+ });
1265
+
1266
+ if (names.length < 2) { return; }
1267
+
1268
+ var tabs = dom.element('div', { 'class': 'ge-toolbar-groups btn-group', role: 'tablist', 'aria-label': t('group.select') });
1269
+ start.insertBefore(tabs, start.firstChild);
1270
+ var stash = mainControls.appendChild(dom.element('div', { 'class': 'ge-toolbar-stash', hidden: 'hidden' }));
1271
+
1272
+ names.forEach(function(name) {
1273
+ var tab = tabs.appendChild(dom.element('button', {
1274
+ type: 'button',
1275
+ 'class': 'btn btn-sm btn-outline-primary',
1276
+ role: 'tab',
1277
+ 'data-ge-group': name,
1278
+ }, groupLabel(name)));
1279
+
1280
+ tab.addEventListener('click', function() { showGroup(name); });
1281
+ });
1282
+
1283
+ showGroup(names.indexOf(toolbarGroup) >= 0 ? toolbarGroup : names[0]);
1284
+
1285
+ function showGroup(name) {
1286
+ toolbarGroup = name;
1287
+
1288
+ addButtons.forEach(function(entry) {
1289
+ (entry.group === name ? entry.home : stash).appendChild(entry.button);
1290
+ });
1291
+
1292
+ dom.children(tabs).forEach(function(tab) {
1293
+ var chosen = tab.getAttribute('data-ge-group') === name;
1294
+ dom.toggleClass(tab, 'active', chosen);
1295
+ tab.setAttribute('aria-selected', chosen ? 'true' : 'false');
1296
+ });
1297
+
1298
+ refitToolbar();
1299
+ }
1300
+ }
1301
+
1302
+ /** A category's label: its group.<name> string, or its first button's when it has none. */
1303
+ function groupLabel(name) {
1304
+ var key = 'group.' + name;
1305
+ if (hasString(settings, key)) { return t(key); }
1306
+
1307
+ var first = addButtons.filter(function(entry) { return entry.group === name; })[0];
1308
+ return first.button.getAttribute('title') || name;
1309
+ }
1310
+
1311
+ /** The add buttons on the toolbar's line: all of them, or the chosen category's. */
1312
+ function shownAddButtons() {
1313
+ return addButtons.filter(function(entry) { return !dom.closest(entry.button, '.ge-toolbar-stash'); });
1314
+ }
1315
+
1206
1316
  /** The toolbar off the page, with what keeps its overflow menu up to date. */
1207
1317
  function removeMainControls() {
1208
1318
  if (toolbarLifetime) { toolbarLifetime.abort(); }
1209
1319
  toolbarLifetime = null;
1210
1320
  closeOverflowMenu = function() {};
1321
+ refitToolbar = function() {};
1211
1322
  mainControls.remove();
1212
1323
  }
1213
1324
 
@@ -1231,13 +1342,10 @@ function build(instance, baseElem, optionsOrMethod) {
1231
1342
  var toggle = dom.one(more, 'button');
1232
1343
  var panel = dom.one(more, '.ge-toolbar-overflow');
1233
1344
  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
- });
1345
+ // The spare group in the menu for each group on the line
1346
+ function spareOf(home) {
1347
+ return home === addRowGroup ? spares[0] : spares[1];
1348
+ }
1241
1349
 
1242
1350
  toggle.setAttribute('title', t('tool.more'));
1243
1351
  toggle.setAttribute('aria-haspopup', 'true');
@@ -1258,6 +1366,8 @@ function build(instance, baseElem, optionsOrMethod) {
1258
1366
  if (e.key === 'Escape') { openOverflow(false); }
1259
1367
  }, signal);
1260
1368
  closeOverflowMenu = function() { openOverflow(false); };
1369
+ // A category chosen: another set of buttons to fit
1370
+ refitToolbar = fit;
1261
1371
 
1262
1372
  fit();
1263
1373
 
@@ -1288,6 +1398,9 @@ function build(instance, baseElem, optionsOrMethod) {
1288
1398
 
1289
1399
  /** Back on the line, then the last ones off it until the rest fit. */
1290
1400
  function fit() {
1401
+ // Only the chosen category's, with toolbar_groups: the others
1402
+ // wait in their stash
1403
+ var homes = shownAddButtons();
1291
1404
  homes.forEach(function(entry) { entry.home.appendChild(entry.button); });
1292
1405
  dom.removeClass(more, 'ge-needed');
1293
1406
 
@@ -1298,7 +1411,8 @@ function build(instance, baseElem, optionsOrMethod) {
1298
1411
 
1299
1412
  dom.addClass(more, 'ge-needed');
1300
1413
  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);
1414
+ var into = spareOf(homes[i].home);
1415
+ into.insertBefore(homes[i].button, into.firstChild);
1302
1416
  }
1303
1417
  }
1304
1418
  }
@@ -3330,6 +3444,127 @@ function build(instance, baseElem, optionsOrMethod) {
3330
3444
  return createTool(drawer, t('tool.move'), 'ge-move', 'bi bi-arrows-move');
3331
3445
  }
3332
3446
 
3447
+ /**
3448
+ * The drawers that span their node - a row's, a column's, a
3449
+ * container's, an element's, a section's - keep their tools to one
3450
+ * line. The tools that don't fit are hidden from the end, and a more
3451
+ * tool at the drawer's end unfolds it to show them all, wrapping as
3452
+ * it would otherwise. They stay where they are, so whatever finds a
3453
+ * tool in its drawer, or hangs a picker off it, still does.
3454
+ *
3455
+ * A text's drawer sits over the text, as wide as its tools, and a
3456
+ * pane's sits by its label: neither is one of them.
3457
+ */
3458
+ var FITTED_DRAWERS = '.ge-tools-drawer:not(.ge-text-drawer):not(.ge-pane-drawer):not(.ge-code-inline)';
3459
+
3460
+ /**
3461
+ * Fit every drawer as it comes onto the canvas, and again when it
3462
+ * changes width or a tool is added to it, shown or hidden. Plugins
3463
+ * add tools at their own moments, so this watches rather than
3464
+ * waiting to be told.
3465
+ */
3466
+ function watchDrawers() {
3467
+ if (settings.drawer_overflow === 'wrap' || !window.ResizeObserver || !window.MutationObserver) { return; }
3468
+
3469
+ var due = new Set();
3470
+ var frame = null;
3471
+
3472
+ // Once a frame, after the layout: moving tools in the observers'
3473
+ // callbacks would resize what they are watching
3474
+ function schedule(drawer) {
3475
+ due.add(drawer);
3476
+ if (frame === null) { frame = window.requestAnimationFrame(flush); }
3477
+ }
3478
+
3479
+ function flush() {
3480
+ frame = null;
3481
+ var drawers = Array.from(due);
3482
+ due.clear();
3483
+ drawers.forEach(function(drawer) {
3484
+ if (!drawer.isConnected) {
3485
+ drawerResizes.unobserve(drawer);
3486
+ return;
3487
+ }
3488
+ fitDrawer(drawer);
3489
+ });
3490
+ }
3491
+
3492
+ var drawerResizes = new window.ResizeObserver(function(entries) {
3493
+ entries.forEach(function(entry) { schedule(entry.target); });
3494
+ });
3495
+
3496
+ var mutations = new window.MutationObserver(function(records) {
3497
+ records.forEach(function(record) {
3498
+ var target = record.target;
3499
+
3500
+ // A tool shown or hidden
3501
+ if (record.type === 'attributes') {
3502
+ var parent = target.parentNode;
3503
+ if (parent && parent.nodeType === 1 && parent.matches(FITTED_DRAWERS)) { schedule(parent); }
3504
+ return;
3505
+ }
3506
+
3507
+ // A tool added
3508
+ if (target.nodeType === 1 && target.matches(FITTED_DRAWERS)) {
3509
+ schedule(target);
3510
+ return;
3511
+ }
3512
+
3513
+ record.addedNodes.forEach(function(node) {
3514
+ if (node.nodeType !== 1) { return; }
3515
+ dom.selfAndAll(node, FITTED_DRAWERS).forEach(function(drawer) {
3516
+ dom.addClass(drawer, 'ge-drawer-fit');
3517
+ drawerResizes.observe(drawer);
3518
+ schedule(drawer);
3519
+ });
3520
+ });
3521
+ });
3522
+ });
3523
+ mutations.observe(canvas, { childList: true, subtree: true, attributes: true, attributeFilter: ['style'] });
3524
+
3525
+ lifetime.signal.addEventListener('abort', function() {
3526
+ mutations.disconnect();
3527
+ drawerResizes.disconnect();
3528
+ if (frame !== null) { window.cancelAnimationFrame(frame); }
3529
+ });
3530
+ }
3531
+
3532
+ /** Hide the tools that don't fit on the drawer's line, from the end, behind its more tool. */
3533
+ function fitDrawer(drawer) {
3534
+ var tools = dom.children(drawer, 'a').filter(function(tool) { return !dom.hasClass(tool, 'ge-drawer-more'); });
3535
+ var expanded = dom.hasClass(drawer, 'ge-drawer-expanded');
3536
+
3537
+ // Measured on one line, with every tool back on it
3538
+ dom.removeClass(drawer, 'ge-drawer-overflow ge-drawer-expanded');
3539
+ tools.forEach(function(tool) { dom.removeClass(tool, 'ge-tool-overflow'); });
3540
+
3541
+ if (!drawerOverflows(drawer, tools)) { return; }
3542
+
3543
+ dom.addClass(drawer, 'ge-drawer-overflow');
3544
+ if (!dom.child(drawer, '.ge-drawer-more')) {
3545
+ createTool(drawer, t('tool.more'), 'ge-drawer-more', 'bi bi-three-dots', function() {
3546
+ dom.toggleClass(drawer, 'ge-drawer-expanded');
3547
+ });
3548
+ }
3549
+
3550
+ for (var i = tools.length - 1; i >= 0 && drawerOverflows(drawer, tools); i--) {
3551
+ dom.addClass(tools[i], 'ge-tool-overflow');
3552
+ }
3553
+
3554
+ dom.toggleClass(drawer, 'ge-drawer-expanded', expanded);
3555
+ }
3556
+
3557
+ function drawerOverflows(drawer, tools) {
3558
+ var style = window.getComputedStyle(drawer);
3559
+ var limit = drawer.getBoundingClientRect().right -
3560
+ parseFloat(style.borderRightWidth) - parseFloat(style.paddingRight);
3561
+
3562
+ return tools.some(function(tool) {
3563
+ var rects = tool.getClientRects();
3564
+ return rects.length > 0 && rects[0].right > limit + 0.5;
3565
+ });
3566
+ }
3567
+
3333
3568
  /**
3334
3569
  * A tool in a drawer: a link with an icon, and what it does. The
3335
3570
  * handlers are a click handler, or { eventName: handler }; each gets
@@ -5533,6 +5768,9 @@ GridEditor.locales = {
5533
5768
  'tool.edit_source': 'Edit Source Code',
5534
5769
  'tool.preview': 'Preview',
5535
5770
  'tool.more': 'More',
5771
+ 'group.rows': 'Rows',
5772
+ 'group.content': 'Content',
5773
+ 'group.select': 'Add',
5536
5774
  'panel.title': '{kind} settings',
5537
5775
  'panel.close': 'Close',
5538
5776
  'panel.done': 'Done',
@@ -52,6 +52,9 @@ GridEditor.locales.es = {
52
52
  'tool.edit_source': 'Editar el código fuente',
53
53
  'tool.preview': 'Vista previa',
54
54
  'tool.more': 'Más',
55
+ 'group.rows': 'Filas',
56
+ 'group.content': 'Contenido',
57
+ 'group.select': 'Añadir',
55
58
  'panel.title': 'Ajustes: {kind}',
56
59
  'panel.close': 'Cerrar',
57
60
  'panel.done': 'Hecho',
@@ -192,6 +192,7 @@ GridEditor.features.sections = function(ge) {
192
192
  toolbar: [{
193
193
  labelKey: 'section.add',
194
194
  kind: 'section',
195
+ group: 'content',
195
196
  create: function() { return createSection(); },
196
197
  }],
197
198
 
@@ -321,6 +321,7 @@ if (!GridEditor.features.text) {
321
321
  iconClass: TEXTS[type].iconClass,
322
322
  className: 'ge-add-text-button',
323
323
  kind: 'text',
324
+ group: 'content',
324
325
  inColumn: true,
325
326
  create: function() { return makeText(type); },
326
327
  };
@@ -67,6 +67,18 @@
67
67
  margin-left: 5px;
68
68
  }
69
69
 
70
+ /* toolbar_groups: the tabs choosing which category of add buttons
71
+ shows, at the start of the line. Never squeezed, and never behind the
72
+ more button */
73
+ .ge-toolbar-groups {
74
+ flex: none;
75
+ margin-right: 8px;
76
+
77
+ > .btn {
78
+ white-space: nowrap;
79
+ }
80
+ }
81
+
70
82
  /* toolbar_overflow: 'menu'. The add buttons keep to one line, and the
71
83
  ones that don't fit go behind a button at its end. The right-hand
72
84
  buttons come before in the markup, so they are ordered back to how
@@ -94,6 +106,15 @@
94
106
  flex: 1 1 auto;
95
107
  min-width: 0;
96
108
  overflow: hidden;
109
+
110
+ /* A group as wide as its buttons and a button as wide as its label:
111
+ squeezed, they would wrap their labels rather than overflow */
112
+ > .btn-group {
113
+ flex: none;
114
+ }
115
+ .btn {
116
+ white-space: nowrap;
117
+ }
97
118
  }
98
119
 
99
120
  .ge-toolbar-more {
@@ -1114,6 +1135,43 @@
1114
1135
  }
1115
1136
  }
1116
1137
 
1138
+ /* drawer_overflow: 'menu'. A drawer keeps its tools to one line; the ones
1139
+ that don't fit are hidden behind a more tool at its end, which unfolds it
1140
+ to show them all */
1141
+ .ge-canvas.ge-editing .ge-tools-drawer.ge-drawer-fit {
1142
+ white-space: nowrap;
1143
+
1144
+ > .ge-details {
1145
+ white-space: normal;
1146
+ }
1147
+ > a.ge-tool-overflow,
1148
+ > a.ge-drawer-more {
1149
+ display: none;
1150
+ }
1151
+
1152
+ &.ge-drawer-overflow {
1153
+ padding-right: 26px;
1154
+
1155
+ > a.ge-drawer-more {
1156
+ display: inline-block;
1157
+ position: absolute;
1158
+ top: 0;
1159
+ right: 0;
1160
+ }
1161
+ }
1162
+
1163
+ &.ge-drawer-expanded {
1164
+ white-space: normal;
1165
+
1166
+ > a.ge-tool-overflow {
1167
+ display: inline-block;
1168
+ }
1169
+ > a.ge-drawer-more {
1170
+ background: rgba(0, 0, 0, 0.08);
1171
+ }
1172
+ }
1173
+ }
1174
+
1117
1175
  /* The handle belongs to editing, and never to the output */
1118
1176
  .ge-canvas:not(.ge-editing) .ge-resize-handle {
1119
1177
  display: none;
@@ -109,6 +109,10 @@ export interface GridEditorOptions {
109
109
  toolbar_drag?: 'auto' | boolean;
110
110
  /** The add buttons that don't fit on the toolbar's line: behind a more button, or onto another line. Default 'menu'. */
111
111
  toolbar_overflow?: 'menu' | 'wrap';
112
+ /** The drawers' tools that don't fit on one line: behind a more tool, or onto more lines. Default 'menu'. */
113
+ drawer_overflow?: 'menu' | 'wrap';
114
+ /** The add buttons in categories - rows, content, and a plugin's own - one shown at a time, chosen with tabs. Default false. */
115
+ toolbar_groups?: boolean;
112
116
  /** A click in a column or a section makes it where the toolbar's buttons add. Default false. */
113
117
  active_target?: boolean;
114
118
  /** The plugins to use, of any kind; null is every one loaded. */