@liedekef/ftable 1.1.47 → 1.1.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ftable.esm.js +468 -12
- package/ftable.js +468 -12
- package/ftable.min.js +3 -3
- package/ftable.umd.js +468 -12
- package/package.json +1 -1
- package/themes/basic/ftable_basic.css +141 -0
- package/themes/basic/ftable_basic.min.css +1 -1
- package/themes/ftable_theme_base.less +166 -0
- package/themes/lightcolor/blue/ftable.css +141 -0
- package/themes/lightcolor/blue/ftable.min.css +1 -1
- package/themes/lightcolor/gray/ftable.css +141 -0
- package/themes/lightcolor/gray/ftable.min.css +1 -1
- package/themes/lightcolor/green/ftable.css +141 -0
- package/themes/lightcolor/green/ftable.min.css +1 -1
- package/themes/lightcolor/orange/ftable.css +141 -0
- package/themes/lightcolor/orange/ftable.min.css +1 -1
- package/themes/lightcolor/red/ftable.css +141 -0
- package/themes/lightcolor/red/ftable.min.css +1 -1
- package/themes/metro/blue/ftable.css +141 -0
- package/themes/metro/blue/ftable.min.css +1 -1
- package/themes/metro/brown/ftable.css +141 -0
- package/themes/metro/brown/ftable.min.css +1 -1
- package/themes/metro/crimson/ftable.css +141 -0
- package/themes/metro/crimson/ftable.min.css +1 -1
- package/themes/metro/darkgray/ftable.css +141 -0
- package/themes/metro/darkgray/ftable.min.css +1 -1
- package/themes/metro/darkorange/ftable.css +141 -0
- package/themes/metro/darkorange/ftable.min.css +1 -1
- package/themes/metro/green/ftable.css +141 -0
- package/themes/metro/green/ftable.min.css +1 -1
- package/themes/metro/lightgray/ftable.css +141 -0
- package/themes/metro/lightgray/ftable.min.css +1 -1
- package/themes/metro/pink/ftable.css +141 -0
- package/themes/metro/pink/ftable.min.css +1 -1
- package/themes/metro/purple/ftable.css +141 -0
- package/themes/metro/purple/ftable.min.css +1 -1
- package/themes/metro/red/ftable.css +141 -0
- package/themes/metro/red/ftable.min.css +1 -1
package/ftable.esm.js
CHANGED
|
@@ -1365,7 +1365,7 @@ class FTableFormBuilder {
|
|
|
1365
1365
|
id: `Edit-${fieldName}`,
|
|
1366
1366
|
className: field.inputClass || null,
|
|
1367
1367
|
placeholder: field.placeholder || null,
|
|
1368
|
-
value
|
|
1368
|
+
value: value
|
|
1369
1369
|
});
|
|
1370
1370
|
}
|
|
1371
1371
|
|
|
@@ -1374,10 +1374,10 @@ class FTableFormBuilder {
|
|
|
1374
1374
|
|
|
1375
1375
|
// extra check for name and multiple
|
|
1376
1376
|
let name = fieldName;
|
|
1377
|
+
let hasMultiple = false;
|
|
1378
|
+
|
|
1377
1379
|
// Apply inputAttributes from field definition
|
|
1378
1380
|
if (field.inputAttributes) {
|
|
1379
|
-
let hasMultiple = false;
|
|
1380
|
-
|
|
1381
1381
|
const parsed = this.parseInputAttributes(field.inputAttributes);
|
|
1382
1382
|
Object.assign(attributes, parsed);
|
|
1383
1383
|
|
|
@@ -1387,6 +1387,13 @@ class FTableFormBuilder {
|
|
|
1387
1387
|
name = `${fieldName}[]`;
|
|
1388
1388
|
}
|
|
1389
1389
|
}
|
|
1390
|
+
|
|
1391
|
+
// If multiple select, create custom UI
|
|
1392
|
+
if (hasMultiple) {
|
|
1393
|
+
return this.createCustomMultiSelect(fieldName, field, value, attributes, name);
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
// Standard single select
|
|
1390
1397
|
attributes.name = name;
|
|
1391
1398
|
|
|
1392
1399
|
const select = FTableDOMHelper.create('select', {
|
|
@@ -1403,6 +1410,208 @@ class FTableFormBuilder {
|
|
|
1403
1410
|
return select;
|
|
1404
1411
|
}
|
|
1405
1412
|
|
|
1413
|
+
createCustomMultiSelect(fieldName, field, value, attributes, name) {
|
|
1414
|
+
// Create container
|
|
1415
|
+
const container = FTableDOMHelper.create('div', {
|
|
1416
|
+
className: 'ftable-multiselect-container',
|
|
1417
|
+
attributes: { 'data-field-name': fieldName }
|
|
1418
|
+
});
|
|
1419
|
+
|
|
1420
|
+
// Create hidden input to store selected values
|
|
1421
|
+
const hiddenInput = FTableDOMHelper.create('input', {
|
|
1422
|
+
type: 'hidden',
|
|
1423
|
+
name: name,
|
|
1424
|
+
id: `Edit-${fieldName}`,
|
|
1425
|
+
value: Array.isArray(value) ? value.join(',') : value || ''
|
|
1426
|
+
});
|
|
1427
|
+
container.appendChild(hiddenInput);
|
|
1428
|
+
|
|
1429
|
+
// Create display area
|
|
1430
|
+
const display = FTableDOMHelper.create('div', {
|
|
1431
|
+
className: 'ftable-multiselect-display',
|
|
1432
|
+
parent: container
|
|
1433
|
+
});
|
|
1434
|
+
|
|
1435
|
+
const selectedDisplay = FTableDOMHelper.create('div', {
|
|
1436
|
+
className: 'ftable-multiselect-selected',
|
|
1437
|
+
parent: display
|
|
1438
|
+
});
|
|
1439
|
+
|
|
1440
|
+
const placeholderText = field.placeholder || this.options.messages.multiSelectPlaceholder || 'Click to select options...';
|
|
1441
|
+
const placeholder = FTableDOMHelper.create('span', {
|
|
1442
|
+
className: 'ftable-multiselect-placeholder',
|
|
1443
|
+
textContent: placeholderText,
|
|
1444
|
+
parent: selectedDisplay
|
|
1445
|
+
});
|
|
1446
|
+
|
|
1447
|
+
// Create dropdown toggle button
|
|
1448
|
+
const toggleBtn = FTableDOMHelper.create('button', {
|
|
1449
|
+
type: 'button',
|
|
1450
|
+
className: 'ftable-multiselect-toggle',
|
|
1451
|
+
innerHTML: '▼',
|
|
1452
|
+
parent: display
|
|
1453
|
+
});
|
|
1454
|
+
|
|
1455
|
+
// Create options dropdown
|
|
1456
|
+
const dropdown = FTableDOMHelper.create('div', {
|
|
1457
|
+
className: 'ftable-multiselect-dropdown',
|
|
1458
|
+
parent: container,
|
|
1459
|
+
style: 'display: none;'
|
|
1460
|
+
});
|
|
1461
|
+
|
|
1462
|
+
// Store selected values and checkbox references
|
|
1463
|
+
const selectedValues = new Set(
|
|
1464
|
+
Array.isArray(value) ? value :
|
|
1465
|
+
value ? value.toString().split(',').filter(v => v) : []
|
|
1466
|
+
);
|
|
1467
|
+
const checkboxMap = new Map(); // Map of value -> checkbox element
|
|
1468
|
+
|
|
1469
|
+
// Function to update display
|
|
1470
|
+
const updateDisplay = () => {
|
|
1471
|
+
selectedDisplay.innerHTML = '';
|
|
1472
|
+
|
|
1473
|
+
if (selectedValues.size === 0) {
|
|
1474
|
+
placeholder.textContent = placeholderText;
|
|
1475
|
+
selectedDisplay.appendChild(placeholder);
|
|
1476
|
+
} else {
|
|
1477
|
+
const selectedArray = Array.from(selectedValues);
|
|
1478
|
+
const optionsMap = new Map();
|
|
1479
|
+
|
|
1480
|
+
// Build options map
|
|
1481
|
+
if (field.options) {
|
|
1482
|
+
const options = Array.isArray(field.options) ? field.options :
|
|
1483
|
+
Object.entries(field.options).map(([k, v]) => ({Value: k, DisplayText: v}));
|
|
1484
|
+
|
|
1485
|
+
options.forEach(opt => {
|
|
1486
|
+
const val = opt.Value !== undefined ? opt.Value :
|
|
1487
|
+
opt.value !== undefined ? opt.value : opt;
|
|
1488
|
+
const text = opt.DisplayText || opt.text || opt;
|
|
1489
|
+
optionsMap.set(val.toString(), text);
|
|
1490
|
+
});
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
selectedArray.forEach(val => {
|
|
1494
|
+
const tag = FTableDOMHelper.create('span', {
|
|
1495
|
+
className: 'ftable-multiselect-tag',
|
|
1496
|
+
parent: selectedDisplay
|
|
1497
|
+
});
|
|
1498
|
+
|
|
1499
|
+
FTableDOMHelper.create('span', {
|
|
1500
|
+
className: 'ftable-multiselect-tag-text',
|
|
1501
|
+
textContent: optionsMap.get(val.toString()) || val,
|
|
1502
|
+
parent: tag
|
|
1503
|
+
});
|
|
1504
|
+
|
|
1505
|
+
const removeBtn = FTableDOMHelper.create('span', {
|
|
1506
|
+
className: 'ftable-multiselect-tag-remove',
|
|
1507
|
+
innerHTML: '×',
|
|
1508
|
+
parent: tag
|
|
1509
|
+
});
|
|
1510
|
+
|
|
1511
|
+
removeBtn.addEventListener('click', (e) => {
|
|
1512
|
+
e.stopPropagation();
|
|
1513
|
+
selectedValues.delete(val);
|
|
1514
|
+
// Update the checkbox state
|
|
1515
|
+
const checkbox = checkboxMap.get(val.toString());
|
|
1516
|
+
if (checkbox) {
|
|
1517
|
+
checkbox.checked = false;
|
|
1518
|
+
}
|
|
1519
|
+
updateDisplay();
|
|
1520
|
+
hiddenInput.value = Array.from(selectedValues).join(',');
|
|
1521
|
+
});
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
hiddenInput.value = Array.from(selectedValues).join(',');
|
|
1526
|
+
};
|
|
1527
|
+
|
|
1528
|
+
// Populate options
|
|
1529
|
+
const populateOptions = () => {
|
|
1530
|
+
if (!field.options) return;
|
|
1531
|
+
|
|
1532
|
+
const options = Array.isArray(field.options) ? field.options :
|
|
1533
|
+
Object.entries(field.options).map(([k, v]) => ({Value: k, DisplayText: v}));
|
|
1534
|
+
|
|
1535
|
+
options.forEach(option => {
|
|
1536
|
+
const optValue = option.Value !== undefined ? option.Value :
|
|
1537
|
+
option.value !== undefined ? option.value : option;
|
|
1538
|
+
|
|
1539
|
+
// Skip if value is empty
|
|
1540
|
+
if (optValue == null || optValue === '') {
|
|
1541
|
+
return; // This continues to the next iteration
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
const optText = option.DisplayText || option.text || option;
|
|
1545
|
+
|
|
1546
|
+
const optionDiv = FTableDOMHelper.create('div', {
|
|
1547
|
+
className: 'ftable-multiselect-option',
|
|
1548
|
+
parent: dropdown
|
|
1549
|
+
});
|
|
1550
|
+
|
|
1551
|
+
const checkbox = FTableDOMHelper.create('input', {
|
|
1552
|
+
type: 'checkbox',
|
|
1553
|
+
className: 'ftable-multiselect-checkbox',
|
|
1554
|
+
checked: selectedValues.has(optValue.toString()),
|
|
1555
|
+
parent: optionDiv
|
|
1556
|
+
});
|
|
1557
|
+
|
|
1558
|
+
// Store checkbox reference
|
|
1559
|
+
checkboxMap.set(optValue.toString(), checkbox);
|
|
1560
|
+
|
|
1561
|
+
const label = FTableDOMHelper.create('label', {
|
|
1562
|
+
className: 'ftable-multiselect-label',
|
|
1563
|
+
textContent: optText,
|
|
1564
|
+
parent: optionDiv
|
|
1565
|
+
});
|
|
1566
|
+
|
|
1567
|
+
// Click anywhere on the option to toggle
|
|
1568
|
+
optionDiv.addEventListener('click', (e) => {
|
|
1569
|
+
e.stopPropagation();
|
|
1570
|
+
|
|
1571
|
+
if (selectedValues.has(optValue.toString())) {
|
|
1572
|
+
selectedValues.delete(optValue.toString());
|
|
1573
|
+
checkbox.checked = false;
|
|
1574
|
+
} else {
|
|
1575
|
+
selectedValues.add(optValue.toString());
|
|
1576
|
+
checkbox.checked = true;
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
updateDisplay();
|
|
1580
|
+
});
|
|
1581
|
+
});
|
|
1582
|
+
};
|
|
1583
|
+
|
|
1584
|
+
// Toggle dropdown
|
|
1585
|
+
const toggleDropdown = (e) => {
|
|
1586
|
+
if (e) e.stopPropagation();
|
|
1587
|
+
const isVisible = dropdown.style.display !== 'none';
|
|
1588
|
+
dropdown.style.display = isVisible ? 'none' : 'block';
|
|
1589
|
+
|
|
1590
|
+
if (!isVisible) {
|
|
1591
|
+
// Close other dropdowns
|
|
1592
|
+
document.querySelectorAll('.ftable-multiselect-dropdown').forEach(dd => {
|
|
1593
|
+
if (dd !== dropdown) dd.style.display = 'none';
|
|
1594
|
+
});
|
|
1595
|
+
}
|
|
1596
|
+
};
|
|
1597
|
+
|
|
1598
|
+
display.addEventListener('click', toggleDropdown);
|
|
1599
|
+
toggleBtn.addEventListener('click', toggleDropdown);
|
|
1600
|
+
|
|
1601
|
+
// Close dropdown when clicking outside
|
|
1602
|
+
document.addEventListener('click', (e) => {
|
|
1603
|
+
if (!container.contains(e.target)) {
|
|
1604
|
+
dropdown.style.display = 'none';
|
|
1605
|
+
}
|
|
1606
|
+
});
|
|
1607
|
+
|
|
1608
|
+
// Initialize
|
|
1609
|
+
populateOptions();
|
|
1610
|
+
updateDisplay();
|
|
1611
|
+
|
|
1612
|
+
return container;
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1406
1615
|
createRadioGroup(fieldName, field, value) {
|
|
1407
1616
|
const wrapper = FTableDOMHelper.create('div', {
|
|
1408
1617
|
className: 'ftable-radio-group'
|
|
@@ -2308,12 +2517,19 @@ class FTable extends FTableEventEmitter {
|
|
|
2308
2517
|
container.appendChild(input.datalistElement);
|
|
2309
2518
|
}
|
|
2310
2519
|
|
|
2311
|
-
|
|
2312
|
-
|
|
2520
|
+
// Handle event listeners - check if it's a custom multiselect container
|
|
2521
|
+
let targetElement = input;
|
|
2522
|
+
if (input.classList && input.classList.contains('ftable-multiselect-container') && input.hiddenSelect) {
|
|
2523
|
+
// It's a custom multiselect - attach listener to the hidden select
|
|
2524
|
+
targetElement = input.hiddenSelect;
|
|
2525
|
+
}
|
|
2526
|
+
|
|
2527
|
+
if (targetElement.tagName === 'SELECT') {
|
|
2528
|
+
targetElement.addEventListener('change', (e) => {
|
|
2313
2529
|
this.handleSearchInputChange(e);
|
|
2314
2530
|
});
|
|
2315
2531
|
} else {
|
|
2316
|
-
|
|
2532
|
+
targetElement.addEventListener('input', (e) => {
|
|
2317
2533
|
this.handleSearchInputChange(e);
|
|
2318
2534
|
});
|
|
2319
2535
|
}
|
|
@@ -2378,12 +2594,6 @@ class FTable extends FTableEventEmitter {
|
|
|
2378
2594
|
}
|
|
2379
2595
|
attributes['data-field-name'] = name;
|
|
2380
2596
|
|
|
2381
|
-
const select = FTableDOMHelper.create('select', {
|
|
2382
|
-
attributes: attributes,
|
|
2383
|
-
id: fieldSearchName,
|
|
2384
|
-
className: 'ftable-toolbarsearch'
|
|
2385
|
-
});
|
|
2386
|
-
|
|
2387
2597
|
let optionsSource;
|
|
2388
2598
|
if (isCheckboxValues && field.values) {
|
|
2389
2599
|
optionsSource = Object.entries(field.values).map(([value, displayText]) => ({
|
|
@@ -2394,6 +2604,24 @@ class FTable extends FTableEventEmitter {
|
|
|
2394
2604
|
optionsSource = await this.formBuilder.getFieldOptions(fieldName);
|
|
2395
2605
|
}
|
|
2396
2606
|
|
|
2607
|
+
// If multiple, create custom UI
|
|
2608
|
+
if (hasMultiple) {
|
|
2609
|
+
return this.createCustomMultiSelectForSearch(
|
|
2610
|
+
fieldSearchName,
|
|
2611
|
+
fieldName,
|
|
2612
|
+
field,
|
|
2613
|
+
optionsSource,
|
|
2614
|
+
attributes
|
|
2615
|
+
);
|
|
2616
|
+
}
|
|
2617
|
+
|
|
2618
|
+
// Standard single select
|
|
2619
|
+
const select = FTableDOMHelper.create('select', {
|
|
2620
|
+
attributes: attributes,
|
|
2621
|
+
id: fieldSearchName,
|
|
2622
|
+
className: 'ftable-toolbarsearch'
|
|
2623
|
+
});
|
|
2624
|
+
|
|
2397
2625
|
// Add empty option only if first option is not already empty
|
|
2398
2626
|
const hasEmptyFirst = optionsSource?.length > 0 &&
|
|
2399
2627
|
(optionsSource[0].Value === '' ||
|
|
@@ -2430,6 +2658,226 @@ class FTable extends FTableEventEmitter {
|
|
|
2430
2658
|
return select;
|
|
2431
2659
|
}
|
|
2432
2660
|
|
|
2661
|
+
createCustomMultiSelectForSearch(fieldSearchName, fieldName, field, optionsSource, attributes) {
|
|
2662
|
+
// Create container
|
|
2663
|
+
const container = FTableDOMHelper.create('div', {
|
|
2664
|
+
className: 'ftable-multiselect-container ftable-multiselect-search ftable-toolbarsearch',
|
|
2665
|
+
attributes: { 'data-field-name': attributes['data-field-name'] }
|
|
2666
|
+
});
|
|
2667
|
+
|
|
2668
|
+
// Create hidden select to maintain compatibility with existing search logic
|
|
2669
|
+
const hiddenSelect = FTableDOMHelper.create('select', {
|
|
2670
|
+
id: fieldSearchName,
|
|
2671
|
+
multiple: true,
|
|
2672
|
+
style: 'display: none;',
|
|
2673
|
+
attributes: attributes
|
|
2674
|
+
});
|
|
2675
|
+
container.appendChild(hiddenSelect);
|
|
2676
|
+
|
|
2677
|
+
// Expose hidden select for external access (needed for event listeners and reset)
|
|
2678
|
+
container.hiddenSelect = hiddenSelect;
|
|
2679
|
+
|
|
2680
|
+
// Create display area
|
|
2681
|
+
const display = FTableDOMHelper.create('div', {
|
|
2682
|
+
className: 'ftable-multiselect-display',
|
|
2683
|
+
parent: container
|
|
2684
|
+
});
|
|
2685
|
+
|
|
2686
|
+
const selectedDisplay = FTableDOMHelper.create('div', {
|
|
2687
|
+
className: 'ftable-multiselect-selected',
|
|
2688
|
+
parent: display
|
|
2689
|
+
});
|
|
2690
|
+
|
|
2691
|
+
const placeholderText = field.searchPlaceholder || field.placeholder || this.options.messages.multiSelectPlaceholder || 'Click to select options...';
|
|
2692
|
+
const placeholder = FTableDOMHelper.create('span', {
|
|
2693
|
+
className: 'ftable-multiselect-placeholder',
|
|
2694
|
+
textContent: placeholderText,
|
|
2695
|
+
parent: selectedDisplay
|
|
2696
|
+
});
|
|
2697
|
+
|
|
2698
|
+
// Create dropdown toggle button
|
|
2699
|
+
const toggleBtn = FTableDOMHelper.create('button', {
|
|
2700
|
+
type: 'button',
|
|
2701
|
+
className: 'ftable-multiselect-toggle',
|
|
2702
|
+
innerHTML: '▼',
|
|
2703
|
+
parent: display
|
|
2704
|
+
});
|
|
2705
|
+
|
|
2706
|
+
// Create options dropdown
|
|
2707
|
+
const dropdown = FTableDOMHelper.create('div', {
|
|
2708
|
+
className: 'ftable-multiselect-dropdown',
|
|
2709
|
+
parent: container,
|
|
2710
|
+
style: 'display: none;'
|
|
2711
|
+
});
|
|
2712
|
+
|
|
2713
|
+
// Store selected values and checkbox references
|
|
2714
|
+
const selectedValues = new Set();
|
|
2715
|
+
const checkboxMap = new Map(); // Map of value -> checkbox element
|
|
2716
|
+
|
|
2717
|
+
// Function to update display and hidden select
|
|
2718
|
+
const updateDisplay = () => {
|
|
2719
|
+
selectedDisplay.innerHTML = '';
|
|
2720
|
+
|
|
2721
|
+
// Update hidden select
|
|
2722
|
+
Array.from(hiddenSelect.options).forEach(opt => {
|
|
2723
|
+
opt.selected = selectedValues.has(opt.value);
|
|
2724
|
+
});
|
|
2725
|
+
|
|
2726
|
+
// Trigger change event on hidden select for search functionality
|
|
2727
|
+
hiddenSelect.dispatchEvent(new Event('change', { bubbles: true }));
|
|
2728
|
+
|
|
2729
|
+
if (selectedValues.size === 0) {
|
|
2730
|
+
placeholder.textContent = placeholderText;
|
|
2731
|
+
selectedDisplay.appendChild(placeholder);
|
|
2732
|
+
} else {
|
|
2733
|
+
const selectedArray = Array.from(selectedValues);
|
|
2734
|
+
const optionsMap = new Map();
|
|
2735
|
+
|
|
2736
|
+
// Build options map
|
|
2737
|
+
if (optionsSource && Array.isArray(optionsSource)) {
|
|
2738
|
+
optionsSource.forEach(opt => {
|
|
2739
|
+
const val = opt.Value !== undefined ? opt.Value :
|
|
2740
|
+
opt.value !== undefined ? opt.value : opt;
|
|
2741
|
+
const text = opt.DisplayText || opt.text || opt;
|
|
2742
|
+
optionsMap.set(val.toString(), text);
|
|
2743
|
+
});
|
|
2744
|
+
}
|
|
2745
|
+
|
|
2746
|
+
selectedArray.forEach(val => {
|
|
2747
|
+
const tag = FTableDOMHelper.create('span', {
|
|
2748
|
+
className: 'ftable-multiselect-tag',
|
|
2749
|
+
parent: selectedDisplay
|
|
2750
|
+
});
|
|
2751
|
+
|
|
2752
|
+
FTableDOMHelper.create('span', {
|
|
2753
|
+
className: 'ftable-multiselect-tag-text',
|
|
2754
|
+
textContent: optionsMap.get(val.toString()) || val,
|
|
2755
|
+
parent: tag
|
|
2756
|
+
});
|
|
2757
|
+
|
|
2758
|
+
const removeBtn = FTableDOMHelper.create('span', {
|
|
2759
|
+
className: 'ftable-multiselect-tag-remove',
|
|
2760
|
+
innerHTML: '×',
|
|
2761
|
+
parent: tag
|
|
2762
|
+
});
|
|
2763
|
+
|
|
2764
|
+
removeBtn.addEventListener('click', (e) => {
|
|
2765
|
+
e.stopPropagation();
|
|
2766
|
+
selectedValues.delete(val);
|
|
2767
|
+
// Update the checkbox state
|
|
2768
|
+
const checkbox = checkboxMap.get(val.toString());
|
|
2769
|
+
if (checkbox) {
|
|
2770
|
+
checkbox.checked = false;
|
|
2771
|
+
}
|
|
2772
|
+
updateDisplay();
|
|
2773
|
+
});
|
|
2774
|
+
});
|
|
2775
|
+
}
|
|
2776
|
+
};
|
|
2777
|
+
|
|
2778
|
+
// Add reset method to container
|
|
2779
|
+
container.resetMultiSelect = () => {
|
|
2780
|
+
selectedValues.clear();
|
|
2781
|
+
checkboxMap.forEach(checkbox => {
|
|
2782
|
+
checkbox.checked = false;
|
|
2783
|
+
});
|
|
2784
|
+
updateDisplay();
|
|
2785
|
+
};
|
|
2786
|
+
|
|
2787
|
+
// Populate options in both hidden select and dropdown
|
|
2788
|
+
const populateOptions = () => {
|
|
2789
|
+
if (!optionsSource) return;
|
|
2790
|
+
|
|
2791
|
+
const options = Array.isArray(optionsSource) ? optionsSource :
|
|
2792
|
+
Object.entries(optionsSource).map(([k, v]) => ({Value: k, DisplayText: v}));
|
|
2793
|
+
|
|
2794
|
+
options.forEach(option => {
|
|
2795
|
+
const optValue = option.Value !== undefined ? option.Value :
|
|
2796
|
+
option.value !== undefined ? option.value : option;
|
|
2797
|
+
|
|
2798
|
+
// Skip if value is empty
|
|
2799
|
+
if (optValue == null || optValue === '') {
|
|
2800
|
+
return; // This continues to the next iteration
|
|
2801
|
+
}
|
|
2802
|
+
|
|
2803
|
+
const optText = option.DisplayText || option.text || option;
|
|
2804
|
+
|
|
2805
|
+
// Add to hidden select
|
|
2806
|
+
FTableDOMHelper.create('option', {
|
|
2807
|
+
value: optValue,
|
|
2808
|
+
textContent: optText,
|
|
2809
|
+
parent: hiddenSelect
|
|
2810
|
+
});
|
|
2811
|
+
|
|
2812
|
+
// Add to visual dropdown
|
|
2813
|
+
const optionDiv = FTableDOMHelper.create('div', {
|
|
2814
|
+
className: 'ftable-multiselect-option',
|
|
2815
|
+
parent: dropdown
|
|
2816
|
+
});
|
|
2817
|
+
|
|
2818
|
+
const checkbox = FTableDOMHelper.create('input', {
|
|
2819
|
+
type: 'checkbox',
|
|
2820
|
+
className: 'ftable-multiselect-checkbox',
|
|
2821
|
+
parent: optionDiv
|
|
2822
|
+
});
|
|
2823
|
+
|
|
2824
|
+
// Store checkbox reference
|
|
2825
|
+
checkboxMap.set(optValue.toString(), checkbox);
|
|
2826
|
+
|
|
2827
|
+
const label = FTableDOMHelper.create('label', {
|
|
2828
|
+
className: 'ftable-multiselect-label',
|
|
2829
|
+
textContent: optText,
|
|
2830
|
+
parent: optionDiv
|
|
2831
|
+
});
|
|
2832
|
+
|
|
2833
|
+
// Click anywhere on the option to toggle
|
|
2834
|
+
optionDiv.addEventListener('click', (e) => {
|
|
2835
|
+
e.stopPropagation();
|
|
2836
|
+
|
|
2837
|
+
if (selectedValues.has(optValue.toString())) {
|
|
2838
|
+
selectedValues.delete(optValue.toString());
|
|
2839
|
+
checkbox.checked = false;
|
|
2840
|
+
} else {
|
|
2841
|
+
selectedValues.add(optValue.toString());
|
|
2842
|
+
checkbox.checked = true;
|
|
2843
|
+
}
|
|
2844
|
+
|
|
2845
|
+
updateDisplay();
|
|
2846
|
+
});
|
|
2847
|
+
});
|
|
2848
|
+
};
|
|
2849
|
+
|
|
2850
|
+
// Toggle dropdown
|
|
2851
|
+
const toggleDropdown = (e) => {
|
|
2852
|
+
if (e) e.stopPropagation();
|
|
2853
|
+
const isVisible = dropdown.style.display !== 'none';
|
|
2854
|
+
dropdown.style.display = isVisible ? 'none' : 'block';
|
|
2855
|
+
|
|
2856
|
+
if (!isVisible) {
|
|
2857
|
+
// Close other dropdowns
|
|
2858
|
+
document.querySelectorAll('.ftable-multiselect-dropdown').forEach(dd => {
|
|
2859
|
+
if (dd !== dropdown) dd.style.display = 'none';
|
|
2860
|
+
});
|
|
2861
|
+
}
|
|
2862
|
+
};
|
|
2863
|
+
|
|
2864
|
+
display.addEventListener('click', toggleDropdown);
|
|
2865
|
+
toggleBtn.addEventListener('click', toggleDropdown);
|
|
2866
|
+
|
|
2867
|
+
// Close dropdown when clicking outside
|
|
2868
|
+
document.addEventListener('click', (e) => {
|
|
2869
|
+
if (!container.contains(e.target)) {
|
|
2870
|
+
dropdown.style.display = 'none';
|
|
2871
|
+
}
|
|
2872
|
+
});
|
|
2873
|
+
|
|
2874
|
+
// Initialize
|
|
2875
|
+
populateOptions();
|
|
2876
|
+
updateDisplay();
|
|
2877
|
+
|
|
2878
|
+
return container;
|
|
2879
|
+
}
|
|
2880
|
+
|
|
2433
2881
|
async createDatalistForSearch(fieldName, field) {
|
|
2434
2882
|
const fieldSearchName = 'ftable-toolbarsearch-' + fieldName;
|
|
2435
2883
|
|
|
@@ -2520,6 +2968,14 @@ class FTable extends FTableEventEmitter {
|
|
|
2520
2968
|
}
|
|
2521
2969
|
});
|
|
2522
2970
|
|
|
2971
|
+
// Clear custom multiselect containers
|
|
2972
|
+
const multiSelectContainers = this.elements.table.querySelectorAll('.ftable-multiselect-container');
|
|
2973
|
+
multiSelectContainers.forEach(container => {
|
|
2974
|
+
if (typeof container.resetMultiSelect === 'function') {
|
|
2975
|
+
container.resetMultiSelect();
|
|
2976
|
+
}
|
|
2977
|
+
});
|
|
2978
|
+
|
|
2523
2979
|
// Reload data without search parameters
|
|
2524
2980
|
this.load();
|
|
2525
2981
|
}
|