datepicker working

This commit is contained in:
Micha
2025-03-09 12:19:33 +01:00
parent 8d6ea5e084
commit fa96bccbf3
33 changed files with 37966 additions and 7024 deletions

View File

@@ -1,22 +1,22 @@
/**
Attaches stand-alone container with editable-form to HTML element. Element is used only for positioning, value is not stored anywhere.<br>
This method applied internally in <code>$().editable()</code>. You should subscribe on it's events (save / cancel) to get profit of it.<br>
Final realization can be different: bootstrap-popover, jqueryui-tooltip, poshytip, inline-div. It depends on which js file you include.<br>
Applied as jQuery method.
Attaches stand-alone container with editable-form to HTML element. Element is used only for positioning, value is not stored anywhere.<br>
This method applied internally in <code>$().editable()</code>. You should subscribe on it's events (save / cancel) to get profit of it.<br>
Final realization can be different: bootstrap-popover, jqueryui-tooltip, poshytip, inline-div. It depends on which js file you include.<br>
Applied as jQuery method.
@class editableContainer
@uses editableform
**/
@class editableContainer
@uses editableform
**/
(function ($) {
"use strict";
var Popup = function (element, options) {
this.init(element, options);
};
var Inline = function (element, options) {
this.init(element, options);
};
};
//methods
Popup.prototype = {
@@ -25,98 +25,104 @@ Applied as jQuery method.
innerCss: null, //tbd in child class
containerClass: 'editable-container editable-popup', //css class applied to container element
defaults: {}, //container itself defaults
init: function(element, options) {
this.$element = $(element);
//since 1.4.1 container do not use data-* directly as they already merged into options.
this.options = $.extend({}, $.fn.editableContainer.defaults, options);
this.options = $.extend({}, $.fn.editableContainer.defaults, options);
this.splitOptions();
//set scope of form callbacks to element
this.formOptions.scope = this.$element[0];
this.formOptions.scope = this.$element[0];
this.initContainer();
//flag to hide container, when saving value will finish
this.delayedHide = false;
//bind 'destroyed' listener to destroy container when element is removed from dom
this.$element.on('destroyed', $.proxy(function(){
this.destroy();
}, this));
}, this));
//attach document handler to close containers on click / escape
if(!$(document).data('editable-handlers-attached')) {
//close all on escape
$(document).on('keyup.editable', function (e) {
if (e.which === 27) {
$('.editable-open').editableContainer('hide');
//todo: return focus on element
$('.editable-open').editableContainer('hide', 'cancel');
//todo: return focus on element
}
});
//close containers when click outside
//close containers when click outside
//(mousedown could be better than click, it closes everything also on drag drop)
$(document).on('click.editable', function(e) {
var $target = $(e.target), i,
exclude_classes = ['.editable-container',
'.ui-datepicker-header',
'.datepicker', //in inline mode datepicker is rendered into body
'.modal-backdrop',
'.bootstrap-wysihtml5-insert-image-modal',
'.bootstrap-wysihtml5-insert-link-modal'
];
exclude_classes = ['.editable-container',
'.ui-datepicker-header',
'.datepicker', //in inline mode datepicker is rendered into body
'.modal-backdrop',
'.bootstrap-wysihtml5-insert-image-modal',
'.bootstrap-wysihtml5-insert-link-modal'
];
// select2 has extra body click in IE
// see: https://github.com/ivaynberg/select2/issues/1058
if ($('.select2-drop-mask').is(':visible')) {
return;
}
//check if element is detached. It occurs when clicking in bootstrap datepicker
if (!$.contains(document.documentElement, e.target)) {
return;
return;
}
//for some reason FF 20 generates extra event (click) in select2 widget with e.target = document
//we need to filter it via construction below. See https://github.com/vitalets/x-editable/issues/199
//Possibly related to http://stackoverflow.com/questions/10119793/why-does-firefox-react-differently-from-webkit-and-ie-to-click-event-on-selec
if($target.is(document)) {
return;
return;
}
//if click inside one of exclude classes --> no nothing
for(i=0; i<exclude_classes.length; i++) {
if($target.is(exclude_classes[i]) || $target.parents(exclude_classes[i]).length) {
return;
}
if($target.is(exclude_classes[i]) || $target.parents(exclude_classes[i]).length) {
return;
}
}
//close all open containers (except one - target)
Popup.prototype.closeOthers(e.target);
});
$(document).data('editable-handlers-attached', true);
}
}
},
//split options on containerOptions and formOptions
splitOptions: function() {
this.containerOptions = {};
this.formOptions = {};
if(!$.fn[this.containerName]) {
throw new Error(this.containerName + ' not found. Have you included corresponding js file?');
throw new Error(this.containerName + ' not found. Have you included corresponding js file?');
}
//keys defined in container defaults go to container, others go to form
for(var k in this.options) {
if(k in this.defaults) {
this.containerOptions[k] = this.options[k];
} else {
this.formOptions[k] = this.options[k];
}
if(k in this.defaults) {
this.containerOptions[k] = this.options[k];
} else {
this.formOptions[k] = this.options[k];
}
}
},
/*
Returns jquery object of container
@method tip()
*/
*/
tip: function() {
return this.container() ? this.container().$tip : null;
},
@@ -135,239 +141,239 @@ Applied as jQuery method.
return container;
},
/* call native method of underlying container, e.g. this.$element.popover('method') */
/* call native method of underlying container, e.g. this.$element.popover('method') */
call: function() {
this.$element[this.containerName].apply(this.$element, arguments);
},
this.$element[this.containerName].apply(this.$element, arguments);
},
initContainer: function(){
this.call(this.containerOptions);
},
renderForm: function() {
this.$form
.editableform(this.formOptions)
.on({
save: $.proxy(this.save, this), //click on submit button (value changed)
nochange: $.proxy(function(){ this.hide('nochange'); }, this), //click on submit button (value NOT changed)
cancel: $.proxy(function(){ this.hide('cancel'); }, this), //click on cancel button
show: $.proxy(function() {
if(this.delayedHide) {
this.hide(this.delayedHide.reason);
this.delayedHide = false;
} else {
this.setPosition();
}
}, this), //re-position container every time form is shown (occurs each time after loading state)
rendering: $.proxy(this.setPosition, this), //this allows to place container correctly when loading shown
resize: $.proxy(this.setPosition, this), //this allows to re-position container when form size is changed
rendered: $.proxy(function(){
/**
Fired when container is shown and form is rendered (for select will wait for loading dropdown options).
**Note:** Bootstrap popover has own `shown` event that now cannot be separated from x-editable's one.
The workaround is to check `arguments.length` that is always `2` for x-editable.
@event shown
@param {Object} event event object
@example
$('#username').on('shown', function(e, editable) {
editable.input.$input.val('overwriting value of input..');
});
**/
/*
TODO: added second param mainly to distinguish from bootstrap's shown event. It's a hotfix that will be solved in future versions via namespaced events.
*/
this.$element.triggerHandler('shown', $(this.options.scope).data('editable'));
}, this)
})
.editableform('render');
},
.editableform(this.formOptions)
.on({
save: $.proxy(this.save, this), //click on submit button (value changed)
nochange: $.proxy(function(){ this.hide('nochange'); }, this), //click on submit button (value NOT changed)
cancel: $.proxy(function(){ this.hide('cancel'); }, this), //click on cancel button
show: $.proxy(function() {
if(this.delayedHide) {
this.hide(this.delayedHide.reason);
this.delayedHide = false;
} else {
this.setPosition();
}
}, this), //re-position container every time form is shown (occurs each time after loading state)
rendering: $.proxy(this.setPosition, this), //this allows to place container correctly when loading shown
resize: $.proxy(this.setPosition, this), //this allows to re-position container when form size is changed
rendered: $.proxy(function(){
/**
Fired when container is shown and form is rendered (for select will wait for loading dropdown options).
**Note:** Bootstrap popover has own `shown` event that now cannot be separated from x-editable's one.
The workaround is to check `arguments.length` that is always `2` for x-editable.
@event shown
@param {Object} event event object
@example
$('#username').on('shown', function(e, editable) {
editable.input.$input.val('overwriting value of input..');
});
**/
/*
TODO: added second param mainly to distinguish from bootstrap's shown event. It's a hotfix that will be solved in future versions via namespaced events.
*/
this.$element.triggerHandler('shown', $(this.options.scope).data('editable'));
}, this)
})
.editableform('render');
},
/**
Shows container with form
@method show()
@param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
**/
/* Note: poshytip owerwrites this method totally! */
Shows container with form
@method show()
@param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
**/
/* Note: poshytip owerwrites this method totally! */
show: function (closeAll) {
this.$element.addClass('editable-open');
if(closeAll !== false) {
//close all open containers (except this)
this.closeOthers(this.$element[0]);
this.closeOthers(this.$element[0]);
}
//show container itself
this.innerShow();
this.tip().addClass(this.containerClass);
/*
Currently, form is re-rendered on every show.
Currently, form is re-rendered on every show.
The main reason is that we dont know, what will container do with content when closed:
remove(), detach() or just hide() - it depends on container.
Detaching form itself before hide and re-insert before show is good solution,
but visually it looks ugly --> container changes size before hide.
*/
//if form already exist - delete previous data
Detaching form itself before hide and re-insert before show is good solution,
but visually it looks ugly --> container changes size before hide.
*/
//if form already exist - delete previous data
if(this.$form) {
//todo: destroy prev data!
//this.$form.destroy();
this.$form.remove();
}
this.$form = $('<div>');
//insert form into container body
if(this.tip().is(this.innerCss)) {
//for inline container
this.tip().append(this.$form);
this.tip().append(this.$form);
} else {
this.tip().find(this.innerCss).append(this.$form);
}
}
//render form
this.renderForm();
},
/**
Hides container with form
@method hide()
@param {string} reason Reason caused hiding. Can be <code>save|cancel|onblur|nochange|undefined (=manual)</code>
**/
hide: function(reason) {
Hides container with form
@method hide()
@param {string} reason Reason caused hiding. Can be <code>save|cancel|onblur|nochange|undefined (=manual)</code>
**/
hide: function(reason) {
if(!this.tip() || !this.tip().is(':visible') || !this.$element.hasClass('editable-open')) {
return;
}
//if form is saving value, schedule hide
if(this.$form.data('editableform').isSaving) {
this.delayedHide = {reason: reason};
return;
return;
} else {
this.delayedHide = false;
}
this.$element.removeClass('editable-open');
this.$element.removeClass('editable-open');
this.innerHide();
/**
Fired when container was hidden. It occurs on both save or cancel.
**Note:** Bootstrap popover has own `hidden` event that now cannot be separated from x-editable's one.
The workaround is to check `arguments.length` that is always `2` for x-editable.
Fired when container was hidden. It occurs on both save or cancel.
**Note:** Bootstrap popover has own `hidden` event that now cannot be separated from x-editable's one.
The workaround is to check `arguments.length` that is always `2` for x-editable.
@event hidden
@param {object} event event object
@param {string} reason Reason caused hiding. Can be <code>save|cancel|onblur|nochange|manual</code>
@example
$('#username').on('hidden', function(e, reason) {
if(reason === 'save' || reason === 'cancel') {
//auto-open next editable
$(this).closest('tr').next().find('.editable').editable('show');
}
});
**/
this.$element.triggerHandler('hidden', reason || 'manual');
@event hidden
@param {object} event event object
@param {string} reason Reason caused hiding. Can be <code>save|cancel|onblur|nochange|manual</code>
@example
$('#username').on('hidden', function(e, reason) {
if(reason === 'save' || reason === 'cancel') {
//auto-open next editable
$(this).closest('tr').next().find('.editable').editable('show');
}
});
**/
this.$element.triggerHandler('hidden', reason || 'manual');
},
/* internal show method. To be overwritten in child classes */
innerShow: function () {
},
},
/* internal hide method. To be overwritten in child classes */
innerHide: function () {
},
/**
Toggles container visibility (show / hide)
@method toggle()
@param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
**/
Toggles container visibility (show / hide)
@method toggle()
@param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
**/
toggle: function(closeAll) {
if(this.container() && this.tip() && this.tip().is(':visible')) {
this.hide();
} else {
this.show(closeAll);
}
}
},
/*
Updates the position of container when content changed.
@method setPosition()
*/
*/
setPosition: function() {
//tbd in child class
},
save: function(e, params) {
/**
Fired when new value was submitted. You can use <code>$(this).data('editableContainer')</code> inside handler to access to editableContainer instance
@event save
@param {Object} event event object
@param {Object} params additional params
@param {mixed} params.newValue submitted value
@param {Object} params.response ajax response
@example
$('#username').on('save', function(e, params) {
//assuming server response: '{success: true}'
var pk = $(this).data('editableContainer').options.pk;
if(params.response && params.response.success) {
alert('value: ' + params.newValue + ' with pk: ' + pk + ' saved!');
} else {
alert('error!');
}
});
**/
/**
Fired when new value was submitted. You can use <code>$(this).data('editableContainer')</code> inside handler to access to editableContainer instance
@event save
@param {Object} event event object
@param {Object} params additional params
@param {mixed} params.newValue submitted value
@param {Object} params.response ajax response
@example
$('#username').on('save', function(e, params) {
//assuming server response: '{success: true}'
var pk = $(this).data('editableContainer').options.pk;
if(params.response && params.response.success) {
alert('value: ' + params.newValue + ' with pk: ' + pk + ' saved!');
} else {
alert('error!');
}
});
**/
this.$element.triggerHandler('save', params);
//hide must be after trigger, as saving value may require methods of plugin, applied to input
this.hide('save');
},
/**
Sets new option
@method option(key, value)
@param {string} key
@param {mixed} value
**/
Sets new option
@method option(key, value)
@param {string} key
@param {mixed} value
**/
option: function(key, value) {
this.options[key] = value;
if(key in this.containerOptions) {
this.containerOptions[key] = value;
this.setContainerOption(key, value);
this.setContainerOption(key, value);
} else {
this.formOptions[key] = value;
if(this.$form) {
this.$form.editableform('option', key, value);
this.$form.editableform('option', key, value);
}
}
},
setContainerOption: function(key, value) {
this.call('option', key, value);
},
/**
Destroys the container instance
@method destroy()
**/
Destroys the container instance
@method destroy()
**/
destroy: function() {
this.hide();
this.innerDestroy();
this.$element.off('destroyed');
this.$element.removeData('editableContainer');
},
/* to be overwritten in child classes */
innerDestroy: function() {
},
},
/*
Closes other containers except one related to passed element.
Closes other containers except one related to passed element.
Other containers can be cancelled or submitted (depends on onblur option)
*/
closeOthers: function(element) {
@@ -377,14 +383,14 @@ Applied as jQuery method.
return;
}
//otherwise cancel or submit all open containers
//otherwise cancel or submit all open containers
var $el = $(el),
ec = $el.data('editableContainer');
ec = $el.data('editableContainer');
if(!ec) {
return;
return;
}
if(ec.options.onblur === 'cancel') {
$el.data('editableContainer').hide('onblur');
} else if(ec.options.onblur === 'submit') {
@@ -393,50 +399,50 @@ Applied as jQuery method.
});
},
/**
Activates input of visible container (e.g. set focus)
@method activate()
**/
Activates input of visible container (e.g. set focus)
@method activate()
**/
activate: function() {
if(this.tip && this.tip().is(':visible') && this.$form) {
this.$form.data('editableform').input.activate();
this.$form.data('editableform').input.activate();
}
}
}
};
/**
jQuery method to initialize editableContainer.
@method $().editableContainer(options)
@params {Object} options
@example
$('#edit').editableContainer({
type: 'text',
url: '/post',
pk: 1,
value: 'hello'
});
**/
jQuery method to initialize editableContainer.
@method $().editableContainer(options)
@params {Object} options
@example
$('#edit').editableContainer({
type: 'text',
url: '/post',
pk: 1,
value: 'hello'
});
**/
$.fn.editableContainer = function (option) {
var args = arguments;
return this.each(function () {
var $this = $(this),
dataKey = 'editableContainer',
data = $this.data(dataKey),
options = typeof option === 'object' && option,
Constructor = (options.mode === 'inline') ? Inline : Popup;
dataKey = 'editableContainer',
data = $this.data(dataKey),
options = typeof option === 'object' && option,
Constructor = (options.mode === 'inline') ? Inline : Popup;
if (!data) {
$this.data(dataKey, (data = new Constructor(this, options)));
}
if (typeof option === 'string') { //call method
if (typeof option === 'string') { //call method
data[option].apply(data, Array.prototype.slice.call(args, 1));
}
}
});
};
};
//store constructors
$.fn.editableContainer.Popup = Popup;
@@ -445,62 +451,62 @@ Applied as jQuery method.
//defaults
$.fn.editableContainer.defaults = {
/**
Initial value of form input
Initial value of form input
@property value
@type mixed
@default null
@private
**/
@property value
@type mixed
@default null
@private
**/
value: null,
/**
Placement of container relative to element. Can be <code>top|right|bottom|left</code>. Not used for inline container.
Placement of container relative to element. Can be <code>top|right|bottom|left</code>. Not used for inline container.
@property placement
@type string
@default 'top'
**/
@property placement
@type string
@default 'top'
**/
placement: 'top',
/**
Whether to hide container on save/cancel.
Whether to hide container on save/cancel.
@property autohide
@type boolean
@default true
@private
**/
@property autohide
@type boolean
@default true
@private
**/
autohide: true,
/**
Action when user clicks outside the container. Can be <code>cancel|submit|ignore</code>.
Setting <code>ignore</code> allows to have several containers open.
Action when user clicks outside the container. Can be <code>cancel|submit|ignore</code>.
Setting <code>ignore</code> allows to have several containers open.
@property onblur
@type string
@default 'cancel'
@since 1.1.1
**/
@property onblur
@type string
@default 'cancel'
@since 1.1.1
**/
onblur: 'cancel',
/**
Animation speed (inline mode only)
@property anim
@type string
@default false
**/
Animation speed (inline mode only)
@property anim
@type string
@default false
**/
anim: false,
/**
Mode of editable, can be `popup` or `inline`
@property mode
@type string
@default 'popup'
@since 1.4.0
**/
mode: 'popup'
Mode of editable, can be `popup` or `inline`
@property mode
@type string
@default 'popup'
@since 1.4.0
**/
mode: 'popup'
};
/*
/*
* workaround to have 'destroyed' event to destroy popover when element is destroyed
* see http://stackoverflow.com/questions/2200494/jquery-trigger-event-when-an-element-is-removed-from-the-dom
*/
@@ -510,6 +516,6 @@ Applied as jQuery method.
o.handler();
}
}
};
};
}(window.jQuery));
}(window.jQuery));

View File

@@ -1,123 +1,79 @@
/*
* Editable Popover (for Bootstrap 5, No jQuery)
/**
* Editable Popover for Bootstrap 5 based on Popper.js
* ---------------------
* requires bootstrap-popover.js
*/
import { Popover } from "bootstrap";
class EditablePopover {
constructor(element, options = {}) {
this.element = element;
this.options = Object.assign({
placement: "top",
trigger: "manual",
content: " ",
container: "body"
}, options);
(function ($) {
"use strict";
this.popoverInstance = null;
this.initPopover();
}
//extend methods
$.extend($.fn.editableContainer.Popup.prototype, {
containerName: 'popover',
containerDataName: 'bs.popover',
innerCss: '.popover-body',
defaults: Popover.Default,
initPopover() {
this.popoverInstance = new Popover(this.element, {
placement: this.options.placement,
trigger: this.options.trigger,
content: this.options.content,
html: true
});
initContainer: function(){
$.extend(this.containerOptions, {
trigger: 'manual',
selector: false,
content: ' ',
template: this.defaults.template
});
if (this.element.dataset.template) {
this.template = this.element.dataset.template;
this.element.removeAttribute("data-template");
//as template property is used in inputs, hide it from popover
var t;
if(this.$element.data('template')) {
t = this.$element.data('template');
this.$element.removeData('template');
}
this.call(this.containerOptions);
if(t) {
//restore data('template')
this.$element.data('template', t);
}
},
/* show */
innerShow: function () {
this.call('show');
},
/* hide */
innerHide: function () {
this.call('hide');
},
/* destroy */
innerDestroy: function() {
this.call('dispose');
},
setContainerOption: function(key, value) {
this.container().options[key] = value;
},
setPosition: function () {
(function() {}).call(this.container());
},
call: function() {
if ( ! $(this.$element).data(this.containerDataName)) {
$(this.$element).data(this.containerDataName,
Popover.getOrCreateInstance(this.$element, this.containerOptions)
);
}
return this.$element[this.containerName].apply(this.$element, arguments);
},
tip: function() {
return this.container() ? $(this.container().tip) : null;
}
});
this.show();
}
show() {
if (this.popoverInstance) {
this.popoverInstance.show();
}
}
hide() {
if (this.popoverInstance) {
this.popoverInstance.hide();
}
}
destroy() {
if (this.popoverInstance) {
this.popoverInstance.dispose();
this.popoverInstance = null;
}
}
setOption(key, value) {
if (this.popoverInstance) {
this.popoverInstance._config[key] = value;
}
}
setPosition() {
if (!this.popoverInstance) {
return;
}
const tip = this.element.nextElementSibling; // Popover element
if (!tip) {
return;
}
let placement = typeof this.options.placement === "function" ? this.options.placement(tip, this.element) : this.options.placement;
const autoToken = /\s?auto?\s?/i;
const autoPlace = autoToken.test(placement);
if (autoPlace) {
placement = placement.replace(autoToken, "") || "top";
}
const pos = this.element.getBoundingClientRect();
const actualWidth = tip.offsetWidth;
const actualHeight = tip.offsetHeight;
if (autoPlace) {
const parent = this.element.parentElement || document.body;
const docScroll = document.documentElement.scrollTop || document.body.scrollTop;
const parentWidth = parent === document.body ? window.innerWidth : parent.offsetWidth;
const parentHeight = parent === document.body ? window.innerHeight : parent.offsetHeight;
const parentLeft = parent === document.body ? 0 : parent.getBoundingClientRect().left;
placement = placement === "bottom" && pos.top + pos.height + actualHeight - docScroll > parentHeight ? "top" :
placement === "top" && pos.top - docScroll - actualHeight < 0 ? "bottom" :
placement === "right" && pos.right + actualWidth > parentWidth ? "left" :
placement === "left" && pos.left - actualWidth < parentLeft ? "right" :
placement;
tip.classList.remove(...["top", "bottom", "left", "right"]);
tip.classList.add(placement);
}
const calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight);
this.applyPlacement(tip, calculatedOffset, placement);
}
getCalculatedOffset(placement, pos, actualWidth, actualHeight) {
switch (placement) {
case "bottom":
return { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 };
case "top":
return { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 };
case "left":
return { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth };
case "right":
return { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width };
}
}
applyPlacement(tip, offset, placement) {
tip.style.top = `${offset.top}px`;
tip.style.left = `${offset.left}px`;
tip.classList.add("show", placement);
}
}
}(window.jQuery));

View File

@@ -1,71 +1,61 @@
/*
Editableform based on Bootstrap 5 (No jQuery)
Editableform based on Twitter Bootstrap 5
*/
(() => {
import $ from "jquery";
(function ($) {
"use strict";
class EditableForm {
constructor(formElement, inputOptions = {}) {
this.formElement = formElement;
this.inputOptions = inputOptions;
this.inputType = inputOptions.type || "text";
this.initInput();
}
//store parent methods
const pInitInput = $.fn.editableform.Constructor.prototype.initInput;
initInput() {
// Supported input types
const stdTypes = ["text", "select", "textarea", "password", "email", "url", "tel", "number", "range", "time", "typeaheadjs"];
console.log("EditableForm:", $.fn.editableform);
$.extend($.fn.editableform.Constructor.prototype, {
initTemplate: function() {
this.$form = $($.fn.editableform.template);
this.$form.find('.control-group').removeClass('control-group');
this.$form.find('.editable-error-block').removeClass('help-block').addClass('invalid-feedback');
},
initInput: function() {
pInitInput.apply(this);
if (stdTypes.includes(this.inputType)) {
this.formElement.classList.add("form-control", "editable");
//for bs5 set default class `form-select-sm` to standard inputs
var emptyInputClass = this.input.options.inputclass === null || this.input.options.inputclass === false;
var defaultClass = 'form-select-sm';
//bs5 add `form-select` class to standard inputs
var stdtypes = 'text,select,textarea,password,email,url,tel,number,range,time,typeaheadjs'.split(',');
if(~$.inArray(this.input.type, stdtypes)) {
this.input.$input.addClass('form-select');
if(emptyInputClass) {
this.input.options.inputclass = defaultClass;
this.input.$input.addClass(defaultClass);
}
}
// Automatically open select dropdown when clicked
if (this.inputType === "select") {
setTimeout(() => {
this.formElement.focus();
this.formElement.click();
}, 50);
}
// Apply Bootstrap 5 button size classes
const buttonContainer = this.formElement.closest(".editable-buttons");
if (buttonContainer) {
if (this.inputOptions.inputClass && this.inputOptions.inputClass.includes("input-lg")) {
buttonContainer.querySelectorAll("button").forEach(btn => btn.classList.add("btn-lg"));
//apply bs3 size class also to buttons (to fit size of control)
var $btn = this.$form.find('.editable-buttons');
var classes = emptyInputClass ? [defaultClass] : this.input.options.inputclass.split(' ');
for(var i=0; i<classes.length; i++) {
if(classes[i].toLowerCase() === 'input-lg') {
$btn.find('button').removeClass('btn-sm').addClass('btn-lg');
}
}
}
}
});
// Create buttons dynamically
function createEditableButtons() {
const btnContainer = document.createElement("div");
btnContainer.classList.add("editable-buttons");
//buttons
$.fn.editableform.buttons =
'<button type="submit" class="btn btn-primary btn-sm editable-submit">'+
'<i class="bi bi-check"></i>'+
'</button>'+
'<button type="button" class="btn btn-secondary btn-sm editable-cancel">'+
'<i class="bi bi-x"></i>'+
'</button>';
const submitButton = document.createElement("button");
submitButton.type = "submit";
submitButton.classList.add("btn", "btn-primary", "btn-sm", "editable-submit");
submitButton.innerHTML = '<i class="bi bi-check"></i>';
const cancelButton = document.createElement("button");
cancelButton.type = "button";
cancelButton.classList.add("btn", "btn-secondary", "btn-sm", "editable-cancel");
cancelButton.innerHTML = '<i class="bi bi-x"></i>';
btnContainer.appendChild(submitButton);
btnContainer.appendChild(cancelButton);
return btnContainer;
}
// Apply Bootstrap 5 validation classes
function applyErrorStyles(element) {
element.classList.add("is-invalid");
const errorBlock = document.createElement("div");
errorBlock.classList.add("invalid-feedback");
errorBlock.innerText = "Invalid input"; // You can dynamically update this message
element.after(errorBlock);
}
})();
//error classes
$.fn.editableform.errorGroupClass = 'has-error';
$.fn.editableform.errorBlockClass = null;
//engine
$.fn.editableform.engine = 'bs5';
}(window.jQuery));