@kouji-ui/core 0.11.1 → 0.12.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 */ []));
@@ -26437,6 +26446,13 @@ class KjSelect {
26437
26446
  * not to this one.
26438
26447
  */
26439
26448
  items = ownListItems(this, this.allItems);
26449
+ /**
26450
+ * Implements `KjListNavigatorConfig.visibleItems`: the items a wrapper has
26451
+ * not hidden through `KjListItem.setVisible` (a search filter, a folded
26452
+ * group). The navigator and type-ahead walk only these.
26453
+ */
26454
+ visibleItems = computed(() => this.items().filter(i => i.visible()), /* @ts-ignore */
26455
+ ...(ngDevMode ? [{ debugName: "visibleItems" }] : /* istanbul ignore next */ []));
26440
26456
  /** Implements `KjListNavigatorConfig.mode`. */
26441
26457
  mode = computed(() => this._multiple() ? 'multi' : 'single', /* @ts-ignore */
26442
26458
  ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
@@ -26564,7 +26580,7 @@ class KjSelectTrigger {
26564
26580
  this._overlayTrigger.attachPanel(panel);
26565
26581
  }
26566
26582
  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: [
26583
+ 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
26584
  {
26569
26585
  provide: KJ_OVERLAY_TRIGGER_EVENT_STRATEGY,
26570
26586
  useFactory: () => listboxClickTrigger(),
@@ -26604,6 +26620,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
26604
26620
  ],
26605
26621
  host: {
26606
26622
  '(keydown)': 'onKeydown($event)',
26623
+ // Runs after the composed `KjOverlayTrigger`'s binding, so it wins: a
26624
+ // searchable panel is a dialog (search field + listbox), not a listbox.
26625
+ '[attr.aria-haspopup]': 'select?._searchable() ? "dialog" : "listbox"',
26607
26626
  },
26608
26627
  }]
26609
26628
  }], ctorParameters: () => [], propDecorators: { kjMultiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjMultiple", required: false }] }] } });
@@ -26760,7 +26779,15 @@ class KjSelectContent {
26760
26779
  ...(ngDevMode ? [{ debugName: "kjAlign" }] : /* istanbul ignore next */ []));
26761
26780
  /** Pixel offset between trigger and panel. */
26762
26781
  kjOffset = input(4, { ...(ngDevMode ? { debugName: "kjOffset" } : /* istanbul ignore next */ {}), transform: pxOffset(4) });
26782
+ /**
26783
+ * The panel holds a search field above an inner listbox the consumer
26784
+ * renders: the panel becomes `role="dialog"` and the trigger advertises
26785
+ * `aria-haspopup="dialog"`. Defaults to `false`.
26786
+ */
26787
+ kjSearchable = input(false, { ...(ngDevMode ? { debugName: "kjSearchable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
26763
26788
  constructor() {
26789
+ const select = inject(KJ_SELECT, { optional: true });
26790
+ effect(() => select?._searchable.set(this.kjSearchable()));
26764
26791
  injectAnchoredPosition({
26765
26792
  side: this.kjSide,
26766
26793
  align: this.kjAlign,
@@ -26769,7 +26796,7 @@ class KjSelectContent {
26769
26796
  });
26770
26797
  }
26771
26798
  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: [
26799
+ 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
26800
  { provide: KJ_OVERLAY_PANEL_ROLE, useValue: 'listbox' },
26774
26801
  { provide: KJ_OVERLAY_MOUNT_STRATEGY, useFactory: () => bodyPortal() },
26775
26802
  { provide: KJ_OVERLAY_POSITION_STRATEGY, useFactory: () => anchoredTo() },
@@ -26777,7 +26804,7 @@ class KjSelectContent {
26777
26804
  // what screen readers announce, so no `aria-activedescendant` is needed
26778
26805
  // on a panel that never holds focus itself.
26779
26806
  { 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 });
26807
+ ], 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
26808
  }
26782
26809
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: KjSelectContent, decorators: [{
26783
26810
  type: Component,
@@ -26786,7 +26813,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
26786
26813
  standalone: true,
26787
26814
  hostDirectives: [
26788
26815
  { directive: KjOverlayPanel, inputs: ['kjFor'] },
26789
- KjListNavigator,
26816
+ // `kjFocusMode` is forwarded so a wrapper that keeps DOM focus in a
26817
+ // search field can switch the panel to `aria-activedescendant`.
26818
+ { directive: KjListNavigator, inputs: ['kjFocusMode', 'kjHostActiveDescendant'] },
26790
26819
  KjListPanelFocus,
26791
26820
  ],
26792
26821
  providers: [
@@ -26799,13 +26828,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
26799
26828
  { provide: KJ_LIST_FOCUS_MODE_DEFAULT, useValue: 'roving' },
26800
26829
  ],
26801
26830
  host: {
26802
- '[attr.aria-multiselectable]': 'selection.mode() === "multi" ? "true" : null',
26831
+ // With `kjSearchable` the panel is a `dialog` holding a search combobox
26832
+ // and an inner `listbox` the wrapper renders, so the listbox-only
26833
+ // attributes move to that inner element. These run after the composed
26834
+ // panel's / navigator's own bindings, so they win.
26835
+ '[attr.role]': 'kjSearchable() ? "dialog" : "listbox"',
26836
+ '[attr.aria-multiselectable]': '!kjSearchable() && selection.mode() === "multi" ? "true" : null',
26803
26837
  },
26804
26838
  changeDetection: ChangeDetectionStrategy.OnPush,
26805
26839
  encapsulation: ViewEncapsulation.None,
26806
26840
  template: `<ng-content />`,
26807
26841
  }]
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 }] }] } });
26842
+ }], 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
26843
 
26810
26844
  /**
26811
26845
  * Individual option inside a `<kj-select-content>`. Public inputs