ng-primitives 0.99.6 → 0.100.0
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/a11y/index.d.ts +68 -24
- package/combobox/index.d.ts +49 -16
- package/fesm2022/ng-primitives-a11y.mjs +116 -96
- package/fesm2022/ng-primitives-a11y.mjs.map +1 -1
- package/fesm2022/ng-primitives-combobox.mjs +151 -51
- package/fesm2022/ng-primitives-combobox.mjs.map +1 -1
- package/fesm2022/ng-primitives-internal.mjs +27 -1
- package/fesm2022/ng-primitives-internal.mjs.map +1 -1
- package/fesm2022/ng-primitives-select.mjs +137 -48
- package/fesm2022/ng-primitives-select.mjs.map +1 -1
- package/fesm2022/ng-primitives-slider.mjs +0 -1
- package/fesm2022/ng-primitives-slider.mjs.map +1 -1
- package/internal/index.d.ts +10 -1
- package/package.json +1 -1
- package/schematics/mcp-setup/index.js +0 -1
- package/schematics/mcp-setup/index.js.map +1 -1
- package/select/index.d.ts +41 -17
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input, computed, HostListener, Directive, booleanAttribute, inject, ViewContainerRef, TemplateRef, Injector, signal, InjectionToken, output } from '@angular/core';
|
|
2
|
+
import { input, computed, HostListener, Directive, booleanAttribute, numberAttribute, inject, ViewContainerRef, TemplateRef, Injector, signal, InjectionToken, output } from '@angular/core';
|
|
3
3
|
import { ngpInteractions } from 'ng-primitives/interactions';
|
|
4
|
-
import { injectElementRef, observeResize } from 'ng-primitives/internal';
|
|
4
|
+
import { injectElementRef, observeResize, scrollIntoViewIfNeeded, domSort } from 'ng-primitives/internal';
|
|
5
5
|
import { uniqueId } from 'ng-primitives/utils';
|
|
6
6
|
import { createStateToken, createStateProvider, createStateInjector, createState } from 'ng-primitives/state';
|
|
7
7
|
import { ngpFormControl } from 'ng-primitives/form-field';
|
|
@@ -129,7 +129,7 @@ class NgpComboboxInput {
|
|
|
129
129
|
/** The id of the dropdown. */
|
|
130
130
|
this.dropdownId = computed(() => this.state().dropdown()?.id(), ...(ngDevMode ? [{ debugName: "dropdownId" }] : []));
|
|
131
131
|
/** The id of the active descendant. */
|
|
132
|
-
this.activeDescendant = computed(() => this.state().activeDescendantManager.
|
|
132
|
+
this.activeDescendant = computed(() => this.state().activeDescendantManager.id(), ...(ngDevMode ? [{ debugName: "activeDescendant" }] : []));
|
|
133
133
|
/** Determine if the pointer was used to focus the input. */
|
|
134
134
|
this.pointerFocused = false;
|
|
135
135
|
/**
|
|
@@ -181,7 +181,7 @@ class NgpComboboxInput {
|
|
|
181
181
|
break;
|
|
182
182
|
case 'Enter':
|
|
183
183
|
if (this.state().open()) {
|
|
184
|
-
const activeItem = this.state().activeDescendantManager.
|
|
184
|
+
const activeItem = this.state().activeDescendantManager.id();
|
|
185
185
|
if (activeItem) {
|
|
186
186
|
this.state().toggleOption(activeItem);
|
|
187
187
|
}
|
|
@@ -315,11 +315,28 @@ class NgpComboboxOption {
|
|
|
315
315
|
alias: 'ngpComboboxOptionDisabled',
|
|
316
316
|
transform: booleanAttribute,
|
|
317
317
|
}]));
|
|
318
|
+
/**
|
|
319
|
+
* The index of the option in the combobox. This can be used to define the order of options
|
|
320
|
+
* when virtual scrolling is used or when the order is not determined by DOM order.
|
|
321
|
+
*/
|
|
322
|
+
this.index = input(undefined, ...(ngDevMode ? [{ debugName: "index", alias: 'ngpComboboxOptionIndex',
|
|
323
|
+
transform: numberAttribute }] : [{
|
|
324
|
+
alias: 'ngpComboboxOptionIndex',
|
|
325
|
+
transform: numberAttribute,
|
|
326
|
+
}]));
|
|
318
327
|
/**
|
|
319
328
|
* Whether this option is the active descendant.
|
|
320
329
|
* @internal
|
|
321
330
|
*/
|
|
322
|
-
this.active = computed(() =>
|
|
331
|
+
this.active = computed(() => {
|
|
332
|
+
// if the option has an index, use that to determine if it's active because this
|
|
333
|
+
// is required for virtual scrolling scenarios
|
|
334
|
+
const index = this.index();
|
|
335
|
+
if (index !== undefined) {
|
|
336
|
+
return this.state().activeDescendantManager.index() === index;
|
|
337
|
+
}
|
|
338
|
+
return this.state().activeDescendantManager.id() === this.id();
|
|
339
|
+
}, ...(ngDevMode ? [{ debugName: "active" }] : []));
|
|
323
340
|
/** Whether this option is selected. */
|
|
324
341
|
this.selected = computed(() => {
|
|
325
342
|
const value = this.value();
|
|
@@ -366,31 +383,46 @@ class NgpComboboxOption {
|
|
|
366
383
|
if (this.disabled()) {
|
|
367
384
|
return;
|
|
368
385
|
}
|
|
369
|
-
this.state().toggleOption(this);
|
|
386
|
+
this.state().toggleOption(this.id());
|
|
370
387
|
}
|
|
371
388
|
/**
|
|
372
389
|
* Scroll the option into view.
|
|
373
390
|
* @internal
|
|
374
391
|
*/
|
|
375
392
|
scrollIntoView() {
|
|
376
|
-
this.elementRef.nativeElement
|
|
393
|
+
scrollIntoViewIfNeeded(this.elementRef.nativeElement);
|
|
377
394
|
}
|
|
378
395
|
/**
|
|
379
396
|
* Whenever the pointer enters the option, activate it.
|
|
380
397
|
* @internal
|
|
381
398
|
*/
|
|
382
399
|
onPointerEnter() {
|
|
383
|
-
|
|
400
|
+
// if we have a known index, use that to activate the option (required for virtual scrolling)
|
|
401
|
+
const index = this.index();
|
|
402
|
+
if (index !== undefined) {
|
|
403
|
+
this.state().activeDescendantManager.activateByIndex(index, {
|
|
404
|
+
scroll: false,
|
|
405
|
+
origin: 'pointer',
|
|
406
|
+
});
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
// otherwise, activate by id
|
|
410
|
+
this.state().activeDescendantManager.activateById(this.id(), {
|
|
411
|
+
scroll: false,
|
|
412
|
+
origin: 'pointer',
|
|
413
|
+
});
|
|
384
414
|
}
|
|
385
415
|
/**
|
|
386
416
|
* Whenever the pointer leaves the option, deactivate it.
|
|
387
417
|
* @internal
|
|
388
418
|
*/
|
|
389
419
|
onPointerLeave() {
|
|
390
|
-
this.state().activeDescendantManager.
|
|
420
|
+
if (this.state().activeDescendantManager.id() === this.id()) {
|
|
421
|
+
this.state().activeDescendantManager.reset({ origin: 'pointer' });
|
|
422
|
+
}
|
|
391
423
|
}
|
|
392
424
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpComboboxOption, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
393
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpComboboxOption, isStandalone: true, selector: "[ngpComboboxOption]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "ngpComboboxOptionValue", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "ngpComboboxOptionDisabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "option" }, listeners: { "click": "select()", "pointerenter": "onPointerEnter()", "pointerleave": "onPointerLeave()" }, properties: { "id": "id()", "attr.tabindex": "-1", "attr.aria-selected": "selected() ? \"true\" : undefined", "attr.data-selected": "selected() ? \"\" : undefined", "attr.data-active": "active() ? \"\" : undefined", "attr.data-disabled": "disabled() ? \"\" : undefined" } }, exportAs: ["ngpComboboxOption"], ngImport: i0 }); }
|
|
425
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpComboboxOption, isStandalone: true, selector: "[ngpComboboxOption]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "ngpComboboxOptionValue", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "ngpComboboxOptionDisabled", isSignal: true, isRequired: false, transformFunction: null }, index: { classPropertyName: "index", publicName: "ngpComboboxOptionIndex", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "option" }, listeners: { "click": "select()", "pointerenter": "onPointerEnter()", "pointerleave": "onPointerLeave()" }, properties: { "id": "id()", "attr.tabindex": "-1", "attr.aria-selected": "selected() ? \"true\" : undefined", "attr.data-selected": "selected() ? \"\" : undefined", "attr.data-active": "active() ? \"\" : undefined", "attr.data-disabled": "disabled() ? \"\" : undefined" } }, exportAs: ["ngpComboboxOption"], ngImport: i0 }); }
|
|
394
426
|
}
|
|
395
427
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpComboboxOption, decorators: [{
|
|
396
428
|
type: Directive,
|
|
@@ -408,7 +440,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImpor
|
|
|
408
440
|
'(click)': 'select()',
|
|
409
441
|
},
|
|
410
442
|
}]
|
|
411
|
-
}], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpComboboxOptionValue", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpComboboxOptionDisabled", required: false }] }], onPointerEnter: [{
|
|
443
|
+
}], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpComboboxOptionValue", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpComboboxOptionDisabled", required: false }] }], index: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpComboboxOptionIndex", required: false }] }], onPointerEnter: [{
|
|
412
444
|
type: HostListener,
|
|
413
445
|
args: ['pointerenter']
|
|
414
446
|
}], onPointerLeave: [{
|
|
@@ -562,6 +594,31 @@ class NgpCombobox {
|
|
|
562
594
|
this.container = input(this.config.container, ...(ngDevMode ? [{ debugName: "container", alias: 'ngpComboboxDropdownContainer' }] : [{
|
|
563
595
|
alias: 'ngpComboboxDropdownContainer',
|
|
564
596
|
}]));
|
|
597
|
+
/**
|
|
598
|
+
* A function that will scroll the active option into view. This can be overridden
|
|
599
|
+
* for cases such as virtual scrolling where we cannot scroll the option directly because
|
|
600
|
+
* it may not be rendered.
|
|
601
|
+
*/
|
|
602
|
+
this.scrollToOption = input(undefined, ...(ngDevMode ? [{ debugName: "scrollToOption", alias: 'ngpComboboxScrollToOption' }] : [{
|
|
603
|
+
alias: 'ngpComboboxScrollToOption',
|
|
604
|
+
}]));
|
|
605
|
+
/**
|
|
606
|
+
* The number of options within the combobox. By default this is calculated based on the
|
|
607
|
+
* options added to the combobox, but in virtual scrolling scenarios the total number of options
|
|
608
|
+
* may be different from the number of rendered options.
|
|
609
|
+
*/
|
|
610
|
+
this.optionCount = input(undefined, ...(ngDevMode ? [{ debugName: "optionCount", alias: 'ngpComboboxOptionCount',
|
|
611
|
+
transform: numberAttribute }] : [{
|
|
612
|
+
alias: 'ngpComboboxOptionCount',
|
|
613
|
+
transform: numberAttribute,
|
|
614
|
+
}]));
|
|
615
|
+
/**
|
|
616
|
+
* Provide all the option values to the combobox. This is useful for virtual scrolling scenarios
|
|
617
|
+
* where not all options are rendered in the DOM. This is not an alternative to adding the options
|
|
618
|
+
* in the DOM, it is only to provide the combobox with the full list of options. This list should match
|
|
619
|
+
* the order of the options as they would appear in the DOM.
|
|
620
|
+
*/
|
|
621
|
+
this.allOptions = input(undefined, ...(ngDevMode ? [{ debugName: "allOptions", alias: 'ngpComboboxOptions' }] : [{ alias: 'ngpComboboxOptions' }]));
|
|
565
622
|
/**
|
|
566
623
|
* Store the combobox input
|
|
567
624
|
* @internal
|
|
@@ -597,6 +654,11 @@ class NgpCombobox {
|
|
|
597
654
|
* @internal
|
|
598
655
|
*/
|
|
599
656
|
this.open = computed(() => this.overlay()?.isOpen() ?? false, ...(ngDevMode ? [{ debugName: "open" }] : []));
|
|
657
|
+
/**
|
|
658
|
+
* The options sorted by their index or DOM position.
|
|
659
|
+
* @internal
|
|
660
|
+
*/
|
|
661
|
+
this.sortedOptions = computed(() => domSort(this.options(), option => option.elementRef.nativeElement, option => option.index()), ...(ngDevMode ? [{ debugName: "sortedOptions" }] : []));
|
|
600
662
|
/**
|
|
601
663
|
* The active key descendant manager.
|
|
602
664
|
* @internal
|
|
@@ -604,13 +666,16 @@ class NgpCombobox {
|
|
|
604
666
|
this.activeDescendantManager = activeDescendantManager({
|
|
605
667
|
// we must wrap the signal in a computed to ensure it is not used before it is defined
|
|
606
668
|
disabled: computed(() => this.state.disabled()),
|
|
607
|
-
|
|
608
|
-
|
|
669
|
+
wrap: signal(true),
|
|
670
|
+
count: computed(() => this.state.allOptions()?.length ?? this.options().length),
|
|
671
|
+
getItemId: index => this.getOptionAtIndex(index)?.id(),
|
|
672
|
+
isItemDisabled: index => this.getOptionAtIndex(index)?.disabled() ?? false,
|
|
673
|
+
scrollIntoView: index => {
|
|
609
674
|
const isPositioned = this.portal()?.overlay()?.isPositioned() ?? false;
|
|
610
|
-
if (!isPositioned ||
|
|
675
|
+
if (!isPositioned || index === -1) {
|
|
611
676
|
return;
|
|
612
677
|
}
|
|
613
|
-
this.
|
|
678
|
+
this.scrollTo(index);
|
|
614
679
|
},
|
|
615
680
|
});
|
|
616
681
|
/** The control status */
|
|
@@ -635,16 +700,27 @@ class NgpCombobox {
|
|
|
635
700
|
}
|
|
636
701
|
this.openChange.emit(true);
|
|
637
702
|
await this.portal()?.show();
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
703
|
+
let selectedOptionIdx = -1;
|
|
704
|
+
// if we have been provided with allOptions, we need to find the selected option(s) from that list
|
|
705
|
+
if (this.state.allOptions()) {
|
|
706
|
+
selectedOptionIdx = this.state
|
|
707
|
+
.allOptions()
|
|
708
|
+
.findIndex(option => this.isOptionSelected(option));
|
|
709
|
+
}
|
|
710
|
+
// if we don't have allOptions, find the selected option(s) from the registered options
|
|
711
|
+
if (selectedOptionIdx === -1) {
|
|
712
|
+
// if there is a selected option(s), set the active descendant to the first selected option
|
|
713
|
+
selectedOptionIdx = this.sortedOptions().findIndex(option => this.isOptionSelected(option.value()));
|
|
714
|
+
}
|
|
715
|
+
// if after checking there is a selected option, set the active descendant to the first option
|
|
716
|
+
if (selectedOptionIdx !== -1) {
|
|
717
|
+
// scroll to and activate the selected option
|
|
718
|
+
this.scrollTo(selectedOptionIdx);
|
|
719
|
+
this.activeDescendantManager.activateByIndex(selectedOptionIdx);
|
|
644
720
|
return;
|
|
645
721
|
}
|
|
646
722
|
// activate the selected option or the first option
|
|
647
|
-
this.activeDescendantManager.
|
|
723
|
+
this.activeDescendantManager.first();
|
|
648
724
|
}
|
|
649
725
|
/**
|
|
650
726
|
* Close the dropdown.
|
|
@@ -691,7 +767,7 @@ class NgpCombobox {
|
|
|
691
767
|
return; // Do nothing in single selection mode
|
|
692
768
|
}
|
|
693
769
|
// Get currently visible regular options (respects filtering)
|
|
694
|
-
const regularOptions = this.
|
|
770
|
+
const regularOptions = this.sortedOptions().filter(opt => opt.value() !== 'all' && opt.value() !== undefined);
|
|
695
771
|
const allValues = regularOptions.map(opt => opt.value());
|
|
696
772
|
this.state.value.set(allValues);
|
|
697
773
|
this.valueChange.emit(allValues);
|
|
@@ -699,7 +775,7 @@ class NgpCombobox {
|
|
|
699
775
|
}
|
|
700
776
|
if (this.state.multiple()) {
|
|
701
777
|
// if the option is already selected, do nothing
|
|
702
|
-
if (this.isOptionSelected(option)) {
|
|
778
|
+
if (this.isOptionSelected(option.value())) {
|
|
703
779
|
return;
|
|
704
780
|
}
|
|
705
781
|
const value = [...this.state.value(), option.value()];
|
|
@@ -721,7 +797,7 @@ class NgpCombobox {
|
|
|
721
797
|
*/
|
|
722
798
|
deselectOption(option) {
|
|
723
799
|
// if the combobox is disabled or the option is not selected, do nothing
|
|
724
|
-
if (this.state.disabled() || !this.isOptionSelected(option)) {
|
|
800
|
+
if (this.state.disabled() || !this.isOptionSelected(option.value())) {
|
|
725
801
|
return;
|
|
726
802
|
}
|
|
727
803
|
// in single selection mode, only allow deselecting if allowDeselect is true
|
|
@@ -752,19 +828,23 @@ class NgpCombobox {
|
|
|
752
828
|
}
|
|
753
829
|
/**
|
|
754
830
|
* Toggle the selection of an option.
|
|
755
|
-
* @param
|
|
831
|
+
* @param id The id of the option to toggle.
|
|
756
832
|
* @internal
|
|
757
833
|
*/
|
|
758
|
-
toggleOption(
|
|
834
|
+
toggleOption(id) {
|
|
759
835
|
if (this.state.disabled()) {
|
|
760
836
|
return;
|
|
761
837
|
}
|
|
838
|
+
const option = this.sortedOptions().find(opt => opt.id() === id);
|
|
839
|
+
if (!option) {
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
762
842
|
// Handle select all for select/deselect all functionality - only works in multiple selection mode
|
|
763
843
|
if (option.value() === 'all') {
|
|
764
844
|
if (!this.state.multiple()) {
|
|
765
845
|
return; // Do nothing in single selection mode
|
|
766
846
|
}
|
|
767
|
-
if (this.isOptionSelected(option)) {
|
|
847
|
+
if (this.isOptionSelected(option.value())) {
|
|
768
848
|
this.deselectOption(option);
|
|
769
849
|
}
|
|
770
850
|
else {
|
|
@@ -774,7 +854,7 @@ class NgpCombobox {
|
|
|
774
854
|
}
|
|
775
855
|
if (this.state.multiple()) {
|
|
776
856
|
// In multiple selection mode, always allow toggling
|
|
777
|
-
if (this.isOptionSelected(option)) {
|
|
857
|
+
if (this.isOptionSelected(option.value())) {
|
|
778
858
|
this.deselectOption(option);
|
|
779
859
|
}
|
|
780
860
|
else {
|
|
@@ -783,7 +863,7 @@ class NgpCombobox {
|
|
|
783
863
|
}
|
|
784
864
|
else {
|
|
785
865
|
// In single selection mode, check if deselection is allowed
|
|
786
|
-
if (this.isOptionSelected(option) && this.state.allowDeselect()) {
|
|
866
|
+
if (this.isOptionSelected(option.value()) && this.state.allowDeselect()) {
|
|
787
867
|
// Deselect the option by setting value to undefined
|
|
788
868
|
this.state.value.set(undefined);
|
|
789
869
|
this.valueChange.emit(undefined);
|
|
@@ -803,7 +883,8 @@ class NgpCombobox {
|
|
|
803
883
|
if (this.state.disabled()) {
|
|
804
884
|
return false;
|
|
805
885
|
}
|
|
806
|
-
|
|
886
|
+
// Handle both NgpComboboxOption and T types
|
|
887
|
+
const optionValue = option.value?.() ?? option;
|
|
807
888
|
const value = this.state.value();
|
|
808
889
|
// Handle select all functionality - only works in multiple selection mode
|
|
809
890
|
if (optionValue === 'all') {
|
|
@@ -811,7 +892,7 @@ class NgpCombobox {
|
|
|
811
892
|
return false; // Never selected in single selection mode
|
|
812
893
|
}
|
|
813
894
|
const selectedValues = Array.isArray(value) ? value : [];
|
|
814
|
-
return areAllOptionsSelected(this.
|
|
895
|
+
return areAllOptionsSelected(this.sortedOptions(), selectedValues, this.state.compareWith());
|
|
815
896
|
}
|
|
816
897
|
if (!value) {
|
|
817
898
|
return false;
|
|
@@ -830,21 +911,21 @@ class NgpCombobox {
|
|
|
830
911
|
if (this.state.disabled()) {
|
|
831
912
|
return;
|
|
832
913
|
}
|
|
833
|
-
const options = this.
|
|
914
|
+
const options = this.sortedOptions();
|
|
834
915
|
// if there are no options, do nothing
|
|
835
916
|
if (options.length === 0) {
|
|
836
917
|
return;
|
|
837
918
|
}
|
|
838
919
|
// if there is no active option, activate the first option
|
|
839
|
-
if (
|
|
840
|
-
const selectedOption = options.
|
|
920
|
+
if (this.activeDescendantManager.index() === -1) {
|
|
921
|
+
const selectedOption = options.findIndex(option => this.isOptionSelected(option.value()));
|
|
841
922
|
// if there is a selected option(s), set the active descendant to the first selected option
|
|
842
|
-
const targetOption = selectedOption
|
|
843
|
-
this.activeDescendantManager.
|
|
923
|
+
const targetOption = selectedOption !== -1 ? selectedOption : 0;
|
|
924
|
+
this.activeDescendantManager.activateByIndex(targetOption, { origin: 'keyboard' });
|
|
844
925
|
return;
|
|
845
926
|
}
|
|
846
927
|
// otherwise activate the next option
|
|
847
|
-
this.activeDescendantManager.next();
|
|
928
|
+
this.activeDescendantManager.next({ origin: 'keyboard' });
|
|
848
929
|
}
|
|
849
930
|
/**
|
|
850
931
|
* Activate the previous option in the list if there is one.
|
|
@@ -854,21 +935,21 @@ class NgpCombobox {
|
|
|
854
935
|
if (this.state.disabled()) {
|
|
855
936
|
return;
|
|
856
937
|
}
|
|
857
|
-
const options = this.
|
|
938
|
+
const options = this.sortedOptions();
|
|
858
939
|
// if there are no options, do nothing
|
|
859
940
|
if (options.length === 0) {
|
|
860
941
|
return;
|
|
861
942
|
}
|
|
862
943
|
// if there is no active option, activate the last option
|
|
863
|
-
if (
|
|
864
|
-
const selectedOption = options.
|
|
944
|
+
if (this.activeDescendantManager.index() === -1) {
|
|
945
|
+
const selectedOption = options.findIndex(option => this.isOptionSelected(option.value()));
|
|
865
946
|
// if there is a selected option(s), set the active descendant to the first selected option
|
|
866
|
-
const targetOption = selectedOption
|
|
867
|
-
this.activeDescendantManager.
|
|
947
|
+
const targetOption = selectedOption !== -1 ? selectedOption : options.length - 1;
|
|
948
|
+
this.activeDescendantManager.activateByIndex(targetOption, { origin: 'keyboard' });
|
|
868
949
|
return;
|
|
869
950
|
}
|
|
870
951
|
// otherwise activate the previous option
|
|
871
|
-
this.activeDescendantManager.previous();
|
|
952
|
+
this.activeDescendantManager.previous({ origin: 'keyboard' });
|
|
872
953
|
}
|
|
873
954
|
/**
|
|
874
955
|
* Register the dropdown portal with the combobox.
|
|
@@ -973,21 +1054,21 @@ class NgpCombobox {
|
|
|
973
1054
|
break;
|
|
974
1055
|
case 'Home':
|
|
975
1056
|
if (this.open()) {
|
|
976
|
-
this.activeDescendantManager.first();
|
|
1057
|
+
this.activeDescendantManager.first({ origin: 'keyboard' });
|
|
977
1058
|
}
|
|
978
1059
|
event.preventDefault();
|
|
979
1060
|
break;
|
|
980
1061
|
case 'End':
|
|
981
1062
|
if (this.open()) {
|
|
982
|
-
this.activeDescendantManager.last();
|
|
1063
|
+
this.activeDescendantManager.last({ origin: 'keyboard' });
|
|
983
1064
|
}
|
|
984
1065
|
event.preventDefault();
|
|
985
1066
|
break;
|
|
986
1067
|
case 'Enter':
|
|
987
1068
|
if (this.open()) {
|
|
988
|
-
const
|
|
989
|
-
if (
|
|
990
|
-
this.toggleOption(
|
|
1069
|
+
const activeId = this.activeDescendantManager.id();
|
|
1070
|
+
if (activeId) {
|
|
1071
|
+
this.toggleOption(activeId);
|
|
991
1072
|
}
|
|
992
1073
|
}
|
|
993
1074
|
event.preventDefault();
|
|
@@ -1032,8 +1113,27 @@ class NgpCombobox {
|
|
|
1032
1113
|
}
|
|
1033
1114
|
this.closeDropdown();
|
|
1034
1115
|
}
|
|
1116
|
+
scrollTo(index) {
|
|
1117
|
+
const scrollToOption = this.state.scrollToOption();
|
|
1118
|
+
if (scrollToOption) {
|
|
1119
|
+
scrollToOption(index);
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
const option = this.getOptionAtIndex(index);
|
|
1123
|
+
if (option) {
|
|
1124
|
+
option.scrollIntoView();
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
getOptionAtIndex(index) {
|
|
1128
|
+
// if the option has an index, use that to get the option because this is required for virtual scrolling scenarios
|
|
1129
|
+
const optionIndex = this.options().findIndex(opt => opt.index() === index);
|
|
1130
|
+
if (optionIndex !== -1) {
|
|
1131
|
+
return this.options()[optionIndex];
|
|
1132
|
+
}
|
|
1133
|
+
return this.sortedOptions()[index];
|
|
1134
|
+
}
|
|
1035
1135
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpCombobox, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1036
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpCombobox, isStandalone: true, selector: "[ngpCombobox]", inputs: { value: { classPropertyName: "value", publicName: "ngpComboboxValue", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "ngpComboboxMultiple", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "ngpComboboxDisabled", isSignal: true, isRequired: false, transformFunction: null }, allowDeselect: { classPropertyName: "allowDeselect", publicName: "ngpComboboxAllowDeselect", isSignal: true, isRequired: false, transformFunction: null }, compareWith: { classPropertyName: "compareWith", publicName: "ngpComboboxCompareWith", isSignal: true, isRequired: false, transformFunction: null }, placement: { classPropertyName: "placement", publicName: "ngpComboboxDropdownPlacement", isSignal: true, isRequired: false, transformFunction: null }, container: { classPropertyName: "container", publicName: "ngpComboboxDropdownContainer", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "ngpComboboxValueChange", openChange: "ngpComboboxOpenChange" }, host: { listeners: { "keydown": "handleKeydown($event)", "blur": "onBlur($event)" }, properties: { "attr.tabindex": "input() ? -1 : (state.disabled() ? -1 : 0)", "attr.data-open": "open() ? \"\" : undefined", "attr.data-disabled": "state.disabled() ? \"\" : undefined", "attr.data-multiple": "state.multiple() ? \"\" : undefined", "attr.data-invalid": "controlStatus()?.invalid ? \"\" : undefined", "attr.data-valid": "controlStatus()?.valid ? \"\" : undefined", "attr.data-touched": "controlStatus()?.touched ? \"\" : undefined", "attr.data-pristine": "controlStatus()?.pristine ? \"\" : undefined", "attr.data-dirty": "controlStatus()?.dirty ? \"\" : undefined", "attr.data-pending": "controlStatus()?.pending ? \"\" : undefined" } }, providers: [provideComboboxState()], exportAs: ["ngpCombobox"], ngImport: i0 }); }
|
|
1136
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpCombobox, isStandalone: true, selector: "[ngpCombobox]", inputs: { value: { classPropertyName: "value", publicName: "ngpComboboxValue", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "ngpComboboxMultiple", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "ngpComboboxDisabled", isSignal: true, isRequired: false, transformFunction: null }, allowDeselect: { classPropertyName: "allowDeselect", publicName: "ngpComboboxAllowDeselect", isSignal: true, isRequired: false, transformFunction: null }, compareWith: { classPropertyName: "compareWith", publicName: "ngpComboboxCompareWith", isSignal: true, isRequired: false, transformFunction: null }, placement: { classPropertyName: "placement", publicName: "ngpComboboxDropdownPlacement", isSignal: true, isRequired: false, transformFunction: null }, container: { classPropertyName: "container", publicName: "ngpComboboxDropdownContainer", isSignal: true, isRequired: false, transformFunction: null }, scrollToOption: { classPropertyName: "scrollToOption", publicName: "ngpComboboxScrollToOption", isSignal: true, isRequired: false, transformFunction: null }, optionCount: { classPropertyName: "optionCount", publicName: "ngpComboboxOptionCount", isSignal: true, isRequired: false, transformFunction: null }, allOptions: { classPropertyName: "allOptions", publicName: "ngpComboboxOptions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "ngpComboboxValueChange", openChange: "ngpComboboxOpenChange" }, host: { listeners: { "keydown": "handleKeydown($event)", "blur": "onBlur($event)" }, properties: { "attr.tabindex": "input() ? -1 : (state.disabled() ? -1 : 0)", "attr.data-open": "open() ? \"\" : undefined", "attr.data-disabled": "state.disabled() ? \"\" : undefined", "attr.data-multiple": "state.multiple() ? \"\" : undefined", "attr.data-invalid": "controlStatus()?.invalid ? \"\" : undefined", "attr.data-valid": "controlStatus()?.valid ? \"\" : undefined", "attr.data-touched": "controlStatus()?.touched ? \"\" : undefined", "attr.data-pristine": "controlStatus()?.pristine ? \"\" : undefined", "attr.data-dirty": "controlStatus()?.dirty ? \"\" : undefined", "attr.data-pending": "controlStatus()?.pending ? \"\" : undefined" } }, providers: [provideComboboxState()], exportAs: ["ngpCombobox"], ngImport: i0 }); }
|
|
1037
1137
|
}
|
|
1038
1138
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpCombobox, decorators: [{
|
|
1039
1139
|
type: Directive,
|
|
@@ -1054,7 +1154,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImpor
|
|
|
1054
1154
|
'[attr.data-pending]': 'controlStatus()?.pending ? "" : undefined',
|
|
1055
1155
|
},
|
|
1056
1156
|
}]
|
|
1057
|
-
}], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpComboboxValue", required: false }] }], valueChange: [{ type: i0.Output, args: ["ngpComboboxValueChange"] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpComboboxMultiple", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpComboboxDisabled", required: false }] }], allowDeselect: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpComboboxAllowDeselect", required: false }] }], openChange: [{ type: i0.Output, args: ["ngpComboboxOpenChange"] }], compareWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpComboboxCompareWith", required: false }] }], placement: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpComboboxDropdownPlacement", required: false }] }], container: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpComboboxDropdownContainer", required: false }] }], handleKeydown: [{
|
|
1157
|
+
}], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpComboboxValue", required: false }] }], valueChange: [{ type: i0.Output, args: ["ngpComboboxValueChange"] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpComboboxMultiple", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpComboboxDisabled", required: false }] }], allowDeselect: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpComboboxAllowDeselect", required: false }] }], openChange: [{ type: i0.Output, args: ["ngpComboboxOpenChange"] }], compareWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpComboboxCompareWith", required: false }] }], placement: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpComboboxDropdownPlacement", required: false }] }], container: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpComboboxDropdownContainer", required: false }] }], scrollToOption: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpComboboxScrollToOption", required: false }] }], optionCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpComboboxOptionCount", required: false }] }], allOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpComboboxOptions", required: false }] }], handleKeydown: [{
|
|
1058
1158
|
type: HostListener,
|
|
1059
1159
|
args: ['keydown', ['$event']]
|
|
1060
1160
|
}], onBlur: [{
|