@keenthemes/ktui 1.0.26 → 1.0.28

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.
Files changed (68) hide show
  1. package/CONTRIBUTING.md +13 -0
  2. package/dist/ktui.js +149 -18
  3. package/dist/ktui.min.js +1 -1
  4. package/dist/ktui.min.js.map +1 -1
  5. package/dist/styles.css +11 -13
  6. package/examples/modal/remote-select-dropdown.html +166 -0
  7. package/examples/modal/select-dropdown-container.html +129 -0
  8. package/examples/select/modal-positioning-test.html +10 -8
  9. package/lib/cjs/components/datatable/datatable.js +13 -1
  10. package/lib/cjs/components/datatable/datatable.js.map +1 -1
  11. package/lib/cjs/components/modal/modal.js +13 -1
  12. package/lib/cjs/components/modal/modal.js.map +1 -1
  13. package/lib/cjs/components/select/dropdown.js +34 -6
  14. package/lib/cjs/components/select/dropdown.js.map +1 -1
  15. package/lib/cjs/components/select/select.js +89 -10
  16. package/lib/cjs/components/select/select.js.map +1 -1
  17. package/lib/esm/components/datatable/datatable.js +13 -1
  18. package/lib/esm/components/datatable/datatable.js.map +1 -1
  19. package/lib/esm/components/modal/modal.js +13 -1
  20. package/lib/esm/components/modal/modal.js.map +1 -1
  21. package/lib/esm/components/select/dropdown.js +34 -6
  22. package/lib/esm/components/select/dropdown.js.map +1 -1
  23. package/lib/esm/components/select/select.js +89 -10
  24. package/lib/esm/components/select/select.js.map +1 -1
  25. package/package.json +13 -5
  26. package/src/components/datatable/datatable.ts +13 -3
  27. package/src/components/modal/modal.ts +15 -1
  28. package/src/components/select/dropdown.ts +42 -6
  29. package/src/components/select/select.ts +123 -10
  30. package/src/components/table/table.css +7 -0
  31. package/webpack.config.js +1 -1
  32. package/lib/cjs/components/config.js +0 -26
  33. package/lib/cjs/components/config.js.map +0 -1
  34. package/lib/cjs/components/config.umd.js +0 -23
  35. package/lib/cjs/components/config.umd.js.map +0 -1
  36. package/lib/cjs/components/menu/index.js +0 -6
  37. package/lib/cjs/components/menu/index.js.map +0 -1
  38. package/lib/cjs/components/menu/menu.js +0 -1021
  39. package/lib/cjs/components/menu/menu.js.map +0 -1
  40. package/lib/cjs/components/menu/types.js +0 -3
  41. package/lib/cjs/components/menu/types.js.map +0 -1
  42. package/lib/cjs/components/theme/index.js +0 -6
  43. package/lib/cjs/components/theme/index.js.map +0 -1
  44. package/lib/cjs/components/theme/theme.js +0 -147
  45. package/lib/cjs/components/theme/theme.js.map +0 -1
  46. package/lib/cjs/components/theme/types.js +0 -3
  47. package/lib/cjs/components/theme/types.js.map +0 -1
  48. package/lib/esm/components/config.js +0 -24
  49. package/lib/esm/components/config.js.map +0 -1
  50. package/lib/esm/components/config.umd.js +0 -23
  51. package/lib/esm/components/config.umd.js.map +0 -1
  52. package/lib/esm/components/menu/index.js +0 -2
  53. package/lib/esm/components/menu/index.js.map +0 -1
  54. package/lib/esm/components/menu/menu.js +0 -1018
  55. package/lib/esm/components/menu/menu.js.map +0 -1
  56. package/lib/esm/components/menu/types.js +0 -2
  57. package/lib/esm/components/menu/types.js.map +0 -1
  58. package/lib/esm/components/theme/index.js +0 -2
  59. package/lib/esm/components/theme/index.js.map +0 -1
  60. package/lib/esm/components/theme/theme.js +0 -144
  61. package/lib/esm/components/theme/theme.js.map +0 -1
  62. package/lib/esm/components/theme/types.js +0 -2
  63. package/lib/esm/components/theme/types.js.map +0 -1
  64. /package/examples/modal/{persistent-test.html → persistent.html} +0 -0
  65. /package/examples/select/{dark-mode-test.html → dark-mode.html} +0 -0
  66. /package/examples/select/{dropdowncontainer-test.html → dropdowncontainer.html} +0 -0
  67. /package/examples/select/{formdata-remote-test.html → formdata-remote.html} +0 -0
  68. /package/examples/select/{global-config-test.html → global-config.html} +0 -0
package/CONTRIBUTING.md CHANGED
@@ -31,6 +31,19 @@ If you need any help, feel free to reach out us via [@keenthemes](https://x.com/
31
31
  ```sh
32
32
  npm run lint
33
33
  ```
34
+ 4. Build the project:
35
+ ```sh
36
+ npm run build
37
+ ```
38
+ 5. Start development mode (watch for changes):
39
+ ```sh
40
+ npm run dev
41
+ ```
42
+
43
+ ### Build Commands
44
+
45
+ - **`npm run build`**: Performs a one-off build and exits. Use this for production builds and CI/CD pipelines.
46
+ - **`npm run dev`**: Starts development mode with file watching. Use this during development for live reloading when files change.
34
47
 
35
48
  ## Format and lint your code
36
49
 
package/dist/ktui.js CHANGED
@@ -8343,7 +8343,19 @@ var KTDataTable = /** @class */ (function (_super) {
8343
8343
  return [3 /*break*/, 5];
8344
8344
  case 4:
8345
8345
  error_1 = _a.sent();
8346
- this._noticeOnTable('Error parsing API response as JSON: ' + String(error_1));
8346
+ // Fire event with complete error context for application handling
8347
+ this._fireEvent('parseError', {
8348
+ response: response,
8349
+ error: String(error_1),
8350
+ status: response.status,
8351
+ statusText: response.statusText
8352
+ });
8353
+ this._dispatchEvent('parseError', {
8354
+ response: response,
8355
+ error: String(error_1),
8356
+ status: response.status,
8357
+ statusText: response.statusText
8358
+ });
8347
8359
  return [2 /*return*/];
8348
8360
  case 5:
8349
8361
  this._fireEvent('fetched', { response: responseData });
@@ -9252,8 +9264,20 @@ var KTModal = /** @class */ (function (_super) {
9252
9264
  KTModal.prototype._handlers = function () {
9253
9265
  var _this = this;
9254
9266
  this._element.addEventListener('click', function (event) {
9255
- if (_this._element !== event.target)
9267
+ var target = event.target;
9268
+ var currentTarget = event.currentTarget;
9269
+ // Only proceed if clicking directly on the backdrop (modal element itself)
9270
+ // This prevents closing when clicking inside modal content or any child elements
9271
+ // (including dropdowns rendered via dropdownContainer pointing to modal)
9272
+ if (target !== currentTarget) {
9273
+ // Stop propagation for clicks inside dropdowns to prevent dropdown from closing
9274
+ // Check if click is inside a dropdown element (KT Select dropdown)
9275
+ var dropdownElement = target.closest('[data-kt-select-dropdown]');
9276
+ if (dropdownElement) {
9277
+ event.stopPropagation();
9278
+ }
9256
9279
  return;
9280
+ }
9257
9281
  // Only hide if both backdropStatic is false AND persistent is false
9258
9282
  if (_this._getOption('backdropStatic') === false &&
9259
9283
  utils_1.default.stringToBoolean(_this._getOption('persistent')) === false) {
@@ -9576,8 +9600,14 @@ var KTSelectDropdown = /** @class */ (function (_super) {
9576
9600
  _this._dropdownElement = dropdownElement;
9577
9601
  _this._config = config;
9578
9602
  _this._ktSelectInstance = ktSelectInstance; // Assign instance
9603
+ // For centered modals, don't move dropdown to container to preserve positioning context
9604
+ // For other cases, move to container if specified
9605
+ var modalParent = _this._getModalContainer();
9606
+ var isCenteredModal = modalParent && modalParent.classList.contains('kt-modal-center');
9607
+ // Only move dropdown if not in centered modal (regardless of strategy override)
9608
+ // This prevents the positioning bug even if user sets dropdownStrategy: 'fixed'
9579
9609
  var container = _this._resolveDropdownContainer();
9580
- if (container) {
9610
+ if (container && !isCenteredModal) {
9581
9611
  if (container !== _this._dropdownElement.parentElement) {
9582
9612
  container.appendChild(_this._dropdownElement);
9583
9613
  }
@@ -9652,17 +9682,40 @@ var KTSelectDropdown = /** @class */ (function (_super) {
9652
9682
  KTSelectDropdown.prototype._getModalContainer = function () {
9653
9683
  return this._element.closest('[data-kt-modal], .kt-modal, .kt-modal-center');
9654
9684
  };
9685
+ /**
9686
+ * Get the appropriate boundary element for Popper positioning
9687
+ * For centered modals, use .kt-modal-content to avoid transform calculation issues
9688
+ * @returns The boundary element, or null if no modal found
9689
+ */
9690
+ KTSelectDropdown.prototype._getModalBoundary = function () {
9691
+ var modalParent = this._getModalContainer();
9692
+ if (!modalParent) {
9693
+ return null;
9694
+ }
9695
+ // For centered modals, use .kt-modal-content as boundary to avoid transform issues
9696
+ if (modalParent.classList.contains('kt-modal-center')) {
9697
+ var modalContent = modalParent.querySelector('.kt-modal-content');
9698
+ return modalContent || modalParent;
9699
+ }
9700
+ // For non-centered modals, use the modal element itself
9701
+ return modalParent;
9702
+ };
9655
9703
  /**
9656
9704
  * Get the appropriate positioning strategy based on context
9657
- * @returns 'fixed' if inside modal, 'absolute' otherwise
9705
+ * @returns 'fixed' if inside non-centered modal, 'absolute' for centered modals or no modal
9658
9706
  */
9659
9707
  KTSelectDropdown.prototype._getPositioningStrategy = function () {
9660
9708
  // Check if config explicitly sets strategy
9661
9709
  if (this._config.dropdownStrategy) {
9662
9710
  return this._config.dropdownStrategy;
9663
9711
  }
9664
- // Use fixed positioning if inside a modal (to handle transform-based centering)
9712
+ // For centered modals, use absolute positioning to avoid transform calculation issues
9713
+ // For non-centered modals, use fixed positioning
9665
9714
  var modalParent = this._getModalContainer();
9715
+ if (modalParent && modalParent.classList.contains('kt-modal-center')) {
9716
+ return 'absolute';
9717
+ }
9718
+ // Use fixed positioning for non-centered modals
9666
9719
  return modalParent ? 'fixed' : 'absolute';
9667
9720
  };
9668
9721
  /**
@@ -9678,9 +9731,8 @@ var KTSelectDropdown = /** @class */ (function (_super) {
9678
9731
  var strategy = this._getPositioningStrategy();
9679
9732
  var preventOverflow = this._config.dropdownPreventOverflow !== false;
9680
9733
  var flip = this._config.dropdownFlip !== false;
9681
- // Detect modal container for boundary
9682
- var modalParent = this._getModalContainer();
9683
- var boundary = modalParent || 'clippingParents';
9734
+ // Get appropriate boundary element for modal context
9735
+ var boundary = this._getModalBoundary() || 'clippingParents';
9684
9736
  // Create new popper instance
9685
9737
  this._popperInstance = (0, core_1.createPopper)(this._toggleElement, this._dropdownElement, {
9686
9738
  placement: placement,
@@ -11894,6 +11946,12 @@ var KTSelect = /** @class */ (function (_super) {
11894
11946
  KTSelect.prototype._completeRemoteSetup = function () {
11895
11947
  // Initialize options
11896
11948
  this._preSelectOptions(this._element);
11949
+ // Prevent browser auto-selection when placeholder is configured
11950
+ if (this._config.placeholder &&
11951
+ this._state.getSelectedOptions().length === 0 &&
11952
+ this._preSelectedValues.length === 0) {
11953
+ this._element.value = '';
11954
+ }
11897
11955
  // Apply pre-selected values captured before remote data was loaded
11898
11956
  if (this._preSelectedValues.length > 0) {
11899
11957
  if (this._config.debug) {
@@ -12136,6 +12194,11 @@ var KTSelect = /** @class */ (function (_super) {
12136
12194
  this._setupElementReferences();
12137
12195
  // Initialize options
12138
12196
  this._preSelectOptions(this._element);
12197
+ // Prevent browser auto-selection when placeholder is configured
12198
+ if (this._config.placeholder &&
12199
+ this._state.getSelectedOptions().length === 0) {
12200
+ this._element.value = '';
12201
+ }
12139
12202
  // Apply disabled state if needed
12140
12203
  this._applyInitialDisabledState();
12141
12204
  // Initialize search if enabled
@@ -13065,10 +13128,18 @@ var KTSelect = /** @class */ (function (_super) {
13065
13128
  };
13066
13129
  /**
13067
13130
  * Update the dropdown to sync with native select element changes
13068
- * For remote selects, refetches data from the server
13131
+ * For remote selects, refetches data from the server and preserves selections
13069
13132
  * Optionally accepts new options to replace existing ones (static selects only)
13070
- * @param newOptions Optional array of new options [{value, text}, ...]
13133
+ *
13134
+ * @param newOptions Optional array of new options [{value, text}, ...] (static selects only)
13071
13135
  * @public
13136
+ * @remarks
13137
+ * - For static selects: rebuilds dropdown from native select or new options
13138
+ * - For remote selects: fetches fresh data, preserves matching selections
13139
+ * - Selections are preserved if their values exist in new remote data
13140
+ * - Selections are cleared if their values don't exist in new data
13141
+ * @fires updated - After update completes successfully
13142
+ * @fires updateError - If remote data fetch fails
13072
13143
  */
13073
13144
  KTSelect.prototype.update = function (newOptions) {
13074
13145
  var _this = this;
@@ -13077,19 +13148,36 @@ var KTSelect = /** @class */ (function (_super) {
13077
13148
  this._remoteModule
13078
13149
  .fetchData()
13079
13150
  .then(function (items) {
13080
- // Clear existing options
13151
+ // Capture currently selected values before clearing
13152
+ var currentlySelected = _this._state.getSelectedOptions();
13153
+ // Clear existing options (also captures to _preSelectedValues)
13081
13154
  _this._clearExistingOptions();
13082
- // Add new options from remote data
13155
+ // Get all available values from new remote data
13156
+ var availableValues = items.map(function (item) { return item.id; });
13157
+ // Filter to only values that exist in new data
13158
+ var validSelections = currentlySelected.filter(function (value) {
13159
+ return availableValues.includes(value);
13160
+ });
13161
+ if (_this._config.debug && currentlySelected.length > 0) {
13162
+ console.log('update(): Preserving selections that exist in new data:', validSelections);
13163
+ }
13164
+ // Add new options from remote data and restore selection state
13083
13165
  items.forEach(function (item) {
13084
13166
  var option = document.createElement('option');
13085
13167
  option.value = item.id;
13086
13168
  option.textContent = item.title;
13087
13169
  if (item.disabled)
13088
13170
  option.disabled = true;
13171
+ // Restore selected attribute for preserved selections
13172
+ if (validSelections.includes(item.id)) {
13173
+ option.selected = true;
13174
+ }
13089
13175
  _this._element.appendChild(option);
13090
13176
  });
13091
13177
  // Rebuild dropdown
13092
13178
  _this._rebuildOptionsFromNative();
13179
+ // Sync selection state from native select (now has selected attributes)
13180
+ _this._syncSelectionFromNative();
13093
13181
  // Dispatch updated event
13094
13182
  _this._dispatchEvent('updated');
13095
13183
  _this._fireEvent('updated');
@@ -13136,19 +13224,38 @@ var KTSelect = /** @class */ (function (_super) {
13136
13224
  // Dispatch reload start event
13137
13225
  this._dispatchEvent('reloadStart');
13138
13226
  this._fireEvent('reloadStart');
13227
+ // Capture currently selected values before clearing
13228
+ var currentlySelected = this._state.getSelectedOptions();
13139
13229
  // Fetch fresh remote data
13140
13230
  return this._remoteModule
13141
13231
  .fetchData()
13142
13232
  .then(function (items) {
13143
- // Clear existing options
13233
+ // Clear existing options (captures to _preSelectedValues)
13144
13234
  _this._clearExistingOptions();
13145
13235
  // Update state with new items
13146
13236
  return _this._state.setItems(items).then(function () {
13147
13237
  // Generate new options HTML
13148
13238
  _this._generateOptionsHtml(_this._element);
13239
+ // Preserve selections by marking matching options as selected
13240
+ var availableValues = items.map(function (item) {
13241
+ return item.id !== undefined ? String(item.id) : '';
13242
+ });
13243
+ var validSelections = currentlySelected.filter(function (value) {
13244
+ return availableValues.includes(value);
13245
+ });
13246
+ if (_this._config.debug && currentlySelected.length > 0) {
13247
+ console.log('reload(): Preserving selections that exist in new data:', validSelections);
13248
+ }
13249
+ // Mark preserved selections on new options
13250
+ validSelections.forEach(function (value) {
13251
+ var option = Array.from(_this._element.querySelectorAll('option')).find(function (opt) { return opt.value === value; });
13252
+ if (option) {
13253
+ option.selected = true;
13254
+ }
13255
+ });
13149
13256
  // Update the dropdown
13150
13257
  _this._updateDropdownWithNewOptions();
13151
- // Sync selection state from native select
13258
+ // Sync selection state from native select (now has selected attributes)
13152
13259
  _this._syncSelectionFromNative();
13153
13260
  // Update visual display
13154
13261
  _this.updateSelectedOptionDisplay();
@@ -13173,8 +13280,17 @@ var KTSelect = /** @class */ (function (_super) {
13173
13280
  };
13174
13281
  /**
13175
13282
  * Refresh the visual display and state without rebuilding options
13176
- * For remote selects, refetches data from the server
13283
+ * For remote selects, refetches data from the server and preserves selections
13284
+ * that exist in the newly fetched data
13285
+ *
13177
13286
  * @public
13287
+ * @remarks
13288
+ * - For static selects: syncs visual state with native select
13289
+ * - For remote selects: fetches fresh data, preserves matching selections
13290
+ * - Selections are preserved if their values exist in new remote data
13291
+ * - Selections are cleared if their values don't exist in new data
13292
+ * @fires refreshed - After refresh completes successfully
13293
+ * @fires refreshError - If remote data fetch fails
13178
13294
  */
13179
13295
  KTSelect.prototype.refresh = function () {
13180
13296
  var _this = this;
@@ -13183,20 +13299,35 @@ var KTSelect = /** @class */ (function (_super) {
13183
13299
  this._remoteModule
13184
13300
  .fetchData()
13185
13301
  .then(function (items) {
13186
- // Clear existing options
13302
+ // Capture currently selected values before clearing
13303
+ var currentlySelected = _this._state.getSelectedOptions();
13304
+ // Clear existing options (also captures to _preSelectedValues)
13187
13305
  _this._clearExistingOptions();
13188
- // Add new options
13306
+ // Get all available values from new remote data
13307
+ var availableValues = items.map(function (item) { return item.id; });
13308
+ // Filter to only values that exist in new data
13309
+ var validSelections = currentlySelected.filter(function (value) {
13310
+ return availableValues.includes(value);
13311
+ });
13312
+ if (_this._config.debug && currentlySelected.length > 0) {
13313
+ console.log('refresh(): Preserving selections that exist in new data:', validSelections);
13314
+ }
13315
+ // Add new options and restore selection state
13189
13316
  items.forEach(function (item) {
13190
13317
  var option = document.createElement('option');
13191
13318
  option.value = item.id;
13192
13319
  option.textContent = item.title;
13193
13320
  if (item.disabled)
13194
13321
  option.disabled = true;
13322
+ // Restore selected attribute for preserved selections
13323
+ if (validSelections.includes(item.id)) {
13324
+ option.selected = true;
13325
+ }
13195
13326
  _this._element.appendChild(option);
13196
13327
  });
13197
13328
  // Rebuild dropdown
13198
13329
  _this._rebuildOptionsFromNative();
13199
- // Sync selection state
13330
+ // Sync selection state from native select (now has selected attributes)
13200
13331
  _this._syncSelectionFromNative();
13201
13332
  // Reapply ARIA attributes
13202
13333
  _this._setAriaAttributes();