@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.
- package/CHANGELOG.md +95 -0
- package/README.md +121 -9
- package/UPGRADING.md +55 -0
- package/dist/grideditor.css +609 -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 +1189 -118
- package/dist/jquery.grideditor.min.js +1 -1
- package/dist/jquery.grideditor.min.js.map +1 -1
- package/dist/locales/grideditor.es.js +58 -0
- package/dist/locales/grideditor.es.min.js +1 -1
- package/dist/locales/grideditor.es.min.js.map +1 -1
- package/dist/plugins/grideditor.alignment.js +81 -0
- package/dist/plugins/grideditor.alignment.min.js +2 -0
- package/dist/plugins/grideditor.alignment.min.js.map +1 -0
- package/dist/plugins/grideditor.float.js +56 -0
- package/dist/plugins/grideditor.float.min.js +2 -0
- package/dist/plugins/grideditor.float.min.js.map +1 -0
- package/dist/plugins/grideditor.gutters.js +145 -0
- package/dist/plugins/grideditor.gutters.min.js +2 -0
- package/dist/plugins/grideditor.gutters.min.js.map +1 -0
- package/dist/plugins/grideditor.order.js +206 -0
- package/dist/plugins/grideditor.order.min.js +2 -0
- package/dist/plugins/grideditor.order.min.js.map +1 -0
- package/dist/plugins/grideditor.sections.js +199 -0
- package/dist/plugins/grideditor.sections.min.js +2 -0
- package/dist/plugins/grideditor.sections.min.js.map +1 -0
- package/dist/plugins/grideditor.spacing.js +299 -0
- package/dist/plugins/grideditor.spacing.min.js +2 -0
- package/dist/plugins/grideditor.spacing.min.js.map +1 -0
- package/dist/plugins/grideditor.textalign.js +60 -0
- package/dist/plugins/grideditor.textalign.min.js +2 -0
- package/dist/plugins/grideditor.textalign.min.js.map +1 -0
- package/dist/plugins/grideditor.visibility.js +172 -0
- package/dist/plugins/grideditor.visibility.min.js +2 -0
- package/dist/plugins/grideditor.visibility.min.js.map +1 -0
- package/docs/events.md +32 -2
- package/docs/locale-keys.md +88 -1
- package/docs/plugins.md +141 -3
- package/example/autocols.html +137 -0
- package/example/autosave.html +5 -0
- package/example/basic.html +5 -0
- package/example/breakpoints.html +12 -5
- package/example/ckeditor.html +5 -0
- package/example/containers.html +4 -0
- package/example/elements.html +5 -0
- package/example/index.html +38 -1
- package/example/locale.html +5 -0
- package/example/plugins.html +5 -0
- package/example/summernote.html +5 -0
- package/example/utilities.html +166 -0
- package/example/wrap_content.html +5 -0
- package/package.json +1 -1
- package/src/js/jquery.grideditor.js +1190 -119
- package/src/js/locales/grideditor.es.js +58 -0
- package/src/js/plugins/grideditor.alignment.js +81 -0
- package/src/js/plugins/grideditor.float.js +56 -0
- package/src/js/plugins/grideditor.gutters.js +145 -0
- package/src/js/plugins/grideditor.order.js +206 -0
- package/src/js/plugins/grideditor.sections.js +199 -0
- package/src/js/plugins/grideditor.spacing.js +299 -0
- package/src/js/plugins/grideditor.textalign.js +60 -0
- package/src/js/plugins/grideditor.visibility.js +172 -0
- package/src/less/grideditor.less +299 -0
|
@@ -28,10 +28,13 @@ var METHODS = {
|
|
|
28
28
|
createRow: { value: true },
|
|
29
29
|
createColumn: { value: true },
|
|
30
30
|
createElement: { value: true },
|
|
31
|
+
createSection: { value: true },
|
|
31
32
|
createContainer: { value: true },
|
|
32
33
|
addTab: { value: true },
|
|
33
34
|
addAccordionItem: { value: true },
|
|
34
35
|
setLocale: {},
|
|
36
|
+
getUtility: { value: true },
|
|
37
|
+
setUtility: { value: true },
|
|
35
38
|
};
|
|
36
39
|
|
|
37
40
|
/**
|
|
@@ -45,20 +48,23 @@ var PLACEMENTS = ['appendTo', 'prependTo', 'insertAfter', 'insertBefore'];
|
|
|
45
48
|
* runs in: a size written for a tier applies to every wider tier that does not
|
|
46
49
|
* override it. Every size and offset class grid-editor reads or writes comes
|
|
47
50
|
* from this table, so adding a tier is a row here and nothing else.
|
|
51
|
+
*
|
|
52
|
+
* `infix` is what Bootstrap's utility classes put between the property and
|
|
53
|
+
* the value - `order-md-2`, `d-none` - and is empty for the smallest tier.
|
|
48
54
|
*/
|
|
49
55
|
var BREAKPOINTS = [
|
|
50
|
-
{ key: 'xs', colPrefix: 'col-', offsetPrefix: 'offset-', min: 0, preview: 400, labelKey: 'view.xs' },
|
|
51
|
-
{ key: 'sm', colPrefix: 'col-sm-', offsetPrefix: 'offset-sm-', min: 576, preview: 576, labelKey: 'view.sm' },
|
|
52
|
-
{ key: 'md', colPrefix: 'col-md-', offsetPrefix: 'offset-md-', min: 768, preview: 768, labelKey: 'view.md' },
|
|
53
|
-
{ key: 'lg', colPrefix: 'col-lg-', offsetPrefix: 'offset-lg-', min: 992, preview: 992, labelKey: 'view.lg' },
|
|
54
|
-
{ key: 'xl', colPrefix: 'col-xl-', offsetPrefix: 'offset-xl-', min: 1200, preview: 1200, labelKey: 'view.xl' },
|
|
55
|
-
{ key: 'xxl', colPrefix: 'col-xxl-', offsetPrefix: 'offset-xxl-', min: 1400, preview: null, labelKey: 'view.xxl' },
|
|
56
|
+
{ key: 'xs', infix: '', colPrefix: 'col-', offsetPrefix: 'offset-', min: 0, preview: 400, labelKey: 'view.xs' },
|
|
57
|
+
{ key: 'sm', infix: 'sm', colPrefix: 'col-sm-', offsetPrefix: 'offset-sm-', min: 576, preview: 576, labelKey: 'view.sm' },
|
|
58
|
+
{ key: 'md', infix: 'md', colPrefix: 'col-md-', offsetPrefix: 'offset-md-', min: 768, preview: 768, labelKey: 'view.md' },
|
|
59
|
+
{ key: 'lg', infix: 'lg', colPrefix: 'col-lg-', offsetPrefix: 'offset-lg-', min: 992, preview: 992, labelKey: 'view.lg' },
|
|
60
|
+
{ key: 'xl', infix: 'xl', colPrefix: 'col-xl-', offsetPrefix: 'offset-xl-', min: 1200, preview: 1200, labelKey: 'view.xl' },
|
|
61
|
+
{ key: 'xxl', infix: 'xxl', colPrefix: 'col-xxl-', offsetPrefix: 'offset-xxl-', min: 1400, preview: null, labelKey: 'view.xxl' },
|
|
56
62
|
];
|
|
57
63
|
|
|
58
64
|
/**
|
|
59
65
|
* The view that edits every tier at once. It is the default, and the one most
|
|
60
|
-
* pages want: a layout that needs no per-device tuning is written once
|
|
61
|
-
*
|
|
66
|
+
* pages want: a layout that needs no per-device tuning is written once, as the
|
|
67
|
+
* class with no breakpoint, and applies at every size.
|
|
62
68
|
*/
|
|
63
69
|
var ALL_VIEW = 'all';
|
|
64
70
|
var ALL_VIEW_LABEL_KEY = 'view.all';
|
|
@@ -72,6 +78,31 @@ var LEGACY_VIEW_INDEXES = ['lg', 'sm', 'xs'];
|
|
|
72
78
|
var MAX_COL_SIZE = 12;
|
|
73
79
|
var MAX_COL_OFFSET = 11;
|
|
74
80
|
|
|
81
|
+
/**
|
|
82
|
+
* The two sizes that are not a number of units: `equal` is Bootstrap's col,
|
|
83
|
+
* which shares what the row has left with the other equal columns, and
|
|
84
|
+
* `auto` is col-auto, as wide as its content.
|
|
85
|
+
*/
|
|
86
|
+
var FLEX_SIZES = ['equal', 'auto'];
|
|
87
|
+
|
|
88
|
+
/** How many columns a row-cols class puts on a line. */
|
|
89
|
+
var ROW_COLS_VALUES = ['1', '2', '3', '4', '5', '6', 'auto'];
|
|
90
|
+
|
|
91
|
+
function rowColsClass(tier, value) {
|
|
92
|
+
return 'row-cols' + (tier.infix ? '-' + tier.infix : '') + '-' + value;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function isUnits(size) {
|
|
96
|
+
return typeof size === 'number';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** The class that gives a column `size` at one tier: col-md-4, col-md, col-md-auto. */
|
|
100
|
+
function sizeClass(tier, size) {
|
|
101
|
+
if (size === 'equal') { return tier.infix ? 'col-' + tier.infix : 'col'; }
|
|
102
|
+
|
|
103
|
+
return tier.colPrefix + size;
|
|
104
|
+
}
|
|
105
|
+
|
|
75
106
|
function breakpoint(key) {
|
|
76
107
|
for (var i = 0; i < BREAKPOINTS.length; i++) {
|
|
77
108
|
if (BREAKPOINTS[i].key === key) { return BREAKPOINTS[i]; }
|
|
@@ -79,9 +110,12 @@ function breakpoint(key) {
|
|
|
79
110
|
return null;
|
|
80
111
|
}
|
|
81
112
|
|
|
82
|
-
/**
|
|
113
|
+
/**
|
|
114
|
+
* The tier a view writes to. The all view writes the base class, the one
|
|
115
|
+
* with no breakpoint, and takes the others off.
|
|
116
|
+
*/
|
|
83
117
|
function tiersFor(view) {
|
|
84
|
-
if (view === ALL_VIEW) { return BREAKPOINTS
|
|
118
|
+
if (view === ALL_VIEW) { return [BREAKPOINTS[0]]; }
|
|
85
119
|
|
|
86
120
|
var tier = breakpoint(view);
|
|
87
121
|
return tier ? [tier] : [];
|
|
@@ -258,11 +292,13 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
258
292
|
'container_tools' : [], // Host tools on container drawers
|
|
259
293
|
'tab_tools' : [], // Host tools on tab drawers
|
|
260
294
|
'accordion_tools' : [], // Host tools on accordion item drawers
|
|
261
|
-
'plugins' : null, //
|
|
295
|
+
'plugins' : null, // Plugins to use, of any kind; null means every one loaded
|
|
296
|
+
'row_cols' : true, // A row's "columns per row" field, row-cols-*
|
|
297
|
+
'utilities' : {}, // Options for the utility plugins, by plugin name
|
|
262
298
|
'elements' : NESTED_SETTINGS.elements, // Element level controls, below the column
|
|
263
299
|
'custom_filter' : '',
|
|
264
300
|
'content_types' : ['tinymce'],
|
|
265
|
-
'valid_col_sizes' : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
|
|
301
|
+
'valid_col_sizes' : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 'equal', 'auto'],
|
|
266
302
|
'valid_col_offsets' : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
|
|
267
303
|
'add_column' : NESTED_SETTINGS.add_column, // The add column tool
|
|
268
304
|
'layout_modes' : VIEW_KEYS.slice(), // Which views the dropdown offers
|
|
@@ -574,54 +610,91 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
574
610
|
}
|
|
575
611
|
|
|
576
612
|
/**
|
|
577
|
-
*
|
|
578
|
-
*
|
|
613
|
+
* What resizing this column to `size` in `view` would write, or null
|
|
614
|
+
* when the budget refuses it or there is nothing to change.
|
|
579
615
|
*
|
|
580
|
-
*
|
|
581
|
-
*
|
|
582
|
-
|
|
583
|
-
/**
|
|
584
|
-
* What resizing this column to `size` would write, or null when the
|
|
585
|
-
* budget refuses it or there is nothing to change.
|
|
616
|
+
* A breakpoint view writes its own tier. The all view writes the base
|
|
617
|
+
* class and takes the size off every other tier, so one size is what
|
|
618
|
+
* applies everywhere; the plan says what it took off.
|
|
586
619
|
*
|
|
587
620
|
* Refused rather than quietly clamped: the offset is something the
|
|
588
|
-
* user set, and a tool that rewrites it is a tool that lies.
|
|
589
|
-
*
|
|
621
|
+
* user set, and a tool that rewrites it is a tool that lies. In the
|
|
622
|
+
* all view the base size has to fit every tier's offset, since it is
|
|
623
|
+
* the size at every tier once the others are gone.
|
|
590
624
|
*/
|
|
591
|
-
function planSize(col, size) {
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
625
|
+
function planSize(col, size, view) {
|
|
626
|
+
view = view || curView;
|
|
627
|
+
|
|
628
|
+
if (view !== ALL_VIEW) {
|
|
629
|
+
var tier = breakpoint(view);
|
|
630
|
+
var wanted = clamp({ size: size, offset: getEffectiveOffset(col, tier) || 0 });
|
|
631
|
+
|
|
632
|
+
if (wanted.refused || getSize(col, tier) === wanted.size) { return null; }
|
|
633
|
+
|
|
634
|
+
return { writes: [{ tier: tier, size: wanted.size }], cleared: null, size: wanted.size };
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
var refused = BREAKPOINTS.some(function(each) {
|
|
638
|
+
return clamp({ size: size, offset: getEffectiveOffset(col, each) || 0 }).refused;
|
|
595
639
|
});
|
|
640
|
+
if (refused) { return null; }
|
|
641
|
+
|
|
642
|
+
var base = BREAKPOINTS[0];
|
|
643
|
+
var writes = [];
|
|
644
|
+
var cleared = [];
|
|
596
645
|
|
|
597
|
-
|
|
646
|
+
BREAKPOINTS.slice(1).forEach(function(each) {
|
|
647
|
+
var own = getSize(col, each);
|
|
648
|
+
if (own === null) { return; }
|
|
598
649
|
|
|
599
|
-
|
|
600
|
-
|
|
650
|
+
writes.push({ tier: each, size: null });
|
|
651
|
+
cleared.push({ breakpoint: each.key, value: own });
|
|
601
652
|
});
|
|
602
|
-
if (unchanged) { return null; }
|
|
603
653
|
|
|
604
|
-
|
|
654
|
+
if (getSize(col, base) !== size) { writes.unshift({ tier: base, size: size }); }
|
|
655
|
+
|
|
656
|
+
return writes.length ? { writes: writes, cleared: cleared, size: size } : null;
|
|
605
657
|
}
|
|
606
658
|
|
|
607
659
|
function writeSize(col, plan) {
|
|
608
|
-
plan.
|
|
660
|
+
plan.writes.forEach(function(write) { setSize(col, write.tier, write.size); });
|
|
609
661
|
stripPixelWidths(col);
|
|
610
662
|
}
|
|
611
663
|
|
|
612
|
-
/**
|
|
613
|
-
function
|
|
664
|
+
/** The all view's payloads say what the write took off the breakpoints. */
|
|
665
|
+
function withCleared(extra, cleared) {
|
|
666
|
+
if (cleared) { extra.cleared = cleared; }
|
|
667
|
+
return extra;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Resize a column, announcing it either side. `size` is units,
|
|
672
|
+
* `equal` or `auto`; `view` is the current one unless the size field
|
|
673
|
+
* of a plugin's panel says otherwise.
|
|
674
|
+
*/
|
|
675
|
+
function resizeColumn(col, size, source, view) {
|
|
676
|
+
view = view || curView;
|
|
677
|
+
|
|
614
678
|
var from = currentSize(col);
|
|
615
|
-
var plan = planSize(col, size);
|
|
679
|
+
var plan = planSize(col, size, view);
|
|
616
680
|
|
|
617
681
|
if (!plan) { return false; }
|
|
618
682
|
|
|
619
683
|
return operate(function() {
|
|
620
|
-
var payload = payloadFor('column', col, {
|
|
684
|
+
var payload = payloadFor('column', col, withCleared({
|
|
685
|
+
source: source,
|
|
686
|
+
breakpoint: view,
|
|
687
|
+
from: from,
|
|
688
|
+
to: plan.size,
|
|
689
|
+
}, plan.cleared));
|
|
621
690
|
|
|
622
|
-
if (!emit('before-resize', payload)) {
|
|
691
|
+
if (!emit('before-resize', payload)) {
|
|
692
|
+
refreshUtilities(col);
|
|
693
|
+
return false;
|
|
694
|
+
}
|
|
623
695
|
|
|
624
696
|
writeSize(col, plan);
|
|
697
|
+
refreshUtilities(col);
|
|
625
698
|
emit('after-resize', payload);
|
|
626
699
|
|
|
627
700
|
return true;
|
|
@@ -631,41 +704,49 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
631
704
|
/**
|
|
632
705
|
* Indent a column, shrinking it when the budget needs it: the offset
|
|
633
706
|
* is what the user asked for, so it is the one that gets its way.
|
|
707
|
+
*
|
|
708
|
+
* Like a size, an offset is written to the tier being edited, or in
|
|
709
|
+
* the all view as the base class with every other tier's taken off.
|
|
634
710
|
*/
|
|
635
711
|
function indentColumn(col, offset, source) {
|
|
636
|
-
var
|
|
712
|
+
var everywhere = curView === ALL_VIEW;
|
|
713
|
+
var tier = everywhere ? BREAKPOINTS[0] : breakpoint(curView);
|
|
637
714
|
var from = currentOffset(col);
|
|
638
715
|
|
|
639
716
|
offset = Math.min(Math.max(offset, 0), MAX_COL_OFFSET);
|
|
640
717
|
|
|
641
|
-
var
|
|
642
|
-
return
|
|
643
|
-
});
|
|
644
|
-
|
|
718
|
+
var others = everywhere ? BREAKPOINTS.slice(1).filter(function(each) {
|
|
719
|
+
return getOffset(col, each) !== null;
|
|
720
|
+
}) : [];
|
|
721
|
+
|
|
722
|
+
if ((getOffset(col, tier) || 0) === offset && !others.length) { return false; }
|
|
645
723
|
|
|
646
724
|
return operate(function() {
|
|
647
|
-
var payload = payloadFor('column', col, {
|
|
725
|
+
var payload = payloadFor('column', col, withCleared({
|
|
648
726
|
source: source,
|
|
649
727
|
from: from,
|
|
650
728
|
to: offset,
|
|
651
|
-
})
|
|
729
|
+
}, everywhere ? others.map(function(each) {
|
|
730
|
+
return { breakpoint: each.key, value: getOffset(col, each) };
|
|
731
|
+
}) : null));
|
|
652
732
|
|
|
653
733
|
if (!emit('before-indent', payload)) { return false; }
|
|
654
734
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
var wanted = clamp({ size: was, offset: offset, leading: 'offset' });
|
|
735
|
+
setOffset(col, tier, offset);
|
|
736
|
+
others.forEach(function(each) { setOffset(col, each, 0); });
|
|
658
737
|
|
|
659
|
-
|
|
738
|
+
// Wherever the column's size and the new offset no longer fit
|
|
739
|
+
// - one tier, or every tier in the all view - the size gives
|
|
740
|
+
// way, at that tier only. An equal or auto column fits anywhere.
|
|
741
|
+
(everywhere ? BREAKPOINTS : [tier]).forEach(function(each) {
|
|
742
|
+
var was = getEffectiveSize(col, each);
|
|
743
|
+
if (!isUnits(was)) { return; }
|
|
660
744
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
// tiers nobody asked it to touch
|
|
664
|
-
if (wanted.size !== null && wanted.size !== was) {
|
|
665
|
-
setSize(col, tier, wanted.size);
|
|
666
|
-
}
|
|
745
|
+
var wanted = clamp({ size: was, offset: offset, leading: 'offset' });
|
|
746
|
+
if (wanted.size !== was) { setSize(col, each, wanted.size); }
|
|
667
747
|
});
|
|
668
748
|
|
|
749
|
+
refreshUtilities(col);
|
|
669
750
|
emit('after-indent', payload);
|
|
670
751
|
|
|
671
752
|
return true;
|
|
@@ -748,6 +829,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
748
829
|
// when their editor is ready, and the drawers go back in.
|
|
749
830
|
canvas.on('ge-rte-ready', '.ge-content', function() {
|
|
750
831
|
plugins('onContentReady', $(this));
|
|
832
|
+
refreshPreviews($(this));
|
|
751
833
|
});
|
|
752
834
|
}
|
|
753
835
|
|
|
@@ -763,17 +845,17 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
763
845
|
addRowGroup = $('<div class="ge-addRowGroup btn-group" />').appendTo(wrapper);
|
|
764
846
|
addContainerGroup = $('<div class="ge-addContainerGroup btn-group" />');
|
|
765
847
|
$.each(settings.new_row_layouts, function(j, layout) {
|
|
848
|
+
var grouped = !Array.isArray(layout);
|
|
766
849
|
var btn = $('<a class="btn btn-sm btn-primary" />')
|
|
767
|
-
.attr('title',
|
|
850
|
+
.attr('title', grouped
|
|
851
|
+
? t('row.add_row_cols', { columns: layout.columns, counts: rowColsText(layout.row_cols) })
|
|
852
|
+
: t('row.add', { layout: layout.join('-') }))
|
|
768
853
|
// What this button makes, in the markup rather than in
|
|
769
854
|
// jQuery data: a drag works on a clone of it
|
|
770
855
|
.attr('data-ge-toolbar', 'row')
|
|
771
|
-
.attr('data-ge-layout', layout.join(','))
|
|
856
|
+
.attr('data-ge-layout', grouped ? JSON.stringify(layout) : layout.join(','))
|
|
772
857
|
.on('click', function() {
|
|
773
|
-
var row =
|
|
774
|
-
layout.forEach(function(i) {
|
|
775
|
-
createColumn(i).appendTo(row);
|
|
776
|
-
});
|
|
858
|
+
var row = rowFromLayoutValue(layout);
|
|
777
859
|
|
|
778
860
|
var added = addNode('row', row, function() {
|
|
779
861
|
row.appendTo(canvas);
|
|
@@ -788,10 +870,14 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
788
870
|
|
|
789
871
|
btn.append('<i class="bi bi-plus"></i>');
|
|
790
872
|
|
|
791
|
-
|
|
873
|
+
// A row of columns shares out the icon as they share the row;
|
|
874
|
+
// a row with row-cols draws one line of its widest count
|
|
875
|
+
var sizes = grouped ? rowColsIcon(layout) : layout;
|
|
792
876
|
var icon = '<div class="row ge-row-icon">';
|
|
793
|
-
|
|
794
|
-
|
|
877
|
+
sizes.forEach(function(size) {
|
|
878
|
+
// Closed explicitly: jQuery 4 no longer expands <div/>, and
|
|
879
|
+
// each column would be parsed inside the one before
|
|
880
|
+
icon += '<div class="column ' + sizeClass(BREAKPOINTS[0], size) + '"></div>';
|
|
795
881
|
});
|
|
796
882
|
icon += '</div>';
|
|
797
883
|
btn.append(icon);
|
|
@@ -823,6 +909,29 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
823
909
|
;
|
|
824
910
|
});
|
|
825
911
|
|
|
912
|
+
// A feature plugin's own buttons, beside the containers': what
|
|
913
|
+
// one makes goes onto the canvas, where the plugin says it belongs
|
|
914
|
+
$.each(FEATURES, function(name, feature) {
|
|
915
|
+
(feature.toolbar || []).forEach(function(item, index) {
|
|
916
|
+
$('<a class="btn btn-sm btn-primary ge-add-container ge-add-feature" />')
|
|
917
|
+
.attr('title', t(item.labelKey))
|
|
918
|
+
.attr('data-ge-toolbar', 'feature')
|
|
919
|
+
.attr('data-ge-feature', name)
|
|
920
|
+
.attr('data-ge-item', index)
|
|
921
|
+
.append('<i class="bi bi-plus"></i>')
|
|
922
|
+
.append($('<span />').text(t(item.labelKey)))
|
|
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
|
+
;
|
|
932
|
+
});
|
|
933
|
+
});
|
|
934
|
+
|
|
826
935
|
// Buttons on right
|
|
827
936
|
layoutDropdown = $('<div class="dropdown pull-right ge-layout-mode">' +
|
|
828
937
|
'<button type="button" class="btn btn-sm btn-primary dropdown-toggle" data-bs-toggle="dropdown"></button>' +
|
|
@@ -998,14 +1107,14 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
998
1107
|
|
|
999
1108
|
if (!under) { return null; }
|
|
1000
1109
|
|
|
1001
|
-
var region = $(under).closest('.column, .ge-canvas');
|
|
1110
|
+
var region = $(under).closest(['.column', '.ge-canvas'].concat(pluginHooks('regions')).join(', '));
|
|
1002
1111
|
if (!region.length || (region[0] !== canvas[0] && !canvas[0].contains(region[0]))) {
|
|
1003
1112
|
return null;
|
|
1004
1113
|
}
|
|
1005
1114
|
|
|
1006
1115
|
var before = null;
|
|
1007
1116
|
|
|
1008
|
-
region.children(
|
|
1117
|
+
region.children(blockSelector()).each(function() {
|
|
1009
1118
|
if (before) { return; }
|
|
1010
1119
|
|
|
1011
1120
|
var box = this.getBoundingClientRect();
|
|
@@ -1015,6 +1124,30 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1015
1124
|
return { region: region, before: before };
|
|
1016
1125
|
}
|
|
1017
1126
|
|
|
1127
|
+
/**
|
|
1128
|
+
* A feature's toolbar button dropped on the canvas. A region that will
|
|
1129
|
+
* not have what it makes - a section dropped on a column - gives way
|
|
1130
|
+
* to the canvas, just after the top level block the pointer was in.
|
|
1131
|
+
*/
|
|
1132
|
+
function insertFeatureFromToolbar(button, where) {
|
|
1133
|
+
var item = FEATURES[button.attr('data-ge-feature')].toolbar[parseInt(button.attr('data-ge-item'), 10)];
|
|
1134
|
+
var made = item.create();
|
|
1135
|
+
|
|
1136
|
+
if (!acceptsBlock(where.region, made)) {
|
|
1137
|
+
var top = where.region.parentsUntil(canvas).addBack().first();
|
|
1138
|
+
where = { region: canvas, before: top.length ? top.next() : null };
|
|
1139
|
+
if (where.before && !where.before.length) { where.before = null; }
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
return addNode(item.kind, made, function() {
|
|
1143
|
+
if (where.before) {
|
|
1144
|
+
made.insertBefore(where.before);
|
|
1145
|
+
} else {
|
|
1146
|
+
made.appendTo(where.region);
|
|
1147
|
+
}
|
|
1148
|
+
}, { parent: where.region, source: 'dragdrop' });
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1018
1151
|
/** A line where the block would go, following the pointer. */
|
|
1019
1152
|
function showDropMarker(pageX, pageY) {
|
|
1020
1153
|
var where = dropPlaceAt(pageX, pageY);
|
|
@@ -1041,6 +1174,10 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1041
1174
|
* the pointer left it.
|
|
1042
1175
|
*/
|
|
1043
1176
|
function insertFromToolbar(button, where) {
|
|
1177
|
+
if (button.attr('data-ge-toolbar') === 'feature') {
|
|
1178
|
+
return insertFeatureFromToolbar(button, where);
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1044
1181
|
var container = button.attr('data-ge-toolbar') === 'container';
|
|
1045
1182
|
var type = button.attr('data-ge-container-type');
|
|
1046
1183
|
var made = container
|
|
@@ -1065,16 +1202,94 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1065
1202
|
}, { parent: where.region, source: 'dragdrop' });
|
|
1066
1203
|
}
|
|
1067
1204
|
|
|
1205
|
+
/**
|
|
1206
|
+
* The column width as a utility family, so the Responsive section of
|
|
1207
|
+
* a column's panel has a field for it: every size, units or not, in
|
|
1208
|
+
* the view being edited. A write is a resize, through the resize
|
|
1209
|
+
* events, not a utility change.
|
|
1210
|
+
*/
|
|
1211
|
+
function widthFamily() {
|
|
1212
|
+
var values = [];
|
|
1213
|
+
for (var units = 1; units <= MAX_COL_SIZE; units++) { values.push(String(units)); }
|
|
1214
|
+
|
|
1215
|
+
return {
|
|
1216
|
+
name: 'col',
|
|
1217
|
+
values: values.concat(FLEX_SIZES),
|
|
1218
|
+
appliesTo: ['column'],
|
|
1219
|
+
labelKey: 'utility.col_width',
|
|
1220
|
+
className: function(key, value) { return sizeClass(breakpoint(key), parseSize(value)); },
|
|
1221
|
+
choices: function() { return settings.valid_col_sizes.map(String); },
|
|
1222
|
+
label: function(value) {
|
|
1223
|
+
if (value === 'equal') { return t('utility.col_equal'); }
|
|
1224
|
+
if (value === 'auto') { return t('utility.col_auto'); }
|
|
1225
|
+
|
|
1226
|
+
return value;
|
|
1227
|
+
},
|
|
1228
|
+
write: function(col, value, view, source) {
|
|
1229
|
+
return resizeColumn(col, value === null ? null : parseSize(value), source, view);
|
|
1230
|
+
},
|
|
1231
|
+
// With no size of its own, a column in a row with row-cols
|
|
1232
|
+
// takes its share from the row, which the field says
|
|
1233
|
+
blank: function(col, view) {
|
|
1234
|
+
var winner = view === ALL_VIEW ? null : rowColsWinner(col, breakpoint(view));
|
|
1235
|
+
return winner ? t('utility.col_from_row', { count: rowColsLabel(winner.value) }) : null;
|
|
1236
|
+
},
|
|
1237
|
+
};
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
/** A size as markup or a select carries it: '4' is 4, 'equal' and 'auto' are themselves. */
|
|
1241
|
+
function parseSize(size) {
|
|
1242
|
+
return /^\d+$/.test(String(size)) ? parseInt(size, 10) : size;
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
/** A row from a toolbar button's data-ge-layout: sizes, or a row-cols layout as JSON. */
|
|
1068
1246
|
function rowFromLayout(layout) {
|
|
1247
|
+
if (/^\s*\{/.test(layout || '')) { return rowFromLayoutValue(JSON.parse(layout)); }
|
|
1248
|
+
|
|
1249
|
+
return rowFromLayoutValue((layout || '').split(',').filter(function(size) {
|
|
1250
|
+
return size !== '';
|
|
1251
|
+
}).map(parseSize));
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
/**
|
|
1255
|
+
* A row from a layout: an array of sizes, or { row_cols, columns } -
|
|
1256
|
+
* row-cols per breakpoint, and that many columns sized by the row.
|
|
1257
|
+
*/
|
|
1258
|
+
function rowFromLayoutValue(layout) {
|
|
1069
1259
|
var row = createRow();
|
|
1070
1260
|
|
|
1071
|
-
|
|
1072
|
-
|
|
1261
|
+
if (Array.isArray(layout)) {
|
|
1262
|
+
layout.forEach(function(size) { createColumn(size).appendTo(row); });
|
|
1263
|
+
return row;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
$.each(layout.row_cols || {}, function(key, value) {
|
|
1267
|
+
var tier = breakpoint(key);
|
|
1268
|
+
if (tier && ROW_COLS_VALUES.indexOf(String(value)) !== -1) { row.addClass(rowColsClass(tier, value)); }
|
|
1073
1269
|
});
|
|
1074
1270
|
|
|
1271
|
+
for (var i = 0; i < (layout.columns || 0); i++) { createColumn(null).appendTo(row); }
|
|
1272
|
+
|
|
1075
1273
|
return row;
|
|
1076
1274
|
}
|
|
1077
1275
|
|
|
1276
|
+
/** "1, md: 3": a row-cols layout's counts, for its button's title. */
|
|
1277
|
+
function rowColsText(counts) {
|
|
1278
|
+
return BREAKPOINTS.filter(function(tier) { return counts && counts[tier.key] !== undefined; })
|
|
1279
|
+
.map(function(tier) { return (tier.infix ? tier.key + ': ' : '') + counts[tier.key]; })
|
|
1280
|
+
.join(', ');
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
/** The icon of a row-cols layout: one line of equal columns, as many as its widest count. */
|
|
1284
|
+
function rowColsIcon(layout) {
|
|
1285
|
+
var widest = 1;
|
|
1286
|
+
$.each(layout.row_cols || {}, function(key, value) {
|
|
1287
|
+
if (value !== 'auto') { widest = Math.max(widest, parseInt(value, 10) || 1); }
|
|
1288
|
+
});
|
|
1289
|
+
|
|
1290
|
+
return Array.apply(null, Array(Math.min(widest, layout.columns || widest))).map(function() { return 'equal'; });
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1078
1293
|
function onScroll(e) {
|
|
1079
1294
|
var $window = $(window);
|
|
1080
1295
|
|
|
@@ -1136,6 +1351,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1136
1351
|
makeSortable();
|
|
1137
1352
|
makeResizable();
|
|
1138
1353
|
switchLayout(curView);
|
|
1354
|
+
refreshPreviews(canvas);
|
|
1139
1355
|
}
|
|
1140
1356
|
|
|
1141
1357
|
function deinit() {
|
|
@@ -1156,6 +1372,11 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1156
1372
|
hideDropMarker();
|
|
1157
1373
|
canvas.find('.ge-tools-drawer').remove();
|
|
1158
1374
|
plugins('onDeinit');
|
|
1375
|
+
// After the rich text editors have let go of their content areas:
|
|
1376
|
+
// one that rebuilt its area's DOM brought the preview styles back
|
|
1377
|
+
// with it, and the attribute recording them came back too
|
|
1378
|
+
clearPreviews(canvas);
|
|
1379
|
+
canvas.find('[data-ge-row-cols]').removeAttr('data-ge-row-cols');
|
|
1159
1380
|
unmarkContainers();
|
|
1160
1381
|
removeSortable();
|
|
1161
1382
|
removeResizable();
|
|
@@ -1224,6 +1445,9 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1224
1445
|
*/
|
|
1225
1446
|
function loadPlugins() {
|
|
1226
1447
|
var api = pluginApi();
|
|
1448
|
+
|
|
1449
|
+
registerFamily('grid', {}, widthFamily());
|
|
1450
|
+
if (settings.row_cols !== false) { registerFamily('grid', {}, rowColsFamily()); }
|
|
1227
1451
|
var wanted = function(name) {
|
|
1228
1452
|
return !settings.plugins || settings.plugins.indexOf(name) !== -1;
|
|
1229
1453
|
};
|
|
@@ -1236,6 +1460,15 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1236
1460
|
if (wanted(name)) { FEATURES[name] = factory(api); }
|
|
1237
1461
|
});
|
|
1238
1462
|
|
|
1463
|
+
$.each($.fn.gridEditor.utilities, function(name, factory) {
|
|
1464
|
+
if (!wanted(name)) { return; }
|
|
1465
|
+
|
|
1466
|
+
UTILITIES[name] = factory(api);
|
|
1467
|
+
(UTILITIES[name].families || []).forEach(function(family) {
|
|
1468
|
+
registerFamily(name, UTILITIES[name], family);
|
|
1469
|
+
});
|
|
1470
|
+
});
|
|
1471
|
+
|
|
1239
1472
|
$.each(FEATURES, function(name, feature) {
|
|
1240
1473
|
$.each(feature.methods || {}, function(method, implementation) {
|
|
1241
1474
|
featureMethods[method] = implementation;
|
|
@@ -1243,7 +1476,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1243
1476
|
});
|
|
1244
1477
|
|
|
1245
1478
|
(settings.plugins || []).forEach(function(name) {
|
|
1246
|
-
if (CONTAINERS[name] || FEATURES[name]) { return; }
|
|
1479
|
+
if (CONTAINERS[name] || FEATURES[name] || UTILITIES[name]) { return; }
|
|
1247
1480
|
|
|
1248
1481
|
warnOnceHere('plugin:' + name, 'the "' + name + '" plugin is not loaded: ' +
|
|
1249
1482
|
'include dist/plugins/grideditor.' + name + '.js after the editor');
|
|
@@ -1253,7 +1486,8 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1253
1486
|
/**
|
|
1254
1487
|
* A hook every loaded plugin may have. Containers first, since a
|
|
1255
1488
|
* feature that looks at the canvas - elements, say - wants the
|
|
1256
|
-
* containers already marked
|
|
1489
|
+
* containers already marked, and utilities last, since they decorate
|
|
1490
|
+
* nodes the other two may have just made.
|
|
1257
1491
|
*/
|
|
1258
1492
|
function plugins(hook, argument) {
|
|
1259
1493
|
$.each(CONTAINERS, function(type, definition) {
|
|
@@ -1262,6 +1496,9 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1262
1496
|
$.each(FEATURES, function(name, feature) {
|
|
1263
1497
|
if (feature[hook]) { feature[hook](argument); }
|
|
1264
1498
|
});
|
|
1499
|
+
$.each(UTILITIES, function(name, utility) {
|
|
1500
|
+
if (utility[hook]) { utility[hook](argument); }
|
|
1501
|
+
});
|
|
1265
1502
|
}
|
|
1266
1503
|
|
|
1267
1504
|
/** What a plugin is handed. See docs/plugins.md. */
|
|
@@ -1287,9 +1524,531 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1287
1524
|
emit: emit,
|
|
1288
1525
|
payloadFor: payloadFor,
|
|
1289
1526
|
operate: operate,
|
|
1527
|
+
kindOf: kindOf,
|
|
1528
|
+
view: getView,
|
|
1529
|
+
viewTiers: function() {
|
|
1530
|
+
return tiersFor(curView).map(function(tier) { return tier.key; });
|
|
1531
|
+
},
|
|
1532
|
+
breakpoints: BREAKPOINTS.map(function(tier) { return tier.key; }),
|
|
1533
|
+
getUtility: getUtility,
|
|
1534
|
+
setUtility: setUtility,
|
|
1535
|
+
utilityField: utilityField,
|
|
1536
|
+
bareStyle: bareStyle,
|
|
1537
|
+
rowFromLayout: rowFromLayoutValue,
|
|
1538
|
+
};
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
/* --------------------------------------------------------------
|
|
1542
|
+
* Utilities: Bootstrap's responsive utility classes.
|
|
1543
|
+
*
|
|
1544
|
+
* A utility plugin declares families - order, d, justify-content -
|
|
1545
|
+
* and this is everything that reads or writes one. Every family is
|
|
1546
|
+
* spelled the way Bootstrap spells them all, {prefix}-{infix}-{value}
|
|
1547
|
+
* with no infix at the smallest tier, and cascades the way the size
|
|
1548
|
+
* classes do: a tier that says nothing takes the nearest smaller one.
|
|
1549
|
+
*
|
|
1550
|
+
* The canvas cannot show a breakpoint's utilities by itself. A
|
|
1551
|
+
* per-breakpoint view narrows the canvas, not the viewport, and
|
|
1552
|
+
* Bootstrap's utilities answer to the viewport with !important. So
|
|
1553
|
+
* each node gets what its classes mean at the view being edited,
|
|
1554
|
+
* as inline !important styles, recorded in an attribute so they come
|
|
1555
|
+
* off again - exactly those, leaving the host's own style alone.
|
|
1556
|
+
* -------------------------------------------------------------- */
|
|
1557
|
+
|
|
1558
|
+
var FAMILIES = {}; // Every family the utility plugins declare, by name
|
|
1559
|
+
var UTILITY_NODES = '.row, .column, .ge-element, [data-ge-container]';
|
|
1560
|
+
var PREVIEW_ATTR = 'data-ge-preview';
|
|
1561
|
+
var utilitiesOpen = false; // Whether the panels show their Responsive section unfolded
|
|
1562
|
+
|
|
1563
|
+
function registerFamily(pluginName, utility, family) {
|
|
1564
|
+
var name = family.name || family.prefix;
|
|
1565
|
+
|
|
1566
|
+
if (FAMILIES[name]) {
|
|
1567
|
+
warn('the "' + pluginName + '" plugin declares the utility "' + name +
|
|
1568
|
+
'", which the "' + FAMILIES[name].plugin + '" plugin already declared: ignored');
|
|
1569
|
+
return;
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
FAMILIES[name] = {
|
|
1573
|
+
plugin: pluginName,
|
|
1574
|
+
family: $.extend({}, family, {
|
|
1575
|
+
name: name,
|
|
1576
|
+
values: family.values.map(String),
|
|
1577
|
+
appliesTo: family.appliesTo || utility.appliesTo || ['row', 'column'],
|
|
1578
|
+
}),
|
|
1290
1579
|
};
|
|
1291
1580
|
}
|
|
1292
1581
|
|
|
1582
|
+
/** The family a caller named, or null and a warning when no plugin declares it. */
|
|
1583
|
+
function familyNamed(name, method) {
|
|
1584
|
+
if (FAMILIES[name]) { return FAMILIES[name].family; }
|
|
1585
|
+
|
|
1586
|
+
warnOnceHere('utility:' + name, method + '(' + JSON.stringify(name) + '): no loaded ' +
|
|
1587
|
+
'plugin declares that utility');
|
|
1588
|
+
return null;
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
/**
|
|
1592
|
+
* Whether a family applies to a kind of node. A container answers to
|
|
1593
|
+
* `container` and to its own type, so a plugin can take them all or
|
|
1594
|
+
* name the ones it means.
|
|
1595
|
+
*/
|
|
1596
|
+
function appliesTo(family, kind) {
|
|
1597
|
+
if (family.appliesTo.indexOf(kind) !== -1) { return true; }
|
|
1598
|
+
|
|
1599
|
+
return !!CONTAINERS[kind] && family.appliesTo.indexOf('container') !== -1;
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
function familiesFor(kind) {
|
|
1603
|
+
return $.map(FAMILIES, function(entry) {
|
|
1604
|
+
return appliesTo(entry.family, kind) ? entry.family : null;
|
|
1605
|
+
});
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
function utilityClass(family, tier, value) {
|
|
1609
|
+
if (family.className) { return family.className(tier.key, value); }
|
|
1610
|
+
|
|
1611
|
+
return family.prefix + (tier.infix ? '-' + tier.infix : '') + '-' + value;
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
/** The value a node carries a class for at exactly this tier, or null. */
|
|
1615
|
+
function ownUtility(node, family, tier) {
|
|
1616
|
+
var classes = (node.attr('class') || '').split(/\s+/);
|
|
1617
|
+
|
|
1618
|
+
for (var i = 0; i < family.values.length; i++) {
|
|
1619
|
+
if (classes.indexOf(utilityClass(family, tier, family.values[i])) !== -1) {
|
|
1620
|
+
return family.values[i];
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
return null;
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
/**
|
|
1628
|
+
* The tier below this one that decides the value here, and what it
|
|
1629
|
+
* says, when this tier says nothing itself. Null when nothing below
|
|
1630
|
+
* says anything either.
|
|
1631
|
+
*/
|
|
1632
|
+
function inheritedUtility(node, family, tier) {
|
|
1633
|
+
for (var i = BREAKPOINTS.indexOf(tier) - 1; i >= 0; i--) {
|
|
1634
|
+
var value = ownUtility(node, family, BREAKPOINTS[i]);
|
|
1635
|
+
if (value !== null) { return { tier: BREAKPOINTS[i], value: value }; }
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
return null;
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
/** What applies at a tier: its own class, or what it inherits. */
|
|
1642
|
+
function effectiveUtility(node, family, tier) {
|
|
1643
|
+
var own = ownUtility(node, family, tier);
|
|
1644
|
+
if (own !== null) { return own; }
|
|
1645
|
+
|
|
1646
|
+
var inherited = inheritedUtility(node, family, tier);
|
|
1647
|
+
return inherited ? inherited.value : null;
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
/** Every tier the node carries a class of this family for. */
|
|
1651
|
+
function utilityTiers(node, family) {
|
|
1652
|
+
return BREAKPOINTS.map(function(tier) {
|
|
1653
|
+
return { tier: tier, value: ownUtility(node, family, tier) };
|
|
1654
|
+
}).filter(function(entry) { return entry.value !== null; });
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
function writeUtility(node, family, tier, value) {
|
|
1658
|
+
family.values.forEach(function(candidate) {
|
|
1659
|
+
node.removeClass(utilityClass(family, tier, candidate));
|
|
1660
|
+
});
|
|
1661
|
+
|
|
1662
|
+
if (value !== null) { node.addClass(utilityClass(family, tier, value)); }
|
|
1663
|
+
if (!node.attr('class')) { node.removeAttr('class'); }
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
/**
|
|
1667
|
+
* What a view reads. A breakpoint reads what applies there. The all
|
|
1668
|
+
* view reads the class with no infix, since that is what it writes:
|
|
1669
|
+
* the one class that means "the same at every size".
|
|
1670
|
+
*/
|
|
1671
|
+
function readUtility(node, family, view) {
|
|
1672
|
+
return view === ALL_VIEW
|
|
1673
|
+
? ownUtility(node, family, BREAKPOINTS[0])
|
|
1674
|
+
: effectiveUtility(node, family, breakpoint(view));
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
/**
|
|
1678
|
+
* What writing `value` in a view comes to, or null when it changes
|
|
1679
|
+
* nothing. A breakpoint writes its own tier and nothing else. The all
|
|
1680
|
+
* view writes the class with no infix and clears the family from
|
|
1681
|
+
* every other tier, and says what it cleared: choosing one value for
|
|
1682
|
+
* every size is choosing it over what the sizes said.
|
|
1683
|
+
*/
|
|
1684
|
+
function planUtility(node, family, view, value) {
|
|
1685
|
+
if (view !== ALL_VIEW) {
|
|
1686
|
+
var tier = breakpoint(view);
|
|
1687
|
+
if (ownUtility(node, family, tier) === value) { return null; }
|
|
1688
|
+
|
|
1689
|
+
return { writes: [{ tier: tier, value: value }], cleared: [] };
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
var writes = [];
|
|
1693
|
+
var cleared = [];
|
|
1694
|
+
|
|
1695
|
+
utilityTiers(node, family).forEach(function(entry) {
|
|
1696
|
+
if (entry.tier === BREAKPOINTS[0]) { return; }
|
|
1697
|
+
|
|
1698
|
+
writes.push({ tier: entry.tier, value: null });
|
|
1699
|
+
cleared.push({ breakpoint: entry.tier.key, value: entry.value });
|
|
1700
|
+
});
|
|
1701
|
+
|
|
1702
|
+
if (ownUtility(node, family, BREAKPOINTS[0]) !== value) {
|
|
1703
|
+
writes.unshift({ tier: BREAKPOINTS[0], value: value });
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
return writes.length ? { writes: writes, cleared: cleared } : null;
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
/** getUtility(node, family, view?): the value that applies, or null. */
|
|
1710
|
+
function getUtility(node, name, view) {
|
|
1711
|
+
node = $(node).first();
|
|
1712
|
+
|
|
1713
|
+
var family = familyNamed(name, 'getUtility');
|
|
1714
|
+
if (!family || !node.length) { return null; }
|
|
1715
|
+
|
|
1716
|
+
var key = view === undefined ? curView : viewKey(view);
|
|
1717
|
+
if (key === null) {
|
|
1718
|
+
warn('getUtility(' + JSON.stringify(view) + '): no such layout mode');
|
|
1719
|
+
return null;
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
return readUtility(node, family, key);
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
/**
|
|
1726
|
+
* setUtility(node, family, value, view?): write a value through the
|
|
1727
|
+
* events. Null is "inherit". The last argument is a view key, or
|
|
1728
|
+
* { view, source } from a plugin that wants its tool named in the
|
|
1729
|
+
* payload. False when a handler canceled or nothing changed.
|
|
1730
|
+
*/
|
|
1731
|
+
function setUtility(node, name, value, options) {
|
|
1732
|
+
options = typeof options == 'string' ? { view: options } : (options || {});
|
|
1733
|
+
node = $(node).first();
|
|
1734
|
+
|
|
1735
|
+
var family = familyNamed(name, 'setUtility');
|
|
1736
|
+
if (!family || !node.length) { return false; }
|
|
1737
|
+
|
|
1738
|
+
var view = options.view === undefined ? curView : viewKey(options.view);
|
|
1739
|
+
if (view === null) {
|
|
1740
|
+
warn('setUtility(' + JSON.stringify(options.view) + '): no such layout mode');
|
|
1741
|
+
return false;
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
value = value === null || value === undefined || value === '' ? null : String(value);
|
|
1745
|
+
if (value !== null && family.values.indexOf(value) === -1) {
|
|
1746
|
+
warn('setUtility: ' + JSON.stringify(value) + ' is not a value of "' + name +
|
|
1747
|
+
'", which takes ' + JSON.stringify(family.values));
|
|
1748
|
+
return false;
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
var kind = kindOf(node);
|
|
1752
|
+
if (!appliesTo(family, kind)) {
|
|
1753
|
+
warn('setUtility: "' + name + '" does not apply to a ' + kind);
|
|
1754
|
+
return false;
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
// A family whose writes are some other operation - the column
|
|
1758
|
+
// width, which is a resize - does them its own way
|
|
1759
|
+
if (family.write) { return family.write(node, value, view, options.source || 'api'); }
|
|
1760
|
+
|
|
1761
|
+
var plan = planUtility(node, family, view, value);
|
|
1762
|
+
if (!plan) { return false; }
|
|
1763
|
+
|
|
1764
|
+
return operate(function() {
|
|
1765
|
+
var payload = payloadFor(kind, node, {
|
|
1766
|
+
family: name,
|
|
1767
|
+
breakpoint: view,
|
|
1768
|
+
tiers: plan.writes.map(function(write) { return write.tier.key; }),
|
|
1769
|
+
from: readUtility(node, family, view),
|
|
1770
|
+
to: value,
|
|
1771
|
+
cleared: plan.cleared,
|
|
1772
|
+
source: options.source || 'api',
|
|
1773
|
+
});
|
|
1774
|
+
|
|
1775
|
+
if (!emit('before-utility', payload)) {
|
|
1776
|
+
// The panel already shows the value that was refused
|
|
1777
|
+
refreshUtilities(node);
|
|
1778
|
+
return false;
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
plan.writes.forEach(function(write) {
|
|
1782
|
+
writeUtility(node, family, write.tier, write.value);
|
|
1783
|
+
});
|
|
1784
|
+
refreshUtilities(node);
|
|
1785
|
+
emit('after-utility', payload);
|
|
1786
|
+
|
|
1787
|
+
return true;
|
|
1788
|
+
});
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
/**
|
|
1792
|
+
* ge.bareStyle(node, family, property): what a css property comes to
|
|
1793
|
+
* on a node with none of the family's classes, at any breakpoint. The
|
|
1794
|
+
* answer a preview needs for "no class applies here" when that is not
|
|
1795
|
+
* a constant: text-align inherits from the parent, and the host's own
|
|
1796
|
+
* css may float an element. The browser is the only one who knows, so
|
|
1797
|
+
* the classes come off for the moment it takes to ask.
|
|
1798
|
+
*/
|
|
1799
|
+
function bareStyle(node, name, property) {
|
|
1800
|
+
var family = familyNamed(name, 'bareStyle');
|
|
1801
|
+
node = $(node).first();
|
|
1802
|
+
if (!family || !node.length) { return null; }
|
|
1803
|
+
|
|
1804
|
+
var original = node.attr('class');
|
|
1805
|
+
BREAKPOINTS.forEach(function(tier) { writeUtility(node, family, tier, null); });
|
|
1806
|
+
|
|
1807
|
+
var value = getComputedStyle(node[0]).getPropertyValue(property);
|
|
1808
|
+
|
|
1809
|
+
if (original === undefined) { node.removeAttr('class'); } else { node.attr('class', original); }
|
|
1810
|
+
|
|
1811
|
+
return value;
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
/** Bring a node's panel and preview up to date with its classes. */
|
|
1815
|
+
function refreshUtilities(node) {
|
|
1816
|
+
var details = node.children('.ge-tools-drawer').children('.ge-details');
|
|
1817
|
+
|
|
1818
|
+
details.children('.ge-classes').val(hostClasses(node).join(' '));
|
|
1819
|
+
details.children('.ge-utilities').each(function() { renderUtilities($(this)); });
|
|
1820
|
+
refreshPreviews(node);
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
/* Preview */
|
|
1824
|
+
|
|
1825
|
+
function utilityNodes(scope) {
|
|
1826
|
+
return scope.find(UTILITY_NODES).addBack(UTILITY_NODES);
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1829
|
+
/**
|
|
1830
|
+
* Put what each node's utilities mean at the view being edited on the
|
|
1831
|
+
* node itself. Only nodes that carry a class of a family get anything,
|
|
1832
|
+
* and they get the family's answer for the view even when that is
|
|
1833
|
+
* "nothing": a wider tier's class is still live in a wide window, and
|
|
1834
|
+
* has to be overruled.
|
|
1835
|
+
*
|
|
1836
|
+
* The all view gets nothing. Its canvas is not narrowed, every tier is
|
|
1837
|
+
* live, and what Bootstrap shows is the truth.
|
|
1838
|
+
*/
|
|
1839
|
+
function refreshPreviews(scope) {
|
|
1840
|
+
clearPreviews(scope);
|
|
1841
|
+
|
|
1842
|
+
if (curView !== ALL_VIEW) { previewTier(scope, breakpoint(curView)); }
|
|
1843
|
+
|
|
1844
|
+
markRowCols(scope);
|
|
1845
|
+
|
|
1846
|
+
// Whatever a plugin marks the canvas with to show its utilities
|
|
1847
|
+
// goes stale at the same moments the preview does
|
|
1848
|
+
plugins('onRefresh', scope);
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
function previewTier(scope, tier) {
|
|
1852
|
+
utilityNodes(scope).each(function() {
|
|
1853
|
+
var node = $(this);
|
|
1854
|
+
var kind = kindOf(node);
|
|
1855
|
+
var styles = {};
|
|
1856
|
+
|
|
1857
|
+
familiesFor(kind).forEach(function(family) {
|
|
1858
|
+
if (!family.preview || !utilityTiers(node, family).length) { return; }
|
|
1859
|
+
|
|
1860
|
+
$.extend(styles, family.preview(effectiveUtility(node, family, tier), node, kind));
|
|
1861
|
+
});
|
|
1862
|
+
|
|
1863
|
+
if (kind === 'column') { $.extend(styles, rowColsPreview(node, tier)); }
|
|
1864
|
+
|
|
1865
|
+
// A plugin whose families settle one property between them
|
|
1866
|
+
// previews the node as a whole
|
|
1867
|
+
$.each(UTILITIES, function(name, utility) {
|
|
1868
|
+
if (utility.preview) { $.extend(styles, utility.preview(node, kind, tier.key)); }
|
|
1869
|
+
});
|
|
1870
|
+
|
|
1871
|
+
if (!$.isEmptyObject(styles)) { applyPreview(node, styles); }
|
|
1872
|
+
});
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
/**
|
|
1876
|
+
* Set inline !important styles, which is the one thing that beats
|
|
1877
|
+
* Bootstrap's own !important, and remember what each property was so
|
|
1878
|
+
* it can be put back. The record is an attribute rather than jQuery
|
|
1879
|
+
* data because a rich text editor rebuilds the DOM of the area it
|
|
1880
|
+
* edits, and the record has to come back with the node.
|
|
1881
|
+
*/
|
|
1882
|
+
function applyPreview(node, styles) {
|
|
1883
|
+
var style = node[0].style;
|
|
1884
|
+
var was = {};
|
|
1885
|
+
|
|
1886
|
+
$.each(styles, function(property, value) {
|
|
1887
|
+
was[property] = [style.getPropertyValue(property), style.getPropertyPriority(property)];
|
|
1888
|
+
style.setProperty(property, String(value), 'important');
|
|
1889
|
+
});
|
|
1890
|
+
|
|
1891
|
+
node.attr(PREVIEW_ATTR, JSON.stringify(was));
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1894
|
+
function clearPreviews(scope) {
|
|
1895
|
+
scope.find('[' + PREVIEW_ATTR + ']').addBack('[' + PREVIEW_ATTR + ']').each(function() {
|
|
1896
|
+
var node = $(this);
|
|
1897
|
+
var style = this.style;
|
|
1898
|
+
var was = {};
|
|
1899
|
+
|
|
1900
|
+
try { was = JSON.parse(node.attr(PREVIEW_ATTR)) || {}; } catch (error) { /* a mangled record: drop it */ }
|
|
1901
|
+
|
|
1902
|
+
$.each(was, function(property, before) {
|
|
1903
|
+
if (before && before[0]) {
|
|
1904
|
+
style.setProperty(property, before[0], before[1]);
|
|
1905
|
+
} else {
|
|
1906
|
+
style.removeProperty(property);
|
|
1907
|
+
}
|
|
1908
|
+
});
|
|
1909
|
+
|
|
1910
|
+
node.removeAttr(PREVIEW_ATTR);
|
|
1911
|
+
if (!node.attr('style')) { node.removeAttr('style'); }
|
|
1912
|
+
});
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
/* The panel */
|
|
1916
|
+
|
|
1917
|
+
/**
|
|
1918
|
+
* The Responsive section of a node's settings panel: one field per
|
|
1919
|
+
* family that applies to the node, reading and writing the view being
|
|
1920
|
+
* edited, and whatever fields a plugin builds itself. Folded until the
|
|
1921
|
+
* user unfolds one, and then unfolded on every node, since whoever
|
|
1922
|
+
* wanted it on one wants it on the next.
|
|
1923
|
+
*/
|
|
1924
|
+
function createUtilitiesSection(node) {
|
|
1925
|
+
var kind = kindOf(node);
|
|
1926
|
+
var fields = familiesFor(kind)
|
|
1927
|
+
.filter(function(family) { return family.panel !== false; })
|
|
1928
|
+
.map(function(family) { return createField(node, family); });
|
|
1929
|
+
|
|
1930
|
+
$.each(UTILITIES, function(name, utility) {
|
|
1931
|
+
var own = utility.panel ? utility.panel(node, kind) : null;
|
|
1932
|
+
if (own && own.length) { fields.push(own); }
|
|
1933
|
+
});
|
|
1934
|
+
|
|
1935
|
+
if (!fields.length) { return null; }
|
|
1936
|
+
|
|
1937
|
+
var section = $('<div class="ge-utilities" />')
|
|
1938
|
+
.toggleClass('ge-open', utilitiesOpen)
|
|
1939
|
+
.data('ge-node', node)
|
|
1940
|
+
;
|
|
1941
|
+
|
|
1942
|
+
$('<a class="ge-utilities-toggle" />')
|
|
1943
|
+
.appendTo(section)
|
|
1944
|
+
.on('click', function() {
|
|
1945
|
+
utilitiesOpen = !section.hasClass('ge-open');
|
|
1946
|
+
canvas.find('.ge-utilities').toggleClass('ge-open', utilitiesOpen);
|
|
1947
|
+
})
|
|
1948
|
+
;
|
|
1949
|
+
|
|
1950
|
+
var body = $('<div class="ge-utilities-body" />').appendTo(section);
|
|
1951
|
+
fields.forEach(function(field) { field.appendTo(body); });
|
|
1952
|
+
|
|
1953
|
+
renderUtilities(section);
|
|
1954
|
+
|
|
1955
|
+
return section;
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
/**
|
|
1959
|
+
* One family's field: its label, a select that writes through
|
|
1960
|
+
* setUtility, and a note. Filled by renderField, again whenever the
|
|
1961
|
+
* view or the node's classes change.
|
|
1962
|
+
*/
|
|
1963
|
+
function createField(node, family) {
|
|
1964
|
+
var field = $('<label class="ge-utility" />').attr('data-ge-family', family.name);
|
|
1965
|
+
|
|
1966
|
+
$('<span class="ge-utility-label" />')
|
|
1967
|
+
.text(family.labelKey ? t(family.labelKey) : family.name)
|
|
1968
|
+
.appendTo(field)
|
|
1969
|
+
;
|
|
1970
|
+
$('<select />')
|
|
1971
|
+
.appendTo(field)
|
|
1972
|
+
.on('change', function() {
|
|
1973
|
+
setUtility(node, family.name, this.value, { source: 'panel' });
|
|
1974
|
+
})
|
|
1975
|
+
;
|
|
1976
|
+
$('<small class="ge-utility-note" />').appendTo(field);
|
|
1977
|
+
|
|
1978
|
+
return field;
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
/** ge.utilityField(node, family): a field a plugin places in a panel of its own. */
|
|
1982
|
+
function utilityField(node, name) {
|
|
1983
|
+
var family = familyNamed(name, 'utilityField');
|
|
1984
|
+
if (!family) { return $(); }
|
|
1985
|
+
|
|
1986
|
+
var field = createField(node, family);
|
|
1987
|
+
renderField(field, node);
|
|
1988
|
+
|
|
1989
|
+
return field;
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
/** Fill a section's fields, the families' and the plugins' own, for the view being edited. */
|
|
1993
|
+
function renderUtilities(section) {
|
|
1994
|
+
var node = section.data('ge-node');
|
|
1995
|
+
|
|
1996
|
+
section.children('.ge-utilities-toggle')
|
|
1997
|
+
.text(t('utility.section', { view: t(labelKeyFor(curView)) }));
|
|
1998
|
+
|
|
1999
|
+
section.find('.ge-utility').each(function() { renderField($(this), node); });
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
function renderField(field, node) {
|
|
2003
|
+
var family = FAMILIES[field.attr('data-ge-family')].family;
|
|
2004
|
+
var select = field.children('select').empty();
|
|
2005
|
+
var choices = family.choices ? family.choices(node, kindOf(node)) : family.values;
|
|
2006
|
+
var own, blank, note = '';
|
|
2007
|
+
|
|
2008
|
+
if (curView === ALL_VIEW) {
|
|
2009
|
+
own = ownUtility(node, family, BREAKPOINTS[0]);
|
|
2010
|
+
blank = t('utility.default');
|
|
2011
|
+
|
|
2012
|
+
var varies = utilityTiers(node, family).filter(function(entry) {
|
|
2013
|
+
return entry.tier !== BREAKPOINTS[0];
|
|
2014
|
+
});
|
|
2015
|
+
if (varies.length) {
|
|
2016
|
+
note = t('utility.varies', {
|
|
2017
|
+
breakpoints: varies.map(function(entry) { return entry.tier.key; }).join(', '),
|
|
2018
|
+
});
|
|
2019
|
+
}
|
|
2020
|
+
} else {
|
|
2021
|
+
var tier = breakpoint(curView);
|
|
2022
|
+
var inherited = inheritedUtility(node, family, tier);
|
|
2023
|
+
|
|
2024
|
+
own = ownUtility(node, family, tier);
|
|
2025
|
+
blank = inherited
|
|
2026
|
+
? t('utility.inherit', { value: labelOf(family, inherited.value), breakpoint: inherited.tier.key })
|
|
2027
|
+
: t('utility.default');
|
|
2028
|
+
|
|
2029
|
+
// What the empty choice means is sometimes not the family's
|
|
2030
|
+
// own business: a column's width can come from its row
|
|
2031
|
+
var custom = family.blank ? family.blank(node, curView) : null;
|
|
2032
|
+
if (custom && own === null) { blank = custom; }
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
// A value the markup carries is shown even when the family
|
|
2036
|
+
// would not offer it here, rather than shown as something else
|
|
2037
|
+
if (own !== null && choices.indexOf(own) === -1) { choices = choices.concat([own]); }
|
|
2038
|
+
|
|
2039
|
+
$('<option value="" />').text(blank).appendTo(select);
|
|
2040
|
+
choices.forEach(function(value) {
|
|
2041
|
+
$('<option />').attr('value', value).text(labelOf(family, value)).appendTo(select);
|
|
2042
|
+
});
|
|
2043
|
+
|
|
2044
|
+
select.val(own === null ? '' : own);
|
|
2045
|
+
field.children('.ge-utility-note').text(note).toggle(note !== '');
|
|
2046
|
+
}
|
|
2047
|
+
|
|
2048
|
+
function labelOf(family, value) {
|
|
2049
|
+
return family.label ? family.label(value) : value;
|
|
2050
|
+
}
|
|
2051
|
+
|
|
1293
2052
|
/* --------------------------------------------------------------
|
|
1294
2053
|
* Containers: tabs, accordions and popups.
|
|
1295
2054
|
*
|
|
@@ -1305,6 +2064,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1305
2064
|
|
|
1306
2065
|
var CONTAINERS = {}; // The container plugins in use, by the type each builds
|
|
1307
2066
|
var FEATURES = {}; // The feature plugins in use, by name
|
|
2067
|
+
var UTILITIES = {}; // The utility plugins in use, by name
|
|
1308
2068
|
var featureMethods = {}; // The methods those features contribute
|
|
1309
2069
|
var containerCounter = 0;
|
|
1310
2070
|
|
|
@@ -1621,9 +2381,10 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1621
2381
|
settings.valid_col_sizes.forEach(function(size) {
|
|
1622
2382
|
$('<a class="ge-size" />')
|
|
1623
2383
|
.attr('data-ge-size', size)
|
|
1624
|
-
.attr('title',
|
|
1625
|
-
.toggleClass('ge-size-tight', size > room)
|
|
1626
|
-
.
|
|
2384
|
+
.attr('title', sizeTitle(size))
|
|
2385
|
+
.toggleClass('ge-size-tight', isUnits(size) && size > room)
|
|
2386
|
+
.toggleClass('ge-size-flex', !isUnits(size))
|
|
2387
|
+
.text(isUnits(size) ? size : sizeClass(BREAKPOINTS[0], size))
|
|
1627
2388
|
.on('click', function(e) {
|
|
1628
2389
|
e.preventDefault();
|
|
1629
2390
|
e.stopPropagation();
|
|
@@ -1644,6 +2405,13 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1644
2405
|
}
|
|
1645
2406
|
|
|
1646
2407
|
/** True when there was one to close, which is also a click's answer. */
|
|
2408
|
+
function sizeTitle(size) {
|
|
2409
|
+
if (size === 'equal') { return t('tool.column_equal'); }
|
|
2410
|
+
if (size === 'auto') { return t('tool.column_auto'); }
|
|
2411
|
+
|
|
2412
|
+
return t('tool.column_size', { size: size });
|
|
2413
|
+
}
|
|
2414
|
+
|
|
1647
2415
|
function closeSizePicker() {
|
|
1648
2416
|
if (!sizePicker) { return false; }
|
|
1649
2417
|
|
|
@@ -1675,7 +2443,8 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1675
2443
|
createTool(drawer, t('tool.add_column'), 'ge-add-column', 'bi bi-plus-circle', function() {
|
|
1676
2444
|
if (closeSizePicker()) { return; } // The picker was open: that was the answer
|
|
1677
2445
|
|
|
1678
|
-
|
|
2446
|
+
// In a row with row-cols the new column takes its share
|
|
2447
|
+
addColumnTo(row, rowColsSource(row, leadingTier()) ? null : settings.add_column.size);
|
|
1679
2448
|
});
|
|
1680
2449
|
|
|
1681
2450
|
attachSizePicker(drawer.find('> .ge-add-column'), row);
|
|
@@ -1695,12 +2464,15 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1695
2464
|
createTool(drawer, t('tool.column_narrower'), 'ge-decrease-col-width', 'bi bi-dash-lg', function(e) {
|
|
1696
2465
|
resizeColumn(col, e.shiftKey
|
|
1697
2466
|
? smallest(settings.valid_col_sizes)
|
|
1698
|
-
: stepThrough(settings.valid_col_sizes,
|
|
2467
|
+
: stepThrough(settings.valid_col_sizes.filter(isUnits), currentUnits(col), -1),
|
|
1699
2468
|
'tool');
|
|
1700
2469
|
});
|
|
1701
2470
|
|
|
1702
2471
|
createTool(drawer, t('tool.column_wider'), 'ge-increase-col-width', 'bi bi-plus-lg', function(e) {
|
|
1703
|
-
resizeColumn(col, e.shiftKey
|
|
2472
|
+
resizeColumn(col, e.shiftKey
|
|
2473
|
+
? widestFor(col)
|
|
2474
|
+
: stepThrough(settings.valid_col_sizes.filter(isUnits), currentUnits(col), 1),
|
|
2475
|
+
'tool');
|
|
1704
2476
|
});
|
|
1705
2477
|
|
|
1706
2478
|
createTool(drawer, t('tool.indent_decrease'), 'ge-decrease-col-offset', 'bi bi-text-indent-right', function(e) {
|
|
@@ -1751,17 +2523,43 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1751
2523
|
* view it is the widest tier, because the canvas is not constrained
|
|
1752
2524
|
* there and the widest tier is what the user is looking at: clicking
|
|
1753
2525
|
* "narrower" on a column authored as col-lg-6 should take it to 5,
|
|
1754
|
-
* not to 11 because no
|
|
2526
|
+
* not to 11 because no base class was ever written. What the all view
|
|
2527
|
+
* writes is the base class all the same.
|
|
1755
2528
|
*/
|
|
1756
2529
|
function leadingTier() {
|
|
1757
2530
|
return curView === ALL_VIEW ? BREAKPOINTS[BREAKPOINTS.length - 1] : breakpoint(curView);
|
|
1758
2531
|
}
|
|
1759
2532
|
|
|
2533
|
+
/** The size that applies in the view: units, `equal` or `auto`. */
|
|
1760
2534
|
function currentSize(col) {
|
|
1761
|
-
var
|
|
2535
|
+
var tier = leadingTier();
|
|
2536
|
+
|
|
2537
|
+
// Sized by its row: it has no size of its own to report
|
|
2538
|
+
if (rowColsWinner(col, tier)) { return null; }
|
|
2539
|
+
|
|
2540
|
+
var size = getEffectiveSize(col, tier);
|
|
1762
2541
|
return size === null ? MAX_COL_SIZE : size;
|
|
1763
2542
|
}
|
|
1764
2543
|
|
|
2544
|
+
/**
|
|
2545
|
+
* The size in units, which is what the tools step through. An equal
|
|
2546
|
+
* or auto column has no number, so it is measured: the width it has
|
|
2547
|
+
* on the canvas, in twelfths of its row.
|
|
2548
|
+
*/
|
|
2549
|
+
function currentUnits(col) {
|
|
2550
|
+
var size = currentSize(col);
|
|
2551
|
+
if (isUnits(size)) { return size; }
|
|
2552
|
+
|
|
2553
|
+
var units = Math.round(col.outerWidth() / rowContentWidth(col.parent()) * MAX_COL_SIZE);
|
|
2554
|
+
return Math.min(Math.max(units, 1), MAX_COL_SIZE);
|
|
2555
|
+
}
|
|
2556
|
+
|
|
2557
|
+
function rowContentWidth(row) {
|
|
2558
|
+
var style = window.getComputedStyle(row[0]);
|
|
2559
|
+
|
|
2560
|
+
return row[0].clientWidth - parseFloat(style.paddingLeft) - parseFloat(style.paddingRight);
|
|
2561
|
+
}
|
|
2562
|
+
|
|
1765
2563
|
function currentOffset(col) {
|
|
1766
2564
|
return getEffectiveOffset(col, leadingTier()) || 0;
|
|
1767
2565
|
}
|
|
@@ -1781,11 +2579,11 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1781
2579
|
}
|
|
1782
2580
|
|
|
1783
2581
|
function smallest(values) {
|
|
1784
|
-
return values.reduce(function(a, b) { return Math.min(a, b); }, MAX_COL_SIZE);
|
|
2582
|
+
return values.filter(isUnits).reduce(function(a, b) { return Math.min(a, b); }, MAX_COL_SIZE);
|
|
1785
2583
|
}
|
|
1786
2584
|
|
|
1787
2585
|
function largest(values) {
|
|
1788
|
-
return values.reduce(function(a, b) { return Math.max(a, b); }, 0);
|
|
2586
|
+
return values.filter(isUnits).reduce(function(a, b) { return Math.max(a, b); }, 0);
|
|
1789
2587
|
}
|
|
1790
2588
|
|
|
1791
2589
|
/**
|
|
@@ -1803,7 +2601,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1803
2601
|
* itself left inside the row.
|
|
1804
2602
|
*/
|
|
1805
2603
|
function deepestFor(col) {
|
|
1806
|
-
var room = spare(col.parent(), leadingTier(), col) -
|
|
2604
|
+
var room = spare(col.parent(), leadingTier(), col) - currentUnits(col);
|
|
1807
2605
|
|
|
1808
2606
|
return Math.min(largest(settings.valid_col_offsets), Math.max(room, 0));
|
|
1809
2607
|
}
|
|
@@ -1846,6 +2644,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1846
2644
|
function isEditorClass(name) {
|
|
1847
2645
|
if (name === 'row' || name === 'column') { return true; }
|
|
1848
2646
|
if (/^(ge-|ui-)/.test(name)) { return true; }
|
|
2647
|
+
if (/^col(-(sm|md|lg|xl|xxl))?(-auto)?$/.test(name)) { return true; }
|
|
1849
2648
|
|
|
1850
2649
|
return BREAKPOINTS.some(function(tier) {
|
|
1851
2650
|
return new RegExp('^(' + tier.colPrefix + '|' + tier.offsetPrefix + ')\\d+$').test(name);
|
|
@@ -1873,6 +2672,13 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1873
2672
|
details.toggle();
|
|
1874
2673
|
});
|
|
1875
2674
|
|
|
2675
|
+
// Beside the gear, because every node that has one is a node a
|
|
2676
|
+
// utility may apply to, whichever plugin built its drawer
|
|
2677
|
+
var kind = kindOf(node);
|
|
2678
|
+
$.each(UTILITIES, function(name, utility) {
|
|
2679
|
+
if (utility.drawerTools) { utility.drawerTools(drawer, node, kind); }
|
|
2680
|
+
});
|
|
2681
|
+
|
|
1876
2682
|
return details.appendTo(drawer);
|
|
1877
2683
|
}
|
|
1878
2684
|
|
|
@@ -1901,6 +2707,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1901
2707
|
.appendTo(detailsDiv)
|
|
1902
2708
|
.on('change', function() {
|
|
1903
2709
|
setHostClasses(container, this.value);
|
|
2710
|
+
refreshUtilities(container);
|
|
1904
2711
|
})
|
|
1905
2712
|
;
|
|
1906
2713
|
|
|
@@ -1913,11 +2720,15 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1913
2720
|
.on('click', function() {
|
|
1914
2721
|
btn.toggleClass('active btn-primary');
|
|
1915
2722
|
container.toggleClass(rowClass.cssClass, btn.hasClass('active'));
|
|
2723
|
+
refreshUtilities(container);
|
|
1916
2724
|
})
|
|
1917
2725
|
.appendTo(classGroup)
|
|
1918
2726
|
;
|
|
1919
2727
|
});
|
|
1920
2728
|
|
|
2729
|
+
var utilities = createUtilitiesSection(container);
|
|
2730
|
+
if (utilities) { utilities.appendTo(detailsDiv); }
|
|
2731
|
+
|
|
1921
2732
|
return detailsDiv;
|
|
1922
2733
|
}
|
|
1923
2734
|
|
|
@@ -1932,15 +2743,137 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1932
2743
|
* column that had none.
|
|
1933
2744
|
*/
|
|
1934
2745
|
function addAllColClasses() {
|
|
1935
|
-
canvas.find('.column, div[class*="col-"]').each(function() {
|
|
2746
|
+
canvas.find('.column, div[class*="col-"], div.col').each(function() {
|
|
1936
2747
|
var col = $(this).addClass('column');
|
|
1937
2748
|
|
|
1938
|
-
|
|
2749
|
+
// A column in a row with row-cols is sized by its row
|
|
2750
|
+
if (sizedTiers(col).length || hasRowCols(col.parent())) { return; }
|
|
1939
2751
|
|
|
1940
2752
|
setSize(col, BREAKPOINTS[0], MAX_COL_SIZE);
|
|
1941
2753
|
});
|
|
1942
2754
|
}
|
|
1943
2755
|
|
|
2756
|
+
/* --------------------------------------------------------------
|
|
2757
|
+
* Columns per row: row-cols-*.
|
|
2758
|
+
*
|
|
2759
|
+
* A row's row-cols class gives every child an equal share of a
|
|
2760
|
+
* line, and a column's own size class takes that share's place.
|
|
2761
|
+
* Which of the two wins at a breakpoint is settled the way
|
|
2762
|
+
* Bootstrap's css settles it: the class from the wider breakpoint,
|
|
2763
|
+
* and at one breakpoint col loses to row-cols, which loses to
|
|
2764
|
+
* col-auto and col-N - the order Bootstrap writes their rules in.
|
|
2765
|
+
* -------------------------------------------------------------- */
|
|
2766
|
+
|
|
2767
|
+
function hasRowCols(row) {
|
|
2768
|
+
return row.hasClass('row') && BREAKPOINTS.some(function(tier) {
|
|
2769
|
+
return ROW_COLS_VALUES.some(function(value) { return row.hasClass(rowColsClass(tier, value)); });
|
|
2770
|
+
});
|
|
2771
|
+
}
|
|
2772
|
+
|
|
2773
|
+
/** The row-cols class that applies to a row at a tier, as { tier, value }, or null. */
|
|
2774
|
+
function rowColsSource(row, tier) {
|
|
2775
|
+
if (!row.hasClass('row')) { return null; }
|
|
2776
|
+
|
|
2777
|
+
for (var i = BREAKPOINTS.indexOf(tier); i >= 0; i--) {
|
|
2778
|
+
for (var v = 0; v < ROW_COLS_VALUES.length; v++) {
|
|
2779
|
+
if (row.hasClass(rowColsClass(BREAKPOINTS[i], ROW_COLS_VALUES[v]))) {
|
|
2780
|
+
return { tier: i, value: ROW_COLS_VALUES[v] };
|
|
2781
|
+
}
|
|
2782
|
+
}
|
|
2783
|
+
}
|
|
2784
|
+
|
|
2785
|
+
return null;
|
|
2786
|
+
}
|
|
2787
|
+
|
|
2788
|
+
/** The column's own size that applies at a tier, as { tier, size }, or null. */
|
|
2789
|
+
function columnSource(col, tier) {
|
|
2790
|
+
for (var i = BREAKPOINTS.indexOf(tier); i >= 0; i--) {
|
|
2791
|
+
var size = getSize(col, BREAKPOINTS[i]);
|
|
2792
|
+
if (size !== null) { return { tier: i, size: size }; }
|
|
2793
|
+
}
|
|
2794
|
+
|
|
2795
|
+
return null;
|
|
2796
|
+
}
|
|
2797
|
+
|
|
2798
|
+
/**
|
|
2799
|
+
* The row-cols class that sizes this column at a tier, or null when
|
|
2800
|
+
* the column's own size does.
|
|
2801
|
+
*/
|
|
2802
|
+
function rowColsWinner(col, tier) {
|
|
2803
|
+
var row = rowColsSource(col.parent(), tier);
|
|
2804
|
+
if (!row) { return null; }
|
|
2805
|
+
|
|
2806
|
+
var own = columnSource(col, tier);
|
|
2807
|
+
if (!own || row.tier > own.tier) { return row; }
|
|
2808
|
+
|
|
2809
|
+
return row.tier === own.tier && own.size === 'equal' ? row : null;
|
|
2810
|
+
}
|
|
2811
|
+
|
|
2812
|
+
/** A column's share of a line when its row sizes it, in units: auto has no number. */
|
|
2813
|
+
function rowColsUnits(winner) {
|
|
2814
|
+
return winner.value === 'auto' ? 0 : MAX_COL_SIZE / parseInt(winner.value, 10);
|
|
2815
|
+
}
|
|
2816
|
+
|
|
2817
|
+
/**
|
|
2818
|
+
* What a breakpoint view shows of the row-cols a column is under. The
|
|
2819
|
+
* view narrows the canvas, not the window, and Bootstrap's
|
|
2820
|
+
* .row-cols-md-3 > * answers to the window - and to nothing the
|
|
2821
|
+
* preview can put on the row, since it styles the row's children.
|
|
2822
|
+
*/
|
|
2823
|
+
function rowColsPreview(col, tier) {
|
|
2824
|
+
var winner = rowColsWinner(col, tier);
|
|
2825
|
+
|
|
2826
|
+
if (winner) {
|
|
2827
|
+
return {
|
|
2828
|
+
flex: '0 0 auto',
|
|
2829
|
+
width: winner.value === 'auto' ? 'auto' : (100 / parseInt(winner.value, 10)) + '%',
|
|
2830
|
+
'max-width': '100%',
|
|
2831
|
+
};
|
|
2832
|
+
}
|
|
2833
|
+
|
|
2834
|
+
// A wider breakpoint's row-cols is live in a wide window; a
|
|
2835
|
+
// column with no size of its own here is a full width one
|
|
2836
|
+
if (hasRowCols(col.parent()) && !columnSource(col, tier)) {
|
|
2837
|
+
return { flex: '0 0 auto', width: '100%', 'max-width': '100%' };
|
|
2838
|
+
}
|
|
2839
|
+
|
|
2840
|
+
return {};
|
|
2841
|
+
}
|
|
2842
|
+
|
|
2843
|
+
/**
|
|
2844
|
+
* The badge on a row whose columns are sized by row-cols in the view,
|
|
2845
|
+
* and in the all view by its row-cols with no breakpoint.
|
|
2846
|
+
*/
|
|
2847
|
+
function markRowCols(scope) {
|
|
2848
|
+
scope.find('.row').addBack('.row').each(function() {
|
|
2849
|
+
var row = $(this);
|
|
2850
|
+
var source = settings.row_cols === false ? null : (curView === ALL_VIEW
|
|
2851
|
+
? (rowColsSource(row, BREAKPOINTS[0]) || null)
|
|
2852
|
+
: rowColsSource(row, breakpoint(curView)));
|
|
2853
|
+
|
|
2854
|
+
if (!source) {
|
|
2855
|
+
row.removeAttr('data-ge-row-cols');
|
|
2856
|
+
} else {
|
|
2857
|
+
row.attr('data-ge-row-cols', rowColsLabel(source.value));
|
|
2858
|
+
}
|
|
2859
|
+
});
|
|
2860
|
+
}
|
|
2861
|
+
|
|
2862
|
+
function rowColsLabel(value) {
|
|
2863
|
+
return value === 'auto' ? t('badge.row_cols_auto') : t('badge.row_cols', { count: value });
|
|
2864
|
+
}
|
|
2865
|
+
|
|
2866
|
+
/** The core's family for the field in a row's panel. */
|
|
2867
|
+
function rowColsFamily() {
|
|
2868
|
+
return {
|
|
2869
|
+
name: 'row-cols',
|
|
2870
|
+
prefix: 'row-cols',
|
|
2871
|
+
values: ROW_COLS_VALUES,
|
|
2872
|
+
appliesTo: ['row'],
|
|
2873
|
+
labelKey: 'utility.row_cols',
|
|
2874
|
+
};
|
|
2875
|
+
}
|
|
2876
|
+
|
|
1944
2877
|
/* --------------------------------------------------------------
|
|
1945
2878
|
* The sizing core.
|
|
1946
2879
|
*
|
|
@@ -1950,9 +2883,18 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1950
2883
|
* names, the 12 unit budget and what an absent class means.
|
|
1951
2884
|
* -------------------------------------------------------------- */
|
|
1952
2885
|
|
|
1953
|
-
/**
|
|
2886
|
+
/**
|
|
2887
|
+
* A column's size at one tier: a number of units, `equal`, `auto`, or
|
|
2888
|
+
* null when that tier says nothing.
|
|
2889
|
+
*/
|
|
1954
2890
|
function getSize(col, tier) {
|
|
1955
|
-
|
|
2891
|
+
var units = readUnits(col, tier.colPrefix);
|
|
2892
|
+
if (units !== null) { return units; }
|
|
2893
|
+
|
|
2894
|
+
if (col.hasClass(sizeClass(tier, 'auto'))) { return 'auto'; }
|
|
2895
|
+
if (col.hasClass(sizeClass(tier, 'equal'))) { return 'equal'; }
|
|
2896
|
+
|
|
2897
|
+
return null;
|
|
1956
2898
|
}
|
|
1957
2899
|
|
|
1958
2900
|
/** The units a column is indented by at one tier, or null. */
|
|
@@ -2002,8 +2944,12 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2002
2944
|
col.attr('class', classes.join(' '));
|
|
2003
2945
|
}
|
|
2004
2946
|
|
|
2005
|
-
|
|
2006
|
-
|
|
2947
|
+
/** One size per tier: writing a size takes whatever size the tier had off. */
|
|
2948
|
+
function setSize(col, tier, size) {
|
|
2949
|
+
writeUnits(col, tier.colPrefix, null);
|
|
2950
|
+
FLEX_SIZES.forEach(function(flex) { col.removeClass(sizeClass(tier, flex)); });
|
|
2951
|
+
|
|
2952
|
+
if (size !== null && size !== undefined) { col.addClass(sizeClass(tier, size)); }
|
|
2007
2953
|
}
|
|
2008
2954
|
|
|
2009
2955
|
/** An offset of 0 is written as no class at all, which is what it means. */
|
|
@@ -2029,7 +2975,11 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2029
2975
|
var sibling = $(this);
|
|
2030
2976
|
if (ignore && sibling[0] === ignore[0]) { return; }
|
|
2031
2977
|
|
|
2032
|
-
|
|
2978
|
+
// An equal or auto column takes what is left, so it uses none;
|
|
2979
|
+
// one its row sizes uses its share of the line
|
|
2980
|
+
var winner = rowColsWinner(sibling, tier);
|
|
2981
|
+
var size = winner ? rowColsUnits(winner) : getEffectiveSize(sibling, tier);
|
|
2982
|
+
used += (isUnits(size) ? size : 0) + (getEffectiveOffset(sibling, tier) || 0);
|
|
2033
2983
|
});
|
|
2034
2984
|
|
|
2035
2985
|
return MAX_COL_SIZE - used;
|
|
@@ -2050,7 +3000,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2050
3000
|
|
|
2051
3001
|
offset = Math.min(Math.max(offset, 0), MAX_COL_OFFSET);
|
|
2052
3002
|
|
|
2053
|
-
if (size === null) { return { size:
|
|
3003
|
+
if (size === null || !isUnits(size)) { return { size: size, offset: offset, refused: false }; }
|
|
2054
3004
|
|
|
2055
3005
|
size = Math.min(Math.max(size, 1), MAX_COL_SIZE);
|
|
2056
3006
|
|
|
@@ -2094,6 +3044,35 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2094
3044
|
return 'ge-' + name + '-' + instanceId;
|
|
2095
3045
|
}
|
|
2096
3046
|
|
|
3047
|
+
/**
|
|
3048
|
+
* What the canvas, the columns and the plugins' regions move: rows,
|
|
3049
|
+
* content areas and containers, and whatever blocks a feature adds -
|
|
3050
|
+
* the sections plugin's sections.
|
|
3051
|
+
*/
|
|
3052
|
+
function blockSelector() {
|
|
3053
|
+
return ['.row', '.ge-content', '[data-ge-container]'].concat(pluginHooks('blocks')).join(', ');
|
|
3054
|
+
}
|
|
3055
|
+
|
|
3056
|
+
/** A selector every feature plugin may contribute to, under one name. */
|
|
3057
|
+
function pluginHooks(name) {
|
|
3058
|
+
return $.map(FEATURES, function(feature) { return feature[name] || null; });
|
|
3059
|
+
}
|
|
3060
|
+
|
|
3061
|
+
/**
|
|
3062
|
+
* Whether a region will have a block dropped in it. Every block goes
|
|
3063
|
+
* everywhere unless a feature says otherwise: a section only on the
|
|
3064
|
+
* canvas, and only rows in a section.
|
|
3065
|
+
*/
|
|
3066
|
+
function acceptsBlock(region, node) {
|
|
3067
|
+
var accepted = true;
|
|
3068
|
+
|
|
3069
|
+
$.each(FEATURES, function(name, feature) {
|
|
3070
|
+
if (accepted && feature.accepts && feature.accepts(region, node) === false) { accepted = false; }
|
|
3071
|
+
});
|
|
3072
|
+
|
|
3073
|
+
return accepted;
|
|
3074
|
+
}
|
|
3075
|
+
|
|
2097
3076
|
/** What must never start a drag, whatever the handle is. */
|
|
2098
3077
|
function dragCancelSelector() {
|
|
2099
3078
|
return settings.drag_handle === 'drawer'
|
|
@@ -2131,6 +3110,16 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2131
3110
|
? { name: groupName(options.group) }
|
|
2132
3111
|
: { name: groupName(options.draggable + '-' + (++soloGroup)), pull: false, put: false };
|
|
2133
3112
|
|
|
3113
|
+
// A list may turn some of its group's items away. A put
|
|
3114
|
+
// function replaces SortableJS's own test that the item comes
|
|
3115
|
+
// from the same group, so it makes that test too: without it
|
|
3116
|
+
// a column would drop into the canvas, or into another editor
|
|
3117
|
+
if (options.group && options.accepts) {
|
|
3118
|
+
group.put = function(to, from, dragged) {
|
|
3119
|
+
return from.options.group.name === group.name && options.accepts($(to.el), $(dragged));
|
|
3120
|
+
};
|
|
3121
|
+
}
|
|
3122
|
+
|
|
2134
3123
|
sortables.push(window.Sortable.create(list, $.extend({
|
|
2135
3124
|
group: group,
|
|
2136
3125
|
draggable: options.draggable,
|
|
@@ -2195,9 +3184,24 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2195
3184
|
});
|
|
2196
3185
|
|
|
2197
3186
|
sortable(canvas.add(canvas.find('.column')), {
|
|
2198
|
-
draggable:
|
|
3187
|
+
draggable: blockSelector(),
|
|
2199
3188
|
group: 'block',
|
|
2200
|
-
|
|
3189
|
+
accepts: acceptsBlock,
|
|
3190
|
+
});
|
|
3191
|
+
|
|
3192
|
+
// A plugin's region can be a block itself - a section is both -
|
|
3193
|
+
// and SortableJS tests a list's own element against the draggable
|
|
3194
|
+
// selector when it counts the list's children, so every child of
|
|
3195
|
+
// a section would count as a block, its drawer first. '>' is
|
|
3196
|
+
// SortableJS for "direct children only", which the list is not.
|
|
3197
|
+
var regions = pluginHooks('regions');
|
|
3198
|
+
if (regions.length) {
|
|
3199
|
+
sortable(canvas.find(regions.join(', ')), {
|
|
3200
|
+
draggable: '>' + blockSelector(),
|
|
3201
|
+
group: 'block',
|
|
3202
|
+
accepts: acceptsBlock,
|
|
3203
|
+
});
|
|
3204
|
+
}
|
|
2201
3205
|
|
|
2202
3206
|
// A plugin makes its own: only it knows which of its parts move
|
|
2203
3207
|
plugins('onSortable', sortable);
|
|
@@ -2369,20 +3373,21 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2369
3373
|
* divider between two columns looks like it should do.
|
|
2370
3374
|
*/
|
|
2371
3375
|
function snapUnits(col, pixels) {
|
|
2372
|
-
var
|
|
2373
|
-
var style = window.getComputedStyle(row[0]);
|
|
2374
|
-
var content = row[0].clientWidth -
|
|
2375
|
-
parseFloat(style.paddingLeft) - parseFloat(style.paddingRight);
|
|
2376
|
-
|
|
2377
|
-
var units = Math.round(pixels / content * MAX_COL_SIZE);
|
|
3376
|
+
var units = Math.round(pixels / rowContentWidth(col.parent()) * MAX_COL_SIZE);
|
|
2378
3377
|
var next = balanceSibling(col);
|
|
3378
|
+
var nextSize = next ? currentSize(next) : null;
|
|
3379
|
+
|
|
3380
|
+
// Measured when the drag started: the column is the width of the
|
|
3381
|
+
// pointer now, which is not what it was
|
|
3382
|
+
var own = col.data('ge-resize-units');
|
|
2379
3383
|
|
|
2380
3384
|
// With a sibling to balance against, the drag may take that
|
|
2381
3385
|
// column's units but not its last one. Without one, the row is
|
|
2382
3386
|
// allowed to wrap - that is what balance false means - so the only
|
|
2383
3387
|
// limit is the column's own budget against its indent.
|
|
2384
|
-
|
|
2385
|
-
|
|
3388
|
+
// An equal or auto sibling gives way by itself, so it sets no limit
|
|
3389
|
+
var room = next && isUnits(nextSize)
|
|
3390
|
+
? own + nextSize - smallest(settings.valid_col_sizes)
|
|
2386
3391
|
: MAX_COL_SIZE - currentOffset(col);
|
|
2387
3392
|
|
|
2388
3393
|
return Math.min(
|
|
@@ -2428,7 +3433,8 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2428
3433
|
if (!allowed) { return false; }
|
|
2429
3434
|
|
|
2430
3435
|
col.data('ge-resize-from', from);
|
|
2431
|
-
|
|
3436
|
+
col.data('ge-resize-units', currentUnits(col));
|
|
3437
|
+
resizeReadout(col, sizeLabel(col.data('ge-resize-units')));
|
|
2432
3438
|
|
|
2433
3439
|
return true;
|
|
2434
3440
|
}
|
|
@@ -2439,30 +3445,33 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2439
3445
|
|
|
2440
3446
|
function resizeStop(col, width) {
|
|
2441
3447
|
var from = col.data('ge-resize-from');
|
|
3448
|
+
var started = col.data('ge-resize-units');
|
|
2442
3449
|
var units = snapUnits(col, width);
|
|
2443
3450
|
|
|
2444
|
-
col.removeData('ge-resize-from');
|
|
3451
|
+
col.removeData('ge-resize-from').removeData('ge-resize-units');
|
|
2445
3452
|
resizeReadout(col, '');
|
|
2446
3453
|
stripPixelWidths(col);
|
|
2447
3454
|
|
|
2448
3455
|
if (from === undefined) { return; }
|
|
2449
3456
|
|
|
2450
3457
|
// A drag of a couple of pixels lands on the size it started from,
|
|
2451
|
-
// and is not a resize
|
|
2452
|
-
|
|
3458
|
+
// and is not a resize - not even of an equal or auto column,
|
|
3459
|
+
// which it would otherwise turn into a number
|
|
3460
|
+
if (units === started) { return; }
|
|
2453
3461
|
|
|
2454
3462
|
var plan = planSize(col, units);
|
|
2455
3463
|
if (!plan) { return; }
|
|
2456
3464
|
|
|
2457
3465
|
operate(function() {
|
|
2458
3466
|
writeSize(col, plan);
|
|
2459
|
-
balanceAfterResize(col,
|
|
3467
|
+
balanceAfterResize(col, units - started);
|
|
3468
|
+
refreshUtilities(col);
|
|
2460
3469
|
|
|
2461
|
-
emit('after-resize', payloadFor('column', col, {
|
|
3470
|
+
emit('after-resize', payloadFor('column', col, withCleared({
|
|
2462
3471
|
source: 'dragdrop',
|
|
2463
3472
|
from: from,
|
|
2464
3473
|
to: plan.size,
|
|
2465
|
-
}));
|
|
3474
|
+
}, plan.cleared)));
|
|
2466
3475
|
});
|
|
2467
3476
|
}
|
|
2468
3477
|
|
|
@@ -2470,12 +3479,22 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2470
3479
|
* Move the delta into the following column, so a full row stays full.
|
|
2471
3480
|
* It is part of the same gesture, so it is not announced separately.
|
|
2472
3481
|
*/
|
|
3482
|
+
/**
|
|
3483
|
+
* The column after takes what the resized one gave or took, unless it
|
|
3484
|
+
* is equal or auto: those take what is left by themselves.
|
|
3485
|
+
*/
|
|
2473
3486
|
function balanceAfterResize(col, delta) {
|
|
2474
3487
|
var next = balanceSibling(col);
|
|
2475
3488
|
if (!next || !delta) { return; }
|
|
2476
3489
|
|
|
2477
|
-
var
|
|
2478
|
-
if (
|
|
3490
|
+
var size = currentSize(next);
|
|
3491
|
+
if (!isUnits(size)) { return; }
|
|
3492
|
+
|
|
3493
|
+
var plan = planSize(next, size - delta);
|
|
3494
|
+
if (plan) {
|
|
3495
|
+
writeSize(next, plan);
|
|
3496
|
+
refreshUtilities(next);
|
|
3497
|
+
}
|
|
2479
3498
|
}
|
|
2480
3499
|
|
|
2481
3500
|
/**
|
|
@@ -2542,13 +3561,15 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2542
3561
|
function apiCreateRow(layout, options) {
|
|
2543
3562
|
var row = createRow();
|
|
2544
3563
|
|
|
2545
|
-
if (layout !== undefined && !Array.isArray(layout)) {
|
|
2546
|
-
warn('createRow: the layout is an array of column sizes, as in [8, 4]
|
|
2547
|
-
'Making an empty row instead.');
|
|
3564
|
+
if (layout !== undefined && !Array.isArray(layout) && !(layout && layout.row_cols)) {
|
|
3565
|
+
warn('createRow: the layout is an array of column sizes, as in [8, 4], or ' +
|
|
3566
|
+
'{ row_cols, columns }. Making an empty row instead.');
|
|
2548
3567
|
layout = [];
|
|
2549
3568
|
}
|
|
2550
3569
|
|
|
2551
|
-
(layout
|
|
3570
|
+
if (layout && !Array.isArray(layout)) { row = rowFromLayoutValue(layout); }
|
|
3571
|
+
|
|
3572
|
+
(Array.isArray(layout) ? layout : []).forEach(function(size) {
|
|
2552
3573
|
createColumn(size).appendTo(row);
|
|
2553
3574
|
});
|
|
2554
3575
|
|
|
@@ -2561,7 +3582,12 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2561
3582
|
function apiCreateColumn(size, options) {
|
|
2562
3583
|
options = options || {};
|
|
2563
3584
|
|
|
2564
|
-
|
|
3585
|
+
// Going into a row with row-cols, no size is a size: the row's share
|
|
3586
|
+
var into = $(options.appendTo || options.prependTo || []).first();
|
|
3587
|
+
|
|
3588
|
+
if (size === undefined && into.length && rowColsSource(into, leadingTier())) {
|
|
3589
|
+
size = null;
|
|
3590
|
+
} else if (!isUnits(size) && FLEX_SIZES.indexOf(size) === -1) {
|
|
2565
3591
|
warn('createColumn: no column size given, using ' + MAX_COL_SIZE);
|
|
2566
3592
|
size = MAX_COL_SIZE;
|
|
2567
3593
|
}
|
|
@@ -2632,8 +3658,8 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2632
3658
|
}
|
|
2633
3659
|
|
|
2634
3660
|
/**
|
|
2635
|
-
* A column sized for the current view:
|
|
2636
|
-
* all view. `offset` indents it, within the same 12 unit budget.
|
|
3661
|
+
* A column sized for the current view: its tier, or the base class in
|
|
3662
|
+
* the all view. `offset` indents it, within the same 12 unit budget.
|
|
2637
3663
|
*/
|
|
2638
3664
|
function createColumn(size, offset) {
|
|
2639
3665
|
var rte = getRTE(settings.content_types[0]);
|
|
@@ -2641,12 +3667,12 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2641
3667
|
.append(createDefaultContentWrapper().html(rte ? rte.initialContent : ''))
|
|
2642
3668
|
;
|
|
2643
3669
|
|
|
2644
|
-
|
|
2645
|
-
|
|
3670
|
+
// The tier being edited, or the base class in the all view
|
|
3671
|
+
var tier = tiersFor(curView)[0];
|
|
3672
|
+
var wanted = clamp({ size: size, offset: offset || 0, leading: 'offset' });
|
|
2646
3673
|
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
});
|
|
3674
|
+
setSize(column, tier, wanted.size === null ? size : wanted.size);
|
|
3675
|
+
setOffset(column, tier, wanted.offset);
|
|
2650
3676
|
|
|
2651
3677
|
return column;
|
|
2652
3678
|
}
|
|
@@ -2758,7 +3784,15 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2758
3784
|
return;
|
|
2759
3785
|
}
|
|
2760
3786
|
|
|
3787
|
+
var from = curView;
|
|
2761
3788
|
switchLayout(key);
|
|
3789
|
+
|
|
3790
|
+
if (key === from) { return; }
|
|
3791
|
+
|
|
3792
|
+
canvas.find('.ge-utilities').each(function() { renderUtilities($(this)); });
|
|
3793
|
+
refreshPreviews(canvas);
|
|
3794
|
+
plugins('onViewChange', key);
|
|
3795
|
+
emit('view-change', { canvas: canvas, breakpoint: key, from: from, to: key });
|
|
2762
3796
|
}
|
|
2763
3797
|
|
|
2764
3798
|
function getView() {
|
|
@@ -2796,12 +3830,23 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2796
3830
|
|
|
2797
3831
|
return featureMethods.createElement(content, options);
|
|
2798
3832
|
},
|
|
3833
|
+
createSection: function(options) {
|
|
3834
|
+
if (!featureMethods.createSection) {
|
|
3835
|
+
warnOnceHere('plugin:sections', 'createSection needs the sections plugin: ' +
|
|
3836
|
+
'include dist/plugins/grideditor.sections.js after the editor');
|
|
3837
|
+
return null;
|
|
3838
|
+
}
|
|
3839
|
+
|
|
3840
|
+
return featureMethods.createSection(options);
|
|
3841
|
+
},
|
|
2799
3842
|
createContainer: apiCreateContainer,
|
|
2800
3843
|
addTab: function(container, options) { return addPaneTo(container, 'tabs', options); },
|
|
2801
3844
|
addAccordionItem: function(container, options) {
|
|
2802
3845
|
return addPaneTo(container, 'accordion', options);
|
|
2803
3846
|
},
|
|
2804
3847
|
setLocale: setLocale,
|
|
3848
|
+
getUtility: getUtility,
|
|
3849
|
+
setUtility: setUtility,
|
|
2805
3850
|
canvas: canvas,
|
|
2806
3851
|
settings: settingsCopy(),
|
|
2807
3852
|
};
|
|
@@ -2853,6 +3898,18 @@ $.fn.gridEditor.containers = {};
|
|
|
2853
3898
|
*/
|
|
2854
3899
|
$.fn.gridEditor.features = {};
|
|
2855
3900
|
|
|
3901
|
+
/**
|
|
3902
|
+
* Utility plugins: Bootstrap's responsive utility classes - order-md-2,
|
|
3903
|
+
* d-lg-none - edited per breakpoint. A plugin declares families of classes
|
|
3904
|
+
* and the editor reads them, writes them, puts them in the settings panel and
|
|
3905
|
+
* previews them in each view. Same factory, same `plugins` setting.
|
|
3906
|
+
*
|
|
3907
|
+
* $.fn.gridEditor.utilities.order = function(ge) {
|
|
3908
|
+
* return { families: [{ name: 'order', prefix: 'order', values: [...] }] };
|
|
3909
|
+
* };
|
|
3910
|
+
*/
|
|
3911
|
+
$.fn.gridEditor.utilities = {};
|
|
3912
|
+
|
|
2856
3913
|
/** Translator for the editor integrations, which get settings and no instance. */
|
|
2857
3914
|
$.fn.gridEditor.t = translate;
|
|
2858
3915
|
|
|
@@ -2874,6 +3931,8 @@ $.fn.gridEditor.locales = {
|
|
|
2874
3931
|
'tool.add_row': 'Add row',
|
|
2875
3932
|
'tool.add_column': 'Add column\n(hold to choose the width)',
|
|
2876
3933
|
'tool.column_size': '{size} of 12',
|
|
3934
|
+
'tool.column_equal': 'Equal: shares what the row has left',
|
|
3935
|
+
'tool.column_auto': 'Auto: as wide as its content',
|
|
2877
3936
|
'tool.delete_row': 'Remove row',
|
|
2878
3937
|
'tool.delete_column': 'Remove col',
|
|
2879
3938
|
'tool.delete_container': 'Remove container',
|
|
@@ -2891,6 +3950,7 @@ $.fn.gridEditor.locales = {
|
|
|
2891
3950
|
'tool.classes_title': 'Css classes, separated by spaces',
|
|
2892
3951
|
'tool.toggle_class': 'Toggle "{label}" styling',
|
|
2893
3952
|
'row.add': 'Add row {layout}',
|
|
3953
|
+
'row.add_row_cols': 'Add a row of {columns} columns, {counts} per row',
|
|
2894
3954
|
'confirm.title': 'Confirm',
|
|
2895
3955
|
'confirm.ok': 'Delete',
|
|
2896
3956
|
'confirm.cancel': 'Cancel',
|
|
@@ -2904,6 +3964,17 @@ $.fn.gridEditor.locales = {
|
|
|
2904
3964
|
'view.lg': 'Desktop',
|
|
2905
3965
|
'view.xl': 'Large desktop',
|
|
2906
3966
|
'view.xxl': 'Widescreen',
|
|
3967
|
+
'utility.section': 'Responsive: {view}',
|
|
3968
|
+
'utility.col_width': 'Width',
|
|
3969
|
+
'utility.col_equal': 'Equal',
|
|
3970
|
+
'utility.col_auto': 'Auto',
|
|
3971
|
+
'utility.col_from_row': 'From the row: {count}',
|
|
3972
|
+
'utility.row_cols': 'Columns per row',
|
|
3973
|
+
'badge.row_cols': '{count} per row',
|
|
3974
|
+
'badge.row_cols_auto': 'As wide as their content',
|
|
3975
|
+
'utility.default': 'Default',
|
|
3976
|
+
'utility.inherit': 'Inherit: {value} (from {breakpoint})',
|
|
3977
|
+
'utility.varies': 'Changes at {breakpoints}; choosing here replaces that',
|
|
2907
3978
|
'error.sortable_missing': 'SortableJS not available! Make sure you loaded the Sortable js file; dragging is off without it.',
|
|
2908
3979
|
'warning.setting_removed': 'The {setting} setting was removed in 4.0. Use {replacement} instead.',
|
|
2909
3980
|
'error.tinymce_missing': 'tinyMCE not available! Make sure you loaded the tinyMCE js file.',
|