@lilydesignsystem/angular-headless 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lilydesignsystem/angular-headless",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "engines": {
5
5
  "node": "=26"
6
6
  },
@@ -3613,16 +3613,53 @@ declare class Icon {
3613
3613
  * IconButton — a button containing only an icon with a required accessible label
3614
3614
  *
3615
3615
  * Headless Angular component. Renders the semantic HTML root with the
3616
- * kebab-case class hook `icon-button` and the consumer-provided `className`.
3617
- * Ships zero CSS; the consumer styles via the class hook.
3616
+ * kebab-case class hook `icon-button` (or `baseClass`) and the consumer-
3617
+ * provided `className`. Ships zero CSS; the consumer styles via the class
3618
+ * hook.
3619
+ *
3620
+ * The `lily-icon-button` selector tag itself is `display: contents`
3621
+ * unconditionally, on every usage — it is a semantics-free structural
3622
+ * wrapper (Angular's component selector always emits a real host element;
3623
+ * there is no Svelte-`<svelte:element>`/no-wrapper-root equivalent), so it
3624
+ * must never participate in layout or box generation. This is the same
3625
+ * "structurally required inline style" exception documented in
3626
+ * spec/headless/index.md for `ThemeProvider`, applied here for the same
3627
+ * reason: composing `<lily-icon-button>` inside a consumer's own template
3628
+ * (e.g. a `*-picker` component) must not insert a visible extra box
3629
+ * between the picker's root and its real `<button>`.
3630
+ *
3631
+ * `baseClass`, `ariaHaspopup`/`ariaExpanded`/`ariaControls`, and `focus()`
3632
+ * exist specifically so a composing consumer can reproduce an exact
3633
+ * `class="{helper}-button" aria-haspopup aria-expanded aria-controls`
3634
+ * contract and call `.focus()` on the real button — Angular has no
3635
+ * generic spread-onto-inputs mechanism (see picker-bar's own contract
3636
+ * note), so these are named inputs rather than a rest-prop spread.
3618
3637
  */
3619
3638
  declare class IconButton {
3620
- /** Optional aria-label override. */
3639
+ /** Accessible label (REQUIRED for icon-only buttons in practice; enforced by convention, not the type system, to match the button's own required aria-label contract). */
3621
3640
  readonly label: _angular_core.InputSignal<string>;
3622
- /** Extra CSS classes appended to the base class. */
3641
+ /** Extra CSS classes appended after the base class. */
3623
3642
  readonly className: _angular_core.InputSignal<string>;
3643
+ /** Base class token, replacing `"icon-button"` outright (not appended). */
3644
+ readonly baseClass: _angular_core.InputSignal<string>;
3645
+ readonly ariaHaspopup: _angular_core.InputSignal<string>;
3646
+ readonly ariaExpanded: _angular_core.InputSignal<boolean | null>;
3647
+ readonly ariaControls: _angular_core.InputSignal<string>;
3648
+ readonly disabled: _angular_core.InputSignal<boolean>;
3649
+ private rootRef?;
3650
+ protected readonly ariaExpandedAttr: _angular_core.Signal<string | null>;
3651
+ /** Focus the real rendered `<button>`. */
3652
+ focus(options?: FocusOptions): void;
3653
+ /**
3654
+ * The real rendered `<button>` element. `focus()` covers the common
3655
+ * case; this exists for a consumer that needs the raw node itself —
3656
+ * e.g. remembering it as a focus-restoration target, or an
3657
+ * `instanceof HTMLElement` / `.contains()` check — where a behaviour
3658
+ * method alone isn't enough.
3659
+ */
3660
+ get element(): HTMLButtonElement | undefined;
3624
3661
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<IconButton, never>;
3625
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<IconButton, "lily-icon-button", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
3662
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<IconButton, "lily-icon-button", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; "baseClass": { "alias": "baseClass"; "required": false; "isSignal": true; }; "ariaHaspopup": { "alias": "ariaHaspopup"; "required": false; "isSignal": true; }; "ariaExpanded": { "alias": "ariaExpanded"; "required": false; "isSignal": true; }; "ariaControls": { "alias": "ariaControls"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
3626
3663
  }
3627
3664
 
3628
3665
  /**
@@ -4306,18 +4343,70 @@ declare class LineChart {
4306
4343
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<LineChart, "lily-line-chart", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
4307
4344
  }
4308
4345
 
4346
+ /** Navigation/focus model. Default `"roving-focus"` — currently inert (no keyboard handling), matching this component's pre-extension behaviour: nothing consumed it, so there was nothing to preserve beyond "does nothing." */
4347
+ type ListboxNavigation = "roving-focus" | "active-descendant";
4309
4348
  /**
4310
4349
  * Listbox — a list of selectable options with keyboard navigation
4311
4350
  *
4312
4351
  * Headless Angular component. Renders the semantic HTML root with the
4313
- * kebab-case class hook `listbox` and the consumer-provided `className`.
4314
- * Ships zero CSS; the consumer styles via the class hook.
4352
+ * kebab-case class hook `listbox` (or `baseClass`) and the consumer-
4353
+ * provided `className`. Ships zero CSS; the consumer styles via the class
4354
+ * hook.
4355
+ *
4356
+ * `navigation="active-descendant"` (opt-in; default `"roving-focus"` is
4357
+ * unchanged) implements the full WAI-ARIA APG listbox keyboard contract
4358
+ * via a virtual cursor: the root itself holds focus and tracks
4359
+ * `activeIndex` (a two-way `model()`, this catalog's established
4360
+ * bindable-state idiom — see `picker-bar`'s own contract note), mirrored
4361
+ * to `aria-activedescendant`. `clamp` (vs. wrap), `typeahead`, `pageSize`
4362
+ * paging, and `activate`/`escape`/`tabOut` outputs let a composing
4363
+ * consumer (e.g. a `*-picker` component) decide what "select" / "cancel"
4364
+ * / "leave" mean, the same division of responsibility as the Svelte
4365
+ * version this was ported from.
4366
+ *
4367
+ * Like `IconButton`, the `lily-listbox` selector tag itself is
4368
+ * `display: contents` unconditionally — a semantics-free structural
4369
+ * wrapper around the real `role="listbox"` element, so composing it
4370
+ * inside another component's template inserts no extra layout box. See
4371
+ * `IconButton`'s doc comment for the full `display: contents` reasoning
4372
+ * (mirrors the `ThemeProvider` precedent in spec/headless/index.md).
4315
4373
  */
4316
4374
  declare class Listbox {
4317
4375
  readonly label: _angular_core.InputSignal<string>;
4318
4376
  readonly className: _angular_core.InputSignal<string>;
4377
+ /** Base class token, replacing `"listbox"` outright (not appended). */
4378
+ readonly baseClass: _angular_core.InputSignal<string>;
4379
+ /** `id` for the real inner `role="listbox"` element — named `elementId`, not `id`, because the host tag's own native `id` attribute (set directly on `<lily-listbox id="...">` by a consumer) is a separate, unrelated thing once `display: contents` puts a real wrapper element between the host and the inner listbox. Needed so a composing consumer's `aria-controls`/`document.getElementById` can reach the real element. */
4380
+ readonly elementId: _angular_core.InputSignal<string>;
4381
+ /** Sets `hidden` on the real inner element. Angular has no generic spread-onto-inputs mechanism (see `IconButton`'s doc comment), so this is a named input rather than a rest-prop spread. */
4382
+ readonly hidden: _angular_core.InputSignal<boolean>;
4383
+ readonly navigation: _angular_core.InputSignal<ListboxNavigation>;
4384
+ /** Arrow keys clamp instead of wrap (active-descendant mode only). */
4385
+ readonly clamp: _angular_core.InputSignal<boolean>;
4386
+ /** Printable-character typeahead (active-descendant mode only). */
4387
+ readonly typeahead: _angular_core.InputSignal<boolean>;
4388
+ /** PageUp/PageDown step size (active-descendant mode only). */
4389
+ readonly pageSize: _angular_core.InputSignal<number>;
4390
+ /** Virtual cursor position. Two-way bindable via `[(activeIndex)]`. -1 = none. */
4391
+ readonly activeIndex: _angular_core.ModelSignal<number>;
4392
+ /** Enter/Space on the active option (active-descendant mode only). */
4393
+ readonly activate: _angular_core.OutputEmitterRef<number>;
4394
+ /** Escape pressed (active-descendant mode only). */
4395
+ readonly escape: _angular_core.OutputEmitterRef<void>;
4396
+ /** Tab pressed, emitted before the key is processed — never prevented, so the consumer can move focus first (e.g. to a trigger button) before the browser's default Tab computes its target (active-descendant mode only). */
4397
+ readonly tabOut: _angular_core.OutputEmitterRef<KeyboardEvent>;
4398
+ private rootRef?;
4399
+ private typeaheadBuffer;
4400
+ private typeaheadTimer?;
4401
+ private options;
4402
+ protected readonly activeId: _angular_core.Signal<string | null>;
4403
+ /** Focus the root (active-descendant mode: the listbox itself holds focus). */
4404
+ focus(options?: FocusOptions): void;
4405
+ private moveActive;
4406
+ private runTypeahead;
4407
+ protected onKeydown(event: KeyboardEvent): void;
4319
4408
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Listbox, never>;
4320
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<Listbox, "lily-listbox", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
4409
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<Listbox, "lily-listbox", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; "baseClass": { "alias": "baseClass"; "required": false; "isSignal": true; }; "elementId": { "alias": "elementId"; "required": false; "isSignal": true; }; "hidden": { "alias": "hidden"; "required": false; "isSignal": true; }; "navigation": { "alias": "navigation"; "required": false; "isSignal": true; }; "clamp": { "alias": "clamp"; "required": false; "isSignal": true; }; "typeahead": { "alias": "typeahead"; "required": false; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "activeIndex": { "alias": "activeIndex"; "required": false; "isSignal": true; }; }, { "activeIndex": "activeIndexChange"; "activate": "activate"; "escape": "escape"; "tabOut": "tabOut"; }, never, ["*"], true, never>;
4321
4410
  }
4322
4411
 
4323
4412
  /**