inviton-powerduck 0.0.226 → 0.0.228

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.
@@ -237,6 +237,15 @@
237
237
  min-height: 150px;
238
238
  }
239
239
 
240
+ .list-header {
241
+ padding: 12px 16px;
242
+ font-size: 0.85rem;
243
+ color: #6c757d;
244
+ background: #f8f9fa;
245
+ border-bottom: 1px solid #e9ecef;
246
+ font-style: italic;
247
+ }
248
+
240
249
  .list-group-label {
241
250
  padding: 8px 16px;
242
251
  font-size: 0.75rem;
@@ -81,6 +81,10 @@ interface SmartDropdownArgs extends FormItemWrapperArgs {
81
81
  changed: (e: SmartDropdownItem[]) => void;
82
82
  /** Called when an item is clicked. Return true to prevent default selection behavior. */
83
83
  onItemClick?: (item: SmartDropdownItem) => boolean;
84
+ /** Delay in ms before showing the loading indicator. Default 650ms. */
85
+ loadingIndicatorDelay?: number;
86
+ /** Optional header shown at the top of the list to guide users. Can be a string or VNode. */
87
+ listHeader?: string | VNode;
84
88
  }
85
89
 
86
90
  @Component
@@ -121,13 +125,17 @@ export default class SmartDropdown extends TsxComponent<SmartDropdownArgs> imple
121
125
  @Prop({ type: Function }) readonly customTriggerRender?: () => VNode;
122
126
  @Prop() readonly changed: (e: SmartDropdownItem[]) => void;
123
127
  @Prop({ type: Function }) readonly onItemClick?: (item: SmartDropdownItem) => boolean;
128
+ @Prop({ type: Number, default: 650 }) readonly loadingIndicatorDelay!: number;
129
+ @Prop() readonly listHeader?: string | VNode;
124
130
 
125
131
  // --- State ---
126
132
  isOpen = false;
127
133
  searchQuery = '';
128
134
  searchResults: SmartDropdownSearchResultItem[] = [];
129
135
  isLoading = false;
136
+ isSearchPending = false;
130
137
  debounceTimer: number | null = null;
138
+ loadingIndicatorTimer: number | null = null;
131
139
  focusedIndex = -1;
132
140
  triggerInputValue = '';
133
141
 
@@ -415,20 +423,36 @@ export default class SmartDropdown extends TsxComponent<SmartDropdownArgs> imple
415
423
  clearTimeout(this.debounceTimer);
416
424
  }
417
425
 
426
+ if (this.loadingIndicatorTimer) {
427
+ clearTimeout(this.loadingIndicatorTimer);
428
+ this.loadingIndicatorTimer = null;
429
+ }
430
+
418
431
  if (val.trim().length === 0) {
419
432
  this.isLoading = false;
433
+ this.isSearchPending = false;
420
434
  this.searchResults = [];
421
435
  return;
422
436
  }
423
437
 
424
- this.isLoading = true;
438
+ this.isSearchPending = true;
425
439
  this.debounceTimer = window.setTimeout(async () => {
440
+ // Start timer to show loading indicator after delay
441
+ this.loadingIndicatorTimer = window.setTimeout(() => {
442
+ this.isLoading = true;
443
+ }, this.loadingIndicatorDelay);
426
444
  try {
427
445
  this.searchResults = await this.searchData(val);
428
446
  } catch (err) {
429
447
  this.searchResults = [];
430
448
  } finally {
449
+ if (this.loadingIndicatorTimer) {
450
+ clearTimeout(this.loadingIndicatorTimer);
451
+ this.loadingIndicatorTimer = null;
452
+ }
453
+
431
454
  this.isLoading = false;
455
+ this.isSearchPending = false;
432
456
  }
433
457
  }, 800);
434
458
  }
@@ -449,7 +473,10 @@ export default class SmartDropdown extends TsxComponent<SmartDropdownArgs> imple
449
473
  if (this.multiselect) {
450
474
  if (shouldBeSelected && !isCurrentlySelected) {
451
475
  // Add item
452
- newSelection = [...this.value, item];
476
+ newSelection = [
477
+ ...this.value,
478
+ item,
479
+ ];
453
480
  } else if (!shouldBeSelected && isCurrentlySelected) {
454
481
  // Remove item
455
482
  newSelection = this.value.filter(i => i.id !== item.id);
@@ -576,6 +603,11 @@ export default class SmartDropdown extends TsxComponent<SmartDropdownArgs> imple
576
603
  // A. Search Results
577
604
  if (this.isSearchActive) {
578
605
  if (this.searchResults.length === 0) {
606
+ // Show loading if search is pending or in progress, otherwise show no results
607
+ if (this.isSearchPending) {
608
+ return <div class="loading-state" role="status">{SmartDropdownResources.loadingTextLong}</div>;
609
+ }
610
+
579
611
  return <div class="empty-state" role="status">{SmartDropdownResources.noResultsSearch}</div>;
580
612
  }
581
613
 
@@ -597,6 +629,11 @@ export default class SmartDropdown extends TsxComponent<SmartDropdownArgs> imple
597
629
 
598
630
  return (
599
631
  <div class="list-wrapper">
632
+ {/* 0. List Header */}
633
+ {this.listHeader && (
634
+ <div class="list-header" role="note">{this.listHeader}</div>
635
+ )}
636
+
600
637
  {/* 1. Pinned Selected */}
601
638
  {hasPinned && (
602
639
  <div class="list-group section-pinned" role="group" aria-label={SmartDropdownResources.selectedTitle}>
@@ -122,7 +122,7 @@ class CheckBoxComponent extends TsxComponent<CheckBoxArgs> implements CheckBoxAr
122
122
  <label class="form-check-label">
123
123
  <input class="form-check-input" type="checkbox" checked={this.value} disabled={this.disabled || undefined} onChange={e => this.raiseChangeEvent(e)} />
124
124
  <span class="form-check-sign"></span>
125
- {this.getCustomRenderedLabel != null ? this.getCustomRenderedLabel(h) : this.checkboxLabelHtml}
125
+ {this.getCustomRenderedLabel(h) ?? this.checkboxLabelHtml}
126
126
  </label>
127
127
  </div>
128
128
  );
@@ -140,7 +140,7 @@ class CheckBoxComponent extends TsxComponent<CheckBoxArgs> implements CheckBoxAr
140
140
  <div class="inv-cbrb-inner">
141
141
  <input class="inv-chckb-clickable" type="checkbox" id={`iei-input-${this.uuid}`} checked={this.value} disabled={this.disabled || undefined} onChange={e => this.raiseChangeEvent(e)} />
142
142
  <label class="inv-chckb-clickable form-check-label " for={`iei-input-${this.uuid}`}>
143
- {this.getCustomRenderedLabel != null ? this.getCustomRenderedLabel(h) : <HtmlLiteral innerHTML={this.checkboxLabelHtml} />}
143
+ {this.getCustomRenderedLabel(h) ?? <HtmlLiteral innerHTML={this.checkboxLabelHtml} />}
144
144
  </label>
145
145
  </div>
146
146
  </div>
@@ -160,7 +160,7 @@ class CheckBoxComponent extends TsxComponent<CheckBoxArgs> implements CheckBoxAr
160
160
  <div class="inv-cbrb-inner">
161
161
  <input class="inv-chckb-clickable" type="checkbox" id={`iei-input-${this.uuid}`} checked={this.value} disabled={this.disabled || undefined} onChange={e => this.raiseChangeEvent(e)} />
162
162
  <label class="inv-chckb-clickable form-check-label " for={`iei-input-${this.uuid}`}>
163
- {this.getCustomRenderedLabel != null ? this.getCustomRenderedLabel(h) : <HtmlLiteral innerHTML={this.checkboxLabelHtml} />}
163
+ {this.getCustomRenderedLabel(h) ?? <HtmlLiteral innerHTML={this.checkboxLabelHtml} />}
164
164
  </label>
165
165
  </div>
166
166
  </div>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "inviton-powerduck",
3
3
  "type": "module",
4
- "version": "0.0.226",
4
+ "version": "0.0.228",
5
5
  "files": [
6
6
  "app/",
7
7
  "common/",