@kouji-ui/core 0.11.1 → 0.13.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.
@@ -5330,6 +5330,12 @@ class KjListNavigator {
5330
5330
  ...(ngDevMode ? [{ debugName: "kjPageSize" }] : /* istanbul ignore next */ []));
5331
5331
  /** Activate item on pointer hover. Default: `false`. */
5332
5332
  kjActivateOnHover = input(false, { ...(ngDevMode ? { debugName: "kjActivateOnHover" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
5333
+ /**
5334
+ * Publish `aria-activedescendant` on this host in `'activedescendant'`
5335
+ * mode. Turn off when another element keeps DOM focus and publishes
5336
+ * `activeId` itself (a search field above the list). Default: `true`.
5337
+ */
5338
+ kjHostActiveDescendant = input(true, { ...(ngDevMode ? { debugName: "kjHostActiveDescendant" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
5333
5339
  /**
5334
5340
  * Focus model. `'activedescendant'` (default) keeps DOM focus on the
5335
5341
  * navigator host and signals the active option via
@@ -5705,7 +5711,7 @@ class KjListNavigator {
5705
5711
  }
5706
5712
  }
5707
5713
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: KjListNavigator, deps: [], target: i0.ɵɵFactoryTarget.Directive });
5708
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.5", type: KjListNavigator, isStandalone: true, selector: "[kjListNavigator]", inputs: { kjOrientation: { classPropertyName: "kjOrientation", publicName: "kjOrientation", isSignal: true, isRequired: false, transformFunction: null }, kjWrap: { classPropertyName: "kjWrap", publicName: "kjWrap", isSignal: true, isRequired: false, transformFunction: null }, kjPageSize: { classPropertyName: "kjPageSize", publicName: "kjPageSize", isSignal: true, isRequired: false, transformFunction: null }, kjActivateOnHover: { classPropertyName: "kjActivateOnHover", publicName: "kjActivateOnHover", isSignal: true, isRequired: false, transformFunction: null }, kjFocusMode: { classPropertyName: "kjFocusMode", publicName: "kjFocusMode", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { kjActiveChange: "kjActiveChange" }, host: { listeners: { "keydown": "_onKeydown($event)", "focusin": "_onFocusin($event)" }, properties: { "attr.aria-activedescendant": "kjFocusMode() === \"activedescendant\" ? activeId() : null" } }, providers: [
5714
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.5", type: KjListNavigator, isStandalone: true, selector: "[kjListNavigator]", inputs: { kjOrientation: { classPropertyName: "kjOrientation", publicName: "kjOrientation", isSignal: true, isRequired: false, transformFunction: null }, kjWrap: { classPropertyName: "kjWrap", publicName: "kjWrap", isSignal: true, isRequired: false, transformFunction: null }, kjPageSize: { classPropertyName: "kjPageSize", publicName: "kjPageSize", isSignal: true, isRequired: false, transformFunction: null }, kjActivateOnHover: { classPropertyName: "kjActivateOnHover", publicName: "kjActivateOnHover", isSignal: true, isRequired: false, transformFunction: null }, kjHostActiveDescendant: { classPropertyName: "kjHostActiveDescendant", publicName: "kjHostActiveDescendant", isSignal: true, isRequired: false, transformFunction: null }, kjFocusMode: { classPropertyName: "kjFocusMode", publicName: "kjFocusMode", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { kjActiveChange: "kjActiveChange" }, host: { listeners: { "keydown": "_onKeydown($event)", "focusin": "_onFocusin($event)" }, properties: { "attr.aria-activedescendant": "kjFocusMode() === \"activedescendant\" && kjHostActiveDescendant() ? activeId() : null" } }, providers: [
5709
5715
  {
5710
5716
  provide: KJ_LIST_FOCUS_MODE,
5711
5717
  // The factory closes over the directive instance via `inject`,
@@ -5734,12 +5740,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
5734
5740
  // really holds focus — must not publish it as well. A container that pins
5735
5741
  // `KJ_LIST_FOCUS_MODE_DEFAULT` to `'roving'` (menu, menubar, tree-select)
5736
5742
  // therefore drops the attribute entirely.
5737
- '[attr.aria-activedescendant]': 'kjFocusMode() === "activedescendant" ? activeId() : null',
5743
+ '[attr.aria-activedescendant]': 'kjFocusMode() === "activedescendant" && kjHostActiveDescendant() ? activeId() : null',
5738
5744
  '(keydown)': '_onKeydown($event)',
5739
5745
  '(focusin)': '_onFocusin($event)',
5740
5746
  },
5741
5747
  }]
5742
- }], ctorParameters: () => [], propDecorators: { kjOrientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjOrientation", required: false }] }], kjWrap: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjWrap", required: false }] }], kjPageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjPageSize", required: false }] }], kjActivateOnHover: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjActivateOnHover", required: false }] }], kjFocusMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjFocusMode", required: false }] }], kjActiveChange: [{ type: i0.Output, args: ["kjActiveChange"] }] } });
5748
+ }], ctorParameters: () => [], propDecorators: { kjOrientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjOrientation", required: false }] }], kjWrap: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjWrap", required: false }] }], kjPageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjPageSize", required: false }] }], kjActivateOnHover: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjActivateOnHover", required: false }] }], kjHostActiveDescendant: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjHostActiveDescendant", required: false }] }], kjFocusMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjFocusMode", required: false }] }], kjActiveChange: [{ type: i0.Output, args: ["kjActiveChange"] }] } });
5743
5749
  /** Types of `<input>` that hold text the user types (Space is a character). */
5744
5750
  const NON_TEXT_INPUT_TYPES = new Set([
5745
5751
  'button',
@@ -26409,6 +26415,9 @@ class KjSelect {
26409
26415
  ...(ngDevMode ? [{ debugName: "_multiple" }] : /* istanbul ignore next */ []));
26410
26416
  /** Whether multi-select is enabled. */
26411
26417
  multiple = this._multiple.asReadonly();
26418
+ /** @internal — written by `KjSelectContent`'s `kjSearchable` input. */
26419
+ _searchable = signal(false, /* @ts-ignore */
26420
+ ...(ngDevMode ? [{ debugName: "_searchable" }] : /* istanbul ignore next */ []));
26412
26421
  /** @internal — set by `KjSelectTrigger` at construction time. */
26413
26422
  _triggerEl = signal(null, /* @ts-ignore */
26414
26423
  ...(ngDevMode ? [{ debugName: "_triggerEl" }] : /* istanbul ignore next */ []));
@@ -26436,7 +26445,26 @@ class KjSelect {
26436
26445
  * inside a palette, a menu inside a select) answer to that composite,
26437
26446
  * not to this one.
26438
26447
  */
26439
- items = ownListItems(this, this.allItems);
26448
+ _viewItems = signal(null, /* @ts-ignore */
26449
+ ...(ngDevMode ? [{ debugName: "_viewItems" }] : /* istanbul ignore next */ []));
26450
+ viewItems = computed(() => this._viewItems()?.() ?? null, /* @ts-ignore */
26451
+ ...(ngDevMode ? [{ debugName: "viewItems" }] : /* istanbul ignore next */ []));
26452
+ /**
26453
+ * @internal Register rows a wrapper stamps in its OWN view
26454
+ * (`<kj-select [kjOptions]>`). A content query never crosses into a
26455
+ * component's view, so those rows join `items()` only through here.
26456
+ */
26457
+ _setViewItems(items) {
26458
+ this._viewItems.set(items);
26459
+ }
26460
+ items = ownListItems(this, this.allItems, this.viewItems);
26461
+ /**
26462
+ * Implements `KjListNavigatorConfig.visibleItems`: the items a wrapper has
26463
+ * not hidden through `KjListItem.setVisible` (a search filter, a folded
26464
+ * group). The navigator and type-ahead walk only these.
26465
+ */
26466
+ visibleItems = computed(() => this.items().filter(i => i.visible()), /* @ts-ignore */
26467
+ ...(ngDevMode ? [{ debugName: "visibleItems" }] : /* istanbul ignore next */ []));
26440
26468
  /** Implements `KjListNavigatorConfig.mode`. */
26441
26469
  mode = computed(() => this._multiple() ? 'multi' : 'single', /* @ts-ignore */
26442
26470
  ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
@@ -26564,7 +26592,7 @@ class KjSelectTrigger {
26564
26592
  this._overlayTrigger.attachPanel(panel);
26565
26593
  }
26566
26594
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: KjSelectTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive });
26567
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.5", type: KjSelectTrigger, isStandalone: true, selector: "[kjSelectTrigger]", inputs: { kjMultiple: { classPropertyName: "kjMultiple", publicName: "kjMultiple", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "keydown": "onKeydown($event)" } }, providers: [
26595
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.5", type: KjSelectTrigger, isStandalone: true, selector: "[kjSelectTrigger]", inputs: { kjMultiple: { classPropertyName: "kjMultiple", publicName: "kjMultiple", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "keydown": "onKeydown($event)" }, properties: { "attr.aria-haspopup": "select?._searchable() ? \"dialog\" : \"listbox\"" } }, providers: [
26568
26596
  {
26569
26597
  provide: KJ_OVERLAY_TRIGGER_EVENT_STRATEGY,
26570
26598
  useFactory: () => listboxClickTrigger(),
@@ -26604,6 +26632,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
26604
26632
  ],
26605
26633
  host: {
26606
26634
  '(keydown)': 'onKeydown($event)',
26635
+ // Runs after the composed `KjOverlayTrigger`'s binding, so it wins: a
26636
+ // searchable panel is a dialog (search field + listbox), not a listbox.
26637
+ '[attr.aria-haspopup]': 'select?._searchable() ? "dialog" : "listbox"',
26607
26638
  },
26608
26639
  }]
26609
26640
  }], ctorParameters: () => [], propDecorators: { kjMultiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjMultiple", required: false }] }] } });
@@ -26760,7 +26791,15 @@ class KjSelectContent {
26760
26791
  ...(ngDevMode ? [{ debugName: "kjAlign" }] : /* istanbul ignore next */ []));
26761
26792
  /** Pixel offset between trigger and panel. */
26762
26793
  kjOffset = input(4, { ...(ngDevMode ? { debugName: "kjOffset" } : /* istanbul ignore next */ {}), transform: pxOffset(4) });
26794
+ /**
26795
+ * The panel holds a search field above an inner listbox the consumer
26796
+ * renders: the panel becomes `role="dialog"` and the trigger advertises
26797
+ * `aria-haspopup="dialog"`. Defaults to `false`.
26798
+ */
26799
+ kjSearchable = input(false, { ...(ngDevMode ? { debugName: "kjSearchable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
26763
26800
  constructor() {
26801
+ const select = inject(KJ_SELECT, { optional: true });
26802
+ effect(() => select?._searchable.set(this.kjSearchable()));
26764
26803
  injectAnchoredPosition({
26765
26804
  side: this.kjSide,
26766
26805
  align: this.kjAlign,
@@ -26769,7 +26808,7 @@ class KjSelectContent {
26769
26808
  });
26770
26809
  }
26771
26810
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: KjSelectContent, deps: [], target: i0.ɵɵFactoryTarget.Component });
26772
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.5", type: KjSelectContent, isStandalone: true, selector: "kj-select-content", inputs: { kjSide: { classPropertyName: "kjSide", publicName: "kjSide", isSignal: true, isRequired: false, transformFunction: null }, kjAlign: { classPropertyName: "kjAlign", publicName: "kjAlign", isSignal: true, isRequired: false, transformFunction: null }, kjOffset: { classPropertyName: "kjOffset", publicName: "kjOffset", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.aria-multiselectable": "selection.mode() === \"multi\" ? \"true\" : null" } }, providers: [
26811
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.5", type: KjSelectContent, isStandalone: true, selector: "kj-select-content", inputs: { kjSide: { classPropertyName: "kjSide", publicName: "kjSide", isSignal: true, isRequired: false, transformFunction: null }, kjAlign: { classPropertyName: "kjAlign", publicName: "kjAlign", isSignal: true, isRequired: false, transformFunction: null }, kjOffset: { classPropertyName: "kjOffset", publicName: "kjOffset", isSignal: true, isRequired: false, transformFunction: null }, kjSearchable: { classPropertyName: "kjSearchable", publicName: "kjSearchable", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.role": "kjSearchable() ? \"dialog\" : \"listbox\"", "attr.aria-multiselectable": "!kjSearchable() && selection.mode() === \"multi\" ? \"true\" : null" } }, providers: [
26773
26812
  { provide: KJ_OVERLAY_PANEL_ROLE, useValue: 'listbox' },
26774
26813
  { provide: KJ_OVERLAY_MOUNT_STRATEGY, useFactory: () => bodyPortal() },
26775
26814
  { provide: KJ_OVERLAY_POSITION_STRATEGY, useFactory: () => anchoredTo() },
@@ -26777,7 +26816,7 @@ class KjSelectContent {
26777
26816
  // what screen readers announce, so no `aria-activedescendant` is needed
26778
26817
  // on a panel that never holds focus itself.
26779
26818
  { provide: KJ_LIST_FOCUS_MODE_DEFAULT, useValue: 'roving' },
26780
- ], hostDirectives: [{ directive: KjOverlayPanel, inputs: ["kjFor", "kjFor"] }, { directive: KjListNavigator }, { directive: KjListPanelFocus }], ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
26819
+ ], hostDirectives: [{ directive: KjOverlayPanel, inputs: ["kjFor", "kjFor"] }, { directive: KjListNavigator, inputs: ["kjFocusMode", "kjFocusMode", "kjHostActiveDescendant", "kjHostActiveDescendant"] }, { directive: KjListPanelFocus }], ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
26781
26820
  }
26782
26821
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: KjSelectContent, decorators: [{
26783
26822
  type: Component,
@@ -26786,7 +26825,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
26786
26825
  standalone: true,
26787
26826
  hostDirectives: [
26788
26827
  { directive: KjOverlayPanel, inputs: ['kjFor'] },
26789
- KjListNavigator,
26828
+ // `kjFocusMode` is forwarded so a wrapper that keeps DOM focus in a
26829
+ // search field can switch the panel to `aria-activedescendant`.
26830
+ { directive: KjListNavigator, inputs: ['kjFocusMode', 'kjHostActiveDescendant'] },
26790
26831
  KjListPanelFocus,
26791
26832
  ],
26792
26833
  providers: [
@@ -26799,13 +26840,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
26799
26840
  { provide: KJ_LIST_FOCUS_MODE_DEFAULT, useValue: 'roving' },
26800
26841
  ],
26801
26842
  host: {
26802
- '[attr.aria-multiselectable]': 'selection.mode() === "multi" ? "true" : null',
26843
+ // With `kjSearchable` the panel is a `dialog` holding a search combobox
26844
+ // and an inner `listbox` the wrapper renders, so the listbox-only
26845
+ // attributes move to that inner element. These run after the composed
26846
+ // panel's / navigator's own bindings, so they win.
26847
+ '[attr.role]': 'kjSearchable() ? "dialog" : "listbox"',
26848
+ '[attr.aria-multiselectable]': '!kjSearchable() && selection.mode() === "multi" ? "true" : null',
26803
26849
  },
26804
26850
  changeDetection: ChangeDetectionStrategy.OnPush,
26805
26851
  encapsulation: ViewEncapsulation.None,
26806
26852
  template: `<ng-content />`,
26807
26853
  }]
26808
- }], ctorParameters: () => [], propDecorators: { kjSide: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjSide", required: false }] }], kjAlign: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjAlign", required: false }] }], kjOffset: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjOffset", required: false }] }] } });
26854
+ }], ctorParameters: () => [], propDecorators: { kjSide: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjSide", required: false }] }], kjAlign: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjAlign", required: false }] }], kjOffset: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjOffset", required: false }] }], kjSearchable: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjSearchable", required: false }] }] } });
26809
26855
 
26810
26856
  /**
26811
26857
  * Individual option inside a `<kj-select-content>`. Public inputs