web-mojo 2.1.978 → 2.1.980

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 (50) hide show
  1. package/dist/admin.cjs.js +1 -1
  2. package/dist/admin.cjs.js.map +1 -1
  3. package/dist/admin.es.js +54 -8
  4. package/dist/admin.es.js.map +1 -1
  5. package/dist/auth.cjs.js +1 -1
  6. package/dist/auth.cjs.js.map +1 -1
  7. package/dist/auth.es.js +2 -2
  8. package/dist/auth.es.js.map +1 -1
  9. package/dist/charts.cjs.js +1 -1
  10. package/dist/charts.es.js +2 -2
  11. package/dist/chunks/{ChatView-uTteoCA-.js → ChatView-BXois2IL.js} +2 -2
  12. package/dist/chunks/{ChatView-uTteoCA-.js.map → ChatView-BXois2IL.js.map} +1 -1
  13. package/dist/chunks/{ChatView-TprskwB8.js → ChatView-rAfKBqDw.js} +3 -3
  14. package/dist/chunks/{ChatView-TprskwB8.js.map → ChatView-rAfKBqDw.js.map} +1 -1
  15. package/dist/chunks/{Dialog-DxLUm896.js → Dialog-CENvQT9n.js} +3 -3
  16. package/dist/chunks/{Dialog-DxLUm896.js.map → Dialog-CENvQT9n.js.map} +1 -1
  17. package/dist/chunks/{Dialog-DZGFqslk.js → Dialog-DZqbxTsP.js} +2 -2
  18. package/dist/chunks/{Dialog-DZGFqslk.js.map → Dialog-DZqbxTsP.js.map} +1 -1
  19. package/dist/chunks/{FormView-CwSPMYzE.js → FormView-095xPgXv.js} +306 -227
  20. package/dist/chunks/FormView-095xPgXv.js.map +1 -0
  21. package/dist/chunks/FormView-DGA3I2IL.js +3 -0
  22. package/dist/chunks/FormView-DGA3I2IL.js.map +1 -0
  23. package/dist/chunks/{MetricsMiniChartWidget-DUexek3e.js → MetricsMiniChartWidget-CVRinHn4.js} +2 -2
  24. package/dist/chunks/{MetricsMiniChartWidget-DUexek3e.js.map → MetricsMiniChartWidget-CVRinHn4.js.map} +1 -1
  25. package/dist/chunks/{MetricsMiniChartWidget-CtMyQWCr.js → MetricsMiniChartWidget-Dez6aHCT.js} +2 -2
  26. package/dist/chunks/{MetricsMiniChartWidget-CtMyQWCr.js.map → MetricsMiniChartWidget-Dez6aHCT.js.map} +1 -1
  27. package/dist/chunks/{PDFViewer-Cqy-VTh9.js → PDFViewer-CwzGbdOv.js} +2 -2
  28. package/dist/chunks/{PDFViewer-Cqy-VTh9.js.map → PDFViewer-CwzGbdOv.js.map} +1 -1
  29. package/dist/chunks/{PDFViewer-UqOOvvMW.js → PDFViewer-dAEmy7XJ.js} +2 -2
  30. package/dist/chunks/{PDFViewer-UqOOvvMW.js.map → PDFViewer-dAEmy7XJ.js.map} +1 -1
  31. package/dist/chunks/{TopNav-Tr_uef0g.js → TopNav-Celew98v.js} +2 -2
  32. package/dist/chunks/{TopNav-Tr_uef0g.js.map → TopNav-Celew98v.js.map} +1 -1
  33. package/dist/chunks/{TopNav-BoPFE5Yo.js → TopNav-Cj9_6vRl.js} +2 -2
  34. package/dist/chunks/{TopNav-BoPFE5Yo.js.map → TopNav-Cj9_6vRl.js.map} +1 -1
  35. package/dist/chunks/{WebApp-CBpWtagL.js → WebApp-7JKRzXMJ.js} +13 -13
  36. package/dist/chunks/{WebApp-CBpWtagL.js.map → WebApp-7JKRzXMJ.js.map} +1 -1
  37. package/dist/chunks/{WebApp-DWLey4KY.js → WebApp-9HUBOegS.js} +2 -2
  38. package/dist/chunks/{WebApp-DWLey4KY.js.map → WebApp-9HUBOegS.js.map} +1 -1
  39. package/dist/core.css +138 -4
  40. package/dist/css/web-mojo.css +1 -1
  41. package/dist/docit.cjs.js +1 -1
  42. package/dist/docit.es.js +3 -3
  43. package/dist/index.cjs.js +1 -1
  44. package/dist/index.es.js +7 -7
  45. package/dist/lightbox.cjs.js +1 -1
  46. package/dist/lightbox.es.js +3 -3
  47. package/package.json +1 -1
  48. package/dist/chunks/FormView-CwSPMYzE.js.map +0 -1
  49. package/dist/chunks/FormView-Tkuf0Sfh.js +0 -3
  50. package/dist/chunks/FormView-Tkuf0Sfh.js.map +0 -1
@@ -1697,6 +1697,7 @@ class FormBuilder {
1697
1697
  labelField = "name",
1698
1698
  valueField = "id",
1699
1699
  excludeIds = [],
1700
+ ignoreIds = [],
1700
1701
  size = 8,
1701
1702
  maxHeight = null,
1702
1703
  showSelectAll = true,
@@ -1721,6 +1722,7 @@ class FormBuilder {
1721
1722
  labelField,
1722
1723
  valueField,
1723
1724
  excludeIds,
1725
+ ignoreIds,
1724
1726
  size,
1725
1727
  maxHeight,
1726
1728
  showSelectAll,
@@ -3349,6 +3351,128 @@ class CollectionSelectView extends View {
3349
3351
  return MOJOUtils.getNestedValue(item, fieldPath);
3350
3352
  }
3351
3353
  }
3354
+ class SearchView extends View {
3355
+ constructor(options = {}) {
3356
+ super({
3357
+ tagName: "div",
3358
+ className: "collection-multiselect-search",
3359
+ template: `
3360
+ <input type="text"
3361
+ class="form-control form-control-sm mb-2"
3362
+ placeholder="{{placeholder}}"
3363
+ data-change-action="search"
3364
+ data-filter="live-search"
3365
+ data-filter-debounce="{{debounce}}" />
3366
+ `,
3367
+ ...options
3368
+ });
3369
+ this.placeholder = options.placeholder || "Search...";
3370
+ this.debounce = options.debounce || 400;
3371
+ }
3372
+ async onChangeSearch(event, element) {
3373
+ const searchValue = element.value.trim();
3374
+ this.emit("search", searchValue);
3375
+ }
3376
+ getValue() {
3377
+ return this.element?.querySelector("input")?.value || "";
3378
+ }
3379
+ clear() {
3380
+ const input = this.element?.querySelector("input");
3381
+ if (input) input.value = "";
3382
+ }
3383
+ }
3384
+ class ListItemsView extends View {
3385
+ constructor(options = {}) {
3386
+ const hasCustomTemplate = !!options.customItemTemplate;
3387
+ const itemContentTemplate = hasCustomTemplate ? `{{{customContent}}}` : `<span {{#disabled}}class="text-muted"{{/disabled}}>{{label}}</span>`;
3388
+ super({
3389
+ tagName: "div",
3390
+ className: "collection-multiselect-items",
3391
+ template: `
3392
+ {{#loading}}
3393
+ <div class="text-center py-3">
3394
+ <div class="spinner-border spinner-border-sm" role="status">
3395
+ <span class="visually-hidden">Loading...</span>
3396
+ </div>
3397
+ </div>
3398
+ {{/loading}}
3399
+
3400
+ {{^loading}}
3401
+ {{#items.length}}
3402
+ {{#showSelectAll}}
3403
+ <div class="collection-multiselect-actions d-flex justify-content-between align-items-center mb-2 py-1">
3404
+ <button type="button"
3405
+ class="btn btn-link btn-sm text-decoration-none p-0 {{#allSelected}}text-muted{{/allSelected}}"
3406
+ data-action="select-all"
3407
+ {{#allSelected}}disabled{{/allSelected}}>
3408
+ <i class="bi bi-check-square me-1"></i>
3409
+ SELECT {{#unselectedCount}}({{unselectedCount}}){{/unselectedCount}}
3410
+ </button>
3411
+ <button type="button"
3412
+ class="btn btn-link btn-sm text-decoration-none p-0 {{#noneSelected}}text-muted{{/noneSelected}}"
3413
+ data-action="deselect-all"
3414
+ {{#noneSelected}}disabled{{/noneSelected}}>
3415
+ DESELECT {{#selectedCount}}({{selectedCount}}){{/selectedCount}}
3416
+ <i class="bi bi-square ms-1"></i>
3417
+ </button>
3418
+ </div>
3419
+ {{/showSelectAll}}
3420
+
3421
+ <div class="collection-multiselect-list border rounded"
3422
+ style="max-height: {{maxHeight}}px; overflow-y: auto;">
3423
+ {{#items}}
3424
+ <div class="collection-multiselect-item d-flex align-items-center py-2 px-3 {{^disabled}}clickable{{/disabled}}"
3425
+ data-action="{{^disabled}}toggle{{/disabled}}"
3426
+ data-value="{{value}}"
3427
+ data-index="{{index}}">
3428
+ <i class="bi {{#selected}}bi-check-square-fill text-primary{{/selected}}{{^selected}}bi-square{{/selected}} me-2"
3429
+ style="font-size: 1.1rem;"></i>
3430
+ ${itemContentTemplate}
3431
+ </div>
3432
+ {{/items}}
3433
+ </div>
3434
+ {{/items.length}}
3435
+
3436
+ {{^items.length}}
3437
+ <div class="collection-multiselect-empty text-muted text-center py-4 border rounded">
3438
+ <i class="bi bi-inbox fs-3 d-block mb-2 opacity-50"></i>
3439
+ <div>No items available</div>
3440
+ </div>
3441
+ {{/^items.length}}
3442
+ {{/loading}}
3443
+ `,
3444
+ ...options
3445
+ });
3446
+ this.items = options.items || [];
3447
+ this.loading = options.loading || false;
3448
+ this.maxHeight = options.maxHeight || 336;
3449
+ this.showSelectAll = options.showSelectAll !== false;
3450
+ this.selectedCount = options.selectedCount || 0;
3451
+ this.totalCount = options.totalCount || 0;
3452
+ this.unselectedCount = options.unselectedCount || 0;
3453
+ this.allSelected = options.allSelected || false;
3454
+ this.noneSelected = options.noneSelected || true;
3455
+ this.customItemTemplate = options.customItemTemplate || null;
3456
+ this.lastClickedIndex = -1;
3457
+ }
3458
+ handleActionToggle(event, element) {
3459
+ const value = element.getAttribute("data-value");
3460
+ const index = parseInt(element.getAttribute("data-index"), 10);
3461
+ this.emit("toggle", { value, index, shiftKey: event.shiftKey });
3462
+ this.lastClickedIndex = index;
3463
+ }
3464
+ async handleActionSelectAll(event) {
3465
+ event.preventDefault();
3466
+ this.emit("select-all");
3467
+ }
3468
+ async handleActionDeselectAll(event) {
3469
+ event.preventDefault();
3470
+ this.emit("deselect-all");
3471
+ }
3472
+ updateState(state) {
3473
+ Object.assign(this, state);
3474
+ }
3475
+ }
3352
3476
  class CollectionMultiSelectView extends View {
3353
3477
  constructor(options = {}) {
3354
3478
  super({
@@ -3361,57 +3485,9 @@ class CollectionMultiSelectView extends View {
3361
3485
  {{label}}{{#required}}<span class="text-danger">*</span>{{/required}}
3362
3486
  </label>
3363
3487
  {{/label}}
3364
-
3365
- {{#enableSearch}}
3366
- <input type="text"
3367
- class="form-control form-control-sm mb-2 collection-multiselect-search"
3368
- placeholder="{{searchPlaceholder}}"
3369
- value="{{searchValue}}" />
3370
- {{/enableSearch}}
3371
-
3372
- {{#loading}}
3373
- <div class="text-center py-3">
3374
- <div class="spinner-border spinner-border-sm" role="status">
3375
- <span class="visually-hidden">Loading...</span>
3376
- </div>
3377
- </div>
3378
- {{/loading}}
3379
-
3380
- {{^loading}}
3381
- {{#items.length}}
3382
- <div class="collection-multiselect-list border rounded p-3" style="max-height: {{maxHeight}}px; overflow-y: auto; background: #fff;">
3383
- {{#items}}
3384
- <div class="d-flex align-items-center mb-2 py-1 px-2 rounded {{^disabled}}hover-bg{{/disabled}}"
3385
- style="cursor: {{^disabled}}pointer{{/disabled}}{{#disabled}}not-allowed{{/disabled}}; user-select: none; transition: background-color 0.15s;"
3386
- data-action="{{^disabled}}toggle-item{{/disabled}}"
3387
- data-value="{{value}}"
3388
- data-index="{{index}}"
3389
- {{#disabled}}data-disabled="true"{{/disabled}}>
3390
- <i class="bi {{#isSelected}}bi-check-square-fill text-primary{{/isSelected}}{{^isSelected}}bi-square{{/isSelected}} me-2"
3391
- style="font-size: 1.25rem;"></i>
3392
- <span {{#disabled}}class="text-muted"{{/disabled}}>{{label}}</span>
3393
- </div>
3394
- {{/items}}
3395
- </div>
3396
-
3397
- {{#showSelectAll}}
3398
- <div class="mt-2">
3399
- <button type="button" class="btn btn-sm btn-outline-secondary me-2" data-action="select-all">
3400
- Select All
3401
- </button>
3402
- <button type="button" class="btn btn-sm btn-outline-secondary" data-action="deselect-all">
3403
- Deselect All
3404
- </button>
3405
- </div>
3406
- {{/showSelectAll}}
3407
- {{/items.length}}
3408
-
3409
- {{^items.length}}
3410
- <div class="text-muted text-center py-3 border rounded">
3411
- No items available
3412
- </div>
3413
- {{/^items.length}}
3414
- {{/loading}}
3488
+
3489
+ <div class="collection-multiselect-search-container"></div>
3490
+ <div class="collection-multiselect-list-container"></div>
3415
3491
 
3416
3492
  {{#help}}
3417
3493
  <div class="form-text">{{help}}</div>
@@ -3430,12 +3506,14 @@ class CollectionMultiSelectView extends View {
3430
3506
  this.required = options.required || false;
3431
3507
  this.disabled = options.disabled || false;
3432
3508
  this.collection = options.collection;
3433
- this.collectionParams = options.collectionParams || {};
3434
- this.defaultParamsOption = options.defaultParams || null;
3435
- this.defaultParams = {};
3436
3509
  this.labelField = options.labelField || "name";
3437
3510
  this.valueField = options.valueField || "id";
3438
3511
  this.excludeIds = options.excludeIds || [];
3512
+ this.ignoreIds = options.ignoreIds || [];
3513
+ this.itemTemplate = options.itemTemplate || null;
3514
+ this.collectionParams = options.collectionParams || {};
3515
+ this.defaultParamsOption = options.defaultParams || null;
3516
+ this.baseParams = {};
3439
3517
  this.requiresActiveGroup = options.requiresActiveGroup || false;
3440
3518
  this.size = options.size || 8;
3441
3519
  this.maxHeight = options.maxHeight || this.size * 42;
@@ -3446,11 +3524,8 @@ class CollectionMultiSelectView extends View {
3446
3524
  this.selectedValues = Array.isArray(options.value) ? options.value : [];
3447
3525
  this.loading = false;
3448
3526
  this.items = [];
3449
- this.lastClickedIndex = -1;
3450
- this.fieldId = options.fieldId || `field_${this.name}`;
3451
- this.searchValue = "";
3452
- this.searchTimer = null;
3453
- this.handleSearchInput = this.handleSearchInput.bind(this);
3527
+ this.searchView = null;
3528
+ this.listView = null;
3454
3529
  }
3455
3530
  onInit() {
3456
3531
  if (this.collection) {
@@ -3458,244 +3533,246 @@ class CollectionMultiSelectView extends View {
3458
3533
  }
3459
3534
  }
3460
3535
  setupCollection() {
3461
- if (!this.collection) {
3462
- console.warn("CollectionMultiSelect: No collection provided");
3463
- return;
3464
- }
3465
- this.defaultParams = { ...this.collection.params };
3466
- if (this.collectionParams && Object.keys(this.collectionParams).length > 0) {
3467
- this.collection.params = { ...this.collection.params, ...this.collectionParams };
3468
- this.defaultParams = { ...this.defaultParams, ...this.collectionParams };
3536
+ this.baseParams = { ...this.collection.params };
3537
+ if (Object.keys(this.collectionParams).length > 0) {
3538
+ Object.assign(this.baseParams, this.collectionParams);
3539
+ Object.assign(this.collection.params, this.collectionParams);
3469
3540
  }
3470
3541
  if (this.defaultParamsOption) {
3471
3542
  const extraParams = typeof this.defaultParamsOption === "function" ? this.defaultParamsOption() : this.defaultParamsOption;
3472
- if (extraParams && typeof extraParams === "object") {
3473
- this.collection.params = { ...this.collection.params, ...extraParams };
3474
- this.defaultParams = { ...this.defaultParams, ...extraParams };
3543
+ if (extraParams) {
3544
+ Object.assign(this.baseParams, extraParams);
3545
+ Object.assign(this.collection.params, extraParams);
3475
3546
  }
3476
3547
  }
3477
3548
  if (this.requiresActiveGroup) {
3478
3549
  const app = this.getApp();
3479
- if (app && app.activeGroup && app.activeGroup.id) {
3550
+ if (app?.activeGroup?.id) {
3551
+ this.baseParams.group = app.activeGroup.id;
3480
3552
  this.collection.params.group = app.activeGroup.id;
3481
- this.defaultParams.group = app.activeGroup.id;
3482
3553
  }
3483
3554
  }
3484
3555
  this.collection.on("fetch:start", () => {
3485
3556
  this.loading = true;
3486
- this.render(false);
3557
+ this.updateListView();
3487
3558
  });
3488
3559
  this.collection.on("fetch:end", () => {
3489
3560
  this.loading = false;
3490
- this.updateItems();
3491
- this.render(false);
3561
+ this.buildItems();
3562
+ this.updateListView();
3492
3563
  });
3493
3564
  if (!this.collection.isEmpty()) {
3494
- this.updateItems();
3565
+ this.buildItems();
3495
3566
  }
3496
3567
  }
3497
3568
  async onAfterRender() {
3498
3569
  await super.onAfterRender();
3499
3570
  if (this.enableSearch) {
3500
- const searchInput = this.element?.querySelector(".collection-multiselect-search");
3501
- if (searchInput) {
3502
- searchInput.addEventListener("input", this.handleSearchInput);
3503
- }
3571
+ this.createSearchView();
3504
3572
  }
3505
- if (this.collection && this.collection.isEmpty()) {
3573
+ this.createListView();
3574
+ if (this.collection?.isEmpty()) {
3506
3575
  this.collection.fetch();
3507
3576
  }
3508
3577
  }
3509
- async onBeforeDestroy() {
3510
- await super.onBeforeDestroy();
3511
- if (this.enableSearch) {
3512
- const searchInput = this.element?.querySelector(".collection-multiselect-search");
3513
- if (searchInput) {
3514
- searchInput.removeEventListener("input", this.handleSearchInput);
3515
- }
3516
- }
3517
- if (this.searchTimer) {
3518
- clearTimeout(this.searchTimer);
3578
+ createSearchView() {
3579
+ const container = this.element?.querySelector(".collection-multiselect-search-container");
3580
+ if (!container) return;
3581
+ this.searchView = new SearchView({
3582
+ placeholder: this.searchPlaceholder,
3583
+ debounce: this.searchDebounce
3584
+ });
3585
+ this.searchView.on("search", (searchValue) => {
3586
+ this.handleSearch(searchValue);
3587
+ });
3588
+ this.searchView.render(true, container);
3589
+ }
3590
+ createListView() {
3591
+ const container = this.element?.querySelector(".collection-multiselect-list-container");
3592
+ if (!container) return;
3593
+ const selectedCount = this.selectedValues.length;
3594
+ const totalCount = this.items.length;
3595
+ const unselectedCount = totalCount - selectedCount;
3596
+ this.listView = new ListItemsView({
3597
+ items: this.items,
3598
+ loading: this.loading,
3599
+ maxHeight: this.maxHeight,
3600
+ showSelectAll: this.showSelectAll,
3601
+ selectedCount,
3602
+ totalCount,
3603
+ unselectedCount,
3604
+ allSelected: selectedCount === totalCount && totalCount > 0,
3605
+ noneSelected: selectedCount === 0,
3606
+ customItemTemplate: this.itemTemplate
3607
+ });
3608
+ this.listView.on("toggle", (data) => {
3609
+ this.handleToggle(data);
3610
+ });
3611
+ this.listView.on("select-all", () => {
3612
+ this.selectAll();
3613
+ });
3614
+ this.listView.on("deselect-all", () => {
3615
+ this.deselectAll();
3616
+ });
3617
+ this.listView.render(true, container);
3618
+ }
3619
+ updateListView() {
3620
+ if (this.listView) {
3621
+ const selectedCount = this.selectedValues.length;
3622
+ const totalCount = this.items.length;
3623
+ const unselectedCount = totalCount - selectedCount;
3624
+ this.listView.updateState({
3625
+ items: this.items,
3626
+ loading: this.loading,
3627
+ selectedCount,
3628
+ totalCount,
3629
+ unselectedCount,
3630
+ allSelected: selectedCount === totalCount && totalCount > 0,
3631
+ noneSelected: selectedCount === 0
3632
+ });
3633
+ this.listView.render(false);
3519
3634
  }
3520
3635
  }
3521
- /**
3522
- * Update items array from collection
3523
- */
3524
- updateItems() {
3525
- const filteredModels = this.collection.models.filter((model) => {
3526
- const modelId = this.getFieldValue(model, this.valueField);
3527
- return !this.excludeIds.some((id) => id == modelId);
3636
+ // Build items array from collection
3637
+ buildItems() {
3638
+ const models = this.collection.models.filter((model) => {
3639
+ const id = this.getFieldValue(model, this.valueField);
3640
+ if (id == null) return false;
3641
+ if (this.excludeIds.includes(id)) return false;
3642
+ if (this.ignoreIds.some((ignoreId) => ignoreId == id)) return false;
3643
+ return true;
3528
3644
  });
3529
- this.items = filteredModels.map((model, index) => {
3530
- const labelValue = this.getFieldValue(model, this.labelField);
3531
- const fieldValue = this.getFieldValue(model, this.valueField);
3532
- return {
3533
- label: labelValue,
3534
- value: fieldValue,
3645
+ this.items = models.map((model, index) => {
3646
+ const modelData = model.toJSON ? model.toJSON() : model;
3647
+ const value = this.getFieldValue(model, this.valueField);
3648
+ const item = {
3649
+ label: this.getFieldValue(model, this.labelField),
3650
+ value,
3535
3651
  index,
3536
- isSelected: this.selectedValues.some((v) => v == fieldValue),
3537
- disabled: this.disabled
3652
+ selected: this.selectedValues.some((v) => v == value),
3653
+ disabled: this.disabled,
3654
+ model: modelData
3655
+ // All model data nested under 'model' context
3538
3656
  };
3657
+ if (this.itemTemplate) {
3658
+ item.customContent = this.renderItemTemplate(item);
3659
+ }
3660
+ return item;
3539
3661
  });
3540
3662
  }
3541
- /**
3542
- * Get field value from model or object, supporting dot notation
3543
- */
3544
- getFieldValue(item, fieldPath) {
3545
- if (!item || !fieldPath) return void 0;
3546
- if (typeof item.get === "function") {
3547
- const value = item.get(fieldPath);
3548
- if (value === void 0 && fieldPath.includes(".")) {
3549
- return MOJOUtils.getNestedValue(item, fieldPath);
3663
+ // Render custom item template
3664
+ renderItemTemplate(itemData) {
3665
+ if (!this.itemTemplate) return "";
3666
+ try {
3667
+ const Mustache2 = window.Mustache || this.constructor.Mustache;
3668
+ if (!Mustache2) {
3669
+ console.warn("Mustache not available for item template rendering");
3670
+ return itemData.label;
3550
3671
  }
3551
- return value;
3672
+ return Mustache2.render(this.itemTemplate, itemData);
3673
+ } catch (error) {
3674
+ console.error("Error rendering item template:", error);
3675
+ return itemData.label;
3552
3676
  }
3553
- return MOJOUtils.getNestedValue(item, fieldPath);
3554
3677
  }
3555
- /**
3556
- * Handle item toggle with shift-click range selection support
3557
- */
3558
- handleActionToggleItem(event, element) {
3559
- const value = element.getAttribute("data-value");
3560
- const clickedIndex = parseInt(element.getAttribute("data-index"), 10);
3561
- const numValue = Number(value);
3562
- const typedValue = !isNaN(numValue) && String(numValue) === value ? numValue : value;
3563
- if (event.shiftKey && this.lastClickedIndex !== -1 && this.lastClickedIndex !== clickedIndex) {
3564
- const isCurrentlySelected = this.selectedValues.some((v) => v == typedValue);
3565
- const shouldSelect = !isCurrentlySelected;
3566
- const start = Math.min(this.lastClickedIndex, clickedIndex);
3567
- const end = Math.max(this.lastClickedIndex, clickedIndex);
3678
+ // Get field value (supports dot notation)
3679
+ getFieldValue(item, field) {
3680
+ if (!item || !field) return void 0;
3681
+ if (typeof item.get === "function") {
3682
+ return item.get(field) ?? MOJOUtils.getNestedValue(item, field);
3683
+ }
3684
+ return MOJOUtils.getNestedValue(item, field);
3685
+ }
3686
+ // Handle search
3687
+ handleSearch(searchValue) {
3688
+ const params = { ...this.baseParams };
3689
+ if (searchValue) {
3690
+ params.search = searchValue;
3691
+ }
3692
+ this.collection.updateParams(params, true);
3693
+ }
3694
+ // Handle item toggle
3695
+ handleToggle({ value, index, shiftKey }) {
3696
+ if (shiftKey && this.listView.lastClickedIndex >= 0) {
3697
+ const start = Math.min(this.listView.lastClickedIndex, index);
3698
+ const end = Math.max(this.listView.lastClickedIndex, index);
3699
+ const shouldSelect = !this.items[index].selected;
3568
3700
  for (let i = start; i <= end; i++) {
3569
3701
  const item = this.items[i];
3570
- if (item && !item.disabled) {
3571
- const itemNumValue = Number(item.value);
3572
- const itemTypedValue = !isNaN(itemNumValue) && String(itemNumValue) === String(item.value) ? itemNumValue : item.value;
3702
+ if (!item.disabled) {
3573
3703
  if (shouldSelect) {
3574
- if (!this.selectedValues.some((v) => v == itemTypedValue)) {
3575
- this.selectedValues.push(itemTypedValue);
3704
+ if (!this.selectedValues.includes(item.value)) {
3705
+ this.selectedValues.push(item.value);
3576
3706
  }
3577
- item.isSelected = true;
3578
3707
  } else {
3579
- this.selectedValues = this.selectedValues.filter((v) => v != itemTypedValue);
3580
- item.isSelected = false;
3708
+ this.selectedValues = this.selectedValues.filter((v) => v != item.value);
3581
3709
  }
3710
+ item.selected = shouldSelect;
3582
3711
  }
3583
3712
  }
3584
3713
  } else {
3585
- const isCurrentlySelected = this.selectedValues.some((v) => v == typedValue);
3586
- if (isCurrentlySelected) {
3587
- this.selectedValues = this.selectedValues.filter((v) => v != typedValue);
3714
+ const item = this.items[index];
3715
+ if (item.selected) {
3716
+ this.selectedValues = this.selectedValues.filter((v) => v != value);
3717
+ item.selected = false;
3588
3718
  } else {
3589
- this.selectedValues.push(typedValue);
3590
- }
3591
- const item = this.items.find((i) => i.value == value);
3592
- if (item) {
3593
- item.isSelected = !isCurrentlySelected;
3719
+ this.selectedValues.push(value);
3720
+ item.selected = true;
3594
3721
  }
3595
3722
  }
3596
- this.lastClickedIndex = clickedIndex;
3597
- this.render(false);
3598
- this.emit("change", {
3599
- value: this.selectedValues,
3600
- name: this.name
3601
- });
3723
+ this.updateListView();
3724
+ this.emit("change", { value: this.selectedValues, name: this.name });
3602
3725
  }
3603
- /**
3604
- * Select all items
3605
- */
3606
- async handleActionSelectAll(event, element) {
3607
- event.preventDefault();
3608
- this.selectedValues = this.items.filter((item) => !item.disabled).map((item) => item.value);
3609
- this.items.forEach((item) => {
3610
- if (!item.disabled) {
3611
- item.isSelected = true;
3612
- }
3613
- });
3614
- this.render(false);
3615
- this.emit("change", {
3616
- value: this.selectedValues,
3617
- name: this.name
3726
+ // Select all
3727
+ selectAll() {
3728
+ this.selectedValues = this.items.filter((i) => !i.disabled).map((i) => i.value);
3729
+ this.items.forEach((i) => {
3730
+ if (!i.disabled) i.selected = true;
3618
3731
  });
3732
+ this.updateListView();
3733
+ this.emit("change", { value: this.selectedValues, name: this.name });
3619
3734
  }
3620
- /**
3621
- * Deselect all items
3622
- */
3623
- async handleActionDeselectAll(event, element) {
3624
- event.preventDefault();
3735
+ // Deselect all
3736
+ deselectAll() {
3625
3737
  this.selectedValues = [];
3626
- this.items.forEach((item) => {
3627
- item.isSelected = false;
3628
- });
3629
- this.render(false);
3630
- this.emit("change", {
3631
- value: this.selectedValues,
3632
- name: this.name
3633
- });
3738
+ this.items.forEach((i) => i.selected = false);
3739
+ this.updateListView();
3740
+ this.emit("change", { value: this.selectedValues, name: this.name });
3634
3741
  }
3635
- /**
3636
- * Handle search input with debouncing
3637
- */
3638
- handleSearchInput(event) {
3639
- this.searchValue = event.target.value;
3640
- if (this.searchTimer) {
3641
- clearTimeout(this.searchTimer);
3742
+ async onBeforeDestroy() {
3743
+ await super.onBeforeDestroy();
3744
+ if (this.searchView) {
3745
+ this.searchView.destroy();
3642
3746
  }
3643
- this.searchTimer = setTimeout(() => {
3644
- this.performSearch();
3645
- }, this.searchDebounce);
3646
- }
3647
- /**
3648
- * Perform search on collection
3649
- */
3650
- async performSearch() {
3651
- if (!this.collection) return;
3652
- try {
3653
- const searchParams = { ...this.defaultParams };
3654
- if (this.searchValue && this.searchValue.trim()) {
3655
- searchParams.search = this.searchValue.trim();
3656
- }
3657
- await this.collection.updateParams(searchParams, true);
3658
- } catch (error) {
3659
- console.error("Search error:", error);
3747
+ if (this.listView) {
3748
+ this.listView.destroy();
3660
3749
  }
3661
3750
  }
3662
- /**
3663
- * Get the current selected values
3664
- */
3751
+ // Public API
3665
3752
  getValue() {
3666
3753
  return this.selectedValues;
3667
3754
  }
3668
- /**
3669
- * Set the selected values
3670
- */
3671
3755
  setValue(values) {
3672
3756
  this.selectedValues = Array.isArray(values) ? values : [];
3673
- this.updateItems();
3674
- this.render();
3757
+ this.buildItems();
3758
+ this.updateListView();
3675
3759
  }
3676
- /**
3677
- * Set the excluded IDs
3678
- */
3679
3760
  setExcludeIds(ids) {
3680
3761
  this.excludeIds = Array.isArray(ids) ? ids : [];
3681
- this.updateItems();
3682
- this.render();
3762
+ this.buildItems();
3763
+ this.updateListView();
3764
+ }
3765
+ setIgnoreIds(ids) {
3766
+ this.ignoreIds = Array.isArray(ids) ? ids : [];
3767
+ this.buildItems();
3768
+ this.updateListView();
3683
3769
  }
3684
- /**
3685
- * Refresh the collection
3686
- */
3687
3770
  async refresh() {
3688
3771
  await this.collection.fetch();
3689
3772
  }
3690
- /**
3691
- * Get form value for form submission
3692
- */
3693
3773
  getFormValue() {
3694
3774
  return this.selectedValues;
3695
3775
  }
3696
- /**
3697
- * Set form value from form data
3698
- */
3699
3776
  setFormValue(value) {
3700
3777
  this.setValue(value);
3701
3778
  }
@@ -5748,6 +5825,8 @@ class FormView extends View {
5748
5825
  collection,
5749
5826
  defaultParams: fieldConfig.defaultParams || null,
5750
5827
  // Can be dict or callback
5828
+ itemTemplate: fieldConfig.itemTemplate || null,
5829
+ // Custom item template
5751
5830
  containerId: null
5752
5831
  // We'll mount directly
5753
5832
  });
@@ -7423,4 +7502,4 @@ export {
7423
7502
  applyFileDropMixin as a,
7424
7503
  FormView$1 as b
7425
7504
  };
7426
- //# sourceMappingURL=FormView-CwSPMYzE.js.map
7505
+ //# sourceMappingURL=FormView-095xPgXv.js.map