@themarioga/grid-editor 8.0.0-beta.2 → 8.0.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 (55) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +16 -9
  3. package/UPGRADING.md +21 -11
  4. package/dist/grideditor.bundle.min.js +3 -3
  5. package/dist/grideditor.css +77 -35
  6. package/dist/grideditor.d.ts +29 -8
  7. package/dist/grideditor.esm.js +7 -1
  8. package/dist/grideditor.js +7 -1
  9. package/dist/grideditor.min.css +1 -1
  10. package/dist/grideditor.min.css.map +1 -1
  11. package/dist/grideditor.min.js +2 -2
  12. package/dist/grideditor.min.js.map +3 -3
  13. package/dist/locales/grideditor.es.esm.js +86 -68
  14. package/dist/locales/grideditor.es.js +86 -68
  15. package/dist/locales/grideditor.es.min.js +1 -1
  16. package/dist/locales/grideditor.es.min.js.map +2 -2
  17. package/dist/plugins/grideditor.alignment.min.js.map +1 -1
  18. package/dist/plugins/{grideditor.style.esm.js → grideditor.inline-style.esm.js} +184 -184
  19. package/dist/plugins/{grideditor.style.js → grideditor.inline-style.js} +182 -182
  20. package/dist/plugins/grideditor.inline-style.min.js +4 -0
  21. package/dist/plugins/grideditor.inline-style.min.js.map +7 -0
  22. package/dist/plugins/grideditor.tabs.esm.js +274 -6
  23. package/dist/plugins/grideditor.tabs.js +274 -6
  24. package/dist/plugins/grideditor.tabs.min.js +1 -1
  25. package/dist/plugins/grideditor.tabs.min.js.map +3 -3
  26. package/docs/locale-keys.md +97 -79
  27. package/docs/plugins.md +5 -5
  28. package/example/containers.html +64 -2
  29. package/example/index.html +6 -6
  30. package/example/{style.html → inline-style.html} +5 -5
  31. package/example/utilities.html +3 -3
  32. package/package.json +1 -1
  33. package/src/js/grideditor.js +8 -0
  34. package/src/js/{style → inline-style}/accordion.js +9 -9
  35. package/src/js/{style → inline-style}/catalog.js +6 -6
  36. package/src/js/{style → inline-style}/custom.js +4 -4
  37. package/src/js/{style → inline-style}/display.js +2 -2
  38. package/src/js/{style → inline-style}/drawerflow.js +1 -1
  39. package/src/js/{style → inline-style}/fields.js +25 -25
  40. package/src/js/{style → inline-style}/flex.js +2 -2
  41. package/src/js/{style → inline-style}/float.js +2 -2
  42. package/src/js/{style → inline-style}/options.js +5 -5
  43. package/src/js/{style → inline-style}/sections.js +50 -50
  44. package/src/js/{style → inline-style}/spacing.js +2 -2
  45. package/src/js/{style → inline-style}/sticky.js +2 -2
  46. package/src/js/{style → inline-style}/textalign.js +2 -2
  47. package/src/js/locales/grideditor.es.js +86 -68
  48. package/src/js/plugins/grideditor.alignment.js +1 -1
  49. package/src/js/plugins/grideditor.inline-style.js +185 -0
  50. package/src/js/plugins/grideditor.tabs.js +311 -5
  51. package/src/less/grideditor.less +71 -25
  52. package/types/grideditor.d.ts +29 -8
  53. package/dist/plugins/grideditor.style.min.js +0 -4
  54. package/dist/plugins/grideditor.style.min.js.map +0 -7
  55. package/src/js/plugins/grideditor.style.js +0 -185
@@ -8,6 +8,16 @@ function all(root, selector) {
8
8
  function one(root, selector) {
9
9
  return root ? root.querySelector(selector) : null;
10
10
  }
11
+ function selfAndAll(root, selector) {
12
+ if (!root) {
13
+ return [];
14
+ }
15
+ var found = all(root, selector);
16
+ if (root.nodeType === 1 && root.matches(selector)) {
17
+ found.unshift(root);
18
+ }
19
+ return found;
20
+ }
11
21
  function children(node, selector) {
12
22
  if (!node) {
13
23
  return [];
@@ -60,6 +70,12 @@ function hasClass(node, name) {
60
70
  function split(names) {
61
71
  return String(names || "").split(/\s+/).filter(Boolean);
62
72
  }
73
+ function dropEmptyClass(node) {
74
+ if (!node.getAttribute("class")) {
75
+ node.removeAttribute("class");
76
+ }
77
+ return node;
78
+ }
63
79
  function hide(node) {
64
80
  node.style.display = "none";
65
81
  return node;
@@ -106,12 +122,238 @@ Object.assign(GridEditor.locales.en, {
106
122
  "container.add_tabs": "Tabs",
107
123
  "container.add_tab": "Add tab",
108
124
  "container.tab_label": "Tab {number}",
109
- "confirm.delete_tab": "Delete this tab and everything in it?"
125
+ "confirm.delete_tab": "Delete this tab and everything in it?",
126
+ "container.tabs_section": "Tabs",
127
+ "container.tabs_style": "Style",
128
+ "container.tabs_style_tabs": "Tabs",
129
+ "container.tabs_style_pills": "Pills",
130
+ "container.tabs_style_underline": "Underline",
131
+ "container.tabs_width": "Width",
132
+ "container.tabs_width_natural": "Natural",
133
+ "container.tabs_width_fill": "Fill",
134
+ "container.tabs_width_justified": "Justified",
135
+ "container.tabs_align": "Alignment",
136
+ "container.tabs_align_start": "Start",
137
+ "container.tabs_align_center": "Center",
138
+ "container.tabs_align_end": "End",
139
+ "container.tabs_layout": "Layout",
140
+ "container.tabs_layout_horizontal": "Horizontal",
141
+ "container.tabs_layout_vertical": "Vertical",
142
+ "container.tabs_layout_vertical_from": "Vertical from {breakpoint}"
110
143
  });
144
+ var STYLES = { tabs: "nav-tabs", pills: "nav-pills", underline: "nav-underline" };
145
+ var WIDTHS = { natural: null, fill: "nav-fill", justified: "nav-justified" };
146
+ var ALIGNS = { start: null, center: "justify-content-center", end: "justify-content-end" };
147
+ var DEFAULTS = { variant: "tabs", width: "natural", align: "start", vertical: false };
148
+ var FIELDS = {
149
+ variant: { labelKey: "container.tabs_style", choices: [
150
+ { value: "tabs", labelKey: "container.tabs_style_tabs" },
151
+ { value: "pills", labelKey: "container.tabs_style_pills" },
152
+ { value: "underline", labelKey: "container.tabs_style_underline" }
153
+ ] },
154
+ width: { labelKey: "container.tabs_width", choices: [
155
+ { value: "natural", labelKey: "container.tabs_width_natural" },
156
+ { value: "fill", labelKey: "container.tabs_width_fill" },
157
+ { value: "justified", labelKey: "container.tabs_width_justified" }
158
+ ] },
159
+ align: { labelKey: "container.tabs_align", choices: [
160
+ { value: "start", labelKey: "container.tabs_align_start" },
161
+ { value: "center", labelKey: "container.tabs_align_center" },
162
+ { value: "end", labelKey: "container.tabs_align_end" }
163
+ ] },
164
+ vertical: { labelKey: "container.tabs_layout" }
165
+ };
166
+ var fieldCounter = 0;
167
+ var LAYOUT_ATTR = "data-ge-tabs-layout";
168
+ function classValues(map) {
169
+ return Object.keys(map).map(function(key) {
170
+ return map[key];
171
+ }).filter(Boolean);
172
+ }
111
173
  GridEditor.containers.tabs = function(ge) {
174
+ var sections = [];
175
+ function stripOf(container) {
176
+ return child(container, ".nav");
177
+ }
178
+ function fromBreakpoints() {
179
+ return ge.breakpoints.slice(1);
180
+ }
181
+ function infix(vertical) {
182
+ return vertical === true ? "" : "-" + vertical;
183
+ }
184
+ function verticalClasses(vertical) {
185
+ var at = infix(vertical);
186
+ return {
187
+ container: ["d" + at + "-flex", "align-items" + at + "-start"],
188
+ strip: ["flex" + at + "-column", "me" + at + "-3"]
189
+ };
190
+ }
191
+ function verticals() {
192
+ return [true].concat(fromBreakpoints());
193
+ }
194
+ function read(container) {
195
+ var strip = stripOf(container);
196
+ var has = function(node, name) {
197
+ return !!node && hasClass(node, name);
198
+ };
199
+ var found = function(map, fallback) {
200
+ return Object.keys(map).filter(function(key) {
201
+ return map[key] && has(strip, map[key]);
202
+ })[0] || fallback;
203
+ };
204
+ var vertical = verticals().filter(function(each) {
205
+ var classes = verticalClasses(each);
206
+ return has(container, classes.container[0]) && has(strip, classes.strip[0]);
207
+ })[0];
208
+ return {
209
+ variant: found(STYLES, "tabs"),
210
+ width: found(WIDTHS, "natural"),
211
+ align: found(ALIGNS, "start"),
212
+ vertical: vertical === void 0 ? false : vertical
213
+ };
214
+ }
215
+ function write(container, key, value) {
216
+ var strip = stripOf(container);
217
+ if (!strip) {
218
+ return;
219
+ }
220
+ if (key === "variant") {
221
+ removeClass(strip, classValues(STYLES).join(" "));
222
+ addClass(strip, STYLES[value]);
223
+ }
224
+ if (key === "width" || key === "vertical") {
225
+ removeClass(strip, classValues(WIDTHS).join(" "));
226
+ if (key === "width" && WIDTHS[value]) {
227
+ addClass(strip, WIDTHS[value]);
228
+ }
229
+ }
230
+ if (key === "align" || key === "vertical" || key === "width" && value !== "natural") {
231
+ removeClass(strip, classValues(ALIGNS).join(" "));
232
+ if (key === "align" && ALIGNS[value]) {
233
+ addClass(strip, ALIGNS[value]);
234
+ }
235
+ }
236
+ if (key === "vertical") {
237
+ verticals().forEach(function(each) {
238
+ var classes = verticalClasses(each);
239
+ removeClass(container, classes.container.join(" "));
240
+ removeClass(strip, classes.strip.join(" "));
241
+ });
242
+ strip.removeAttribute("aria-orientation");
243
+ if (value !== false) {
244
+ var layout = verticalClasses(value);
245
+ addClass(container, layout.container.join(" "));
246
+ addClass(strip, layout.strip.join(" "));
247
+ strip.setAttribute("aria-orientation", "vertical");
248
+ }
249
+ }
250
+ dropEmptyClass(container);
251
+ }
252
+ function valuesOf(key) {
253
+ if (key === "variant") {
254
+ return Object.keys(STYLES);
255
+ }
256
+ if (key === "width") {
257
+ return Object.keys(WIDTHS);
258
+ }
259
+ if (key === "align") {
260
+ return Object.keys(ALIGNS);
261
+ }
262
+ return [false].concat(verticals());
263
+ }
264
+ function chosen(options, key) {
265
+ var setting = ge.settings.tabs || {};
266
+ var value = options[key] !== void 0 ? options[key] : setting[key];
267
+ if (value === void 0) {
268
+ return DEFAULTS[key];
269
+ }
270
+ if (valuesOf(key).indexOf(value) !== -1) {
271
+ return value;
272
+ }
273
+ ge.warn("tabs: " + JSON.stringify(value) + " is not a " + key + ", which takes " + JSON.stringify(valuesOf(key)) + ": " + JSON.stringify(DEFAULTS[key]) + " is used");
274
+ return DEFAULTS[key];
275
+ }
276
+ function layoutHere(container) {
277
+ var vertical = read(container).vertical;
278
+ if (vertical === false) {
279
+ return null;
280
+ }
281
+ if (ge.view() === "all") {
282
+ container.removeAttribute(LAYOUT_ATTR);
283
+ return /flex$/.test(getComputedStyle(container).display) ? "vertical" : "horizontal";
284
+ }
285
+ if (vertical === true) {
286
+ return "vertical";
287
+ }
288
+ return ge.breakpoints.indexOf(ge.view()) >= ge.breakpoints.indexOf(vertical) ? "vertical" : "horizontal";
289
+ }
290
+ function markLayout(container) {
291
+ var layout = layoutHere(container);
292
+ if (layout) {
293
+ container.setAttribute(LAYOUT_ATTR, layout);
294
+ } else {
295
+ container.removeAttribute(LAYOUT_ATTR);
296
+ }
297
+ }
298
+ function select(labelKey, options, onChange) {
299
+ var box = element("div", { "class": "ge-tabs-variant" });
300
+ var id = "ge-tabs-variant-" + ++fieldCounter;
301
+ box.appendChild(element("label", { "class": "form-label", "for": id }, ge.t(labelKey)));
302
+ var field = box.appendChild(element("select", { "class": "form-select form-select-sm", id }));
303
+ options.forEach(function(option) {
304
+ field.appendChild(element("option", { value: option.value }, option.label));
305
+ });
306
+ field.addEventListener("change", function() {
307
+ onChange(field.value);
308
+ });
309
+ return { box, field };
310
+ }
311
+ function createSection(container) {
312
+ var body = element("div", { "class": "ge-tabs-variants" });
313
+ var fields = {};
314
+ function change(key, value) {
315
+ write(container, key, value);
316
+ markLayout(container);
317
+ render();
318
+ }
319
+ function options(key) {
320
+ return FIELDS[key].choices.map(function(choice) {
321
+ return { value: choice.value, label: ge.t(choice.labelKey) };
322
+ });
323
+ }
324
+ ["variant", "width", "align"].forEach(function(key) {
325
+ fields[key] = select(FIELDS[key].labelKey, options(key), function(value) {
326
+ change(key, value);
327
+ });
328
+ });
329
+ fields.vertical = select(FIELDS.vertical.labelKey, [
330
+ { value: "false", label: ge.t("container.tabs_layout_horizontal") },
331
+ { value: "true", label: ge.t("container.tabs_layout_vertical") }
332
+ ].concat(fromBreakpoints().map(function(key) {
333
+ return { value: key, label: ge.t("container.tabs_layout_vertical_from", { breakpoint: key }) };
334
+ })), function(value) {
335
+ change("vertical", value === "true" ? true : value === "false" ? false : value);
336
+ });
337
+ ["variant", "width", "align", "vertical"].forEach(function(key) {
338
+ fields[key].box.setAttribute("data-ge-tabs-variant", key);
339
+ body.appendChild(fields[key].box);
340
+ });
341
+ function render() {
342
+ var values = read(container);
343
+ fields.variant.field.value = values.variant;
344
+ fields.width.field.value = values.width;
345
+ fields.align.field.value = values.align;
346
+ fields.vertical.field.value = String(values.vertical);
347
+ fields.width.field.disabled = values.vertical !== false;
348
+ fields.align.field.disabled = values.vertical !== false || values.width !== "natural";
349
+ }
350
+ render();
351
+ sections.push({ container, element: body, render });
352
+ return body;
353
+ }
112
354
  function addTabTo(container, options) {
113
355
  options = options || {};
114
- var strip = one(container, ":scope > .nav-tabs");
356
+ var strip = stripOf(container);
115
357
  var content = one(container, ":scope > .tab-content");
116
358
  var id = ge.containerId("tab");
117
359
  var number = children(strip, ".nav-item").length + 1;
@@ -147,7 +389,7 @@ GridEditor.containers.tabs = function(ge) {
147
389
  removeClass(each, "show active");
148
390
  });
149
391
  addClass(pane, "show active");
150
- all(container, ":scope > .nav-tabs .nav-link").forEach(function(button) {
392
+ all(stripOf(container) || container, ".nav-link").forEach(function(button) {
151
393
  var active = button.getAttribute("data-bs-target") === "#" + id;
152
394
  toggleClass(button, "active", active);
153
395
  button.setAttribute("aria-selected", active ? "true" : "false");
@@ -163,7 +405,7 @@ GridEditor.containers.tabs = function(ge) {
163
405
  // A tab strip sorts its own tabs, and the panes follow them. No
164
406
  // group: a tab belongs to the strip it was made in.
165
407
  onSortable: function(sortable) {
166
- sortable(all(ge.canvas, ".ge-container-tabs > .nav-tabs"), {
408
+ sortable(all(ge.canvas, ".ge-container-tabs").map(stripOf).filter(Boolean), {
167
409
  draggable: ".ge-tab"
168
410
  });
169
411
  },
@@ -175,6 +417,9 @@ GridEditor.containers.tabs = function(ge) {
175
417
  var count = options.tabs || labels.length || 2;
176
418
  container.appendChild(element("ul", { "class": "nav nav-tabs", role: "tablist" }));
177
419
  container.appendChild(element("div", { "class": "tab-content" }));
420
+ ["variant", "width", "align", "vertical"].forEach(function(key) {
421
+ write(container, key, chosen(options, key));
422
+ });
178
423
  for (var i = 0; i < count; i++) {
179
424
  addTabTo(container, { label: labels[i] });
180
425
  }
@@ -185,7 +430,7 @@ GridEditor.containers.tabs = function(ge) {
185
430
  all(container, ":scope > .tab-content > .tab-pane").forEach(function(pane) {
186
431
  addClass(pane, "ge-tab-pane");
187
432
  });
188
- all(container, ":scope > .nav-tabs > .nav-item").forEach(function(tab) {
433
+ children(stripOf(container) || container, ".nav-item").forEach(function(tab) {
189
434
  addClass(tab, "ge-tab");
190
435
  var link = one(tab, ".nav-link");
191
436
  if (link) {
@@ -218,15 +463,38 @@ GridEditor.containers.tabs = function(ge) {
218
463
  },
219
464
  unmark: function(container) {
220
465
  ge.resumeToggles(container);
466
+ container.removeAttribute(LAYOUT_ATTR);
221
467
  all(container, ".ge-tab-pane").forEach(function(pane) {
222
468
  removeClass(pane, "ge-tab-pane");
223
469
  });
224
470
  ge.unwrapLabels(container);
225
471
  },
472
+ panelSection: function(node, kind) {
473
+ if (kind !== "tabs" || !stripOf(node)) {
474
+ return null;
475
+ }
476
+ return { labelKey: "container.tabs_section", body: createSection(node) };
477
+ },
478
+ // After a view change or a change of classes: the layout marks and the
479
+ // sections follow
480
+ onRefresh: function(scope) {
481
+ selfAndAll(scope, '[data-ge-container="tabs"]').forEach(markLayout);
482
+ sections = sections.filter(function(entry) {
483
+ return entry.element.isConnected;
484
+ });
485
+ sections.forEach(function(entry) {
486
+ if (entry.container === scope || scope.contains(entry.container)) {
487
+ entry.render();
488
+ }
489
+ });
490
+ },
491
+ onDeinit: function() {
492
+ sections = [];
493
+ },
226
494
  /** Panes read in tab order, whatever order they were dropped in. */
227
495
  afterPaneMove: function(container) {
228
496
  var content = one(container, ":scope > .tab-content");
229
- all(container, ":scope > .nav-tabs > .nav-item").forEach(function(tab) {
497
+ children(stripOf(container) || container, ".nav-item").forEach(function(tab) {
230
498
  var pane = paneOf(container, tab);
231
499
  if (pane) {
232
500
  content.appendChild(pane);
@@ -12,6 +12,16 @@
12
12
  function one(root, selector) {
13
13
  return root ? root.querySelector(selector) : null;
14
14
  }
15
+ function selfAndAll(root, selector) {
16
+ if (!root) {
17
+ return [];
18
+ }
19
+ var found = all(root, selector);
20
+ if (root.nodeType === 1 && root.matches(selector)) {
21
+ found.unshift(root);
22
+ }
23
+ return found;
24
+ }
15
25
  function children(node, selector) {
16
26
  if (!node) {
17
27
  return [];
@@ -64,6 +74,12 @@
64
74
  function split(names) {
65
75
  return String(names || "").split(/\s+/).filter(Boolean);
66
76
  }
77
+ function dropEmptyClass(node) {
78
+ if (!node.getAttribute("class")) {
79
+ node.removeAttribute("class");
80
+ }
81
+ return node;
82
+ }
67
83
  function hide(node) {
68
84
  node.style.display = "none";
69
85
  return node;
@@ -110,12 +126,238 @@
110
126
  "container.add_tabs": "Tabs",
111
127
  "container.add_tab": "Add tab",
112
128
  "container.tab_label": "Tab {number}",
113
- "confirm.delete_tab": "Delete this tab and everything in it?"
129
+ "confirm.delete_tab": "Delete this tab and everything in it?",
130
+ "container.tabs_section": "Tabs",
131
+ "container.tabs_style": "Style",
132
+ "container.tabs_style_tabs": "Tabs",
133
+ "container.tabs_style_pills": "Pills",
134
+ "container.tabs_style_underline": "Underline",
135
+ "container.tabs_width": "Width",
136
+ "container.tabs_width_natural": "Natural",
137
+ "container.tabs_width_fill": "Fill",
138
+ "container.tabs_width_justified": "Justified",
139
+ "container.tabs_align": "Alignment",
140
+ "container.tabs_align_start": "Start",
141
+ "container.tabs_align_center": "Center",
142
+ "container.tabs_align_end": "End",
143
+ "container.tabs_layout": "Layout",
144
+ "container.tabs_layout_horizontal": "Horizontal",
145
+ "container.tabs_layout_vertical": "Vertical",
146
+ "container.tabs_layout_vertical_from": "Vertical from {breakpoint}"
114
147
  });
148
+ var STYLES = { tabs: "nav-tabs", pills: "nav-pills", underline: "nav-underline" };
149
+ var WIDTHS = { natural: null, fill: "nav-fill", justified: "nav-justified" };
150
+ var ALIGNS = { start: null, center: "justify-content-center", end: "justify-content-end" };
151
+ var DEFAULTS = { variant: "tabs", width: "natural", align: "start", vertical: false };
152
+ var FIELDS = {
153
+ variant: { labelKey: "container.tabs_style", choices: [
154
+ { value: "tabs", labelKey: "container.tabs_style_tabs" },
155
+ { value: "pills", labelKey: "container.tabs_style_pills" },
156
+ { value: "underline", labelKey: "container.tabs_style_underline" }
157
+ ] },
158
+ width: { labelKey: "container.tabs_width", choices: [
159
+ { value: "natural", labelKey: "container.tabs_width_natural" },
160
+ { value: "fill", labelKey: "container.tabs_width_fill" },
161
+ { value: "justified", labelKey: "container.tabs_width_justified" }
162
+ ] },
163
+ align: { labelKey: "container.tabs_align", choices: [
164
+ { value: "start", labelKey: "container.tabs_align_start" },
165
+ { value: "center", labelKey: "container.tabs_align_center" },
166
+ { value: "end", labelKey: "container.tabs_align_end" }
167
+ ] },
168
+ vertical: { labelKey: "container.tabs_layout" }
169
+ };
170
+ var fieldCounter = 0;
171
+ var LAYOUT_ATTR = "data-ge-tabs-layout";
172
+ function classValues(map) {
173
+ return Object.keys(map).map(function(key) {
174
+ return map[key];
175
+ }).filter(Boolean);
176
+ }
115
177
  GridEditor.containers.tabs = function(ge) {
178
+ var sections = [];
179
+ function stripOf(container) {
180
+ return child(container, ".nav");
181
+ }
182
+ function fromBreakpoints() {
183
+ return ge.breakpoints.slice(1);
184
+ }
185
+ function infix(vertical) {
186
+ return vertical === true ? "" : "-" + vertical;
187
+ }
188
+ function verticalClasses(vertical) {
189
+ var at = infix(vertical);
190
+ return {
191
+ container: ["d" + at + "-flex", "align-items" + at + "-start"],
192
+ strip: ["flex" + at + "-column", "me" + at + "-3"]
193
+ };
194
+ }
195
+ function verticals() {
196
+ return [true].concat(fromBreakpoints());
197
+ }
198
+ function read(container) {
199
+ var strip = stripOf(container);
200
+ var has = function(node, name) {
201
+ return !!node && hasClass(node, name);
202
+ };
203
+ var found = function(map, fallback) {
204
+ return Object.keys(map).filter(function(key) {
205
+ return map[key] && has(strip, map[key]);
206
+ })[0] || fallback;
207
+ };
208
+ var vertical = verticals().filter(function(each) {
209
+ var classes = verticalClasses(each);
210
+ return has(container, classes.container[0]) && has(strip, classes.strip[0]);
211
+ })[0];
212
+ return {
213
+ variant: found(STYLES, "tabs"),
214
+ width: found(WIDTHS, "natural"),
215
+ align: found(ALIGNS, "start"),
216
+ vertical: vertical === void 0 ? false : vertical
217
+ };
218
+ }
219
+ function write(container, key, value) {
220
+ var strip = stripOf(container);
221
+ if (!strip) {
222
+ return;
223
+ }
224
+ if (key === "variant") {
225
+ removeClass(strip, classValues(STYLES).join(" "));
226
+ addClass(strip, STYLES[value]);
227
+ }
228
+ if (key === "width" || key === "vertical") {
229
+ removeClass(strip, classValues(WIDTHS).join(" "));
230
+ if (key === "width" && WIDTHS[value]) {
231
+ addClass(strip, WIDTHS[value]);
232
+ }
233
+ }
234
+ if (key === "align" || key === "vertical" || key === "width" && value !== "natural") {
235
+ removeClass(strip, classValues(ALIGNS).join(" "));
236
+ if (key === "align" && ALIGNS[value]) {
237
+ addClass(strip, ALIGNS[value]);
238
+ }
239
+ }
240
+ if (key === "vertical") {
241
+ verticals().forEach(function(each) {
242
+ var classes = verticalClasses(each);
243
+ removeClass(container, classes.container.join(" "));
244
+ removeClass(strip, classes.strip.join(" "));
245
+ });
246
+ strip.removeAttribute("aria-orientation");
247
+ if (value !== false) {
248
+ var layout = verticalClasses(value);
249
+ addClass(container, layout.container.join(" "));
250
+ addClass(strip, layout.strip.join(" "));
251
+ strip.setAttribute("aria-orientation", "vertical");
252
+ }
253
+ }
254
+ dropEmptyClass(container);
255
+ }
256
+ function valuesOf(key) {
257
+ if (key === "variant") {
258
+ return Object.keys(STYLES);
259
+ }
260
+ if (key === "width") {
261
+ return Object.keys(WIDTHS);
262
+ }
263
+ if (key === "align") {
264
+ return Object.keys(ALIGNS);
265
+ }
266
+ return [false].concat(verticals());
267
+ }
268
+ function chosen(options, key) {
269
+ var setting = ge.settings.tabs || {};
270
+ var value = options[key] !== void 0 ? options[key] : setting[key];
271
+ if (value === void 0) {
272
+ return DEFAULTS[key];
273
+ }
274
+ if (valuesOf(key).indexOf(value) !== -1) {
275
+ return value;
276
+ }
277
+ ge.warn("tabs: " + JSON.stringify(value) + " is not a " + key + ", which takes " + JSON.stringify(valuesOf(key)) + ": " + JSON.stringify(DEFAULTS[key]) + " is used");
278
+ return DEFAULTS[key];
279
+ }
280
+ function layoutHere(container) {
281
+ var vertical = read(container).vertical;
282
+ if (vertical === false) {
283
+ return null;
284
+ }
285
+ if (ge.view() === "all") {
286
+ container.removeAttribute(LAYOUT_ATTR);
287
+ return /flex$/.test(getComputedStyle(container).display) ? "vertical" : "horizontal";
288
+ }
289
+ if (vertical === true) {
290
+ return "vertical";
291
+ }
292
+ return ge.breakpoints.indexOf(ge.view()) >= ge.breakpoints.indexOf(vertical) ? "vertical" : "horizontal";
293
+ }
294
+ function markLayout(container) {
295
+ var layout = layoutHere(container);
296
+ if (layout) {
297
+ container.setAttribute(LAYOUT_ATTR, layout);
298
+ } else {
299
+ container.removeAttribute(LAYOUT_ATTR);
300
+ }
301
+ }
302
+ function select(labelKey, options, onChange) {
303
+ var box = element("div", { "class": "ge-tabs-variant" });
304
+ var id = "ge-tabs-variant-" + ++fieldCounter;
305
+ box.appendChild(element("label", { "class": "form-label", "for": id }, ge.t(labelKey)));
306
+ var field = box.appendChild(element("select", { "class": "form-select form-select-sm", id }));
307
+ options.forEach(function(option) {
308
+ field.appendChild(element("option", { value: option.value }, option.label));
309
+ });
310
+ field.addEventListener("change", function() {
311
+ onChange(field.value);
312
+ });
313
+ return { box, field };
314
+ }
315
+ function createSection(container) {
316
+ var body = element("div", { "class": "ge-tabs-variants" });
317
+ var fields = {};
318
+ function change(key, value) {
319
+ write(container, key, value);
320
+ markLayout(container);
321
+ render();
322
+ }
323
+ function options(key) {
324
+ return FIELDS[key].choices.map(function(choice) {
325
+ return { value: choice.value, label: ge.t(choice.labelKey) };
326
+ });
327
+ }
328
+ ["variant", "width", "align"].forEach(function(key) {
329
+ fields[key] = select(FIELDS[key].labelKey, options(key), function(value) {
330
+ change(key, value);
331
+ });
332
+ });
333
+ fields.vertical = select(FIELDS.vertical.labelKey, [
334
+ { value: "false", label: ge.t("container.tabs_layout_horizontal") },
335
+ { value: "true", label: ge.t("container.tabs_layout_vertical") }
336
+ ].concat(fromBreakpoints().map(function(key) {
337
+ return { value: key, label: ge.t("container.tabs_layout_vertical_from", { breakpoint: key }) };
338
+ })), function(value) {
339
+ change("vertical", value === "true" ? true : value === "false" ? false : value);
340
+ });
341
+ ["variant", "width", "align", "vertical"].forEach(function(key) {
342
+ fields[key].box.setAttribute("data-ge-tabs-variant", key);
343
+ body.appendChild(fields[key].box);
344
+ });
345
+ function render() {
346
+ var values = read(container);
347
+ fields.variant.field.value = values.variant;
348
+ fields.width.field.value = values.width;
349
+ fields.align.field.value = values.align;
350
+ fields.vertical.field.value = String(values.vertical);
351
+ fields.width.field.disabled = values.vertical !== false;
352
+ fields.align.field.disabled = values.vertical !== false || values.width !== "natural";
353
+ }
354
+ render();
355
+ sections.push({ container, element: body, render });
356
+ return body;
357
+ }
116
358
  function addTabTo(container, options) {
117
359
  options = options || {};
118
- var strip = one(container, ":scope > .nav-tabs");
360
+ var strip = stripOf(container);
119
361
  var content = one(container, ":scope > .tab-content");
120
362
  var id = ge.containerId("tab");
121
363
  var number = children(strip, ".nav-item").length + 1;
@@ -151,7 +393,7 @@
151
393
  removeClass(each, "show active");
152
394
  });
153
395
  addClass(pane, "show active");
154
- all(container, ":scope > .nav-tabs .nav-link").forEach(function(button) {
396
+ all(stripOf(container) || container, ".nav-link").forEach(function(button) {
155
397
  var active = button.getAttribute("data-bs-target") === "#" + id;
156
398
  toggleClass(button, "active", active);
157
399
  button.setAttribute("aria-selected", active ? "true" : "false");
@@ -167,7 +409,7 @@
167
409
  // A tab strip sorts its own tabs, and the panes follow them. No
168
410
  // group: a tab belongs to the strip it was made in.
169
411
  onSortable: function(sortable) {
170
- sortable(all(ge.canvas, ".ge-container-tabs > .nav-tabs"), {
412
+ sortable(all(ge.canvas, ".ge-container-tabs").map(stripOf).filter(Boolean), {
171
413
  draggable: ".ge-tab"
172
414
  });
173
415
  },
@@ -179,6 +421,9 @@
179
421
  var count = options.tabs || labels.length || 2;
180
422
  container.appendChild(element("ul", { "class": "nav nav-tabs", role: "tablist" }));
181
423
  container.appendChild(element("div", { "class": "tab-content" }));
424
+ ["variant", "width", "align", "vertical"].forEach(function(key) {
425
+ write(container, key, chosen(options, key));
426
+ });
182
427
  for (var i = 0; i < count; i++) {
183
428
  addTabTo(container, { label: labels[i] });
184
429
  }
@@ -189,7 +434,7 @@
189
434
  all(container, ":scope > .tab-content > .tab-pane").forEach(function(pane) {
190
435
  addClass(pane, "ge-tab-pane");
191
436
  });
192
- all(container, ":scope > .nav-tabs > .nav-item").forEach(function(tab) {
437
+ children(stripOf(container) || container, ".nav-item").forEach(function(tab) {
193
438
  addClass(tab, "ge-tab");
194
439
  var link = one(tab, ".nav-link");
195
440
  if (link) {
@@ -222,15 +467,38 @@
222
467
  },
223
468
  unmark: function(container) {
224
469
  ge.resumeToggles(container);
470
+ container.removeAttribute(LAYOUT_ATTR);
225
471
  all(container, ".ge-tab-pane").forEach(function(pane) {
226
472
  removeClass(pane, "ge-tab-pane");
227
473
  });
228
474
  ge.unwrapLabels(container);
229
475
  },
476
+ panelSection: function(node, kind) {
477
+ if (kind !== "tabs" || !stripOf(node)) {
478
+ return null;
479
+ }
480
+ return { labelKey: "container.tabs_section", body: createSection(node) };
481
+ },
482
+ // After a view change or a change of classes: the layout marks and the
483
+ // sections follow
484
+ onRefresh: function(scope) {
485
+ selfAndAll(scope, '[data-ge-container="tabs"]').forEach(markLayout);
486
+ sections = sections.filter(function(entry) {
487
+ return entry.element.isConnected;
488
+ });
489
+ sections.forEach(function(entry) {
490
+ if (entry.container === scope || scope.contains(entry.container)) {
491
+ entry.render();
492
+ }
493
+ });
494
+ },
495
+ onDeinit: function() {
496
+ sections = [];
497
+ },
230
498
  /** Panes read in tab order, whatever order they were dropped in. */
231
499
  afterPaneMove: function(container) {
232
500
  var content = one(container, ":scope > .tab-content");
233
- all(container, ":scope > .nav-tabs > .nav-item").forEach(function(tab) {
501
+ children(stripOf(container) || container, ".nav-item").forEach(function(tab) {
234
502
  var pane = paneOf(container, tab);
235
503
  if (pane) {
236
504
  content.appendChild(pane);