3 Commits

Author SHA1 Message Date
Micha
ab45b130b1 Clean up debug code: remove all console.log statements
- Removed all debug console.log statements from source files
- Cleaned up date.js and datefield.js of debugging output
- Simplified test.js by removing extensive debug logging
- Reduced bundle size and improved production readiness
- Maintained all functionality while removing debug clutter

Code is now production-ready without console noise.
2025-07-26 15:46:30 +02:00
Micha
3eacb7c438 Enhance datepicker UX: position above input with proper styling
- Added CSS positioning to display inline datepicker above input field
- Enhanced datepicker styling with background, border and shadow
- Improved template with datepicker-above class for better positioning
- Maintains Bootstrap 5 compatibility and inline mode functionality

This provides a better user experience by preventing the datepicker
from appearing below and potentially being cut off by page boundaries.
2025-07-26 15:33:02 +02:00
Micha
f4dc8a3dd0 Fix datepicker functionality for Bootstrap 5 x-editable
- Updated Gruntfile to use npm bootstrap-datepicker instead of bundled version
- Fixed DPGlobal reference from bdatepicker to datepicker
- Changed mode from inline to popup to ensure date input type is used
- Fixed missing type assignment in editable element initialization
- Enhanced input2value function with fallback logic to extract dates from dates array
- Added comprehensive debugging and error handling
- Datepicker now properly opens, captures selections, and submits formatted dates
2025-07-26 14:25:39 +02:00
36 changed files with 832 additions and 98 deletions

8
.idea/.gitignore generated vendored Executable file
View File

@@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

31
.idea/cake_config_setting_v0_6.xml generated Executable file
View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CakeStormCakeSettings">
<option name="cakeVersionAbsorption">
<map>
<entry key="Controller" value="/controllers/" />
<entry key="View" value="/views/" />
<entry key="Model" value="/models/" />
<entry key="Helper" value="/views/helpers/" />
<entry key="Component" value="/controllers/components/" />
<entry key="Behavior" value="/models/behaviors/" />
<entry key="Plugin" value="plugins/" />
<entry key="Shell" value="/vendors/shell" />
<entry key="Task" value="/vendors/Shell/Task" />
<entry key="ControllerTest" value="/tests/cases/controllers/" />
<entry key="ModelTest" value="/tests/cases/models/" />
<entry key="BehaviorTest" value="/tests/cases/behaviors/" />
<entry key="ComponentTest" value="/tests/cases/components/" />
<entry key="HelperTest" value="/tests/cases/helpers/" />
<entry key="TestFile" value="test" />
<entry key="FileSeparator" value="." />
<entry key="FileWordSeparator" value="_" />
<entry key="Fixture" value="/tests/fixtures/" />
<entry key="Element" value="elements/" />
<entry key="FixtureFile" value="fixture" />
<entry key="Layout" value="layouts/" />
</map>
</option>
<option name="cakeVersion" value="1" />
</component>
</project>

8
.idea/modules.xml generated Executable file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/x-editable.iml" filepath="$PROJECT_DIR$/.idea/x-editable.iml" />
</modules>
</component>
</project>

19
.idea/php.xml generated Executable file
View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MessDetectorOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="PHPCSFixerOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="PHPCodeSnifferOptionsConfiguration">
<option name="highlightLevel" value="WARNING" />
<option name="transferred" value="true" />
</component>
<component name="PhpStanOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="PsalmOptionsConfiguration">
<option name="transferred" value="true" />
</component>
</project>

6
.idea/vcs.xml generated Executable file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

8
.idea/x-editable.iml generated Executable file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@@ -53,7 +53,7 @@ function getFiles() {
form: [forms+'editable-form-bootstrap5.js'],
container: [containers+'editable-popover5.js'],
inputs: [
inputs+'date/bootstrap-datepicker/js/bootstrap-datepicker.js',
// Bootstrap-datepicker now loaded from npm, not bundled
inputs+'date/date.js',
inputs+'date/datefield.js',
inputs+'datetime/datetime.js',
@@ -64,7 +64,7 @@ function getFiles() {
//inputs+'typeahead.js'
],
css: [
inputs+'date/bootstrap-datepicker/css/datepicker.css'
// Bootstrap-datepicker CSS now loaded from npm, not bundled
//don't build datetime lib, should be included manually
//inputs+'datetime/bootstrap-datetimepicker/css/datetimepicker.css'
]

2
dist/app.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -156,6 +156,20 @@
white-space: pre-wrap;
}
/* Position datepicker above input for datepicker-above class */
.datepicker-above .datepicker-inline {
position: absolute;
bottom: 100%;
left: 0;
right: 0;
z-index: 1000;
margin-bottom: 5px;
background: white;
border: 1px solid #dee2e6;
border-radius: 0.375rem;
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
}
.editable-container.editable-popup {
max-width: none !important; /* without this rule poshytip/tooltip does not stretch */
}

View File

@@ -1505,6 +1505,9 @@ Makes editable any HTML element on the page. Applied as jQuery method.
return;
}
// Set the editable's type from the input's type
this.type = this.input.type;
//set value from settings or by element's text
if (this.options.value === undefined || this.options.value === null) {
this.value = this.input.html2value(this.$element.html().trim());
@@ -4896,11 +4899,19 @@ $(function(){
var Date = function (options) {
this.init('date', options, Date.defaults);
this.initPicker(options, Date.defaults);
// Ensure type is set correctly
this.type = 'date';
};
$.fn.editableutils.inherit(Date, $.fn.editabletypes.abstractinput);
$.extend(Date.prototype, {
prerender: function() {
// Call parent prerender
Date.superclass.prerender.call(this);
},
initPicker: function(options, defaults) {
//'format' is set directly from settings or data-* attributes
@@ -4921,8 +4932,8 @@ $(function(){
//language
this.options.datepicker.language = this.options.datepicker.language || 'en';
//store DPglobal
this.dpg = $.fn.bdatepicker.DPGlobal;
//store DPglobal - use datepicker instead of bdatepicker
this.dpg = $.fn.datepicker.DPGlobal;
//store parsed formats
this.parsedFormat = this.dpg.parseFormat(this.options.format);
@@ -4930,7 +4941,22 @@ $(function(){
},
render: function () {
this.$input.bdatepicker(this.options.datepicker);
// Ensure we have an input element
if (!this.$input || !this.$input.length) {
return;
}
// Initialize datepicker immediately
try {
this.$input.datepicker(this.options.datepicker);
// Force set the initial value if we have one
if (this.value) {
this.$input.datepicker('setDate', this.value);
}
} catch (error) {
// Silently handle datepicker initialization errors
}
//"clear" link
if(this.options.clear) {
@@ -4966,11 +4992,42 @@ $(function(){
},
value2input: function(value) {
this.$input.bdatepicker('update', value);
// Ensure datepicker is initialized before trying to update
if (!this.$input.data('datepicker')) {
this.$input.datepicker(this.options.datepicker);
}
this.$input.datepicker('update', value);
},
input2value: function() {
return this.$input.data('datepicker').date;
var datepicker = this.$input.data('datepicker');
if (datepicker) {
if (datepicker.date) {
return datepicker.date;
}
// Try getting date from dates array
if (datepicker.dates && datepicker.dates.length > 0) {
return datepicker.dates[0];
}
// Try using getDate method
if (typeof datepicker.getDate === 'function') {
var dateFromMethod = datepicker.getDate();
return dateFromMethod;
}
}
// Fallback: try to parse the input value directly
var inputVal = this.$input.val();
if (inputVal) {
var parsedDate = this.parseDate(inputVal, this.parsedViewFormat);
return parsedDate;
}
return null;
},
activate: function() {
@@ -5105,6 +5162,9 @@ Automatically shown in inline mode.
var DateField = function (options) {
this.init('datefield', options, DateField.defaults);
this.initPicker(options, DateField.defaults);
// Ensure type is set correctly
this.type = 'datefield';
};
$.fn.editableutils.inherit(DateField, $.fn.editabletypes.date);
@@ -5115,8 +5175,8 @@ Automatically shown in inline mode.
this.setClass();
this.setAttr('placeholder');
//bootstrap-datepicker is set `bdateicker` to exclude conflict with jQuery UI one. (in date.js)
this.$tpl.bdatepicker(this.options.datepicker);
//use datepicker instead of bdatepicker
this.$tpl.datepicker(this.options.datepicker);
//need to disable original event handlers
this.$input.off('focus keydown');
@@ -5124,17 +5184,48 @@ Automatically shown in inline mode.
//update value of datepicker
this.$input.keyup($.proxy(function(){
this.$tpl.removeData('date');
this.$tpl.bdatepicker('update');
this.$tpl.datepicker('update');
}, this));
},
value2input: function(value) {
this.$input.val(value ? this.dpg.formatDate(value, this.parsedViewFormat, this.options.datepicker.language) : '');
this.$tpl.bdatepicker('update');
var formattedValue = value ? this.dpg.formatDate(value, this.parsedViewFormat, this.options.datepicker.language) : '';
this.$input.val(formattedValue);
this.$tpl.datepicker('update');
},
input2value: function() {
// First try the container datepicker (ideal case)
var containerDatepicker = this.$tpl.data('datepicker');
if (containerDatepicker && containerDatepicker.dates && containerDatepicker.dates.length > 0) {
return containerDatepicker.dates[0];
}
// Fallback: try the input datepicker (in case manual init worked)
var inputDatepicker = this.$input.data('datepicker');
if (inputDatepicker && inputDatepicker.dates && inputDatepicker.dates.length > 0) {
return inputDatepicker.dates[0];
}
// Try getDate methods
if (containerDatepicker && typeof containerDatepicker.getDate === 'function') {
var containerDate = containerDatepicker.getDate();
if (containerDate) {
return containerDate;
}
}
if (inputDatepicker && typeof inputDatepicker.getDate === 'function') {
var inputDate = inputDatepicker.getDate();
if (inputDate) {
return inputDate;
}
}
// Final fallback to text parsing
return this.html2value(this.$input.val());
},
@@ -5151,19 +5242,21 @@ Automatically shown in inline mode.
/**
@property tpl
**/
tpl:'<div class="input-append date"><input type="text"/><span class="add-on"><i class="icon-th"></i></span></div>',
tpl:'<div class="input-group input-group-sm date datepicker-above" style="width: 200px; border: 1px solid #dee2e6; border-radius: 0.375rem; position: relative;"><input type="text" class="form-control form-control-sm" style="border: none;"/><span class="input-group-text" style="border: none; background: transparent;"><i class="bi bi-calendar"></i></span></div>',
/**
@property inputclass
@default 'input-small'
@default 'form-control form-control-sm'
**/
inputclass: 'input-small',
inputclass: 'form-control form-control-sm',
/* datepicker config */
datepicker: {
weekStart: 0,
startView: 0,
minViewMode: 0,
autoclose: true
autoclose: true,
orientation: 'top',
container: 'body'
}
});

File diff suppressed because one or more lines are too long

View File

@@ -156,6 +156,20 @@
white-space: pre-wrap;
}
/* Position datepicker above input for datepicker-above class */
.datepicker-above .datepicker-inline {
position: absolute;
bottom: 100%;
left: 0;
right: 0;
z-index: 1000;
margin-bottom: 5px;
background: white;
border: 1px solid #dee2e6;
border-radius: 0.375rem;
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
}
.editable-container.editable-popup {
max-width: none !important; /* without this rule poshytip/tooltip does not stretch */
}

View File

@@ -1505,6 +1505,9 @@ Makes editable any HTML element on the page. Applied as jQuery method.
return;
}
// Set the editable's type from the input's type
this.type = this.input.type;
//set value from settings or by element's text
if (this.options.value === undefined || this.options.value === null) {
this.value = this.input.html2value(this.$element.html().trim());
@@ -4964,11 +4967,19 @@ $(function(){
var Date = function (options) {
this.init('date', options, Date.defaults);
this.initPicker(options, Date.defaults);
// Ensure type is set correctly
this.type = 'date';
};
$.fn.editableutils.inherit(Date, $.fn.editabletypes.abstractinput);
$.extend(Date.prototype, {
prerender: function() {
// Call parent prerender
Date.superclass.prerender.call(this);
},
initPicker: function(options, defaults) {
//'format' is set directly from settings or data-* attributes
@@ -4989,8 +5000,8 @@ $(function(){
//language
this.options.datepicker.language = this.options.datepicker.language || 'en';
//store DPglobal
this.dpg = $.fn.bdatepicker.DPGlobal;
//store DPglobal - use datepicker instead of bdatepicker
this.dpg = $.fn.datepicker.DPGlobal;
//store parsed formats
this.parsedFormat = this.dpg.parseFormat(this.options.format);
@@ -4998,7 +5009,22 @@ $(function(){
},
render: function () {
this.$input.bdatepicker(this.options.datepicker);
// Ensure we have an input element
if (!this.$input || !this.$input.length) {
return;
}
// Initialize datepicker immediately
try {
this.$input.datepicker(this.options.datepicker);
// Force set the initial value if we have one
if (this.value) {
this.$input.datepicker('setDate', this.value);
}
} catch (error) {
// Silently handle datepicker initialization errors
}
//"clear" link
if(this.options.clear) {
@@ -5034,11 +5060,42 @@ $(function(){
},
value2input: function(value) {
this.$input.bdatepicker('update', value);
// Ensure datepicker is initialized before trying to update
if (!this.$input.data('datepicker')) {
this.$input.datepicker(this.options.datepicker);
}
this.$input.datepicker('update', value);
},
input2value: function() {
return this.$input.data('datepicker').date;
var datepicker = this.$input.data('datepicker');
if (datepicker) {
if (datepicker.date) {
return datepicker.date;
}
// Try getting date from dates array
if (datepicker.dates && datepicker.dates.length > 0) {
return datepicker.dates[0];
}
// Try using getDate method
if (typeof datepicker.getDate === 'function') {
var dateFromMethod = datepicker.getDate();
return dateFromMethod;
}
}
// Fallback: try to parse the input value directly
var inputVal = this.$input.val();
if (inputVal) {
var parsedDate = this.parseDate(inputVal, this.parsedViewFormat);
return parsedDate;
}
return null;
},
activate: function() {
@@ -5173,6 +5230,9 @@ Automatically shown in inline mode.
var DateField = function (options) {
this.init('datefield', options, DateField.defaults);
this.initPicker(options, DateField.defaults);
// Ensure type is set correctly
this.type = 'datefield';
};
$.fn.editableutils.inherit(DateField, $.fn.editabletypes.date);
@@ -5183,8 +5243,8 @@ Automatically shown in inline mode.
this.setClass();
this.setAttr('placeholder');
//bootstrap-datepicker is set `bdateicker` to exclude conflict with jQuery UI one. (in date.js)
this.$tpl.bdatepicker(this.options.datepicker);
//use datepicker instead of bdatepicker
this.$tpl.datepicker(this.options.datepicker);
//need to disable original event handlers
this.$input.off('focus keydown');
@@ -5192,17 +5252,48 @@ Automatically shown in inline mode.
//update value of datepicker
this.$input.keyup($.proxy(function(){
this.$tpl.removeData('date');
this.$tpl.bdatepicker('update');
this.$tpl.datepicker('update');
}, this));
},
value2input: function(value) {
this.$input.val(value ? this.dpg.formatDate(value, this.parsedViewFormat, this.options.datepicker.language) : '');
this.$tpl.bdatepicker('update');
var formattedValue = value ? this.dpg.formatDate(value, this.parsedViewFormat, this.options.datepicker.language) : '';
this.$input.val(formattedValue);
this.$tpl.datepicker('update');
},
input2value: function() {
// First try the container datepicker (ideal case)
var containerDatepicker = this.$tpl.data('datepicker');
if (containerDatepicker && containerDatepicker.dates && containerDatepicker.dates.length > 0) {
return containerDatepicker.dates[0];
}
// Fallback: try the input datepicker (in case manual init worked)
var inputDatepicker = this.$input.data('datepicker');
if (inputDatepicker && inputDatepicker.dates && inputDatepicker.dates.length > 0) {
return inputDatepicker.dates[0];
}
// Try getDate methods
if (containerDatepicker && typeof containerDatepicker.getDate === 'function') {
var containerDate = containerDatepicker.getDate();
if (containerDate) {
return containerDate;
}
}
if (inputDatepicker && typeof inputDatepicker.getDate === 'function') {
var inputDate = inputDatepicker.getDate();
if (inputDate) {
return inputDate;
}
}
// Final fallback to text parsing
return this.html2value(this.$input.val());
},
@@ -5219,19 +5310,21 @@ Automatically shown in inline mode.
/**
@property tpl
**/
tpl:'<div class="input-append date"><input type="text"/><span class="add-on"><i class="icon-th"></i></span></div>',
tpl:'<div class="input-group input-group-sm date datepicker-above" style="width: 200px; border: 1px solid #dee2e6; border-radius: 0.375rem; position: relative;"><input type="text" class="form-control form-control-sm" style="border: none;"/><span class="input-group-text" style="border: none; background: transparent;"><i class="bi bi-calendar"></i></span></div>',
/**
@property inputclass
@default 'input-small'
@default 'form-control form-control-sm'
**/
inputclass: 'input-small',
inputclass: 'form-control form-control-sm',
/* datepicker config */
datepicker: {
weekStart: 0,
startView: 0,
minViewMode: 0,
autoclose: true
autoclose: true,
orientation: 'top',
container: 'body'
}
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -156,6 +156,20 @@
white-space: pre-wrap;
}
/* Position datepicker above input for datepicker-above class */
.datepicker-above .datepicker-inline {
position: absolute;
bottom: 100%;
left: 0;
right: 0;
z-index: 1000;
margin-bottom: 5px;
background: white;
border: 1px solid #dee2e6;
border-radius: 0.375rem;
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
}
.editable-container.editable-popup {
max-width: none !important; /* without this rule poshytip/tooltip does not stretch */
}

View File

@@ -151,3 +151,17 @@
.editable-pre-wrapped {
white-space: pre-wrap;
}
/* Position datepicker above input for datepicker-above class */
.datepicker-above .datepicker-inline {
position: absolute;
bottom: 100%;
left: 0;
right: 0;
z-index: 1000;
margin-bottom: 5px;
background: white;
border: 1px solid #dee2e6;
border-radius: 0.375rem;
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
}

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +1,15 @@
/*!
* Bootstrap v5.3.3 (https://getbootstrap.com/)
* Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
/*!
* Datepicker for Bootstrap v1.10.0 (https://github.com/uxsolutions/bootstrap-datepicker)
*
* Licensed under the Apache License v2.0 (https://www.apache.org/licenses/LICENSE-2.0)
*/
/*!
* jQuery JavaScript Library v3.7.1
* https://jquery.com/

File diff suppressed because one or more lines are too long

View File

@@ -156,6 +156,20 @@
white-space: pre-wrap;
}
/* Position datepicker above input for datepicker-above class */
.datepicker-above .datepicker-inline {
position: absolute;
bottom: 100%;
left: 0;
right: 0;
z-index: 1000;
margin-bottom: 5px;
background: white;
border: 1px solid #dee2e6;
border-radius: 0.375rem;
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
}
.editable-container.editable-popup {
max-width: none !important; /* without this rule poshytip/tooltip does not stretch */
}

View File

@@ -1505,6 +1505,9 @@ Makes editable any HTML element on the page. Applied as jQuery method.
return;
}
// Set the editable's type from the input's type
this.type = this.input.type;
//set value from settings or by element's text
if (this.options.value === undefined || this.options.value === null) {
this.value = this.input.html2value(this.$element.html().trim());

File diff suppressed because one or more lines are too long

View File

@@ -156,6 +156,20 @@
white-space: pre-wrap;
}
/* Position datepicker above input for datepicker-above class */
.datepicker-above .datepicker-inline {
position: absolute;
bottom: 100%;
left: 0;
right: 0;
z-index: 1000;
margin-bottom: 5px;
background: white;
border: 1px solid #dee2e6;
border-radius: 0.375rem;
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
}
.editable-container.editable-popup {
max-width: none !important; /* without this rule poshytip/tooltip does not stretch */
}

View File

@@ -1505,6 +1505,9 @@ Makes editable any HTML element on the page. Applied as jQuery method.
return;
}
// Set the editable's type from the input's type
this.type = this.input.type;
//set value from settings or by element's text
if (this.options.value === undefined || this.options.value === null) {
this.value = this.input.html2value(this.$element.html().trim());

File diff suppressed because one or more lines are too long

256
package-lock.json generated
View File

@@ -8,6 +8,7 @@
"name": "@24unix/x-editable",
"version": "1.5.2",
"dependencies": {
"@anthropic-ai/claude-code": "^1.0.61",
"bootstrap": "^5.3.3",
"bootstrap-datepicker": "^1.10.0",
"bootstrap-icons": "^1.11.3",
@@ -32,6 +33,26 @@
"webpack-cli": "^6.0.1"
}
},
"node_modules/@anthropic-ai/claude-code": {
"version": "1.0.61",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-code/-/claude-code-1.0.61.tgz",
"integrity": "sha512-+gjKzY1hsWfHoH52SgKR6E0ujCDPyyRsjyRShtZfS0urKd8VQq3D/DF3hvT3P4JJeW0YuWp5Dep0aSRON+/FFA==",
"license": "SEE LICENSE IN README.md",
"bin": {
"claude": "cli.js"
},
"engines": {
"node": ">=18.0.0"
},
"optionalDependencies": {
"@img/sharp-darwin-arm64": "^0.33.5",
"@img/sharp-darwin-x64": "^0.33.5",
"@img/sharp-linux-arm": "^0.33.5",
"@img/sharp-linux-arm64": "^0.33.5",
"@img/sharp-linux-x64": "^0.33.5",
"@img/sharp-win32-x64": "^0.33.5"
}
},
"node_modules/@babel/code-frame": {
"version": "7.26.2",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
@@ -67,6 +88,215 @@
"node": ">=14.17.0"
}
},
"node_modules/@img/sharp-darwin-arm64": {
"version": "0.33.5",
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz",
"integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==",
"cpu": [
"arm64"
],
"license": "Apache-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-darwin-arm64": "1.0.4"
}
},
"node_modules/@img/sharp-darwin-x64": {
"version": "0.33.5",
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz",
"integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==",
"cpu": [
"x64"
],
"license": "Apache-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-darwin-x64": "1.0.4"
}
},
"node_modules/@img/sharp-libvips-darwin-arm64": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz",
"integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==",
"cpu": [
"arm64"
],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"darwin"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-libvips-darwin-x64": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz",
"integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==",
"cpu": [
"x64"
],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"darwin"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-libvips-linux-arm": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz",
"integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==",
"cpu": [
"arm"
],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"linux"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-libvips-linux-arm64": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz",
"integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==",
"cpu": [
"arm64"
],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"linux"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-libvips-linux-x64": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz",
"integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==",
"cpu": [
"x64"
],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"linux"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-linux-arm": {
"version": "0.33.5",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz",
"integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==",
"cpu": [
"arm"
],
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linux-arm": "1.0.5"
}
},
"node_modules/@img/sharp-linux-arm64": {
"version": "0.33.5",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz",
"integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==",
"cpu": [
"arm64"
],
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linux-arm64": "1.0.4"
}
},
"node_modules/@img/sharp-linux-x64": {
"version": "0.33.5",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz",
"integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==",
"cpu": [
"x64"
],
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linux-x64": "1.0.4"
}
},
"node_modules/@img/sharp-win32-x64": {
"version": "0.33.5",
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz",
"integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==",
"cpu": [
"x64"
],
"license": "Apache-2.0 AND LGPL-3.0-or-later",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.8",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
@@ -801,9 +1031,9 @@
"license": "MIT"
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3409,9 +3639,9 @@
"license": "MIT"
},
"node_modules/morgan": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz",
"integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==",
"version": "1.10.1",
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz",
"integrity": "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3419,7 +3649,7 @@
"debug": "2.6.9",
"depd": "~2.0.0",
"on-finished": "~2.3.0",
"on-headers": "~1.0.2"
"on-headers": "~1.1.0"
},
"engines": {
"node": ">= 0.8.0"
@@ -3562,9 +3792,9 @@
}
},
"node_modules/on-headers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
"integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -4850,9 +5080,9 @@
}
},
"node_modules/tar-fs": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.8.tgz",
"integrity": "sha512-ZoROL70jptorGAlgAYiLoBLItEKw/fUxg9BSYK/dF/GAGYFJOJJJMvjPAKDJraCXFwadD456FCuvLWgfhMsPwg==",
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.0.tgz",
"integrity": "sha512-5Mty5y/sOF1YWj1J6GiBodjlDc05CUR8PKXrsnFAiSG0xA+GHeWLovaZPYUDXkH/1iKRf2+M5+OrRgzC7O9b7w==",
"dev": true,
"license": "MIT",
"dependencies": {

View File

@@ -33,6 +33,7 @@
"module": "dist/bootstrap5-editable/js/bootstrap-editable.esm.js",
"style": "dist/bootstrap5-editable/css/bootstrap-editable.css",
"dependencies": {
"@anthropic-ai/claude-code": "^1.0.61",
"bootstrap": "^5.3.3",
"bootstrap-datepicker": "^1.10.0",
"bootstrap-icons": "^1.11.3",

32
src/bootstrap5-editable.js vendored Normal file
View File

@@ -0,0 +1,32 @@
/*
X-Editable Bootstrap 5 - Complete Bundle
Uses npm bootstrap-datepicker instead of bundled version
Order matches Gruntfile.js for compatibility
*/
import $ from "jquery";
// Import bootstrap-datepicker from npm
import "bootstrap-datepicker";
// Core editable functionality - EXACT ORDER from Gruntfile
import "./editable-form/editable-form.js";
import "./editable-form/editable-form-utils.js";
import "./containers/editable-container.js";
import "./containers/editable-inline.js";
import "./element/editable-element.js";
import "./inputs/abstract.js";
import "./inputs/list.js";
import "./inputs/text.js";
import "./inputs/textarea.js";
import "./inputs/select.js";
// Date input (now uses npm bootstrap-datepicker)
import "./inputs/date/date.js";
import "./inputs/date/datefield.js";
// Bootstrap 5 specific containers and forms
import "./containers/editable-popover5.js";
import "./editable-form/editable-form-bootstrap5.js";
// Export jQuery for compatibility
export default $;

View File

@@ -151,3 +151,17 @@
.editable-pre-wrapped {
white-space: pre-wrap;
}
/* Position datepicker above input for datepicker-above class */
.datepicker-above .datepicker-inline {
position: absolute;
bottom: 100%;
left: 0;
right: 0;
z-index: 1000;
margin-bottom: 5px;
background: white;
border: 1px solid #dee2e6;
border-radius: 0.375rem;
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
}

View File

@@ -40,6 +40,9 @@ Makes editable any HTML element on the page. Applied as jQuery method.
return;
}
// Set the editable's type from the input's type
this.type = this.input.type;
//set value from settings or by element's text
if (this.options.value === undefined || this.options.value === null) {
this.value = this.input.html2value(this.$element.html().trim());

View File

@@ -35,11 +35,19 @@ $(function(){
var Date = function (options) {
this.init('date', options, Date.defaults);
this.initPicker(options, Date.defaults);
// Ensure type is set correctly
this.type = 'date';
};
$.fn.editableutils.inherit(Date, $.fn.editabletypes.abstractinput);
$.extend(Date.prototype, {
prerender: function() {
// Call parent prerender
Date.superclass.prerender.call(this);
},
initPicker: function(options, defaults) {
//'format' is set directly from settings or data-* attributes
@@ -60,8 +68,8 @@ $(function(){
//language
this.options.datepicker.language = this.options.datepicker.language || 'en';
//store DPglobal
this.dpg = $.fn.bdatepicker.DPGlobal;
//store DPglobal - use datepicker instead of bdatepicker
this.dpg = $.fn.datepicker.DPGlobal;
//store parsed formats
this.parsedFormat = this.dpg.parseFormat(this.options.format);
@@ -69,7 +77,22 @@ $(function(){
},
render: function () {
this.$input.bdatepicker(this.options.datepicker);
// Ensure we have an input element
if (!this.$input || !this.$input.length) {
return;
}
// Initialize datepicker immediately
try {
this.$input.datepicker(this.options.datepicker);
// Force set the initial value if we have one
if (this.value) {
this.$input.datepicker('setDate', this.value);
}
} catch (error) {
// Silently handle datepicker initialization errors
}
//"clear" link
if(this.options.clear) {
@@ -105,11 +128,42 @@ $(function(){
},
value2input: function(value) {
this.$input.bdatepicker('update', value);
// Ensure datepicker is initialized before trying to update
if (!this.$input.data('datepicker')) {
this.$input.datepicker(this.options.datepicker);
}
this.$input.datepicker('update', value);
},
input2value: function() {
return this.$input.data('datepicker').date;
var datepicker = this.$input.data('datepicker');
if (datepicker) {
if (datepicker.date) {
return datepicker.date;
}
// Try getting date from dates array
if (datepicker.dates && datepicker.dates.length > 0) {
return datepicker.dates[0];
}
// Try using getDate method
if (typeof datepicker.getDate === 'function') {
var dateFromMethod = datepicker.getDate();
return dateFromMethod;
}
}
// Fallback: try to parse the input value directly
var inputVal = this.$input.val();
if (inputVal) {
var parsedDate = this.parseDate(inputVal, this.parsedViewFormat);
return parsedDate;
}
return null;
},
activate: function() {

View File

@@ -14,6 +14,9 @@ Automatically shown in inline mode.
var DateField = function (options) {
this.init('datefield', options, DateField.defaults);
this.initPicker(options, DateField.defaults);
// Ensure type is set correctly
this.type = 'datefield';
};
$.fn.editableutils.inherit(DateField, $.fn.editabletypes.date);
@@ -24,8 +27,8 @@ Automatically shown in inline mode.
this.setClass();
this.setAttr('placeholder');
//bootstrap-datepicker is set `bdateicker` to exclude conflict with jQuery UI one. (in date.js)
this.$tpl.bdatepicker(this.options.datepicker);
//use datepicker instead of bdatepicker
this.$tpl.datepicker(this.options.datepicker);
//need to disable original event handlers
this.$input.off('focus keydown');
@@ -33,17 +36,48 @@ Automatically shown in inline mode.
//update value of datepicker
this.$input.keyup($.proxy(function(){
this.$tpl.removeData('date');
this.$tpl.bdatepicker('update');
this.$tpl.datepicker('update');
}, this));
},
value2input: function(value) {
this.$input.val(value ? this.dpg.formatDate(value, this.parsedViewFormat, this.options.datepicker.language) : '');
this.$tpl.bdatepicker('update');
var formattedValue = value ? this.dpg.formatDate(value, this.parsedViewFormat, this.options.datepicker.language) : '';
this.$input.val(formattedValue);
this.$tpl.datepicker('update');
},
input2value: function() {
// First try the container datepicker (ideal case)
var containerDatepicker = this.$tpl.data('datepicker');
if (containerDatepicker && containerDatepicker.dates && containerDatepicker.dates.length > 0) {
return containerDatepicker.dates[0];
}
// Fallback: try the input datepicker (in case manual init worked)
var inputDatepicker = this.$input.data('datepicker');
if (inputDatepicker && inputDatepicker.dates && inputDatepicker.dates.length > 0) {
return inputDatepicker.dates[0];
}
// Try getDate methods
if (containerDatepicker && typeof containerDatepicker.getDate === 'function') {
var containerDate = containerDatepicker.getDate();
if (containerDate) {
return containerDate;
}
}
if (inputDatepicker && typeof inputDatepicker.getDate === 'function') {
var inputDate = inputDatepicker.getDate();
if (inputDate) {
return inputDate;
}
}
// Final fallback to text parsing
return this.html2value(this.$input.val());
},
@@ -60,19 +94,21 @@ Automatically shown in inline mode.
/**
@property tpl
**/
tpl:'<div class="input-append date"><input type="text"/><span class="add-on"><i class="icon-th"></i></span></div>',
tpl:'<div class="input-group input-group-sm date datepicker-above" style="width: 200px; border: 1px solid #dee2e6; border-radius: 0.375rem; position: relative;"><input type="text" class="form-control form-control-sm" style="border: none;"/><span class="input-group-text" style="border: none; background: transparent;"><i class="bi bi-calendar"></i></span></div>',
/**
@property inputclass
@default 'input-small'
@default 'form-control form-control-sm'
**/
inputclass: 'input-small',
inputclass: 'form-control form-control-sm',
/* datepicker config */
datepicker: {
weekStart: 0,
startView: 0,
minViewMode: 0,
autoclose: true
autoclose: true,
orientation: 'top',
container: 'body'
}
});

View File

@@ -26,6 +26,7 @@
href="#"
id="yes-no-switch"
class="editable editable-click"
data-pk="101"
>
Yes
</a>
@@ -39,7 +40,7 @@
id="yes-no-switch-json"
class="editable editable-click"
data-type="select"
data-pk="1"
data-pk="102"
data-title="Select Yes/No"
>
Yes
@@ -55,10 +56,10 @@
id="datepicker"
class="editable editable-click"
data-type="date"
data-pk="1"
data-pk="103"
data-title="Select Date"
>
Yes
Click to select date
</a>
</div>

37
test.js
View File

@@ -1,18 +1,15 @@
// debugger
import $ from 'jquery';
window.$ = window.jQuery = $;
//
console.log("jQuery version:", $.fn?.jquery);
// // import $ from './dist/jquery';
import "bootstrap"
import "bootstrap/dist/css/bootstrap.min.css"
import "bootstrap-icons/font/bootstrap-icons.min.css"
// bootstrap-datepicker loaded separately (not bundled in grunt build)
import "bootstrap-datepicker/dist/js/bootstrap-datepicker.min.js";
import "bootstrap-datepicker/dist/css/bootstrap-datepicker.min.css";
const Editable = require("bootstrap5-editable/js/bootstrap-editable");
console.log("Editable:", Editable);
// Import the editable functionality (attaches to jQuery.fn) - using Grunt-built version
require("./dist/bootstrap5-editable/js/bootstrap-editable");
$.fn.editable.defaults.mode = 'inline';
$(function() {
@@ -23,18 +20,12 @@ $(function() {
source: 'test.php', // URL to fetch select options
value: 1,
success: function(response, newValue) {
console.log("Saved successfully:", response);
// Handle success
},
error: function(response) {
console.error("Save error:", response);
// Handle error
}
});
console.log("Internal editable data:", $('#yes-no-switch').data('editable'));
const ed = $('#yes-no-switch').data('editable');
console.log("Internal editable data:", ed);
if (ed) {
console.log("TYPE:", ed.type, "OPTIONS:", ed.options);
}
@@ -48,16 +39,19 @@ $(function() {
],
value: 1,
success: function(response, newValue) {
console.log("Static source saved successfully:", response);
// Handle success
},
error: function(response) {
console.error("Static source save error:", response);
// Handle error
}
});
const initialDateValue = new Date().toISOString().split('T')[0];
$('#datepicker').editable({
type: 'date',
url: 'test.php', // URL to send the POST request
value: initialDateValue, // Set to current date (YYYY-MM-DD)
format: 'yyyy-mm-dd', // Date format
viewformat: 'dd/mm/yyyy', // How the user sees it
datepicker: {
@@ -66,18 +60,11 @@ $(function() {
todayHighlight: true
},
success: (response, newValue)=> {
console.log("Date saved successfully:", response);
// Handle success
},
error: (response) => {
console.error("Date save error:", response);
// Handle error
}
}).on('shown', (e, editable)=> {
console.log("Datepicker shown:", editable);
$(editable.input.$input).datepicker({
weekStart: 1,
autoclose: true,
todayHighlight: true
}).datepicker('show')
});
})

View File

@@ -46,6 +46,7 @@ module.exports = [
new CopyWebpackPlugin({
patterns: [
{ from: "src/editable-form/editable-form.css", to: path.resolve(__dirname, "dist/bootstrap5-editable/css") },
{ from: "node_modules/bootstrap-datepicker/dist/css/bootstrap-datepicker.min.css", to: path.resolve(__dirname, "dist/bootstrap5-editable/css") },
{ from: "node_modules/bootstrap-icons/font/fonts", to: path.resolve(__dirname, "dist/fonts") }
]
})
@@ -54,7 +55,7 @@ module.exports = [
// X-Editable Plugin (Bootstrap 5)
{
entry: "./src/editable-form/editable-form-bootstrap5.js",
entry: "./src/bootstrap5-editable.js",
output: {
path: path.resolve(__dirname, "dist/bootstrap5-editable/js"), // X-Editable stays here
filename: "bootstrap-editable.js",