@themarioga/grid-editor 8.4.0 → 8.5.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 (36) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +27 -1
  3. package/dist/grideditor.bundle.min.js +3 -3
  4. package/dist/grideditor.d.ts +17 -1
  5. package/dist/grideditor.esm.js +13 -3
  6. package/dist/grideditor.js +13 -3
  7. package/dist/grideditor.min.js +2 -2
  8. package/dist/grideditor.min.js.map +2 -2
  9. package/dist/locales/grideditor.es.esm.js +1 -0
  10. package/dist/locales/grideditor.es.js +1 -0
  11. package/dist/locales/grideditor.es.min.js +1 -1
  12. package/dist/locales/grideditor.es.min.js.map +2 -2
  13. package/dist/plugins/grideditor.ckeditor.esm.js +1 -1
  14. package/dist/plugins/grideditor.ckeditor.js +1 -1
  15. package/dist/plugins/grideditor.ckeditor.min.js +1 -1
  16. package/dist/plugins/grideditor.ckeditor.min.js.map +2 -2
  17. package/dist/plugins/grideditor.elements.esm.js +72 -1
  18. package/dist/plugins/grideditor.elements.js +72 -1
  19. package/dist/plugins/grideditor.elements.min.js +1 -1
  20. package/dist/plugins/grideditor.elements.min.js.map +3 -3
  21. package/dist/plugins/grideditor.summernote.esm.js +1 -1
  22. package/dist/plugins/grideditor.summernote.js +1 -1
  23. package/dist/plugins/grideditor.summernote.min.js +1 -1
  24. package/dist/plugins/grideditor.summernote.min.js.map +2 -2
  25. package/dist/plugins/grideditor.tinymce.esm.js +1 -1
  26. package/dist/plugins/grideditor.tinymce.js +1 -1
  27. package/dist/plugins/grideditor.tinymce.min.js +1 -1
  28. package/dist/plugins/grideditor.tinymce.min.js.map +2 -2
  29. package/docs/locale-keys.md +2 -1
  30. package/docs/plugins.md +6 -5
  31. package/package.json +1 -1
  32. package/src/js/grideditor.js +6 -0
  33. package/src/js/locales/grideditor.es.js +1 -0
  34. package/src/js/plugins/grideditor.elements.js +94 -1
  35. package/src/js/text/grideditor.text.js +1 -1
  36. package/types/grideditor.d.ts +17 -1
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.4.0",
36
+ "version": "8.5.0",
37
37
  "main": "dist/grideditor.js",
38
38
  "module": "dist/grideditor.esm.js",
39
39
  "types": "dist/grideditor.d.ts",
@@ -142,6 +142,7 @@ var NESTED_SETTINGS = {
142
142
  enabled: 'auto', // 'auto' turns them on when the page has any
143
143
  selector: '[data-ge-element]', // What the host marks an element with
144
144
  auto: false, // Treat every child of a content area as an element
145
+ types: [], // The elements the toolbar offers: { type, html, label | labelKey, iconClass, group }
145
146
  },
146
147
  resize: {
147
148
  enabled: true, // The handle on the column's edge
@@ -1239,6 +1240,7 @@ function build(instance, baseElem, optionsOrMethod) {
1239
1240
  var CORE_GROUPS = [
1240
1241
  { name: 'rows', labelKey: 'group.rows' },
1241
1242
  { name: 'content', labelKey: 'group.content' },
1243
+ { name: 'elements', labelKey: 'group.elements' },
1242
1244
  ];
1243
1245
 
1244
1246
  /** A plugin's category, when it names one. */
@@ -1452,6 +1454,8 @@ function build(instance, baseElem, optionsOrMethod) {
1452
1454
  if (activeTarget) { addToTarget(button, item.source || 'tool'); return; }
1453
1455
 
1454
1456
  var made = item.create();
1457
+ // A plugin that could not make one says so with null
1458
+ if (!made) { return; }
1455
1459
 
1456
1460
  // One that belongs in a column brings a row and a column
1457
1461
  // of its own, the way a container does
@@ -1611,6 +1615,7 @@ function build(instance, baseElem, optionsOrMethod) {
1611
1615
  function insertFeatureFromToolbar(button, where, source) {
1612
1616
  var item = FEATURES[button.getAttribute('data-ge-feature')].toolbar[parseInt(button.getAttribute('data-ge-item'), 10)];
1613
1617
  var made = item.create();
1618
+ if (!made) { return null; }
1614
1619
  var placed = made;
1615
1620
 
1616
1621
  if (item.inColumn) {
@@ -5770,6 +5775,7 @@ GridEditor.locales = {
5770
5775
  'tool.more': 'More',
5771
5776
  'group.rows': 'Rows',
5772
5777
  'group.content': 'Content',
5778
+ 'group.elements': 'Elements',
5773
5779
  'group.select': 'Add',
5774
5780
  'panel.title': '{kind} settings',
5775
5781
  'panel.close': 'Close',
@@ -54,6 +54,7 @@ GridEditor.locales.es = {
54
54
  'tool.more': 'Más',
55
55
  'group.rows': 'Filas',
56
56
  'group.content': 'Contenido',
57
+ 'group.elements': 'Elementos',
57
58
  'group.select': 'Añadir',
58
59
  'panel.title': 'Ajustes: {kind}',
59
60
  'panel.close': 'Cerrar',
@@ -28,11 +28,95 @@ var NOT_ELEMENTS = '.row, .ge-content, .ge-text-block, [data-ge-container], .ge-
28
28
  GridEditor.features.elements = function(ge) {
29
29
 
30
30
  var warnedIntoText = false;
31
+ var types = validTypes();
31
32
 
32
33
  function elementsEnabled() {
33
34
  if (ge.settings.elements.enabled !== 'auto') { return !!ge.settings.elements.enabled; }
34
35
 
35
- return ge.settings.elements.auto || !!ge.canvas.querySelector(ge.settings.elements.selector);
36
+ // A host that offers elements on the toolbar uses them, even on a
37
+ // page that has none yet
38
+ return ge.settings.elements.auto || types.length > 0 ||
39
+ !!ge.canvas.querySelector(ge.settings.elements.selector);
40
+ }
41
+
42
+ /** The elements.types the toolbar can offer. One that can't be made is left out, with a word to the host. */
43
+ function validTypes() {
44
+ return (ge.settings.elements.types || []).filter(function(type, index) {
45
+ var named = type && typeof type.type === 'string' && type.type.trim() !== '';
46
+ var html = type && (typeof type.html === 'function' || (typeof type.html === 'string' && type.html.trim() !== ''));
47
+
48
+ if (!named || !html) {
49
+ ge.warn('elements.types[' + index + ']: a type needs a type name and its html, as a string or a ' +
50
+ 'function; it is left off the toolbar.');
51
+ }
52
+
53
+ return named && html;
54
+ });
55
+ }
56
+
57
+ /** A type's toolbar button. */
58
+ function typeItem(type) {
59
+ var mismatched = false;
60
+
61
+ return {
62
+ // Asked again whenever the toolbar is built, so setLocale
63
+ // translates a labelKey
64
+ label: function() { return labelOf(type); },
65
+ iconClass: type.iconClass,
66
+ group: type.group || 'elements',
67
+ kind: 'element',
68
+ inColumn: true,
69
+ create: function() {
70
+ var element = makeElement(type);
71
+
72
+ if (element && !mismatched && !ge.settings.elements.auto && !element.matches(ge.settings.elements.selector)) {
73
+ mismatched = true;
74
+ ge.warn('elements.types "' + type.type + '": what it makes does not match elements.selector "' +
75
+ ge.settings.elements.selector + '", so it will not be an element once the editor looks again.');
76
+ }
77
+
78
+ return element;
79
+ },
80
+ };
81
+ }
82
+
83
+ function labelOf(type) {
84
+ if (type.labelKey) { return ge.t(type.labelKey); }
85
+
86
+ return type.label || type.type;
87
+ }
88
+
89
+ /**
90
+ * What a type's button makes: the root of its html, marked as the host
91
+ * marks its own, or a div around it when it has no single root. Null,
92
+ * with a word to the host, when there is nothing to make.
93
+ */
94
+ function makeElement(type) {
95
+ var made;
96
+
97
+ try {
98
+ made = typeof type.html === 'function' ? type.html() : type.html;
99
+ } catch (error) {
100
+ ge.warn('elements.types "' + type.type + '": its html function threw (' + error.message + '); nothing was added.');
101
+ return null;
102
+ }
103
+
104
+ var nodes = typeof made === 'string' ? dom.parse(made) : (made && made.nodeType ? [made] : []);
105
+ // Whitespace between tags is not content
106
+ nodes = nodes.filter(function(node) { return node.nodeType !== 3 || node.textContent.trim() !== ''; });
107
+
108
+ if (!nodes.length) {
109
+ ge.warn('elements.types "' + type.type + '": its html came out empty; nothing was added.');
110
+ return null;
111
+ }
112
+
113
+ var element = nodes.length === 1 && nodes[0].nodeType === 1 ? nodes[0] : dom.element('div');
114
+ if (element !== nodes[0] || nodes.length > 1) { fill(element, nodes); }
115
+
116
+ element.setAttribute('data-ge-element', type.type);
117
+ if (!element.hasAttribute('data-ge-label')) { element.setAttribute('data-ge-label', labelOf(type)); }
118
+
119
+ return element;
36
120
  }
37
121
 
38
122
  /** Whether a child of a column is an element. */
@@ -121,6 +205,11 @@ GridEditor.features.elements = function(ge) {
121
205
  return placed;
122
206
  }
123
207
 
208
+ /** enabled: false takes the toolbar's element buttons away too. */
209
+ function elementsOff() {
210
+ return ge.settings.elements.enabled === false;
211
+ }
212
+
124
213
  /** A place a caller named: an element, or the first one a selector matches. */
125
214
  function nodeFrom(node) {
126
215
  if (typeof node === 'string') { return document.querySelector(node); }
@@ -163,6 +252,10 @@ GridEditor.features.elements = function(ge) {
163
252
  createElement: apiCreateElement,
164
253
  },
165
254
 
255
+ // A button for each type the host offers, in the elements category
256
+ // unless the type names another
257
+ toolbar: elementsOff() ? [] : types.map(typeItem),
258
+
166
259
  /** A node this plugin marked is an element, whatever else it is. */
167
260
  kindOf: function(node) {
168
261
  return dom.hasClass(node, 'ge-element') ? 'element' : null;
@@ -321,7 +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
+ group: 'elements',
325
325
  inColumn: true,
326
326
  create: function() { return makeText(type); },
327
327
  };
@@ -125,7 +125,7 @@ export interface GridEditorOptions {
125
125
  inline_style?: InlineStyleOptions;
126
126
  /** The tabs plugin: the variant of the tabs containers made new. */
127
127
  tabs?: TabsOptions;
128
- elements?: { enabled?: boolean | 'auto'; selector?: string; auto?: boolean };
128
+ elements?: { enabled?: boolean | 'auto'; selector?: string; auto?: boolean; types?: ElementType[] };
129
129
  custom_filter?: CustomFilter | CustomFilter[] | '';
130
130
  valid_col_sizes?: ColumnSize[];
131
131
  valid_col_offsets?: number[];
@@ -286,6 +286,22 @@ export type GridEditorCallbacks = {
286
286
  };
287
287
 
288
288
  /** A plugin's factory: called once per editor with the handle in docs/plugins.md. */
289
+ /** An element the toolbar offers, from elements.types. */
290
+ export interface ElementType {
291
+ /** What it is: its data-ge-element. */
292
+ type: string;
293
+ /** Its markup: html, or a function called for each one made that returns html or a node. */
294
+ html: string | (() => string | Node);
295
+ /** Its button's label, as it is. */
296
+ label?: string;
297
+ /** Its button's label, a locale key: wins over label, and follows setLocale. */
298
+ labelKey?: string;
299
+ /** The button shows this icon alone, with the label as its title. */
300
+ iconClass?: string;
301
+ /** Its toolbar_groups category. Default 'elements'. */
302
+ group?: string;
303
+ }
304
+
289
305
  export type PluginFactory = ((ge: any) => Record<string, unknown>) & { always?: boolean };
290
306
 
291
307
  export interface CreateColumnOptions extends Placement {