@star-ng/ui 0.1.0 → 0.2.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.
@@ -1,11 +1,11 @@
1
1
  import { _IdGenerator } from "@angular/cdk/a11y";
2
2
  import { CdkMenuTrigger } from "@angular/cdk/menu";
3
3
  import * as i0 from "@angular/core";
4
- import { ChangeDetectionStrategy, Component, DestroyRef, Directive, TemplateRef, booleanAttribute, computed, contentChild, inject, input, model, output, signal, viewChild } from "@angular/core";
5
- import { STAR_UI_TEXT, StarDirectionService, matchesFilter, optionText, readOption } from "@star-ng/ui/core";
4
+ import { ChangeDetectionStrategy, Component, DestroyRef, Directive, Injector, TemplateRef, afterNextRender, booleanAttribute, computed, contentChild, inject, input, model, output, signal, viewChild } from "@angular/core";
5
+ import { STAR_UI_TEXT, StarDirectionService, matchesFilter, optionText, readOption, starListKeyboardTarget, starListLayout, starListPageSize, starListRevealOffset, starListRevealRows, starListRowRendered } from "@star-ng/ui/core";
6
6
  import { STAR_MENU_POSITIONS, StarMenu, StarMenuInDialog } from "@star-ng/ui/menu";
7
7
  import { CdkConnectedOverlay, CdkOverlayOrigin } from "@angular/cdk/overlay";
8
- import { NgTemplateOutlet } from "@angular/common";
8
+ import { DOCUMENT, NgTemplateOutlet } from "@angular/common";
9
9
  var StarCascadeSelect = class StarCascadeSelect {
10
10
  directionSync = inject(StarDirectionService);
11
11
  text = inject(STAR_UI_TEXT);
@@ -628,6 +628,8 @@ var StarOptionListBase = class StarOptionListBase {
628
628
  directionSync = inject(StarDirectionService);
629
629
  text = inject(STAR_UI_TEXT);
630
630
  ids = inject(_IdGenerator);
631
+ document = inject(DOCUMENT);
632
+ injector = inject(Injector);
631
633
  options = input([], ...ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []);
632
634
  optionLabel = input(null, ...ngDevMode ? [{ debugName: "optionLabel" }] : /* istanbul ignore next */ []);
633
635
  optionValue = input(null, ...ngDevMode ? [{ debugName: "optionValue" }] : /* istanbul ignore next */ []);
@@ -713,35 +715,8 @@ var StarOptionListBase = class StarOptionListBase {
713
715
  rank: order.get(this.groupOf(option)) ?? 0
714
716
  })).sort((a, b) => a.rank - b.rank || a.index - b.index).map(({ option }) => option);
715
717
  }
716
- rows = computed(() => {
717
- const grouped = !!this.optionGroup();
718
- const rows = [];
719
- let current = null;
720
- let groupId = null;
721
- this.visibleOptions().forEach((option, index) => {
722
- if (grouped) {
723
- const label = this.groupOf(option);
724
- if (label !== current) {
725
- current = label;
726
- groupId = `${this.listId}-group-${rows.length}`;
727
- rows.push({
728
- kind: "group",
729
- key: `g:${label}`,
730
- label,
731
- id: groupId
732
- });
733
- }
734
- }
735
- rows.push({
736
- kind: "option",
737
- key: `o:${index}`,
738
- option,
739
- index,
740
- groupId
741
- });
742
- });
743
- return rows;
744
- }, ...ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []);
718
+ layout = computed(() => starListLayout(this.visibleOptions(), this.optionGroup() ? (option) => this.groupOf(option) : null, this.listId), ...ngDevMode ? [{ debugName: "layout" }] : /* istanbul ignore next */ []);
719
+ rows = computed(() => this.layout().rows, ...ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []);
745
720
  listScrollTop = signal(0, ...ngDevMode ? [{ debugName: "listScrollTop" }] : /* istanbul ignore next */ []);
746
721
  listHeight = signal(0, ...ngDevMode ? [{ debugName: "listHeight" }] : /* istanbul ignore next */ []);
747
722
  virtualWindow = computed(() => {
@@ -775,7 +750,15 @@ var StarOptionListBase = class StarOptionListBase {
775
750
  this.listScrollTop.set(list.scrollTop);
776
751
  this.listHeight.set(list.clientHeight);
777
752
  }
778
- activeId = computed(() => this.open() && this.activeIndex() >= 0 ? this.optionId(this.activeIndex()) : null, ...ngDevMode ? [{ debugName: "activeId" }] : /* istanbul ignore next */ []);
753
+ activeId = computed(() => {
754
+ const index = this.activeIndex();
755
+ if (!this.open() || index < 0) return null;
756
+ const row = this.layout().rowOfOption[index];
757
+ return row !== void 0 && starListRowRendered(row, this.virtualWindow()) ? this.optionId(index) : null;
758
+ }, ...ngDevMode ? [{ debugName: "activeId" }] : /* istanbul ignore next */ []);
759
+ describedBy(row) {
760
+ return row.groupId !== null && starListRowRendered(row.groupRow, this.virtualWindow()) ? row.groupId : null;
761
+ }
779
762
  emptyText = computed(() => this.filterValue() ? this.text.emptyFilterMessage : this.emptyMessage() ?? this.text.emptyMessage, ...ngDevMode ? [{ debugName: "emptyText" }] : /* istanbul ignore next */ []);
780
763
  typeahead = "";
781
764
  typeaheadTimer;
@@ -837,7 +820,14 @@ var StarOptionListBase = class StarOptionListBase {
837
820
  }
838
821
  onPanelAttached() {
839
822
  this.filterInput()?.nativeElement.focus();
823
+ const height = this.listElement()?.clientHeight;
824
+ if (this.virtualScroll() && height) this.listHeight.set(height);
840
825
  this.scrollActiveIntoView();
826
+ if (this.virtualScroll()) afterNextRender(() => {
827
+ const list = this.listElement();
828
+ if (list?.clientHeight) this.listHeight.set(list.clientHeight);
829
+ this.scrollActiveIntoView();
830
+ }, { injector: this.injector });
841
831
  }
842
832
  onOutsideClick(event) {
843
833
  if (this.trigger().nativeElement.contains(event.target)) return;
@@ -853,9 +843,14 @@ var StarOptionListBase = class StarOptionListBase {
853
843
  onFilterInput(value) {
854
844
  this.filterValue.set(value);
855
845
  this.filterChange.emit(value);
856
- this.listScrollTop.set(0);
846
+ this.resetScroll();
857
847
  this.activeIndex.set(this.firstEnabledFrom(0, 1));
858
848
  }
849
+ resetScroll() {
850
+ this.listScrollTop.set(0);
851
+ const list = this.listElement();
852
+ if (list) list.scrollTop = 0;
853
+ }
859
854
  onOptionClick(option, index) {
860
855
  if (this.isDisabled(option)) return;
861
856
  this.activeIndex.set(index);
@@ -889,6 +884,12 @@ var StarOptionListBase = class StarOptionListBase {
889
884
  this.activeIndex.set(event.key === "Home" ? this.firstEnabledFrom(0, 1) : this.firstEnabledFrom(this.visibleOptions().length - 1, -1));
890
885
  this.scrollActiveIntoView();
891
886
  return;
887
+ case "PageDown":
888
+ case "PageUp":
889
+ if (!open || !this.virtualScroll()) return;
890
+ event.preventDefault();
891
+ this.movePage(event.key === "PageDown" ? 1 : -1);
892
+ return;
892
893
  case "Enter":
893
894
  event.preventDefault();
894
895
  if (!open) this.openPanel();
@@ -897,6 +898,10 @@ var StarOptionListBase = class StarOptionListBase {
897
898
  case " ":
898
899
  if (event.target instanceof HTMLInputElement) return;
899
900
  event.preventDefault();
901
+ if (this.typeahead) {
902
+ this.runTypeahead(" ");
903
+ return;
904
+ }
900
905
  if (!open) this.openPanel();
901
906
  else this.chooseActive(this.closeOnChoose);
902
907
  return;
@@ -927,6 +932,15 @@ var StarOptionListBase = class StarOptionListBase {
927
932
  if (next >= 0) this.activeIndex.set(next);
928
933
  this.scrollActiveIntoView();
929
934
  }
935
+ movePage(step) {
936
+ const count = this.visibleOptions().length;
937
+ const height = this.listElement()?.clientHeight || VIRTUAL_FALLBACK_HEIGHT;
938
+ const target = starListKeyboardTarget(step === 1 ? "PageDown" : "PageUp", this.activeIndex(), count, starListPageSize(height, this.virtualItemSize()));
939
+ const next = this.firstEnabledFrom(target, step);
940
+ const found = next >= 0 ? next : this.firstEnabledFrom(target, step === 1 ? -1 : 1);
941
+ if (found >= 0) this.activeIndex.set(found);
942
+ this.scrollActiveIntoView();
943
+ }
930
944
  firstEnabledFrom(start, step) {
931
945
  const options = this.visibleOptions();
932
946
  for (let i = start; i >= 0 && i < options.length; i += step) if (!this.isDisabled(options[i])) return i;
@@ -944,26 +958,33 @@ var StarOptionListBase = class StarOptionListBase {
944
958
  if (!this.open() && this.closeOnChoose) this.choose(options[match]);
945
959
  this.scrollActiveIntoView();
946
960
  }
961
+ listElement() {
962
+ return this.document.getElementById(this.listId);
963
+ }
964
+ applyScroll(list, offset, frames) {
965
+ if (!list.isConnected || this.listScrollTop() !== offset) return;
966
+ list.scrollTop = offset;
967
+ if (frames > 0 && Math.abs(list.scrollTop - offset) > 1) this.document.defaultView?.requestAnimationFrame?.(() => this.applyScroll(list, offset, frames - 1));
968
+ }
947
969
  scrollActiveIntoView() {
948
- const id = this.activeId();
949
- if (!id) return;
970
+ if (!this.open() || this.activeIndex() < 0) return;
950
971
  if (this.virtualScroll()) {
951
- const active = this.activeIndex();
952
- const line = this.rows().findIndex((row) => row.kind === "option" && row.index === active);
953
- const list = this.trigger().nativeElement.ownerDocument.getElementById(this.listId);
954
- if (line < 0 || !list) return;
955
- const size = Math.max(1, this.virtualItemSize());
972
+ const list = this.listElement();
973
+ const layout = this.layout();
974
+ if (!list || layout.rowOfOption[this.activeIndex()] === void 0) return;
956
975
  const height = list.clientHeight || VIRTUAL_FALLBACK_HEIGHT;
957
- const top = line * size;
958
- let scrollTop = this.listScrollTop();
959
- if (top < scrollTop) scrollTop = top;
960
- else if (top + size > scrollTop + height) scrollTop = top + size - height;
961
- this.listScrollTop.set(scrollTop);
976
+ const inset = parseFloat(getComputedStyle(list).paddingTop) || 0;
977
+ const [first, last] = starListRevealRows(layout, this.activeIndex());
978
+ const offset = starListRevealOffset(first, last, Math.max(1, this.virtualItemSize()), list.clientHeight ? list.scrollTop : this.listScrollTop(), height, inset);
962
979
  this.listHeight.set(height);
963
- queueMicrotask(() => list.scrollTop = scrollTop);
980
+ if (offset === null) return;
981
+ this.listScrollTop.set(offset);
982
+ queueMicrotask(() => this.applyScroll(list, offset, 12));
964
983
  return;
965
984
  }
966
- queueMicrotask(() => this.trigger().nativeElement.ownerDocument.getElementById(id)?.scrollIntoView?.({ block: "nearest" }));
985
+ const id = this.activeId();
986
+ if (!id) return;
987
+ queueMicrotask(() => this.document.getElementById(id)?.scrollIntoView?.({ block: "nearest" }));
967
988
  }
968
989
  static ɵfac = i0.ɵɵngDeclareFactory({
969
990
  minVersion: "12.0.0",
@@ -1439,9 +1460,11 @@ var StarMultiSelect = class StarMultiSelect extends StarOptionListBase {
1439
1460
  if (!options.length || selected === 0) return false;
1440
1461
  return selected === options.length ? true : "mixed";
1441
1462
  }, ...ngDevMode ? [{ debugName: "allState" }] : /* istanbul ignore next */ []);
1463
+ chosenSet = computed(() => this.compareWith() === Object.is ? new Set(this.value()) : null, ...ngDevMode ? [{ debugName: "chosenSet" }] : /* istanbul ignore next */ []);
1442
1464
  isSelected(option) {
1443
1465
  const optionValue = this.valueOf(option);
1444
- return this.value().some((value) => this.equals(value, optionValue));
1466
+ const chosen = this.chosenSet();
1467
+ return chosen ? chosen.has(optionValue) : this.value().some((value) => this.equals(value, optionValue));
1445
1468
  }
1446
1469
  choose(option) {
1447
1470
  if (this.isSelected(option)) {
@@ -1463,7 +1486,8 @@ var StarMultiSelect = class StarMultiSelect extends StarOptionListBase {
1463
1486
  const options = this.selectableVisible();
1464
1487
  if (this.allState() === true) {
1465
1488
  const visibleValues = options.map((option) => this.valueOf(option));
1466
- this.value.update((values) => values.filter((value) => !visibleValues.some((visible) => this.equals(visible, value))));
1489
+ const visibleSet = this.compareWith() === Object.is ? new Set(visibleValues) : null;
1490
+ this.value.update((values) => values.filter((value) => visibleSet ? !visibleSet.has(value) : !visibleValues.some((visible) => this.equals(visible, value))));
1467
1491
  return;
1468
1492
  }
1469
1493
  const additions = options.filter((option) => !this.isSelected(option)).map((option) => this.valueOf(option));
@@ -1697,7 +1721,7 @@ var StarMultiSelect = class StarMultiSelect extends StarOptionListBase {
1697
1721
  [class.star-listbox__option--selected]="isSelected(option)"
1698
1722
  [attr.aria-selected]="isSelected(option)"
1699
1723
  [attr.aria-disabled]="isDisabled(option) || null"
1700
- [attr.aria-describedby]="row.groupId"
1724
+ [attr.aria-describedby]="describedBy(row)"
1701
1725
  [attr.aria-setsize]="virtualScroll() ? visibleOptions().length : null"
1702
1726
  [attr.aria-posinset]="virtualScroll() ? i + 1 : null"
1703
1727
  (click)="onOptionClick(option, i)"
@@ -1979,7 +2003,7 @@ i0.ɵɵngDeclareClassMetadata({
1979
2003
  [class.star-listbox__option--selected]="isSelected(option)"
1980
2004
  [attr.aria-selected]="isSelected(option)"
1981
2005
  [attr.aria-disabled]="isDisabled(option) || null"
1982
- [attr.aria-describedby]="row.groupId"
2006
+ [attr.aria-describedby]="describedBy(row)"
1983
2007
  [attr.aria-setsize]="virtualScroll() ? visibleOptions().length : null"
1984
2008
  [attr.aria-posinset]="virtualScroll() ? i + 1 : null"
1985
2009
  (click)="onOptionClick(option, i)"
@@ -2235,7 +2259,7 @@ var StarSelect = class StarSelect extends StarOptionListBase {
2235
2259
  [class.star-listbox__option--selected]="isSelected(option)"
2236
2260
  [attr.aria-selected]="isSelected(option)"
2237
2261
  [attr.aria-disabled]="isDisabled(option) || null"
2238
- [attr.aria-describedby]="row.groupId"
2262
+ [attr.aria-describedby]="describedBy(row)"
2239
2263
  [attr.aria-setsize]="virtualScroll() ? visibleOptions().length : null"
2240
2264
  [attr.aria-posinset]="virtualScroll() ? i + 1 : null"
2241
2265
  (click)="onOptionClick(option, i)"
@@ -2470,7 +2494,7 @@ i0.ɵɵngDeclareClassMetadata({
2470
2494
  [class.star-listbox__option--selected]="isSelected(option)"
2471
2495
  [attr.aria-selected]="isSelected(option)"
2472
2496
  [attr.aria-disabled]="isDisabled(option) || null"
2473
- [attr.aria-describedby]="row.groupId"
2497
+ [attr.aria-describedby]="describedBy(row)"
2474
2498
  [attr.aria-setsize]="virtualScroll() ? visibleOptions().length : null"
2475
2499
  [attr.aria-posinset]="virtualScroll() ? i + 1 : null"
2476
2500
  (click)="onOptionClick(option, i)"