@themarioga/grid-editor 6.0.0-beta.1 → 6.0.0-beta.3
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 +15 -0
- package/dist/grideditor.css +166 -100
- 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 +400 -23
- package/dist/jquery.grideditor.min.js +1 -1
- package/dist/jquery.grideditor.min.js.map +1 -1
- package/dist/locales/grideditor.es.js +13 -1
- package/dist/locales/grideditor.es.min.js +1 -1
- package/dist/locales/grideditor.es.min.js.map +1 -1
- package/dist/plugins/grideditor.sections.js +2 -2
- package/dist/plugins/grideditor.sections.min.js +1 -1
- package/dist/plugins/grideditor.sections.min.js.map +1 -1
- package/dist/plugins/grideditor.spacing.js +1 -1
- package/dist/plugins/grideditor.spacing.min.js +1 -1
- package/dist/plugins/grideditor.spacing.min.js.map +1 -1
- package/docs/locale-keys.md +24 -1
- package/docs/plugins.md +2 -1
- package/example/utilities.html +30 -7
- package/package.json +1 -1
- package/src/js/jquery.grideditor.js +400 -23
- package/src/js/locales/grideditor.es.js +13 -1
- package/src/js/plugins/grideditor.sections.js +2 -2
- package/src/js/plugins/grideditor.spacing.js +1 -1
- package/src/less/grideditor.less +186 -112
|
@@ -363,6 +363,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
363
363
|
'locale_strings' : {}, // Overrides for individual keys
|
|
364
364
|
'callbacks' : {}, // before_*/after_* functions, the events by another route
|
|
365
365
|
'confirm_delete' : true, // Ask before deleting a row or a column
|
|
366
|
+
'settings_panel' : 'offcanvas', // Where a node's settings open: 'offcanvas', 'popover', 'modal' or 'inline'
|
|
366
367
|
'drag' : NESTED_SETTINGS.drag // How a drag behaves, whatever drives it
|
|
367
368
|
}, optionsOrMethod);
|
|
368
369
|
|
|
@@ -426,6 +427,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
426
427
|
handle.settings = settingsCopy();
|
|
427
428
|
|
|
428
429
|
removeConfirmModal();
|
|
430
|
+
removeSettingsPanels();
|
|
429
431
|
mainControls.remove();
|
|
430
432
|
createMainControls();
|
|
431
433
|
reset();
|
|
@@ -1491,6 +1493,12 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1491
1493
|
}
|
|
1492
1494
|
|
|
1493
1495
|
function init() {
|
|
1496
|
+
// The node whose settings were open is gone - deleted, say - and
|
|
1497
|
+
// its panel with it
|
|
1498
|
+
if (openSettingsState && !$.contains(document.documentElement, openSettingsState.node[0])) {
|
|
1499
|
+
closeSettings();
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1494
1502
|
runFilter(true);
|
|
1495
1503
|
canvas.addClass('ge-editing');
|
|
1496
1504
|
canvas.toggleClass('ge-drag-drawer', settings.drag_handle === 'drawer');
|
|
@@ -1510,6 +1518,8 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1510
1518
|
}
|
|
1511
1519
|
|
|
1512
1520
|
function deinit() {
|
|
1521
|
+
// Its panel goes home to its drawer first, and both go together
|
|
1522
|
+
closeSettings();
|
|
1513
1523
|
canvas.removeClass('ge-editing ge-drag-drawer ge-dropping');
|
|
1514
1524
|
canvas.find('.ge-content').each(function() {
|
|
1515
1525
|
closeText($(this));
|
|
@@ -1562,6 +1572,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1562
1572
|
function destroy() {
|
|
1563
1573
|
deinit();
|
|
1564
1574
|
removeConfirmModal();
|
|
1575
|
+
removeSettingsPanels();
|
|
1565
1576
|
mainControls.remove();
|
|
1566
1577
|
htmlTextArea.remove();
|
|
1567
1578
|
$(window).off('scroll', onScroll);
|
|
@@ -1720,6 +1731,8 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1720
1731
|
// A text editor has rewritten a content area, so whatever the
|
|
1721
1732
|
// editor and its plugins had put in there goes back in
|
|
1722
1733
|
textReady: function(block) { block.trigger('ge-rte-ready'); },
|
|
1734
|
+
// A node's settings panel, in its drawer or open outside it
|
|
1735
|
+
detailsOf: detailsOf,
|
|
1723
1736
|
// A node's drawer: its first child, or for a content area the
|
|
1724
1737
|
// one beside it in its text block
|
|
1725
1738
|
drawerOf: function(node) {
|
|
@@ -2010,9 +2023,9 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2010
2023
|
|
|
2011
2024
|
/** Bring a node's panel and preview up to date with its classes. */
|
|
2012
2025
|
function refreshUtilities(node) {
|
|
2013
|
-
var details = node
|
|
2026
|
+
var details = detailsOf(node);
|
|
2014
2027
|
|
|
2015
|
-
details.
|
|
2028
|
+
details.find('.ge-classes').val(hostClasses(node).join(' '));
|
|
2016
2029
|
details.children('.ge-utilities').each(function() { renderUtilities($(this)); });
|
|
2017
2030
|
refreshPreviews(node);
|
|
2018
2031
|
}
|
|
@@ -2140,7 +2153,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2140
2153
|
.appendTo(section)
|
|
2141
2154
|
.on('click', function() {
|
|
2142
2155
|
utilitiesOpen = !section.hasClass('ge-open');
|
|
2143
|
-
|
|
2156
|
+
settingsScope().find('.ge-utilities').toggleClass('ge-open', utilitiesOpen);
|
|
2144
2157
|
})
|
|
2145
2158
|
;
|
|
2146
2159
|
|
|
@@ -2164,7 +2177,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2164
2177
|
.text(family.labelKey ? t(family.labelKey) : family.name)
|
|
2165
2178
|
.appendTo(field)
|
|
2166
2179
|
;
|
|
2167
|
-
$('<select />')
|
|
2180
|
+
$('<select class="form-select form-select-sm" />')
|
|
2168
2181
|
.appendTo(field)
|
|
2169
2182
|
.on('change', function() {
|
|
2170
2183
|
setUtility(node, family.name, this.value, { source: 'panel' });
|
|
@@ -2897,9 +2910,10 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2897
2910
|
*/
|
|
2898
2911
|
function addSettingsTool(drawer, node, presets) {
|
|
2899
2912
|
var details = createDetails(node, presets || []);
|
|
2913
|
+
node.data('ge-details', details);
|
|
2900
2914
|
|
|
2901
2915
|
createTool(drawer, t('tool.settings'), 'ge-settings', 'bi bi-gear-fill', function() {
|
|
2902
|
-
details
|
|
2916
|
+
toggleSettings(node, details, $(this));
|
|
2903
2917
|
});
|
|
2904
2918
|
|
|
2905
2919
|
// Beside the gear, because every node that has one is a node a
|
|
@@ -2919,12 +2933,18 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2919
2933
|
|
|
2920
2934
|
function createDetails(container, cssClasses) {
|
|
2921
2935
|
var detailsDiv = $('<div class="ge-details" />');
|
|
2936
|
+
var general = $('<div class="ge-details-general" />').appendTo(detailsDiv);
|
|
2937
|
+
var field = function(label) {
|
|
2938
|
+
return $('<label class="ge-field" />')
|
|
2939
|
+
.append($('<span class="ge-field-label" />').text(label))
|
|
2940
|
+
.appendTo(general);
|
|
2941
|
+
};
|
|
2922
2942
|
|
|
2923
|
-
$('<input class="ge-id" />')
|
|
2943
|
+
$('<input class="ge-id form-control form-control-sm" />')
|
|
2924
2944
|
.attr('placeholder', t('tool.id_placeholder'))
|
|
2925
2945
|
.val(container.attr('id'))
|
|
2926
2946
|
.attr('title', t('tool.id_title'))
|
|
2927
|
-
.appendTo(
|
|
2947
|
+
.appendTo(field(t('panel.id')))
|
|
2928
2948
|
.on('change', function() {
|
|
2929
2949
|
// An empty field means no id, not an empty one
|
|
2930
2950
|
if (this.value === '') {
|
|
@@ -2935,25 +2955,28 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2935
2955
|
})
|
|
2936
2956
|
;
|
|
2937
2957
|
|
|
2938
|
-
$('<input class="ge-classes" />')
|
|
2958
|
+
$('<input class="ge-classes form-control form-control-sm" />')
|
|
2939
2959
|
.attr('placeholder', t('tool.classes_placeholder'))
|
|
2940
2960
|
.attr('title', t('tool.classes_title'))
|
|
2941
2961
|
.val(hostClasses(container).join(' '))
|
|
2942
|
-
.appendTo(
|
|
2962
|
+
.appendTo(field(t('panel.classes')))
|
|
2943
2963
|
.on('change', function() {
|
|
2944
2964
|
setHostClasses(container, this.value);
|
|
2945
2965
|
refreshUtilities(container);
|
|
2946
2966
|
})
|
|
2947
2967
|
;
|
|
2948
2968
|
|
|
2949
|
-
|
|
2969
|
+
// Bootstrap 5's outline buttons, filled while their class is on;
|
|
2970
|
+
// up to 5.x they carried Bootstrap 3's btn-default, which 5 lacks
|
|
2971
|
+
var classGroup = $('<div class="btn-group btn-group-sm ge-presets" role="group" />').appendTo(general);
|
|
2950
2972
|
cssClasses.forEach(function(rowClass) {
|
|
2951
|
-
var btn = $('<a class="btn btn-sm btn-
|
|
2973
|
+
var btn = $('<a role="button" class="btn btn-sm btn-outline-secondary" />')
|
|
2952
2974
|
.html(rowClass.label)
|
|
2953
2975
|
.attr('title', rowClass.title ? rowClass.title : t('tool.toggle_class', { label: rowClass.label }))
|
|
2954
|
-
.toggleClass('active
|
|
2976
|
+
.toggleClass('active', container.hasClass(rowClass.cssClass))
|
|
2977
|
+
.attr('aria-pressed', container.hasClass(rowClass.cssClass) ? 'true' : 'false')
|
|
2955
2978
|
.on('click', function() {
|
|
2956
|
-
btn.toggleClass('active btn
|
|
2979
|
+
btn.toggleClass('active').attr('aria-pressed', btn.hasClass('active') ? 'true' : 'false');
|
|
2957
2980
|
container.toggleClass(rowClass.cssClass, btn.hasClass('active'));
|
|
2958
2981
|
refreshUtilities(container);
|
|
2959
2982
|
})
|
|
@@ -2967,6 +2990,330 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2967
2990
|
return detailsDiv;
|
|
2968
2991
|
}
|
|
2969
2992
|
|
|
2993
|
+
/* --------------------------------------------------------------
|
|
2994
|
+
* Where a node's settings open.
|
|
2995
|
+
*
|
|
2996
|
+
* Each node's panel - createDetails' fields, and what plugins add to
|
|
2997
|
+
* them - is built into its drawer, as ever. settings_panel says where
|
|
2998
|
+
* it shows: 'inline' unfolds it in the drawer; 'offcanvas', 'popover'
|
|
2999
|
+
* and 'modal' move it into one of Bootstrap's, outside the canvas,
|
|
3000
|
+
* while it is open, and back into its drawer when it closes, so
|
|
3001
|
+
* everything that finds a panel through its node keeps working.
|
|
3002
|
+
*
|
|
3003
|
+
* Bootstrap's markup and styles, but the editor's own opening and
|
|
3004
|
+
* placing: a page may load Bootstrap's javascript without Popper, or
|
|
3005
|
+
* not at all. The modal is Bootstrap's own when Bootstrap is there.
|
|
3006
|
+
* -------------------------------------------------------------- */
|
|
3007
|
+
|
|
3008
|
+
var PANEL_MODES = ['offcanvas', 'popover', 'modal', 'inline'];
|
|
3009
|
+
var settingsPanels = {}; // One of each, built on first use, outside the canvas
|
|
3010
|
+
var openSettingsState = null; // What is open: { mode, node, details, home, next, gear }
|
|
3011
|
+
|
|
3012
|
+
function panelMode() {
|
|
3013
|
+
if (PANEL_MODES.indexOf(settings.settings_panel) !== -1) { return settings.settings_panel; }
|
|
3014
|
+
|
|
3015
|
+
warnOnceHere('settings_panel', 'settings_panel "' + settings.settings_panel + '" is not one of ' +
|
|
3016
|
+
PANEL_MODES.join(', ') + ': using offcanvas');
|
|
3017
|
+
return 'offcanvas';
|
|
3018
|
+
}
|
|
3019
|
+
|
|
3020
|
+
/** A node's panel, wherever it is at the moment. */
|
|
3021
|
+
function detailsOf(node) {
|
|
3022
|
+
var details = node.data('ge-details');
|
|
3023
|
+
return details && details.length ? details : $();
|
|
3024
|
+
}
|
|
3025
|
+
|
|
3026
|
+
/** Where settings fields are found: the canvas, and the panel open outside it. */
|
|
3027
|
+
function settingsScope() {
|
|
3028
|
+
return openSettingsState && openSettingsState.mode !== 'inline'
|
|
3029
|
+
? canvas.add(openSettingsState.details)
|
|
3030
|
+
: canvas;
|
|
3031
|
+
}
|
|
3032
|
+
|
|
3033
|
+
/** The gear: its node's panel, opened or closed; another node's closes first. */
|
|
3034
|
+
function toggleSettings(node, details, gear) {
|
|
3035
|
+
var mode = panelMode();
|
|
3036
|
+
|
|
3037
|
+
if (mode === 'inline') {
|
|
3038
|
+
details.toggle();
|
|
3039
|
+
return;
|
|
3040
|
+
}
|
|
3041
|
+
|
|
3042
|
+
var same = openSettingsState && openSettingsState.details[0] === details[0];
|
|
3043
|
+
closeSettings();
|
|
3044
|
+
if (!same) { openSettings(mode, node, details, gear); }
|
|
3045
|
+
}
|
|
3046
|
+
|
|
3047
|
+
function openSettings(mode, node, details, gear) {
|
|
3048
|
+
var panel = settingsPanel(mode);
|
|
3049
|
+
|
|
3050
|
+
openSettingsState = {
|
|
3051
|
+
mode: mode,
|
|
3052
|
+
node: node,
|
|
3053
|
+
details: details,
|
|
3054
|
+
home: details.parent(),
|
|
3055
|
+
next: details.next(),
|
|
3056
|
+
gear: gear,
|
|
3057
|
+
};
|
|
3058
|
+
|
|
3059
|
+
panel.find('.ge-settings-title').text(t('panel.title', { kind: kindLabel(node) }));
|
|
3060
|
+
details.appendTo(panel.find('.ge-settings-body')).show();
|
|
3061
|
+
node.addClass('ge-settings-target');
|
|
3062
|
+
|
|
3063
|
+
var ns = '.ge-settings-' + instanceId;
|
|
3064
|
+
$(document).on('keydown' + ns, function(e) {
|
|
3065
|
+
if (e.key === 'Escape') { closeSettings(); }
|
|
3066
|
+
});
|
|
3067
|
+
|
|
3068
|
+
if (mode === 'offcanvas') {
|
|
3069
|
+
// From the bottom on a phone, from the side anywhere wider
|
|
3070
|
+
var narrow = window.innerWidth < 576;
|
|
3071
|
+
panel.toggleClass('offcanvas-end', !narrow).toggleClass('offcanvas-bottom', narrow);
|
|
3072
|
+
panel.removeClass('hiding');
|
|
3073
|
+
panel[0].getBoundingClientRect(); // So the slide in is a transition, not a jump
|
|
3074
|
+
panel.addClass('show');
|
|
3075
|
+
} else if (mode === 'popover') {
|
|
3076
|
+
placePopover(panel, gear);
|
|
3077
|
+
$(window).on('scroll' + ns + ' resize' + ns, function() { placePopover(panel, gear); });
|
|
3078
|
+
|
|
3079
|
+
// And again whenever what is in it changes size: the
|
|
3080
|
+
// Responsive section unfolding, a plugin swapping a field
|
|
3081
|
+
if (window.ResizeObserver) {
|
|
3082
|
+
openSettingsState.observer = new window.ResizeObserver(function() { placePopover(panel, gear); });
|
|
3083
|
+
openSettingsState.observer.observe(details[0]);
|
|
3084
|
+
}
|
|
3085
|
+
// A press anywhere else puts it away, as a popover does
|
|
3086
|
+
$(document).on('mousedown' + ns + ' touchstart' + ns, function(e) {
|
|
3087
|
+
if (!$(e.target).closest(panel).length && !$(e.target).closest(gear).length) { closeSettings(); }
|
|
3088
|
+
});
|
|
3089
|
+
} else {
|
|
3090
|
+
showModal(panel);
|
|
3091
|
+
}
|
|
3092
|
+
}
|
|
3093
|
+
|
|
3094
|
+
/** Put what is open away, and its panel back in its drawer. */
|
|
3095
|
+
function closeSettings() {
|
|
3096
|
+
var open = openSettingsState;
|
|
3097
|
+
if (!open) { return; }
|
|
3098
|
+
openSettingsState = null;
|
|
3099
|
+
|
|
3100
|
+
var panel = settingsPanels[open.mode];
|
|
3101
|
+
$(document).off('.ge-settings-' + instanceId);
|
|
3102
|
+
$(window).off('.ge-settings-' + instanceId);
|
|
3103
|
+
if (open.observer) { open.observer.disconnect(); }
|
|
3104
|
+
|
|
3105
|
+
if (open.mode === 'offcanvas') {
|
|
3106
|
+
panel.removeClass('show').addClass('hiding');
|
|
3107
|
+
window.setTimeout(function() { panel.removeClass('hiding'); }, 300);
|
|
3108
|
+
} else if (open.mode === 'popover') {
|
|
3109
|
+
panel.hide();
|
|
3110
|
+
} else {
|
|
3111
|
+
hideModal(panel);
|
|
3112
|
+
}
|
|
3113
|
+
|
|
3114
|
+
open.node.removeClass('ge-settings-target');
|
|
3115
|
+
open.details.css('display', '');
|
|
3116
|
+
|
|
3117
|
+
// Where it was in its drawer, if the drawer is still there: a
|
|
3118
|
+
// node deleted while its settings were open took it with it
|
|
3119
|
+
if (open.home.length && $.contains(document.documentElement, open.home[0])) {
|
|
3120
|
+
if (open.next.length && open.next.parent()[0] === open.home[0]) {
|
|
3121
|
+
open.details.insertBefore(open.next);
|
|
3122
|
+
} else {
|
|
3123
|
+
open.details.appendTo(open.home);
|
|
3124
|
+
}
|
|
3125
|
+
} else {
|
|
3126
|
+
open.details.remove();
|
|
3127
|
+
}
|
|
3128
|
+
}
|
|
3129
|
+
|
|
3130
|
+
/** Built once per mode, on first use, and taken away by destroy() or setLocale(). */
|
|
3131
|
+
function settingsPanel(mode) {
|
|
3132
|
+
if (settingsPanels[mode]) { return settingsPanels[mode]; }
|
|
3133
|
+
|
|
3134
|
+
var closeButton = function(extra) {
|
|
3135
|
+
return $('<button type="button" class="btn-close ge-settings-close" />')
|
|
3136
|
+
.addClass(extra || '')
|
|
3137
|
+
.attr('aria-label', t('panel.close'));
|
|
3138
|
+
};
|
|
3139
|
+
var panel;
|
|
3140
|
+
|
|
3141
|
+
if (mode === 'offcanvas') {
|
|
3142
|
+
panel = $('<div class="offcanvas offcanvas-end ge-settings-panel ge-settings-offcanvas" tabindex="-1" role="dialog" />')
|
|
3143
|
+
.append($('<div class="offcanvas-header" />')
|
|
3144
|
+
.append('<h5 class="offcanvas-title ge-settings-title"></h5>')
|
|
3145
|
+
.append(closeButton()))
|
|
3146
|
+
.append('<div class="offcanvas-body ge-settings-body"></div>');
|
|
3147
|
+
} else if (mode === 'popover') {
|
|
3148
|
+
panel = $('<div class="popover bs-popover-bottom ge-settings-panel ge-settings-popover" role="dialog" />')
|
|
3149
|
+
.append('<div class="popover-arrow"></div>')
|
|
3150
|
+
.append($('<div class="popover-header" />')
|
|
3151
|
+
.append('<span class="ge-settings-title"></span>')
|
|
3152
|
+
.append(closeButton()))
|
|
3153
|
+
.append('<div class="popover-body ge-settings-body"></div>')
|
|
3154
|
+
.hide();
|
|
3155
|
+
} else {
|
|
3156
|
+
panel = $('<div class="modal fade ge-settings-panel ge-settings-modal" tabindex="-1" role="dialog" aria-hidden="true" />')
|
|
3157
|
+
.append($('<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable" />')
|
|
3158
|
+
.append($('<div class="modal-content" />')
|
|
3159
|
+
.append($('<div class="modal-header" />')
|
|
3160
|
+
.append('<h5 class="modal-title ge-settings-title"></h5>')
|
|
3161
|
+
.append(closeButton()))
|
|
3162
|
+
.append('<div class="modal-body ge-settings-body"></div>')
|
|
3163
|
+
.append($('<div class="modal-footer" />')
|
|
3164
|
+
.append($('<button type="button" class="btn btn-primary ge-settings-close" />')
|
|
3165
|
+
.text(t('panel.done'))))));
|
|
3166
|
+
}
|
|
3167
|
+
|
|
3168
|
+
panel.on('click', '.ge-settings-close', function(e) {
|
|
3169
|
+
e.preventDefault();
|
|
3170
|
+
closeSettings();
|
|
3171
|
+
});
|
|
3172
|
+
|
|
3173
|
+
settingsPanels[mode] = panel.appendTo('body');
|
|
3174
|
+
return panel;
|
|
3175
|
+
}
|
|
3176
|
+
|
|
3177
|
+
function removeSettingsPanels() {
|
|
3178
|
+
closeSettings();
|
|
3179
|
+
|
|
3180
|
+
$.each(settingsPanels, function(mode, panel) {
|
|
3181
|
+
if (mode === 'modal' && window.bootstrap && window.bootstrap.Modal) {
|
|
3182
|
+
var instance = window.bootstrap.Modal.getInstance(panel[0]);
|
|
3183
|
+
if (instance) { instance.dispose(); }
|
|
3184
|
+
}
|
|
3185
|
+
panel.remove();
|
|
3186
|
+
});
|
|
3187
|
+
if (settingsBackdrop) { settingsBackdrop.remove(); settingsBackdrop = null; }
|
|
3188
|
+
settingsPanels = {};
|
|
3189
|
+
}
|
|
3190
|
+
|
|
3191
|
+
/**
|
|
3192
|
+
* Under the gear, or over it when there is more room there, inside
|
|
3193
|
+
* the window, and no taller than the room it has: past that its body
|
|
3194
|
+
* scrolls.
|
|
3195
|
+
*/
|
|
3196
|
+
function placePopover(panel, gear) {
|
|
3197
|
+
if (!$.contains(document.documentElement, gear[0])) { return; }
|
|
3198
|
+
|
|
3199
|
+
panel.show();
|
|
3200
|
+
|
|
3201
|
+
var tool = gear[0].getBoundingClientRect();
|
|
3202
|
+
var gap = 8;
|
|
3203
|
+
var body = panel.children('.popover-body').css('max-height', '');
|
|
3204
|
+
var width = panel.outerWidth();
|
|
3205
|
+
var height = panel.outerHeight();
|
|
3206
|
+
var roomBelow = window.innerHeight - tool.bottom - 2 * gap;
|
|
3207
|
+
var roomAbove = tool.top - 2 * gap;
|
|
3208
|
+
var below = height <= roomBelow || roomBelow >= roomAbove;
|
|
3209
|
+
var room = below ? roomBelow : roomAbove;
|
|
3210
|
+
|
|
3211
|
+
if (height > room) {
|
|
3212
|
+
body.css('max-height', Math.max(120, room - (height - body.outerHeight())));
|
|
3213
|
+
height = panel.outerHeight();
|
|
3214
|
+
}
|
|
3215
|
+
|
|
3216
|
+
var top = below ? tool.bottom + gap : tool.top - gap - height;
|
|
3217
|
+
var left = Math.max(gap, Math.min(tool.left + tool.width / 2 - 24, window.innerWidth - width - gap));
|
|
3218
|
+
|
|
3219
|
+
panel
|
|
3220
|
+
.toggleClass('bs-popover-bottom', below)
|
|
3221
|
+
.toggleClass('bs-popover-top', !below)
|
|
3222
|
+
.css({ top: top + window.pageYOffset, left: left + window.pageXOffset })
|
|
3223
|
+
;
|
|
3224
|
+
|
|
3225
|
+
// The arrow points at the gear, wherever the popover had to go
|
|
3226
|
+
panel.children('.popover-arrow').css('left',
|
|
3227
|
+
Math.max(gap, Math.min(tool.left + tool.width / 2 - left - 8, width - 24)));
|
|
3228
|
+
}
|
|
3229
|
+
|
|
3230
|
+
var settingsBackdrop = null; // The modal's backdrop, when Bootstrap's javascript is not there to make one
|
|
3231
|
+
|
|
3232
|
+
/**
|
|
3233
|
+
* Bootstrap's modal ignores a hide while it is still fading in, and a
|
|
3234
|
+
* show while it is fading out, so the editor asks for what it wants
|
|
3235
|
+
* and has it done once the running transition ends: a panel closed as
|
|
3236
|
+
* it opens - getHtml, a second click - would otherwise stay open, and
|
|
3237
|
+
* empty. A hide Bootstrap starts itself, from Escape or the backdrop,
|
|
3238
|
+
* closes the settings as the close button does.
|
|
3239
|
+
*/
|
|
3240
|
+
function showModal(panel) {
|
|
3241
|
+
if (!window.bootstrap || !window.bootstrap.Modal) {
|
|
3242
|
+
settingsBackdrop = $('<div class="modal-backdrop fade show ge-settings-backdrop" />')
|
|
3243
|
+
.appendTo('body')
|
|
3244
|
+
.on('click', function() { closeSettings(); });
|
|
3245
|
+
panel.addClass('show').css('display', 'block').removeAttr('aria-hidden');
|
|
3246
|
+
return;
|
|
3247
|
+
}
|
|
3248
|
+
|
|
3249
|
+
var modal = window.bootstrap.Modal.getOrCreateInstance(panel[0]);
|
|
3250
|
+
|
|
3251
|
+
if (!panel.data('ge-watched')) {
|
|
3252
|
+
panel.data('ge-watched', true);
|
|
3253
|
+
panel.on('hide.bs.modal', function() {
|
|
3254
|
+
// Not asked for: Escape or the backdrop
|
|
3255
|
+
if (panel.data('ge-wanted') === 'open') {
|
|
3256
|
+
panel.data('ge-wanted', 'closed');
|
|
3257
|
+
panel.data('ge-busy', true);
|
|
3258
|
+
}
|
|
3259
|
+
});
|
|
3260
|
+
panel.on('shown.bs.modal', function() {
|
|
3261
|
+
panel.data('ge-busy', false);
|
|
3262
|
+
if (panel.data('ge-wanted') === 'closed') { hideModal(panel); }
|
|
3263
|
+
});
|
|
3264
|
+
panel.on('hidden.bs.modal', function() {
|
|
3265
|
+
panel.data('ge-busy', false);
|
|
3266
|
+
if (panel.data('ge-wanted') === 'open') {
|
|
3267
|
+
showModal(panel);
|
|
3268
|
+
} else if (openSettingsState && openSettingsState.mode === 'modal') {
|
|
3269
|
+
closeSettings();
|
|
3270
|
+
}
|
|
3271
|
+
});
|
|
3272
|
+
}
|
|
3273
|
+
|
|
3274
|
+
panel.data('ge-wanted', 'open');
|
|
3275
|
+
if (!panel.data('ge-busy')) {
|
|
3276
|
+
panel.data('ge-busy', true);
|
|
3277
|
+
modal.show();
|
|
3278
|
+
}
|
|
3279
|
+
}
|
|
3280
|
+
|
|
3281
|
+
function hideModal(panel) {
|
|
3282
|
+
if (!window.bootstrap || !window.bootstrap.Modal) {
|
|
3283
|
+
if (settingsBackdrop) { settingsBackdrop.remove(); settingsBackdrop = null; }
|
|
3284
|
+
panel.removeClass('show').css('display', '').attr('aria-hidden', 'true');
|
|
3285
|
+
return;
|
|
3286
|
+
}
|
|
3287
|
+
|
|
3288
|
+
panel.data('ge-wanted', 'closed');
|
|
3289
|
+
|
|
3290
|
+
// Already out of sight - Bootstrap hid it itself, and this is its
|
|
3291
|
+
// hidden event closing the settings - and nothing is under way:
|
|
3292
|
+
// a hide now would be ignored, and no event would come to say so
|
|
3293
|
+
if (!panel.data('ge-busy') && panel.hasClass('show')) {
|
|
3294
|
+
panel.data('ge-busy', true);
|
|
3295
|
+
window.bootstrap.Modal.getOrCreateInstance(panel[0]).hide();
|
|
3296
|
+
}
|
|
3297
|
+
}
|
|
3298
|
+
|
|
3299
|
+
/** What a panel's title calls a node. */
|
|
3300
|
+
function kindLabel(node) {
|
|
3301
|
+
var kind = kindOf(node);
|
|
3302
|
+
|
|
3303
|
+
if (CONTAINERS[kind] && CONTAINERS[kind].labelKey) { return t(CONTAINERS[kind].labelKey); }
|
|
3304
|
+
|
|
3305
|
+
switch (kind) {
|
|
3306
|
+
case 'row': return t('panel.kind_row');
|
|
3307
|
+
case 'column': return t('panel.kind_column');
|
|
3308
|
+
case 'text': return t('panel.kind_text');
|
|
3309
|
+
case 'element': return t('panel.kind_element');
|
|
3310
|
+
case 'section': return t('panel.kind_section');
|
|
3311
|
+
case 'tab': return t('panel.kind_tab');
|
|
3312
|
+
case 'accordion-item': return t('panel.kind_accordion_item');
|
|
3313
|
+
default: return kind;
|
|
3314
|
+
}
|
|
3315
|
+
}
|
|
3316
|
+
|
|
2970
3317
|
/**
|
|
2971
3318
|
* Make sure every column is marked as one, and that a column with no
|
|
2972
3319
|
* sizing at all gets some.
|
|
@@ -3316,7 +3663,11 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
3316
3663
|
// With the whole drawer as the handle, the tools inside it are
|
|
3317
3664
|
// still tools: a drag starting on one would swallow its click,
|
|
3318
3665
|
// and the settings panel has fields to type in
|
|
3319
|
-
|
|
3666
|
+
// - the info tools excepted, which do nothing when clicked:
|
|
3667
|
+
// a text's drawer is small and made of little but tools, and
|
|
3668
|
+
// with those turned away as well there was nothing to hold
|
|
3669
|
+
? '.ge-tools-drawer > a:not(.ge-text-info):not(.ge-element-info), .ge-details, ' +
|
|
3670
|
+
'input, textarea, button, select, option'
|
|
3320
3671
|
: 'input, textarea, button, select, option';
|
|
3321
3672
|
}
|
|
3322
3673
|
|
|
@@ -3384,7 +3735,14 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
3384
3735
|
// synthetic events, so the tests could not exist without
|
|
3385
3736
|
// this; it also gives one helper across browsers
|
|
3386
3737
|
forceFallback: true,
|
|
3387
|
-
|
|
3738
|
+
|
|
3739
|
+
// The copy that follows the pointer is appended to the
|
|
3740
|
+
// list the drag started in rather than to the body: it is
|
|
3741
|
+
// positioned fixed either way, and inside the canvas it
|
|
3742
|
+
// looks like what it is a copy of. On the body none of the
|
|
3743
|
+
// editing styles reached it - its drawers came out
|
|
3744
|
+
// unstyled, their settings panels open.
|
|
3745
|
+
fallbackOnBody: false,
|
|
3388
3746
|
|
|
3389
3747
|
ghostClass: 'ge-drag-placeholder',
|
|
3390
3748
|
chosenClass: 'ge-drag-chosen',
|
|
@@ -4138,16 +4496,23 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
4138
4496
|
|
|
4139
4497
|
createMoveTool(drawer);
|
|
4140
4498
|
|
|
4141
|
-
//
|
|
4142
|
-
//
|
|
4143
|
-
if (type && TEXTS[type]) {
|
|
4144
|
-
createTool(drawer, t('tool.text_info', { editor: textLabel(type) }), 'ge-text-info', 'bi bi-fonts');
|
|
4145
|
-
} else if (type) {
|
|
4499
|
+
// That no editor can edit it: a type whose plugin is not loaded is
|
|
4500
|
+
// still a block to move and delete, but not to edit
|
|
4501
|
+
if (type && !TEXTS[type]) {
|
|
4146
4502
|
createTool(drawer, t('text.no_editor', { type: type }), 'ge-text-info ge-text-missing',
|
|
4147
4503
|
'bi bi-exclamation-triangle');
|
|
4148
4504
|
}
|
|
4149
4505
|
|
|
4150
|
-
addSettingsTool(drawer, block, settings.text_classes);
|
|
4506
|
+
var details = addSettingsTool(drawer, block, settings.text_classes);
|
|
4507
|
+
|
|
4508
|
+
// Which editor edits it, first in its settings: something to
|
|
4509
|
+
// know about it, not something to do with it
|
|
4510
|
+
if (type && TEXTS[type]) {
|
|
4511
|
+
$('<div class="ge-field ge-text-editor" />')
|
|
4512
|
+
.append($('<span class="ge-field-label" />').text(t('panel.editor')))
|
|
4513
|
+
.append($('<span class="ge-field-value" />').text(textLabel(type)))
|
|
4514
|
+
.prependTo(details.children('.ge-details-general'));
|
|
4515
|
+
}
|
|
4151
4516
|
|
|
4152
4517
|
settings.text_tools.forEach(function(hostTool) {
|
|
4153
4518
|
createTool(drawer, hostTool.title || '', hostTool.className || '',
|
|
@@ -4370,7 +4735,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
4370
4735
|
|
|
4371
4736
|
if (key === from) { return; }
|
|
4372
4737
|
|
|
4373
|
-
|
|
4738
|
+
settingsScope().find('.ge-utilities').each(function() { renderUtilities($(this)); });
|
|
4374
4739
|
refreshPreviews(canvas);
|
|
4375
4740
|
plugins('onViewChange', key);
|
|
4376
4741
|
emit('view-change', { canvas: canvas, breakpoint: key, from: from, to: key });
|
|
@@ -4536,7 +4901,6 @@ $.fn.gridEditor.locales = {
|
|
|
4536
4901
|
'tool.add_row': 'Add row',
|
|
4537
4902
|
'tool.add_text': 'Add text',
|
|
4538
4903
|
'tool.add_text_type': 'Add {editor} text',
|
|
4539
|
-
'tool.text_info': 'Text: {editor}',
|
|
4540
4904
|
'tool.delete_text': 'Remove text',
|
|
4541
4905
|
'text.add': 'Text',
|
|
4542
4906
|
'text.add_type': 'Text ({editor})',
|
|
@@ -4556,6 +4920,19 @@ $.fn.gridEditor.locales = {
|
|
|
4556
4920
|
'tool.indent_increase': 'Increase indent\n(hold shift for max)',
|
|
4557
4921
|
'tool.edit_source': 'Edit Source Code',
|
|
4558
4922
|
'tool.preview': 'Preview',
|
|
4923
|
+
'panel.title': '{kind} settings',
|
|
4924
|
+
'panel.close': 'Close',
|
|
4925
|
+
'panel.done': 'Done',
|
|
4926
|
+
'panel.id': 'Id',
|
|
4927
|
+
'panel.classes': 'Classes',
|
|
4928
|
+
'panel.editor': 'Editor',
|
|
4929
|
+
'panel.kind_row': 'Row',
|
|
4930
|
+
'panel.kind_column': 'Column',
|
|
4931
|
+
'panel.kind_text': 'Text',
|
|
4932
|
+
'panel.kind_element': 'Element',
|
|
4933
|
+
'panel.kind_section': 'Section',
|
|
4934
|
+
'panel.kind_tab': 'Tab',
|
|
4935
|
+
'panel.kind_accordion_item': 'Accordion item',
|
|
4559
4936
|
'tool.id_placeholder': 'id',
|
|
4560
4937
|
'tool.id_title': 'Set a unique identifier',
|
|
4561
4938
|
'tool.classes_placeholder': 'classes',
|