@themarioga/grid-editor 4.0.0 → 5.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 (65) hide show
  1. package/CHANGELOG.md +95 -0
  2. package/README.md +121 -9
  3. package/UPGRADING.md +55 -0
  4. package/dist/grideditor.css +609 -0
  5. package/dist/grideditor.min.css +1 -1
  6. package/dist/grideditor.min.css.map +1 -1
  7. package/dist/jquery.grideditor.bundle.min.js +2 -2
  8. package/dist/jquery.grideditor.js +1189 -118
  9. package/dist/jquery.grideditor.min.js +1 -1
  10. package/dist/jquery.grideditor.min.js.map +1 -1
  11. package/dist/locales/grideditor.es.js +58 -0
  12. package/dist/locales/grideditor.es.min.js +1 -1
  13. package/dist/locales/grideditor.es.min.js.map +1 -1
  14. package/dist/plugins/grideditor.alignment.js +81 -0
  15. package/dist/plugins/grideditor.alignment.min.js +2 -0
  16. package/dist/plugins/grideditor.alignment.min.js.map +1 -0
  17. package/dist/plugins/grideditor.float.js +56 -0
  18. package/dist/plugins/grideditor.float.min.js +2 -0
  19. package/dist/plugins/grideditor.float.min.js.map +1 -0
  20. package/dist/plugins/grideditor.gutters.js +145 -0
  21. package/dist/plugins/grideditor.gutters.min.js +2 -0
  22. package/dist/plugins/grideditor.gutters.min.js.map +1 -0
  23. package/dist/plugins/grideditor.order.js +206 -0
  24. package/dist/plugins/grideditor.order.min.js +2 -0
  25. package/dist/plugins/grideditor.order.min.js.map +1 -0
  26. package/dist/plugins/grideditor.sections.js +199 -0
  27. package/dist/plugins/grideditor.sections.min.js +2 -0
  28. package/dist/plugins/grideditor.sections.min.js.map +1 -0
  29. package/dist/plugins/grideditor.spacing.js +299 -0
  30. package/dist/plugins/grideditor.spacing.min.js +2 -0
  31. package/dist/plugins/grideditor.spacing.min.js.map +1 -0
  32. package/dist/plugins/grideditor.textalign.js +60 -0
  33. package/dist/plugins/grideditor.textalign.min.js +2 -0
  34. package/dist/plugins/grideditor.textalign.min.js.map +1 -0
  35. package/dist/plugins/grideditor.visibility.js +172 -0
  36. package/dist/plugins/grideditor.visibility.min.js +2 -0
  37. package/dist/plugins/grideditor.visibility.min.js.map +1 -0
  38. package/docs/events.md +32 -2
  39. package/docs/locale-keys.md +88 -1
  40. package/docs/plugins.md +141 -3
  41. package/example/autocols.html +137 -0
  42. package/example/autosave.html +5 -0
  43. package/example/basic.html +5 -0
  44. package/example/breakpoints.html +12 -5
  45. package/example/ckeditor.html +5 -0
  46. package/example/containers.html +4 -0
  47. package/example/elements.html +5 -0
  48. package/example/index.html +38 -1
  49. package/example/locale.html +5 -0
  50. package/example/plugins.html +5 -0
  51. package/example/summernote.html +5 -0
  52. package/example/utilities.html +166 -0
  53. package/example/wrap_content.html +5 -0
  54. package/package.json +1 -1
  55. package/src/js/jquery.grideditor.js +1190 -119
  56. package/src/js/locales/grideditor.es.js +58 -0
  57. package/src/js/plugins/grideditor.alignment.js +81 -0
  58. package/src/js/plugins/grideditor.float.js +56 -0
  59. package/src/js/plugins/grideditor.gutters.js +145 -0
  60. package/src/js/plugins/grideditor.order.js +206 -0
  61. package/src/js/plugins/grideditor.sections.js +199 -0
  62. package/src/js/plugins/grideditor.spacing.js +299 -0
  63. package/src/js/plugins/grideditor.textalign.js +60 -0
  64. package/src/js/plugins/grideditor.visibility.js +172 -0
  65. package/src/less/grideditor.less +299 -0
@@ -21,6 +21,8 @@
21
21
  'tool.add_row': 'Añadir fila',
22
22
  'tool.add_column': 'Añadir columna\n(mantén pulsado para elegir el ancho)',
23
23
  'tool.column_size': '{size} de 12',
24
+ 'tool.column_equal': 'Igual: reparte lo que queda de la fila',
25
+ 'tool.column_auto': 'Auto: tan ancha como su contenido',
24
26
  'tool.delete_row': 'Borrar fila',
25
27
  'tool.delete_column': 'Borrar columna',
26
28
  'tool.delete_element': 'Borrar elemento',
@@ -41,6 +43,7 @@
41
43
  'tool.classes_title': 'Clases css, separadas por espacios',
42
44
  'tool.toggle_class': 'Aplicar o quitar el estilo "{label}"',
43
45
  'row.add': 'Añadir fila {layout}',
46
+ 'row.add_row_cols': 'Añadir una fila de {columns} columnas, {counts} por fila',
44
47
  'container.add_tabs': 'Pestañas',
45
48
  'container.add_tab': 'Añadir pestaña',
46
49
  'container.tab_label': 'Pestaña {number}',
@@ -69,6 +72,61 @@
69
72
  'view.lg': 'Escritorio',
70
73
  'view.xl': 'Escritorio grande',
71
74
  'view.xxl': 'Pantalla panorámica',
75
+ 'utility.section': 'Responsive: {view}',
76
+ 'utility.col_width': 'Ancho',
77
+ 'utility.col_equal': 'Igual',
78
+ 'utility.col_auto': 'Auto',
79
+ 'section.add': 'Sección',
80
+ 'section.width': 'Ancho',
81
+ 'section.fixed': 'Fijo',
82
+ 'section.fluid': 'Ancho completo',
83
+ 'section.from': 'Fijo desde {breakpoint}',
84
+ 'tool.add_row_to_section': 'Añadir fila',
85
+ 'tool.delete_section': 'Quitar sección',
86
+ 'confirm.delete_section': '¿Seguro que quieres borrar esta sección y todo su contenido?',
87
+ 'utility.col_from_row': 'De la fila: {count}',
88
+ 'utility.row_cols': 'Columnas por fila',
89
+ 'badge.row_cols': '{count} por fila',
90
+ 'badge.row_cols_auto': 'Tan anchas como su contenido',
91
+ 'utility.default': 'Por defecto',
92
+ 'utility.inherit': 'Hereda: {value} (de {breakpoint})',
93
+ 'utility.varies': 'Cambia en {breakpoints}; elegir aquí lo sustituye',
94
+ 'utility.visibility': 'Visibilidad',
95
+ 'utility.visibility_hidden': 'Oculto',
96
+ 'utility.visibility_shown': 'Visible',
97
+ 'tool.hide_in_view': 'Ocultar en esta vista',
98
+ 'tool.show_in_view': 'Mostrar en esta vista',
99
+ 'badge.hidden_in': 'Oculto en {breakpoints}',
100
+ 'utility.order': 'Orden',
101
+ 'utility.order_first': 'Primero',
102
+ 'utility.order_last': 'Último',
103
+ 'tool.order_earlier': 'Antes en esta vista',
104
+ 'tool.order_later': 'Después en esta vista',
105
+ 'badge.order': 'Orden: {value}',
106
+ 'utility.justify_content': 'Justificar columnas',
107
+ 'utility.align_items': 'Alinear columnas',
108
+ 'utility.align_self': 'Alinearse',
109
+ 'utility.gutters': 'Espaciado entre columnas',
110
+ 'utility.gutters_x': 'Espaciado horizontal',
111
+ 'utility.gutters_y': 'Espaciado vertical',
112
+ 'utility.padding': 'Relleno',
113
+ 'utility.margin': 'Margen',
114
+ 'utility.side_all': 'Todos los lados',
115
+ 'utility.side_x': 'Izquierda y derecha',
116
+ 'utility.side_y': 'Arriba y abajo',
117
+ 'utility.side_t': 'Arriba',
118
+ 'utility.side_b': 'Abajo',
119
+ 'utility.side_s': 'Inicio',
120
+ 'utility.side_e': 'Fin',
121
+ 'utility.text_align': 'Alineación del texto',
122
+ 'utility.text_start': 'Inicio',
123
+ 'utility.text_center': 'Centro',
124
+ 'utility.text_end': 'Fin',
125
+ 'utility.float': 'Flotar',
126
+ 'utility.float_start': 'Inicio',
127
+ 'utility.float_end': 'Fin',
128
+ 'utility.float_none': 'Ninguno',
129
+ 'utility.spacing_gutter': 'El relleno lateral de una columna es su espaciado: cambiarlo cambia el espacio entre columnas',
72
130
  'error.sortable_missing': '¡SortableJS no está disponible! Asegúrate de haber cargado el archivo js de Sortable; sin él no se puede arrastrar.',
73
131
  'warning.setting_removed': 'El ajuste {setting} se eliminó en la 4.0. Usa {replacement} en su lugar.',
74
132
  'error.tinymce_missing': '¡tinyMCE no está disponible! Asegúrate de haber cargado el archivo js de tinyMCE.',
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Alignment for grid-editor.
3
+ *
4
+ * A utility plugin: load this file after the editor and a row's columns can be
5
+ * justified and aligned per breakpoint, and a column can align itself, with
6
+ * Bootstrap's justify-content-{breakpoint}-*, align-items-{breakpoint}-* and
7
+ * align-self-{breakpoint}-* classes. What it can ask the editor for is the
8
+ * handle its factory is called with, described in docs/plugins.md.
9
+ *
10
+ * <script src="dist/jquery.grideditor.min.js"></script>
11
+ * <script src="dist/plugins/grideditor.alignment.min.js"></script>
12
+ *
13
+ * Fields only, in the settings panels: alignment is set now and then, not
14
+ * clicked through, and a drawer is short of room.
15
+ */
16
+ (function($) {
17
+
18
+ $.extend($.fn.gridEditor.locales.en, {
19
+ 'utility.justify_content': 'Justify columns',
20
+ 'utility.align_items': 'Align columns',
21
+ 'utility.align_self': 'Align self',
22
+ });
23
+
24
+ /** What Bootstrap's value names come to in css. */
25
+ var FLEX = {
26
+ start: 'flex-start',
27
+ end: 'flex-end',
28
+ center: 'center',
29
+ between: 'space-between',
30
+ around: 'space-around',
31
+ evenly: 'space-evenly',
32
+ baseline: 'baseline',
33
+ stretch: 'stretch',
34
+ auto: 'auto',
35
+ };
36
+
37
+ /**
38
+ * A family whose preview is its one css property. `none` is what the
39
+ * property is when no class says anything, which a view has to show over a
40
+ * wider breakpoint's class that is live in a wide window.
41
+ */
42
+ function family(definition) {
43
+ return $.extend({
44
+ prefix: definition.name,
45
+ preview: function(value) {
46
+ var styles = {};
47
+ styles[definition.name] = value === null ? definition.none : FLEX[value];
48
+ return styles;
49
+ },
50
+ }, definition);
51
+ }
52
+
53
+ $.fn.gridEditor.utilities.alignment = function() {
54
+ return {
55
+ families: [
56
+ family({
57
+ name: 'justify-content',
58
+ labelKey: 'utility.justify_content',
59
+ values: ['start', 'center', 'end', 'between', 'around', 'evenly'],
60
+ appliesTo: ['row'],
61
+ none: 'normal',
62
+ }),
63
+ family({
64
+ name: 'align-items',
65
+ labelKey: 'utility.align_items',
66
+ values: ['start', 'center', 'end', 'baseline', 'stretch'],
67
+ appliesTo: ['row'],
68
+ none: 'normal',
69
+ }),
70
+ family({
71
+ name: 'align-self',
72
+ labelKey: 'utility.align_self',
73
+ values: ['auto', 'start', 'center', 'end', 'baseline', 'stretch'],
74
+ appliesTo: ['column'],
75
+ none: 'auto',
76
+ }),
77
+ ],
78
+ };
79
+ };
80
+
81
+ })(jQuery);
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Floating elements for grid-editor.
3
+ *
4
+ * A utility plugin: load this file after the editor and after the elements
5
+ * plugin, and an element can float to the start or the end of its content
6
+ * area per breakpoint, with the text around it, using Bootstrap's
7
+ * float-{breakpoint}-start, -end and -none classes. What it can ask the
8
+ * editor for is the handle its factory is called with, described in
9
+ * docs/plugins.md.
10
+ *
11
+ * <script src="dist/jquery.grideditor.min.js"></script>
12
+ * <script src="dist/plugins/grideditor.elements.min.js"></script>
13
+ * <script src="dist/plugins/grideditor.float.min.js"></script>
14
+ *
15
+ * Elements only. A floated row or column stops being part of the grid, and a
16
+ * container sits beside the content areas rather than in their text.
17
+ */
18
+ (function($) {
19
+
20
+ $.extend($.fn.gridEditor.locales.en, {
21
+ 'utility.float': 'Float',
22
+ 'utility.float_start': 'Start',
23
+ 'utility.float_end': 'End',
24
+ 'utility.float_none': 'None',
25
+ });
26
+
27
+ /** Start and end are left and right: Bootstrap's css is left to right. */
28
+ var CSS = { start: 'left', end: 'right', none: 'none' };
29
+
30
+ $.fn.gridEditor.utilities.float = function(ge) {
31
+
32
+ function label(value) {
33
+ if (value === 'start') { return ge.t('utility.float_start'); }
34
+ if (value === 'end') { return ge.t('utility.float_end'); }
35
+
36
+ return ge.t('utility.float_none');
37
+ }
38
+
39
+ return {
40
+ families: [{
41
+ name: 'float',
42
+ prefix: 'float',
43
+ values: ['start', 'end', 'none'],
44
+ appliesTo: ['element'],
45
+ labelKey: 'utility.float',
46
+ label: label,
47
+
48
+ /** With no class applying here, whatever the host's css floats it as. */
49
+ preview: function(value, node) {
50
+ return { float: value === null ? ge.bareStyle(node, 'float', 'float') : CSS[value] };
51
+ },
52
+ }],
53
+ };
54
+ };
55
+
56
+ })(jQuery);
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Gutters for grid-editor.
3
+ *
4
+ * A utility plugin: load this file after the editor and a row's gutters can
5
+ * differ per breakpoint, with Bootstrap's g-{breakpoint}-*, gx-{breakpoint}-*
6
+ * and gy-{breakpoint}-* classes. What it can ask the editor for is the handle
7
+ * its factory is called with, described in docs/plugins.md.
8
+ *
9
+ * <script src="dist/jquery.grideditor.min.js"></script>
10
+ * <script src="dist/plugins/grideditor.gutters.min.js"></script>
11
+ *
12
+ * The editor draws its own frame round every column, narrower than any
13
+ * gutter, so a row with gutter classes is drawn with its real gutters instead.
14
+ *
15
+ * utilities.gutters.scale is what 0 to 5 come to, for a page that changed
16
+ * Bootstrap's $spacers.
17
+ */
18
+ (function($) {
19
+
20
+ $.extend($.fn.gridEditor.locales.en, {
21
+ 'utility.gutters': 'Gutters',
22
+ 'utility.gutters_x': 'Horizontal gutters',
23
+ 'utility.gutters_y': 'Vertical gutters',
24
+ });
25
+
26
+ var VALUES = ['0', '1', '2', '3', '4', '5'];
27
+
28
+ /** Bootstrap's own: $spacers, and what a row's gutters are with no class. */
29
+ var SCALE = ['0', '.25rem', '.5rem', '1rem', '1.5rem', '3rem'];
30
+ var DEFAULT_X = '1.5rem';
31
+ var DEFAULT_Y = '0';
32
+
33
+ var INFIXES = ['', 'sm', 'md', 'lg', 'xl', 'xxl'];
34
+ var CLASS_PATTERN = /(?:^|\s)g[xy]?-(?:(?:sm|md|lg|xl|xxl)-)?[0-5](?:\s|$)/;
35
+
36
+ $.fn.gridEditor.utilities.gutters = function(ge) {
37
+
38
+ var options = $.extend({ scale: SCALE }, ge.settings.utilities.gutters);
39
+
40
+ /**
41
+ * The class of a family that decides a row's gutters at a breakpoint:
42
+ * the widest breakpoint up to it that has one, as { tier, value }.
43
+ */
44
+ function source(row, prefix, tier) {
45
+ var classes = (row.attr('class') || '').split(/\s+/);
46
+
47
+ for (var i = tier; i >= 0; i--) {
48
+ for (var value = VALUES.length - 1; value >= 0; value--) {
49
+ var name = prefix + (INFIXES[i] ? '-' + INFIXES[i] : '') + '-' + value;
50
+ if (classes.indexOf(name) !== -1) { return { tier: i, value: value }; }
51
+ }
52
+ }
53
+
54
+ return null;
55
+ }
56
+
57
+ /**
58
+ * One axis the way Bootstrap's css settles it. g and gx (or gy) set the
59
+ * same variable, every breakpoint's rules come after the smaller ones',
60
+ * and within a breakpoint the bigger values come later. So the class
61
+ * that wins is the one from the wider breakpoint, then the bigger one -
62
+ * not the more specific family, whatever that would suggest.
63
+ */
64
+ function axis(row, prefix, fallback) {
65
+ var tier = ge.breakpoints.indexOf(ge.view());
66
+ var winner = [source(row, 'g', tier), source(row, prefix, tier)]
67
+ .filter(Boolean)
68
+ .sort(function(a, b) { return b.tier - a.tier || b.value - a.value; })[0];
69
+
70
+ return winner ? options.scale[winner.value] : fallback;
71
+ }
72
+
73
+ function preview(axes) {
74
+ return function(value, row) {
75
+ var styles = {};
76
+
77
+ if (axes.indexOf('x') !== -1) { styles['--bs-gutter-x'] = axis(row, 'gx', DEFAULT_X); }
78
+ if (axes.indexOf('y') !== -1) { styles['--bs-gutter-y'] = axis(row, 'gy', DEFAULT_Y); }
79
+
80
+ return styles;
81
+ };
82
+ }
83
+
84
+ /**
85
+ * Choosing g sets both axes, so a gx or gy the same breakpoint had is
86
+ * taken off: left on, the bigger of the two would win, and the field the
87
+ * user just chose would not be what the row shows.
88
+ */
89
+ function afterUtility(e, payload) {
90
+ if (payload.family !== 'g') { return; }
91
+
92
+ ['gx', 'gy'].forEach(function(family) {
93
+ ge.setUtility(payload.node, family, null, { view: payload.breakpoint, source: payload.source });
94
+ });
95
+ }
96
+
97
+ function mark(scope) {
98
+ scope.find('.row').addBack('.row').each(function() {
99
+ var row = $(this);
100
+ row.toggleClass('ge-gutters', CLASS_PATTERN.test(row.attr('class') || ''));
101
+ });
102
+ }
103
+
104
+ return {
105
+ families: [
106
+ {
107
+ name: 'g',
108
+ prefix: 'g',
109
+ values: VALUES,
110
+ appliesTo: ['row'],
111
+ labelKey: 'utility.gutters',
112
+ preview: preview(['x', 'y']),
113
+ },
114
+ {
115
+ name: 'gx',
116
+ prefix: 'gx',
117
+ values: VALUES,
118
+ appliesTo: ['row'],
119
+ labelKey: 'utility.gutters_x',
120
+ preview: preview(['x']),
121
+ },
122
+ {
123
+ name: 'gy',
124
+ prefix: 'gy',
125
+ values: VALUES,
126
+ appliesTo: ['row'],
127
+ labelKey: 'utility.gutters_y',
128
+ preview: preview(['y']),
129
+ },
130
+ ],
131
+
132
+ onInit: function() {
133
+ ge.canvas.off('grideditor:after-utility.ge-gutters').on('grideditor:after-utility.ge-gutters', afterUtility);
134
+ },
135
+
136
+ onRefresh: mark,
137
+
138
+ onDeinit: function() {
139
+ ge.canvas.off('grideditor:after-utility.ge-gutters');
140
+ ge.canvas.find('.ge-gutters').removeClass('ge-gutters');
141
+ },
142
+ };
143
+ };
144
+
145
+ })(jQuery);
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Column order for grid-editor.
3
+ *
4
+ * A utility plugin: load this file after the editor and a column's place in
5
+ * its row can differ per breakpoint, with Bootstrap's order-{breakpoint}-*
6
+ * classes. What it can ask the editor for is the handle its factory is called
7
+ * with, described in docs/plugins.md.
8
+ *
9
+ * <script src="dist/jquery.grideditor.min.js"></script>
10
+ * <script src="dist/plugins/grideditor.order.min.js"></script>
11
+ *
12
+ * Two arrows in each column's drawer move it one place earlier or later in
13
+ * the view being edited, by numbering the row's columns; the markup does not
14
+ * move. Dragging still moves the markup, as it always has.
15
+ *
16
+ * utilities.order.drawer: false leaves the arrows out, and the panel field is
17
+ * the only control.
18
+ */
19
+ (function($) {
20
+
21
+ $.extend($.fn.gridEditor.locales.en, {
22
+ 'utility.order': 'Order',
23
+ 'utility.order_first': 'First',
24
+ 'utility.order_last': 'Last',
25
+ 'tool.order_earlier': 'Earlier in this view',
26
+ 'tool.order_later': 'Later in this view',
27
+ 'badge.order': 'Order: {value}',
28
+ });
29
+
30
+ var VALUES = ['first', '0', '1', '2', '3', '4', '5', 'last'];
31
+
32
+ /** Where each value puts a column, as Bootstrap's css has it. */
33
+ var RANK = { first: -1, last: 6 };
34
+
35
+ /** A column that carries any class of the family, at any breakpoint. */
36
+ var CLASS_PATTERN = /(?:^|\s)order-(?:(?:sm|md|lg|xl|xxl)-)?(?:first|last|[0-5])(?:\s|$)/;
37
+
38
+ $.fn.gridEditor.utilities.order = function(ge) {
39
+
40
+ var options = $.extend({ drawer: true }, ge.settings.utilities.order);
41
+ var warnedAboutDrag = false;
42
+
43
+ function rank(value) {
44
+ if (value === null) { return 0; }
45
+
46
+ return RANK[value] !== undefined ? RANK[value] : parseInt(value, 10);
47
+ }
48
+
49
+ function label(value) {
50
+ if (value === 'first') { return ge.t('utility.order_first'); }
51
+ if (value === 'last') { return ge.t('utility.order_last'); }
52
+
53
+ return value;
54
+ }
55
+
56
+ /** The columns of a row in the order a view shows them, given what each one's order is. */
57
+ function visualOrder(columns, read) {
58
+ return columns.get()
59
+ .map(function(element, index) {
60
+ return { element: element, index: index, rank: rank(read($(element))) };
61
+ })
62
+ .sort(function(a, b) { return a.rank - b.rank || a.index - b.index; })
63
+ .map(function(entry) { return entry.element; });
64
+ }
65
+
66
+ function sameOrder(a, b) {
67
+ return a.every(function(element, index) { return element === b[index]; });
68
+ }
69
+
70
+ /**
71
+ * The values that put n columns in n places, or null past eight: that
72
+ * is how many places Bootstrap's order classes have.
73
+ */
74
+ function slots(count) {
75
+ if (count <= 6) { return VALUES.slice(1, 1 + count); }
76
+ if (count === 7) { return VALUES.slice(1); }
77
+ if (count === 8) { return VALUES.slice(); }
78
+
79
+ return null;
80
+ }
81
+
82
+ /**
83
+ * Move a column one place earlier or later in the view being edited.
84
+ *
85
+ * The row's columns are numbered in their new order. When that order is
86
+ * the markup's own, the numbers are taken off instead - unless what is
87
+ * left underneath, the breakpoint below, would then order them some other
88
+ * way, and the numbers are what keep them in the markup's order.
89
+ */
90
+ function move(col, direction) {
91
+ var view = ge.view();
92
+ var columns = col.parent().children('.column');
93
+ var shown = visualOrder(columns, function(column) { return ge.getUtility(column, 'order', view); });
94
+ var from = shown.indexOf(col[0]);
95
+ var to = from + direction;
96
+
97
+ if (to < 0 || to >= shown.length) { return; }
98
+
99
+ shown.splice(to, 0, shown.splice(from, 1)[0]);
100
+
101
+ var values = null;
102
+ if (!sameOrder(shown, columns.get()) || !inheritsMarkupOrder(columns, view)) {
103
+ values = slots(shown.length);
104
+
105
+ if (!values) {
106
+ ge.warn('a row of ' + shown.length + ' columns cannot be reordered with Bootstrap\'s ' +
107
+ 'order classes, which have eight places');
108
+ return;
109
+ }
110
+ }
111
+
112
+ ge.operate(function() {
113
+ shown.forEach(function(element, index) {
114
+ ge.setUtility($(element), 'order', values ? values[index] : null, { source: 'tool' });
115
+ });
116
+ });
117
+ }
118
+
119
+ /** Whether taking this view's classes off leaves the columns in the markup's order. */
120
+ function inheritsMarkupOrder(columns, view) {
121
+ var index = ge.breakpoints.indexOf(view);
122
+ if (index <= 0) { return true; }
123
+
124
+ var below = ge.breakpoints[index - 1];
125
+
126
+ return sameOrder(visualOrder(columns, function(column) {
127
+ return ge.getUtility(column, 'order', below);
128
+ }), columns.get());
129
+ }
130
+
131
+ /** Whether a row's columns are ordered by class, rather than by the markup, in this view. */
132
+ function ordered(row) {
133
+ return row.children('.column').get().some(function(element) {
134
+ return ge.getUtility($(element), 'order', ge.view()) !== null;
135
+ });
136
+ }
137
+
138
+ /** The badge: a column ordered by class in this view says where. */
139
+ function mark(scope) {
140
+ scope.find('.column').addBack('.column').each(function() {
141
+ var col = $(this);
142
+ var value = CLASS_PATTERN.test(col.attr('class') || '')
143
+ ? ge.getUtility(col, 'order', ge.view())
144
+ : null;
145
+
146
+ if (value === null) {
147
+ col.removeAttr('data-ge-order');
148
+ } else {
149
+ col.attr('data-ge-order', ge.t('badge.order', { value: label(value) }));
150
+ }
151
+ });
152
+ }
153
+
154
+ /**
155
+ * A column dropped in a row ordered by class lands where the markup puts
156
+ * it, and shows up where its order does, which is worth saying once.
157
+ */
158
+ function afterMove(e, payload) {
159
+ if (warnedAboutDrag || payload.kind !== 'column' || !payload.to) { return; }
160
+ if (!ordered(payload.to.parent)) { return; }
161
+
162
+ warnedAboutDrag = true;
163
+ ge.warn('a column was dropped in a row whose columns carry order classes in this view: ' +
164
+ 'where it shows is set by those classes, not by where it sits in the markup');
165
+ }
166
+
167
+ return {
168
+ families: [{
169
+ name: 'order',
170
+ prefix: 'order',
171
+ values: VALUES,
172
+ appliesTo: ['column'],
173
+ labelKey: 'utility.order',
174
+ label: label,
175
+ preview: function(value) {
176
+ return { order: rank(value) };
177
+ },
178
+ }],
179
+
180
+ drawerTools: function(drawer, node, kind) {
181
+ if (!options.drawer || kind !== 'column') { return; }
182
+
183
+ ge.createTool(drawer, ge.t('tool.order_earlier'), 'ge-order-earlier', 'bi bi-chevron-left', function() {
184
+ move(node, -1);
185
+ });
186
+ ge.createTool(drawer, ge.t('tool.order_later'), 'ge-order-later', 'bi bi-chevron-right', function() {
187
+ move(node, 1);
188
+ });
189
+ },
190
+
191
+ // init runs again whenever a node is added, without a deinit
192
+ // between, so the listener is replaced rather than stacked
193
+ onInit: function() {
194
+ ge.canvas.off('grideditor:after-move.ge-order').on('grideditor:after-move.ge-order', afterMove);
195
+ },
196
+
197
+ onRefresh: mark,
198
+
199
+ onDeinit: function() {
200
+ ge.canvas.off('grideditor:after-move.ge-order');
201
+ ge.canvas.find('[data-ge-order]').removeAttr('data-ge-order');
202
+ },
203
+ };
204
+ };
205
+
206
+ })(jQuery);