@themarioga/grid-editor 5.0.0 → 5.1.1
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 +37 -0
- package/README.md +66 -3
- package/dist/grideditor.css +8 -0
- package/dist/grideditor.min.css +1 -1
- package/dist/grideditor.min.css.map +1 -1
- package/dist/jquery.grideditor.bundle.min.js +2 -2
- package/dist/jquery.grideditor.js +150 -35
- package/dist/jquery.grideditor.min.js +1 -1
- package/dist/jquery.grideditor.min.js.map +1 -1
- package/dist/locales/grideditor.es.js +4 -0
- package/dist/locales/grideditor.es.min.js +1 -1
- package/dist/locales/grideditor.es.min.js.map +1 -1
- package/dist/plugins/grideditor.clipboard.js +277 -0
- package/dist/plugins/grideditor.clipboard.min.js +2 -0
- package/dist/plugins/grideditor.clipboard.min.js.map +1 -0
- package/dist/plugins/grideditor.sections.js +4 -4
- package/dist/plugins/grideditor.sections.min.js +1 -1
- package/dist/plugins/grideditor.sections.min.js.map +1 -1
- package/docs/events.md +4 -1
- package/docs/locale-keys.md +13 -0
- package/docs/plugins.md +12 -1
- package/example/clipboard.html +100 -0
- package/example/index.html +8 -0
- package/example/wrap_content.html +66 -16
- package/package.json +1 -1
- package/src/js/jquery.grideditor.js +150 -35
- package/src/js/locales/grideditor.es.js +4 -0
- package/src/js/plugins/grideditor.clipboard.js +277 -0
- package/src/js/plugins/grideditor.sections.js +4 -4
- package/src/less/grideditor.less +10 -0
|
@@ -2,37 +2,50 @@
|
|
|
2
2
|
<html>
|
|
3
3
|
<head>
|
|
4
4
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
5
|
-
|
|
5
|
+
|
|
6
6
|
<!-- CSS dependencies -->
|
|
7
7
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
|
|
8
8
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.13.1/font/bootstrap-icons.css">
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
<!-- Javascript dependencies -->
|
|
11
11
|
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
|
|
12
12
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js" integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI" crossorigin="anonymous"></script>
|
|
13
13
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/tinymce/6.8.6/tinymce.min.js"></script>
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
<!-- Grid editor CSS -->
|
|
16
16
|
<link rel="stylesheet" type="text/css" href="../dist/grideditor.css" />
|
|
17
17
|
<!-- Grid editor javascript -->
|
|
18
18
|
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.6/Sortable.min.js"></script>
|
|
19
19
|
<script src="../dist/jquery.grideditor.min.js"></script>
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
<script>
|
|
22
22
|
$(function() {
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
var grid = $('#myGrid');
|
|
24
|
+
|
|
25
|
+
// 1. The markup as the page wrote it, captured before the editor
|
|
26
|
+
// touches it
|
|
27
|
+
$('#before').text(grid.html().trim());
|
|
28
|
+
|
|
29
|
+
// 2. The editor finds no .row in there, so it wraps everything
|
|
30
|
+
// into one row with a single full-width column
|
|
31
|
+
grid.gridEditor({
|
|
25
32
|
new_row_layouts: [[12], [6, 6], [9, 3], [3, 9]],
|
|
26
33
|
content_types: ['tinymce'],
|
|
27
34
|
});
|
|
28
|
-
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
|
|
35
|
+
|
|
36
|
+
// 3. What getHtml hands back: the same content, now a grid, with
|
|
37
|
+
// the marking the editor needs to read it back
|
|
38
|
+
// 4. What getPlainHtml hands back: the same, without that marking
|
|
39
|
+
function showResult() {
|
|
40
|
+
$('#after').text(grid.gridEditor('getHtml').trim());
|
|
41
|
+
$('#plain').text(grid.gridEditor('getPlainHtml').trim());
|
|
42
|
+
}
|
|
43
|
+
showResult();
|
|
44
|
+
$('#refresh').on('click', showResult);
|
|
32
45
|
});
|
|
33
46
|
</script>
|
|
34
|
-
|
|
35
|
-
<title>Grid Editor example</title>
|
|
47
|
+
|
|
48
|
+
<title>Grid Editor example - wrapped content</title>
|
|
36
49
|
</head>
|
|
37
50
|
<body>
|
|
38
51
|
<!-- Back to the demo index -->
|
|
@@ -40,10 +53,47 @@
|
|
|
40
53
|
<a class="link-secondary" href="index.html"><i class="bi bi-arrow-left"></i> All demos</a>
|
|
41
54
|
</div>
|
|
42
55
|
|
|
43
|
-
<div
|
|
44
|
-
<
|
|
45
|
-
<p>
|
|
56
|
+
<div class="container py-3">
|
|
57
|
+
<h2>Wrapped content</h2>
|
|
58
|
+
<p class="text-body-secondary">
|
|
59
|
+
The editor below was started on plain markup — a heading, a
|
|
60
|
+
paragraph and a list, with no <code>.row</code> or
|
|
61
|
+
<code>.col-*</code> anywhere. Grid editor does not refuse it: it
|
|
62
|
+
wraps all of it into one row with a single
|
|
63
|
+
<code>col-lg-12</code> column, and from there it is an ordinary
|
|
64
|
+
grid. Add rows, split the column, edit the text, then press
|
|
65
|
+
<em>Refresh</em> to see the html that comes out: as
|
|
66
|
+
<code>getHtml</code> returns it, to save and edit again, and as
|
|
67
|
+
<code>getPlainHtml</code> returns it, to publish.
|
|
68
|
+
</p>
|
|
69
|
+
|
|
70
|
+
<h5 class="mt-4">1. The markup in the page</h5>
|
|
71
|
+
<pre class="bg-body-tertiary border rounded p-3 small"><code id="before"></code></pre>
|
|
72
|
+
|
|
73
|
+
<h5 class="mt-4">2. The editor, started on that markup</h5>
|
|
74
|
+
<div id="myGrid">
|
|
75
|
+
<h1>Hello!</h1>
|
|
76
|
+
<p>This page was written as plain html, with no Bootstrap grid.</p>
|
|
77
|
+
<ul>
|
|
78
|
+
<li>No row</li>
|
|
79
|
+
<li>No column</li>
|
|
80
|
+
</ul>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
<h5 class="mt-4 d-flex align-items-center gap-2">
|
|
84
|
+
3. The result of <code>getHtml</code>
|
|
85
|
+
<button id="refresh" class="btn btn-sm btn-outline-secondary"><i class="bi bi-arrow-clockwise"></i> Refresh</button>
|
|
86
|
+
</h5>
|
|
87
|
+
<pre class="bg-body-tertiary border rounded p-3 small"><code id="after"></code></pre>
|
|
88
|
+
|
|
89
|
+
<h5 class="mt-4">4. The result of <code>getPlainHtml</code></h5>
|
|
90
|
+
<p class="text-body-secondary small">
|
|
91
|
+
The same markup without the editor’s <code>ge-*</code> classes,
|
|
92
|
+
<code>column</code> class and <code>data-ge-*</code> attributes.
|
|
93
|
+
For publishing: the editor cannot read it back as it was.
|
|
94
|
+
</p>
|
|
95
|
+
<pre class="bg-body-tertiary border rounded p-3 small"><code id="plain"></code></pre>
|
|
46
96
|
</div>
|
|
47
97
|
|
|
48
98
|
</body>
|
|
49
|
-
</html>
|
|
99
|
+
</html>
|
package/package.json
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
var METHODS = {
|
|
20
20
|
getHtml: { value: true, noInstance: function(element) { return element.html(); } },
|
|
21
|
+
getPlainHtml: { value: true, noInstance: function(element) { return plainHtml(element.html()); } },
|
|
21
22
|
init: {},
|
|
22
23
|
deinit: {},
|
|
23
24
|
reset: {},
|
|
@@ -247,6 +248,56 @@ function dispatch(set, name, args) {
|
|
|
247
248
|
return set;
|
|
248
249
|
}
|
|
249
250
|
|
|
251
|
+
/**
|
|
252
|
+
* Html with grid-editor's own marking taken off: the ge-* classes, the
|
|
253
|
+
* column class and the data-ge-* attributes. A div that was only there for
|
|
254
|
+
* the editor - a content area, a bare element or container wrapper - is left
|
|
255
|
+
* with nothing on it, and goes, its children taking its place.
|
|
256
|
+
*
|
|
257
|
+
* The marking is what lets the editor read its markup back, so this is for
|
|
258
|
+
* publishing, not for saving something to edit again.
|
|
259
|
+
*/
|
|
260
|
+
function plainHtml(html) {
|
|
261
|
+
// Parsed in a document of its own, which is inert: no script in the
|
|
262
|
+
// markup runs and no image starts loading while it is being cleaned
|
|
263
|
+
var root = $(document.implementation.createHTMLDocument('').body);
|
|
264
|
+
root[0].innerHTML = html;
|
|
265
|
+
var emptied = [];
|
|
266
|
+
|
|
267
|
+
root.find('*').each(function() {
|
|
268
|
+
var node = $(this);
|
|
269
|
+
var marked = false;
|
|
270
|
+
|
|
271
|
+
$.each($.makeArray(this.attributes), function(i, attribute) {
|
|
272
|
+
if (attribute.name.indexOf('data-ge-') === 0) {
|
|
273
|
+
node.removeAttr(attribute.name);
|
|
274
|
+
marked = true;
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
var classes = (node.attr('class') || '').split(/\s+/).filter(Boolean);
|
|
279
|
+
var kept = classes.filter(function(name) {
|
|
280
|
+
return name !== 'column' && name.indexOf('ge-') !== 0;
|
|
281
|
+
});
|
|
282
|
+
if (kept.length !== classes.length) { marked = true; }
|
|
283
|
+
if (kept.length) {
|
|
284
|
+
node.attr('class', kept.join(' '));
|
|
285
|
+
} else {
|
|
286
|
+
node.removeAttr('class');
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (marked && this.tagName === 'DIV' && !this.attributes.length) {
|
|
290
|
+
emptied.push(this);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
emptied.forEach(function(div) {
|
|
295
|
+
$(div).replaceWith($(div).contents());
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
return root[0].innerHTML;
|
|
299
|
+
}
|
|
300
|
+
|
|
250
301
|
$.fn.gridEditor = function( optionsOrMethod ) {
|
|
251
302
|
|
|
252
303
|
var self = this;
|
|
@@ -910,25 +961,19 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
910
961
|
});
|
|
911
962
|
|
|
912
963
|
// A feature plugin's own buttons, beside the containers': what
|
|
913
|
-
// one makes goes onto the canvas, where the plugin says it belongs
|
|
964
|
+
// one makes goes onto the canvas, where the plugin says it belongs.
|
|
965
|
+
// One with align 'end' goes on the right instead, as an icon,
|
|
966
|
+
// beside the source and preview buttons.
|
|
967
|
+
var endItems = [];
|
|
914
968
|
$.each(FEATURES, function(name, feature) {
|
|
915
969
|
(feature.toolbar || []).forEach(function(item, index) {
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
.
|
|
920
|
-
|
|
921
|
-
.
|
|
922
|
-
|
|
923
|
-
.on('click', function() {
|
|
924
|
-
var made = item.create();
|
|
925
|
-
|
|
926
|
-
addNode(item.kind, made, function() {
|
|
927
|
-
made.appendTo(canvas);
|
|
928
|
-
}, { parent: canvas, source: 'tool' });
|
|
929
|
-
})
|
|
930
|
-
.appendTo(addContainerGroup)
|
|
931
|
-
;
|
|
970
|
+
var button = featureButton(name, item, index);
|
|
971
|
+
|
|
972
|
+
if (item.align === 'end') {
|
|
973
|
+
endItems.push(button);
|
|
974
|
+
} else {
|
|
975
|
+
button.appendTo(addContainerGroup);
|
|
976
|
+
}
|
|
932
977
|
});
|
|
933
978
|
});
|
|
934
979
|
|
|
@@ -994,8 +1039,39 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
994
1039
|
.appendTo(btnGroup)
|
|
995
1040
|
;
|
|
996
1041
|
|
|
1042
|
+
// Floated right after the source and preview buttons, so it
|
|
1043
|
+
// stands to their left
|
|
1044
|
+
if (endItems.length) {
|
|
1045
|
+
$('<div class="btn-group pull-right ge-toolbar-end" />')
|
|
1046
|
+
.append(endItems)
|
|
1047
|
+
.appendTo(wrapper)
|
|
1048
|
+
;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
997
1051
|
makeToolbarDraggable();
|
|
998
1052
|
}
|
|
1053
|
+
|
|
1054
|
+
/** A feature plugin's toolbar button: its label as text, or as the title alone at the end. */
|
|
1055
|
+
function featureButton(name, item, index) {
|
|
1056
|
+
var button = $('<a class="btn btn-sm btn-primary ge-add-container ge-add-feature" />')
|
|
1057
|
+
.attr('title', t(item.labelKey))
|
|
1058
|
+
.attr('data-ge-toolbar', 'feature')
|
|
1059
|
+
.attr('data-ge-feature', name)
|
|
1060
|
+
.attr('data-ge-item', index)
|
|
1061
|
+
.append($('<i />').addClass(item.iconClass || 'bi bi-plus'))
|
|
1062
|
+
.on('click', function() {
|
|
1063
|
+
var made = item.create();
|
|
1064
|
+
|
|
1065
|
+
addNode(item.kind, made, function() {
|
|
1066
|
+
made.appendTo(canvas);
|
|
1067
|
+
}, { parent: canvas, source: item.source || 'tool' });
|
|
1068
|
+
})
|
|
1069
|
+
;
|
|
1070
|
+
|
|
1071
|
+
if (item.align !== 'end') { button.append($('<span />').text(t(item.labelKey))); }
|
|
1072
|
+
|
|
1073
|
+
return button;
|
|
1074
|
+
}
|
|
999
1075
|
|
|
1000
1076
|
/**
|
|
1001
1077
|
* The toolbar's buttons as a palette: drag one onto the canvas and
|
|
@@ -1145,7 +1221,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1145
1221
|
} else {
|
|
1146
1222
|
made.appendTo(where.region);
|
|
1147
1223
|
}
|
|
1148
|
-
}, { parent: where.region, source: 'dragdrop' });
|
|
1224
|
+
}, { parent: where.region, source: item.source || 'dragdrop' });
|
|
1149
1225
|
}
|
|
1150
1226
|
|
|
1151
1227
|
/** A line where the block would go, following the pointer. */
|
|
@@ -1395,6 +1471,23 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1395
1471
|
return html;
|
|
1396
1472
|
}
|
|
1397
1473
|
|
|
1474
|
+
/**
|
|
1475
|
+
* One node's markup, as getHtml would give it: the canvas goes out of
|
|
1476
|
+
* editing to read it and comes back, as it does for getHtml.
|
|
1477
|
+
*/
|
|
1478
|
+
function nodeHtml(node) {
|
|
1479
|
+
deinit();
|
|
1480
|
+
stripPixelWidths(node);
|
|
1481
|
+
var html = node[0].outerHTML;
|
|
1482
|
+
init();
|
|
1483
|
+
return html;
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
/** getHtml with grid-editor's own classes and attributes taken off. */
|
|
1487
|
+
function getPlainHtml() {
|
|
1488
|
+
return plainHtml(getHtml());
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1398
1491
|
function destroy() {
|
|
1399
1492
|
deinit();
|
|
1400
1493
|
removeConfirmModal();
|
|
@@ -1535,6 +1628,12 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1535
1628
|
utilityField: utilityField,
|
|
1536
1629
|
bareStyle: bareStyle,
|
|
1537
1630
|
rowFromLayout: rowFromLayoutValue,
|
|
1631
|
+
nodeHtml: nodeHtml,
|
|
1632
|
+
toolbarItems: function(name) {
|
|
1633
|
+
return mainControls
|
|
1634
|
+
? mainControls.find('[data-ge-toolbar="feature"][data-ge-feature="' + name + '"]')
|
|
1635
|
+
: $();
|
|
1636
|
+
},
|
|
1538
1637
|
};
|
|
1539
1638
|
}
|
|
1540
1639
|
|
|
@@ -2128,17 +2227,6 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2128
2227
|
|
|
2129
2228
|
createMoveTool(drawer);
|
|
2130
2229
|
addSettingsTool(drawer, container, settings.container_classes);
|
|
2131
|
-
if (definition.addPane) {
|
|
2132
|
-
createTool(drawer, t(definition.addPaneKey), 'ge-add-pane', 'bi bi-plus-circle', function() {
|
|
2133
|
-
var pane = definition.addPane(container, {});
|
|
2134
|
-
|
|
2135
|
-
addNode(definition.paneKind, pane, function() {}, {
|
|
2136
|
-
parent: container,
|
|
2137
|
-
source: 'tool',
|
|
2138
|
-
container: container,
|
|
2139
|
-
});
|
|
2140
|
-
});
|
|
2141
|
-
}
|
|
2142
2230
|
|
|
2143
2231
|
settings.container_tools.forEach(function(hostTool) {
|
|
2144
2232
|
createTool(drawer, hostTool.title || '', hostTool.className || '',
|
|
@@ -2152,6 +2240,19 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2152
2240
|
container.slideUp(removed);
|
|
2153
2241
|
});
|
|
2154
2242
|
});
|
|
2243
|
+
|
|
2244
|
+
// Delete then add, the order rows and columns have
|
|
2245
|
+
if (definition.addPane) {
|
|
2246
|
+
createTool(drawer, t(definition.addPaneKey), 'ge-add-pane', 'bi bi-plus-circle', function() {
|
|
2247
|
+
var pane = definition.addPane(container, {});
|
|
2248
|
+
|
|
2249
|
+
addNode(definition.paneKind, pane, function() {}, {
|
|
2250
|
+
parent: container,
|
|
2251
|
+
source: 'tool',
|
|
2252
|
+
container: container,
|
|
2253
|
+
});
|
|
2254
|
+
});
|
|
2255
|
+
}
|
|
2155
2256
|
}
|
|
2156
2257
|
|
|
2157
2258
|
/**
|
|
@@ -2673,11 +2774,16 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2673
2774
|
});
|
|
2674
2775
|
|
|
2675
2776
|
// Beside the gear, because every node that has one is a node a
|
|
2676
|
-
// utility may apply to, whichever plugin built its drawer
|
|
2777
|
+
// utility may apply to, whichever plugin built its drawer. A
|
|
2778
|
+
// feature plugin's tools go there too, for the same reason: the
|
|
2779
|
+
// gear is the one tool every drawer has.
|
|
2677
2780
|
var kind = kindOf(node);
|
|
2678
2781
|
$.each(UTILITIES, function(name, utility) {
|
|
2679
2782
|
if (utility.drawerTools) { utility.drawerTools(drawer, node, kind); }
|
|
2680
2783
|
});
|
|
2784
|
+
$.each(FEATURES, function(name, feature) {
|
|
2785
|
+
if (feature.drawerTools) { feature.drawerTools(drawer, node, kind); }
|
|
2786
|
+
});
|
|
2681
2787
|
|
|
2682
2788
|
return details.appendTo(drawer);
|
|
2683
2789
|
}
|
|
@@ -3544,7 +3650,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
3544
3650
|
var add = function() {
|
|
3545
3651
|
return addNode(kind, node, function() {
|
|
3546
3652
|
node[placement](options[placement]);
|
|
3547
|
-
}, { parent: parent, source: 'api' });
|
|
3653
|
+
}, { parent: parent, source: options.source || 'api' });
|
|
3548
3654
|
};
|
|
3549
3655
|
|
|
3550
3656
|
if (operationDepth > 0) {
|
|
@@ -3738,11 +3844,19 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
3738
3844
|
return $();
|
|
3739
3845
|
}
|
|
3740
3846
|
|
|
3847
|
+
/**
|
|
3848
|
+
* A content area for the first content type. With no content types
|
|
3849
|
+
* it is a content area and nothing more: there is no type to name.
|
|
3850
|
+
*/
|
|
3741
3851
|
function createDefaultContentWrapper() {
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3852
|
+
var type = settings.content_types[0];
|
|
3853
|
+
var contentArea = $('<div class="ge-content"/>');
|
|
3854
|
+
|
|
3855
|
+
if (type) {
|
|
3856
|
+
contentArea.addClass('ge-content-type-' + type).attr('data-ge-content-type', type);
|
|
3857
|
+
}
|
|
3858
|
+
|
|
3859
|
+
return contentArea;
|
|
3746
3860
|
}
|
|
3747
3861
|
|
|
3748
3862
|
/**
|
|
@@ -3810,6 +3924,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
3810
3924
|
*/
|
|
3811
3925
|
var handle = {
|
|
3812
3926
|
getHtml: getHtml,
|
|
3927
|
+
getPlainHtml: getPlainHtml,
|
|
3813
3928
|
// init and reset are deferred when a handler calls them, so an
|
|
3814
3929
|
// operation in flight finishes before the canvas is rebuilt
|
|
3815
3930
|
init: function() { defer(init); },
|
|
@@ -84,6 +84,10 @@
|
|
|
84
84
|
'tool.add_row_to_section': 'Añadir fila',
|
|
85
85
|
'tool.delete_section': 'Quitar sección',
|
|
86
86
|
'confirm.delete_section': '¿Seguro que quieres borrar esta sección y todo su contenido?',
|
|
87
|
+
'tool.copy': 'Copiar',
|
|
88
|
+
'tool.paste': 'Pegar',
|
|
89
|
+
'clipboard.paste_row': 'Pegar fila',
|
|
90
|
+
'clipboard.paste_section': 'Pegar sección',
|
|
87
91
|
'utility.col_from_row': 'De la fila: {count}',
|
|
88
92
|
'utility.row_cols': 'Columnas por fila',
|
|
89
93
|
'badge.row_cols': '{count} por fila',
|