@lilydesignsystem/angular-motion-picker 0.1.0 → 0.1.2

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,6 +1,7 @@
1
1
  import { NgTemplateOutlet } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
- import { Directive, input, model, output, contentChild, TemplateRef, viewChild, signal, computed, inject, ChangeDetectorRef, DestroyRef, effect, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { Directive, input, model, output, contentChild, TemplateRef, viewChild, signal, computed, inject, ChangeDetectorRef, effect, ChangeDetectionStrategy, Component } from '@angular/core';
4
+ import { IconButton, Listbox } from '@lilydesignsystem/angular-headless';
4
5
 
5
6
  /**
6
7
  * Resolve a motion slug to its display label: each hyphen-separated
@@ -53,10 +54,10 @@ class MotionPickerIcon {
53
54
  static ngTemplateContextGuard(_dir, _ctx) {
54
55
  return true;
55
56
  }
56
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MotionPickerIcon, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
57
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.4", type: MotionPickerIcon, isStandalone: true, selector: "ng-template[lilyMotionPickerIcon]", ngImport: i0 }); }
57
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MotionPickerIcon, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
58
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.7", type: MotionPickerIcon, isStandalone: true, selector: "ng-template[lilyMotionPickerIcon]", ngImport: i0 }); }
58
59
  }
59
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MotionPickerIcon, decorators: [{
60
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MotionPickerIcon, decorators: [{
60
61
  type: Directive,
61
62
  args: [{
62
63
  selector: "ng-template[lilyMotionPickerIcon]",
@@ -102,6 +103,9 @@ class MotionPicker {
102
103
  ...(ngDevMode ? [{ debugName: "iconTemplate" }] : /* istanbul ignore next */ []));
103
104
  this.rootRef = viewChild.required("rootEl", /* @ts-ignore */
104
105
  ...(ngDevMode ? [{ debugName: "rootRef" }] : /* istanbul ignore next */ []));
106
+ // Angular resolves a template-ref-variable on a component tag to the
107
+ // component INSTANCE by default (not its ElementRef) — exactly what's
108
+ // needed to call the headless components' own public `focus()` methods.
105
109
  this.buttonRef = viewChild.required("buttonEl", /* @ts-ignore */
106
110
  ...(ngDevMode ? [{ debugName: "buttonRef" }] : /* istanbul ignore next */ []));
107
111
  this.listRef = viewChild.required("listEl", /* @ts-ignore */
@@ -112,12 +116,6 @@ class MotionPicker {
112
116
  ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
113
117
  this.activeIndex = signal(-1, /* @ts-ignore */
114
118
  ...(ngDevMode ? [{ debugName: "activeIndex" }] : /* istanbul ignore next */ []));
115
- /** `aria-activedescendant` is only meaningful while the listbox is open. */
116
- this.activeDescendant = computed(() => {
117
- const i = this.activeIndex();
118
- return this.open() && i >= 0 ? this.optionId(i) : null;
119
- }, /* @ts-ignore */
120
- ...(ngDevMode ? [{ debugName: "activeDescendant" }] : /* istanbul ignore next */ []));
121
119
  this.childContext = computed(() => {
122
120
  const args = {
123
121
  value: this.value(),
@@ -127,8 +125,6 @@ class MotionPicker {
127
125
  return { $implicit: args, ...args };
128
126
  }, /* @ts-ignore */
129
127
  ...(ngDevMode ? [{ debugName: "childContext" }] : /* istanbul ignore next */ []));
130
- // Typeahead buffer: APG listbox behaviour. Reset after a pause.
131
- this.typeahead = "";
132
128
  this.initialised = false;
133
129
  // ---------------------------------------------------------------
134
130
  // Open / close
@@ -139,7 +135,17 @@ class MotionPicker {
139
135
  * Flushing detection first is the Angular equivalent of Svelte's
140
136
  * synchronous update (same fix the date-time-picker port recorded). */
141
137
  this.cdr = inject(ChangeDetectorRef);
142
- inject(DestroyRef).onDestroy(() => clearTimeout(this.typeaheadTimer));
138
+ // Arrow/Home/End/PageUp/PageDown/typeahead/Escape/Tab keyboard handling
139
+ // inside the open list is owned by the composed `lily-listbox`'s
140
+ // `navigation="active-descendant"` mode (see
141
+ // @lilydesignsystem/angular-headless); this component only decides
142
+ // what open/close/choose/scroll mean, and keeps the highlighted option
143
+ // in view whenever activeIndex changes.
144
+ this.scrollOnActiveIndexChange = effect(() => {
145
+ this.activeIndex();
146
+ this.scrollActiveIntoView();
147
+ }, /* @ts-ignore */
148
+ ...(ngDevMode ? [{ debugName: "scrollOnActiveIndexChange" }] : /* istanbul ignore next */ []));
143
149
  effect(() => {
144
150
  const current = this.value();
145
151
  if (!this.initialised) {
@@ -208,7 +214,7 @@ class MotionPicker {
208
214
  // aria-activedescendant, per the APG listbox pattern.
209
215
  this.cdr.detectChanges();
210
216
  queueMicrotask(() => {
211
- this.listRef().nativeElement.focus({ preventScroll: true });
217
+ this.listRef().focus({ preventScroll: true });
212
218
  this.scrollActiveIntoView();
213
219
  });
214
220
  }
@@ -219,7 +225,7 @@ class MotionPicker {
219
225
  this.open.set(false);
220
226
  this.activeIndex.set(-1);
221
227
  if (refocus)
222
- queueMicrotask(() => this.buttonRef().nativeElement.focus({ preventScroll: true }));
228
+ queueMicrotask(() => this.buttonRef().focus({ preventScroll: true }));
223
229
  }
224
230
  choose(index) {
225
231
  const slug = this.motions()[index];
@@ -231,43 +237,15 @@ class MotionPicker {
231
237
  const i = this.activeIndex();
232
238
  if (i < 0)
233
239
  return;
234
- const el = this.listRef().nativeElement.children[i];
240
+ // getElementById rather than reaching into the composed Listbox's
241
+ // DOM: Listbox exposes behaviour (focus()) and state (activeIndex),
242
+ // not its rendered children, the same "consumer owns the option
243
+ // elements" division of responsibility the headless component
244
+ // documents for itself.
245
+ const el = document.getElementById(this.optionId(i));
235
246
  // jsdom does not implement scrollIntoView; call it only if present.
236
247
  el?.scrollIntoView?.({ block: "nearest" });
237
248
  }
238
- moveActive(delta) {
239
- const count = this.motions().length;
240
- if (count === 0)
241
- return;
242
- // Clamp rather than wrap, per the APG listbox pattern.
243
- this.activeIndex.set(Math.min(Math.max(this.activeIndex() + delta, 0), count - 1));
244
- this.scrollActiveIntoView();
245
- }
246
- runTypeahead(char) {
247
- const lower = char.toLowerCase();
248
- // APG listbox typeahead: a single character moves to the NEXT
249
- // option starting with it, and repeating that character keeps
250
- // cycling. Only a buffer of differing characters refines the
251
- // match, and that buffer stays anchored on the active option.
252
- const sameCharRun = this.typeahead === "" || [...this.typeahead].every((c) => c === lower);
253
- this.typeahead += lower;
254
- clearTimeout(this.typeaheadTimer);
255
- this.typeaheadTimer = setTimeout(() => (this.typeahead = ""), 500);
256
- const query = sameCharRun ? lower : this.typeahead;
257
- const motions = this.motions();
258
- const anchor = this.activeIndex() < 0 ? 0 : this.activeIndex();
259
- const start = sameCharRun ? anchor + 1 : anchor;
260
- // Search forward, wrapping once — typeahead wraps even though the
261
- // arrows clamp, or options above the cursor would be untypable.
262
- for (let n = 0; n < motions.length; n++) {
263
- const i = (start + n) % motions.length;
264
- if (this.labelFor(motions[i]).toLowerCase().startsWith(query)) {
265
- this.activeIndex.set(i);
266
- this.scrollActiveIntoView();
267
- return;
268
- }
269
- }
270
- }
271
249
  // ---------------------------------------------------------------
272
250
  // Keyboard
273
251
  // ---------------------------------------------------------------
@@ -285,66 +263,14 @@ class MotionPicker {
285
263
  break;
286
264
  }
287
265
  }
288
- onListKeydown(event) {
289
- switch (event.key) {
290
- case "ArrowDown":
291
- event.preventDefault();
292
- this.moveActive(1);
293
- break;
294
- case "ArrowUp":
295
- event.preventDefault();
296
- this.moveActive(-1);
297
- break;
298
- case "Home":
299
- event.preventDefault();
300
- this.activeIndex.set(0);
301
- this.scrollActiveIntoView();
302
- break;
303
- case "End":
304
- event.preventDefault();
305
- this.activeIndex.set(this.motions().length - 1);
306
- this.scrollActiveIntoView();
307
- break;
308
- case "Enter":
309
- case " ":
310
- event.preventDefault();
311
- if (this.activeIndex() >= 0)
312
- this.choose(this.activeIndex());
313
- break;
314
- case "Escape":
315
- event.preventDefault();
316
- this.closeList();
317
- break;
318
- case "PageUp":
319
- event.preventDefault();
320
- this.moveActive(-10);
321
- break;
322
- case "PageDown":
323
- // ±10, clamped: an APG-optional key for long motion lists.
324
- event.preventDefault();
325
- this.moveActive(10);
326
- break;
327
- case "Tab":
328
- // Tab moves on — but focus goes to the button FIRST, without
329
- // cancelling the key. Hiding the focused list drops focus to
330
- // <body>, and the browser then computes the default Tab move
331
- // from the top of the document, so tabbing out of an open
332
- // picker teleported the user to the page's first tab stop.
333
- // From the button, the default Tab lands exactly where leaving
334
- // the picker should. The button always exists, so no
335
- // detectChanges is needed before the focus move; guard the
336
- // METHOD because jsdom-shaped hosts may not implement it.
337
- this.buttonRef().nativeElement.focus?.({ preventScroll: true });
338
- this.closeList(false);
339
- break;
340
- default:
341
- if (event.key.length === 1 &&
342
- !event.ctrlKey &&
343
- !event.metaKey &&
344
- !event.altKey) {
345
- this.runTypeahead(event.key);
346
- }
347
- }
266
+ /** `lily-listbox` Tab handling never prevents the key — focus goes to
267
+ * the button FIRST so the browser's default Tab move computes from
268
+ * the picker's position rather than from <body> (hiding the focused
269
+ * list drops focus there, and the browser would otherwise compute the
270
+ * default Tab move from the top of the document). */
271
+ onListTabOut() {
272
+ this.buttonRef().focus({ preventScroll: true });
273
+ this.closeList(false);
348
274
  }
349
275
  onRootFocusOut(event) {
350
276
  const next = event.relatedTarget;
@@ -377,8 +303,8 @@ class MotionPicker {
377
303
  }
378
304
  this.motionChange.emit(slug);
379
305
  }
380
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MotionPicker, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
381
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: MotionPicker, isStandalone: true, selector: "lily-motion-picker", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, motions: { classPropertyName: "motions", publicName: "motions", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, defaultValue: { classPropertyName: "defaultValue", publicName: "defaultValue", isSignal: true, isRequired: false, transformFunction: null }, storageKey: { classPropertyName: "storageKey", publicName: "storageKey", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, target: { classPropertyName: "target", publicName: "target", isSignal: true, isRequired: false, transformFunction: null }, motionLabels: { classPropertyName: "motionLabels", publicName: "motionLabels", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", motionChange: "motionChange" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, queries: [{ propertyName: "iconTemplate", first: true, predicate: TemplateRef, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "rootRef", first: true, predicate: ["rootEl"], descendants: true, isSignal: true }, { propertyName: "buttonRef", first: true, predicate: ["buttonEl"], descendants: true, isSignal: true }, { propertyName: "listRef", first: true, predicate: ["listEl"], descendants: true, isSignal: true }], ngImport: i0, template: `
306
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MotionPicker, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
307
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MotionPicker, isStandalone: true, selector: "lily-motion-picker", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, motions: { classPropertyName: "motions", publicName: "motions", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, defaultValue: { classPropertyName: "defaultValue", publicName: "defaultValue", isSignal: true, isRequired: false, transformFunction: null }, storageKey: { classPropertyName: "storageKey", publicName: "storageKey", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, target: { classPropertyName: "target", publicName: "target", isSignal: true, isRequired: false, transformFunction: null }, motionLabels: { classPropertyName: "motionLabels", publicName: "motionLabels", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", motionChange: "motionChange" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, queries: [{ propertyName: "iconTemplate", first: true, predicate: TemplateRef, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "rootRef", first: true, predicate: ["rootEl"], descendants: true, isSignal: true }, { propertyName: "buttonRef", first: true, predicate: ["buttonEl"], descendants: true, isSignal: true }, { propertyName: "listRef", first: true, predicate: ["listEl"], descendants: true, isSignal: true }], ngImport: i0, template: `
382
308
  <div
383
309
  #rootEl
384
310
  class="motion-picker {{ className() }}"
@@ -386,14 +312,13 @@ class MotionPicker {
386
312
  >
387
313
  <input type="hidden" [name]="name()" [value]="value()" />
388
314
 
389
- <button
315
+ <lily-icon-button
390
316
  #buttonEl
391
- type="button"
392
- class="motion-picker-button"
393
- [attr.aria-label]="label() || null"
394
- aria-haspopup="listbox"
395
- [attr.aria-expanded]="open()"
396
- [attr.aria-controls]="listId"
317
+ [label]="label()"
318
+ baseClass="motion-picker-button"
319
+ ariaHaspopup="listbox"
320
+ [ariaExpanded]="open()"
321
+ [ariaControls]="listId"
397
322
  (click)="toggle()"
398
323
  (keydown)="onButtonKeydown($event)"
399
324
  >
@@ -418,18 +343,22 @@ class MotionPicker {
418
343
  <path d="M5 3v10M11 3v10" />
419
344
  </svg>
420
345
  }
421
- </button>
346
+ </lily-icon-button>
422
347
 
423
- <ul
348
+ <lily-listbox
424
349
  #listEl
425
- class="motion-picker-list"
426
- [id]="listId"
427
- role="listbox"
428
- [attr.aria-label]="label() || null"
429
- [attr.aria-activedescendant]="activeDescendant()"
430
- tabindex="-1"
431
- [attr.hidden]="open() ? null : ''"
432
- (keydown)="onListKeydown($event)"
350
+ [label]="label()"
351
+ baseClass="motion-picker-list"
352
+ [elementId]="listId"
353
+ navigation="active-descendant"
354
+ [clamp]="true"
355
+ [typeahead]="true"
356
+ [pageSize]="10"
357
+ [(activeIndex)]="activeIndex"
358
+ [hidden]="!open()"
359
+ (activate)="choose($event)"
360
+ (escape)="closeList()"
361
+ (tabOut)="onListTabOut()"
433
362
  >
434
363
  @for (motion of motions(); track motion; let i = $index) {
435
364
  <li
@@ -443,16 +372,16 @@ class MotionPicker {
443
372
  {{ labelFor(motion) }}
444
373
  </li>
445
374
  }
446
- </ul>
375
+ </lily-listbox>
447
376
  </div>
448
- `, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
377
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: IconButton, selector: "lily-icon-button", inputs: ["label", "className", "baseClass", "ariaHaspopup", "ariaExpanded", "ariaControls", "tabIndex", "disabled"] }, { kind: "component", type: Listbox, selector: "lily-listbox", inputs: ["label", "className", "baseClass", "elementId", "hidden", "navigation", "clamp", "typeahead", "pageSize", "activeIndex"], outputs: ["activeIndexChange", "activate", "escape", "tabOut"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
449
378
  }
450
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: MotionPicker, decorators: [{
379
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MotionPicker, decorators: [{
451
380
  type: Component,
452
381
  args: [{
453
382
  selector: "lily-motion-picker",
454
383
  standalone: true,
455
- imports: [NgTemplateOutlet],
384
+ imports: [NgTemplateOutlet, IconButton, Listbox],
456
385
  changeDetection: ChangeDetectionStrategy.OnPush,
457
386
  host: {
458
387
  "(document:click)": "onDocumentClick($event)",
@@ -465,14 +394,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImpor
465
394
  >
466
395
  <input type="hidden" [name]="name()" [value]="value()" />
467
396
 
468
- <button
397
+ <lily-icon-button
469
398
  #buttonEl
470
- type="button"
471
- class="motion-picker-button"
472
- [attr.aria-label]="label() || null"
473
- aria-haspopup="listbox"
474
- [attr.aria-expanded]="open()"
475
- [attr.aria-controls]="listId"
399
+ [label]="label()"
400
+ baseClass="motion-picker-button"
401
+ ariaHaspopup="listbox"
402
+ [ariaExpanded]="open()"
403
+ [ariaControls]="listId"
476
404
  (click)="toggle()"
477
405
  (keydown)="onButtonKeydown($event)"
478
406
  >
@@ -497,18 +425,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImpor
497
425
  <path d="M5 3v10M11 3v10" />
498
426
  </svg>
499
427
  }
500
- </button>
428
+ </lily-icon-button>
501
429
 
502
- <ul
430
+ <lily-listbox
503
431
  #listEl
504
- class="motion-picker-list"
505
- [id]="listId"
506
- role="listbox"
507
- [attr.aria-label]="label() || null"
508
- [attr.aria-activedescendant]="activeDescendant()"
509
- tabindex="-1"
510
- [attr.hidden]="open() ? null : ''"
511
- (keydown)="onListKeydown($event)"
432
+ [label]="label()"
433
+ baseClass="motion-picker-list"
434
+ [elementId]="listId"
435
+ navigation="active-descendant"
436
+ [clamp]="true"
437
+ [typeahead]="true"
438
+ [pageSize]="10"
439
+ [(activeIndex)]="activeIndex"
440
+ [hidden]="!open()"
441
+ (activate)="choose($event)"
442
+ (escape)="closeList()"
443
+ (tabOut)="onListTabOut()"
512
444
  >
513
445
  @for (motion of motions(); track motion; let i = $index) {
514
446
  <li
@@ -522,7 +454,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImpor
522
454
  {{ labelFor(motion) }}
523
455
  </li>
524
456
  }
525
- </ul>
457
+ </lily-listbox>
526
458
  </div>
527
459
  `,
528
460
  }]
@@ -1 +1 @@
1
- {"version":3,"file":"lilydesignsystem-angular-motion-picker.mjs","sources":["../../motion-picker.component.ts","../../lilydesignsystem-angular-motion-picker.ts"],"sourcesContent":["import { NgTemplateOutlet } from \"@angular/common\";\nimport {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n DestroyRef,\n Directive,\n ElementRef,\n TemplateRef,\n computed,\n contentChild,\n effect,\n inject,\n input,\n model,\n output,\n signal,\n viewChild,\n} from \"@angular/core\";\n\n/**\n * Default button icon: a bundled SVG (two pause bars), not a Unicode\n * character. Reversed 2026-09-16 from the font-dependent-glyph\n * convention (was U+23F8 PAUSE SIGN + U+FE0E, exported as `PAUSE_SIGN`\n * — removed, not renamed). \"Stop the moving parts\" still reads\n * directly from two bars; a bundled outline SVG matches the other four\n * picker icons as one consistent visual family regardless of the\n * consumer's fonts.\n */\n\n/** Context passed to a custom icon `<ng-template>` (the button icon). */\nexport type ChildArgs = {\n /** Currently selected motion slug. */\n value: string;\n /** Is the listbox open? */\n open: boolean;\n /** Resolve a slug to its display label. */\n labelFor: (motion: string) => string;\n};\n\n/**\n * Resolve a motion slug to its display label: each hyphen-separated\n * word title-cased, so a slug like \"no-preference\" renders as\n * \"No Preference\". Mirrors `sizeName` in text-size-picker and\n * `themeName` in theme-picker.\n */\nexport function motionName(motion: string): string {\n return motion\n .split(\"-\")\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(\" \");\n}\n\n/**\n * True when the platform reports a preference for reduced motion.\n * SSR-safe: `window`/`matchMedia` are absent on the server, so this\n * resolves to `false` there and the client re-derives it on mount.\n */\nexport function prefersReducedMotion(): boolean {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") {\n return false;\n }\n try {\n return window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n } catch {\n return false;\n }\n}\n\nlet uid = 0;\n/** Stable per-instance id prefix; SSR-safe (no Math.random / Date.now). */\nexport function nextMotionPickerId(): string {\n uid += 1;\n return `motion-picker-${uid}`;\n}\n\n/**\n * Optional marker for the projected icon template. Gives consumers typed\n * `let-` variables:\n *\n * ```html\n * <lily-motion-picker ...>\n * <ng-template lilyMotionPickerIcon let-args>{{ args.labelFor(args.value) }}</ng-template>\n * </lily-motion-picker>\n * ```\n *\n * The component queries any projected `<ng-template>`, so the marker is\n * for type-checking and readability, not for matching.\n */\n@Directive({\n selector: \"ng-template[lilyMotionPickerIcon]\",\n standalone: true,\n})\nexport class MotionPickerIcon {\n static ngTemplateContextGuard(\n _dir: MotionPickerIcon,\n _ctx: unknown,\n ): _ctx is ChildArgs & { $implicit: ChildArgs } {\n return true;\n }\n}\n\n/**\n * MotionPicker — `data-motion` reduced-motion picker.\n *\n * Renders an icon button that opens a WAI-ARIA APG listbox of motion\n * slugs. On every change the component sets `data-motion=\"{slug}\"` on\n * the document root (or on a consumer-supplied target), with optional\n * `localStorage` persistence. Its initial value defers to the\n * platform's `(prefers-reduced-motion: reduce)` media query before\n * falling back to a fixed default — the one behaviour difference from\n * its `theme-picker`/`text-size-picker` siblings. Ships no CSS; the\n * consumer decides what `[data-motion=\"reduce\"]` actually suppresses.\n * See `spec/index.md` for the full contract.\n */\n@Component({\n selector: \"lily-motion-picker\",\n standalone: true,\n imports: [NgTemplateOutlet],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n \"(document:click)\": \"onDocumentClick($event)\",\n },\n template: `\n <div\n #rootEl\n class=\"motion-picker {{ className() }}\"\n (focusout)=\"onRootFocusOut($event)\"\n >\n <input type=\"hidden\" [name]=\"name()\" [value]=\"value()\" />\n\n <button\n #buttonEl\n type=\"button\"\n class=\"motion-picker-button\"\n [attr.aria-label]=\"label() || null\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"listId\"\n (click)=\"toggle()\"\n (keydown)=\"onButtonKeydown($event)\"\n >\n @if (iconTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"childContext()\"\n />\n } @else {\n <svg\n class=\"motion-picker-icon\"\n viewBox=\"0 0 16 16\"\n width=\"1.05rem\"\n height=\"1.05rem\"\n aria-hidden=\"true\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.6\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"M5 3v10M11 3v10\" />\n </svg>\n }\n </button>\n\n <ul\n #listEl\n class=\"motion-picker-list\"\n [id]=\"listId\"\n role=\"listbox\"\n [attr.aria-label]=\"label() || null\"\n [attr.aria-activedescendant]=\"activeDescendant()\"\n tabindex=\"-1\"\n [attr.hidden]=\"open() ? null : ''\"\n (keydown)=\"onListKeydown($event)\"\n >\n @for (motion of motions(); track motion; let i = $index) {\n <li\n class=\"motion-picker-option\"\n [id]=\"optionId(i)\"\n role=\"option\"\n [attr.aria-selected]=\"motion === value()\"\n [attr.data-active]=\"i === activeIndex() ? '' : null\"\n (click)=\"choose(i)\"\n >\n {{ labelFor(motion) }}\n </li>\n }\n </ul>\n </div>\n `,\n})\nexport class MotionPicker {\n readonly label = input.required<string>();\n readonly motions = input.required<string[]>();\n readonly value = model<string>(\"\");\n readonly defaultValue = input<string>(\"\");\n readonly storageKey = input<string>(\"\");\n readonly name = input<string>(\"motion\");\n readonly target = input<HTMLElement | null>(null);\n readonly motionLabels = input<Record<string, string>>({});\n readonly className = input<string>(\"\");\n readonly motionChange = output<string>();\n\n /** Projected icon template; replaces the default icon when supplied. */\n protected readonly iconTemplate = contentChild(TemplateRef);\n\n private readonly rootRef =\n viewChild.required<ElementRef<HTMLDivElement>>(\"rootEl\");\n private readonly buttonRef =\n viewChild.required<ElementRef<HTMLButtonElement>>(\"buttonEl\");\n private readonly listRef =\n viewChild.required<ElementRef<HTMLUListElement>>(\"listEl\");\n\n private readonly baseId = nextMotionPickerId();\n protected readonly listId = `${this.baseId}-list`;\n\n protected readonly open = signal(false);\n protected readonly activeIndex = signal(-1);\n\n /** `aria-activedescendant` is only meaningful while the listbox is open. */\n protected readonly activeDescendant = computed(() => {\n const i = this.activeIndex();\n return this.open() && i >= 0 ? this.optionId(i) : null;\n });\n\n protected readonly childContext = computed(() => {\n const args: ChildArgs = {\n value: this.value(),\n open: this.open(),\n labelFor: (motion: string) => this.labelFor(motion),\n };\n return { $implicit: args, ...args };\n });\n\n // Typeahead buffer: APG listbox behaviour. Reset after a pause.\n private typeahead = \"\";\n private typeaheadTimer: ReturnType<typeof setTimeout> | undefined;\n\n private initialised = false;\n\n constructor() {\n inject(DestroyRef).onDestroy(() => clearTimeout(this.typeaheadTimer));\n\n effect(() => {\n const current = this.value();\n\n if (!this.initialised) {\n this.initialised = true;\n let initial = current;\n\n const sk = this.storageKey();\n if (!initial && sk) {\n try {\n initial =\n (typeof localStorage !== \"undefined\"\n ? localStorage.getItem(sk)\n : null) ?? \"\";\n } catch {\n // ignore privacy errors\n }\n }\n\n if (!initial) {\n // Unlike text-size-picker's \"medium\" default, motion has a\n // real external signal to defer to: the platform's own\n // (prefers-reduced-motion: reduce) media query.\n const motions = this.motions();\n const dv = this.defaultValue();\n const osPreferred = prefersReducedMotion() ? \"reduce\" : \"no-preference\";\n initial =\n dv ||\n (motions.includes(osPreferred) ? osPreferred : undefined) ||\n motions[0] ||\n \"\";\n }\n\n if (initial && initial !== current) {\n this.value.set(initial);\n return;\n }\n }\n\n if (current) this.applyMotion(current);\n });\n }\n\n protected optionId(index: number): string {\n return `${this.baseId}-option-${index}`;\n }\n\n labelFor(motion: string): string {\n const labels = this.motionLabels();\n if (motion in labels) return labels[motion];\n return motionName(motion);\n }\n\n // ---------------------------------------------------------------\n // Open / close\n // ---------------------------------------------------------------\n\n /** Under zoneless change detection the signal write that un-hides the\n * list has not reached the DOM when a microtask runs, so focusing the\n * still-hidden list fails silently and Escape lands on the button.\n * Flushing detection first is the Angular equivalent of Svelte's\n * synchronous update (same fix the date-time-picker port recorded). */\n private readonly cdr = inject(ChangeDetectorRef);\n\n protected toggle(): void {\n if (this.open()) this.closeList();\n else this.openList();\n }\n\n /** Open the listbox, activating `startIndex` (default: the selection). */\n openList(startIndex?: number): void {\n const selected = this.motions().indexOf(this.value());\n // An empty list has no option to activate; -1 keeps\n // aria-activedescendant off rather than pointing at an id that\n // does not exist.\n this.activeIndex.set(\n this.motions().length === 0\n ? -1\n : (startIndex ?? (selected >= 0 ? selected : 0)),\n );\n this.open.set(true);\n // Focus moves to the listbox; the active option is conveyed via\n // aria-activedescendant, per the APG listbox pattern.\n this.cdr.detectChanges();\n queueMicrotask(() => {\n this.listRef().nativeElement.focus({ preventScroll: true });\n this.scrollActiveIntoView();\n });\n }\n\n /** Close the listbox; `refocus` returns focus to the button. */\n closeList(refocus = true): void {\n if (!this.open()) return;\n this.open.set(false);\n this.activeIndex.set(-1);\n if (refocus) queueMicrotask(() => this.buttonRef().nativeElement.focus({ preventScroll: true }));\n }\n\n protected choose(index: number): void {\n const slug = this.motions()[index];\n if (slug) this.value.set(slug);\n this.closeList();\n }\n\n private scrollActiveIntoView(): void {\n const i = this.activeIndex();\n if (i < 0) return;\n const el = this.listRef().nativeElement.children[i] as\n HTMLElement | undefined;\n // jsdom does not implement scrollIntoView; call it only if present.\n el?.scrollIntoView?.({ block: \"nearest\" });\n }\n\n private moveActive(delta: number): void {\n const count = this.motions().length;\n if (count === 0) return;\n // Clamp rather than wrap, per the APG listbox pattern.\n this.activeIndex.set(\n Math.min(Math.max(this.activeIndex() + delta, 0), count - 1),\n );\n this.scrollActiveIntoView();\n }\n\n private runTypeahead(char: string): void {\n const lower = char.toLowerCase();\n // APG listbox typeahead: a single character moves to the NEXT\n // option starting with it, and repeating that character keeps\n // cycling. Only a buffer of differing characters refines the\n // match, and that buffer stays anchored on the active option.\n const sameCharRun =\n this.typeahead === \"\" || [...this.typeahead].every((c) => c === lower);\n this.typeahead += lower;\n clearTimeout(this.typeaheadTimer);\n this.typeaheadTimer = setTimeout(() => (this.typeahead = \"\"), 500);\n const query = sameCharRun ? lower : this.typeahead;\n const motions = this.motions();\n const anchor = this.activeIndex() < 0 ? 0 : this.activeIndex();\n const start = sameCharRun ? anchor + 1 : anchor;\n // Search forward, wrapping once — typeahead wraps even though the\n // arrows clamp, or options above the cursor would be untypable.\n for (let n = 0; n < motions.length; n++) {\n const i = (start + n) % motions.length;\n if (this.labelFor(motions[i]).toLowerCase().startsWith(query)) {\n this.activeIndex.set(i);\n this.scrollActiveIntoView();\n return;\n }\n }\n }\n\n // ---------------------------------------------------------------\n // Keyboard\n // ---------------------------------------------------------------\n\n protected onButtonKeydown(event: KeyboardEvent): void {\n switch (event.key) {\n case \"ArrowDown\":\n case \"Enter\":\n case \" \":\n event.preventDefault();\n this.openList();\n break;\n case \"ArrowUp\":\n event.preventDefault();\n this.openList(this.motions().length - 1);\n break;\n }\n }\n\n protected onListKeydown(event: KeyboardEvent): void {\n switch (event.key) {\n case \"ArrowDown\":\n event.preventDefault();\n this.moveActive(1);\n break;\n case \"ArrowUp\":\n event.preventDefault();\n this.moveActive(-1);\n break;\n case \"Home\":\n event.preventDefault();\n this.activeIndex.set(0);\n this.scrollActiveIntoView();\n break;\n case \"End\":\n event.preventDefault();\n this.activeIndex.set(this.motions().length - 1);\n this.scrollActiveIntoView();\n break;\n case \"Enter\":\n case \" \":\n event.preventDefault();\n if (this.activeIndex() >= 0) this.choose(this.activeIndex());\n break;\n case \"Escape\":\n event.preventDefault();\n this.closeList();\n break;\n case \"PageUp\":\n event.preventDefault();\n this.moveActive(-10);\n break;\n case \"PageDown\":\n // ±10, clamped: an APG-optional key for long motion lists.\n event.preventDefault();\n this.moveActive(10);\n break;\n case \"Tab\":\n // Tab moves on — but focus goes to the button FIRST, without\n // cancelling the key. Hiding the focused list drops focus to\n // <body>, and the browser then computes the default Tab move\n // from the top of the document, so tabbing out of an open\n // picker teleported the user to the page's first tab stop.\n // From the button, the default Tab lands exactly where leaving\n // the picker should. The button always exists, so no\n // detectChanges is needed before the focus move; guard the\n // METHOD because jsdom-shaped hosts may not implement it.\n this.buttonRef().nativeElement.focus?.({ preventScroll: true });\n this.closeList(false);\n break;\n default:\n if (\n event.key.length === 1 &&\n !event.ctrlKey &&\n !event.metaKey &&\n !event.altKey\n ) {\n this.runTypeahead(event.key);\n }\n }\n }\n\n protected onRootFocusOut(event: FocusEvent): void {\n const next = event.relatedTarget as Node | null;\n if (next && this.rootRef().nativeElement.contains(next)) return;\n this.closeList(false);\n }\n\n protected onDocumentClick(event: Event): void {\n if (!this.open()) return;\n const t = event.target as Node | null;\n if (t && !this.rootRef().nativeElement.contains(t)) this.closeList(false);\n }\n\n // ---------------------------------------------------------------\n // Apply\n // ---------------------------------------------------------------\n\n private applyMotion(slug: string): void {\n if (typeof document === \"undefined\" || !slug) return;\n (this.target() ?? document.documentElement).setAttribute(\n \"data-motion\",\n slug,\n );\n\n const sk = this.storageKey();\n if (sk) {\n try {\n localStorage.setItem(sk, slug);\n } catch {\n // ignore quota / privacy errors\n }\n }\n this.motionChange.emit(slug);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lilydesignsystem-angular-motion-picker.mjs","sources":["../../motion-picker.component.ts","../../lilydesignsystem-angular-motion-picker.ts"],"sourcesContent":["import { NgTemplateOutlet } from \"@angular/common\";\nimport {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n Directive,\n ElementRef,\n TemplateRef,\n computed,\n contentChild,\n effect,\n inject,\n input,\n model,\n output,\n signal,\n viewChild,\n} from \"@angular/core\";\nimport { IconButton, Listbox } from \"@lilydesignsystem/angular-headless\";\n\n/**\n * Default button icon: a bundled SVG (two pause bars), not a Unicode\n * character. Reversed 2026-09-16 from the font-dependent-glyph\n * convention (was U+23F8 PAUSE SIGN + U+FE0E, exported as `PAUSE_SIGN`\n * — removed, not renamed). \"Stop the moving parts\" still reads\n * directly from two bars; a bundled outline SVG matches the other four\n * picker icons as one consistent visual family regardless of the\n * consumer's fonts.\n */\n\n/** Context passed to a custom icon `<ng-template>` (the button icon). */\nexport type ChildArgs = {\n /** Currently selected motion slug. */\n value: string;\n /** Is the listbox open? */\n open: boolean;\n /** Resolve a slug to its display label. */\n labelFor: (motion: string) => string;\n};\n\n/**\n * Resolve a motion slug to its display label: each hyphen-separated\n * word title-cased, so a slug like \"no-preference\" renders as\n * \"No Preference\". Mirrors `sizeName` in text-size-picker and\n * `themeName` in theme-picker.\n */\nexport function motionName(motion: string): string {\n return motion\n .split(\"-\")\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(\" \");\n}\n\n/**\n * True when the platform reports a preference for reduced motion.\n * SSR-safe: `window`/`matchMedia` are absent on the server, so this\n * resolves to `false` there and the client re-derives it on mount.\n */\nexport function prefersReducedMotion(): boolean {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") {\n return false;\n }\n try {\n return window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n } catch {\n return false;\n }\n}\n\nlet uid = 0;\n/** Stable per-instance id prefix; SSR-safe (no Math.random / Date.now). */\nexport function nextMotionPickerId(): string {\n uid += 1;\n return `motion-picker-${uid}`;\n}\n\n/**\n * Optional marker for the projected icon template. Gives consumers typed\n * `let-` variables:\n *\n * ```html\n * <lily-motion-picker ...>\n * <ng-template lilyMotionPickerIcon let-args>{{ args.labelFor(args.value) }}</ng-template>\n * </lily-motion-picker>\n * ```\n *\n * The component queries any projected `<ng-template>`, so the marker is\n * for type-checking and readability, not for matching.\n */\n@Directive({\n selector: \"ng-template[lilyMotionPickerIcon]\",\n standalone: true,\n})\nexport class MotionPickerIcon {\n static ngTemplateContextGuard(\n _dir: MotionPickerIcon,\n _ctx: unknown,\n ): _ctx is ChildArgs & { $implicit: ChildArgs } {\n return true;\n }\n}\n\n/**\n * MotionPicker — `data-motion` reduced-motion picker.\n *\n * Renders an icon button that opens a WAI-ARIA APG listbox of motion\n * slugs. On every change the component sets `data-motion=\"{slug}\"` on\n * the document root (or on a consumer-supplied target), with optional\n * `localStorage` persistence. Its initial value defers to the\n * platform's `(prefers-reduced-motion: reduce)` media query before\n * falling back to a fixed default — the one behaviour difference from\n * its `theme-picker`/`text-size-picker` siblings. Ships no CSS; the\n * consumer decides what `[data-motion=\"reduce\"]` actually suppresses.\n * See `spec/index.md` for the full contract.\n */\n@Component({\n selector: \"lily-motion-picker\",\n standalone: true,\n imports: [NgTemplateOutlet, IconButton, Listbox],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n \"(document:click)\": \"onDocumentClick($event)\",\n },\n template: `\n <div\n #rootEl\n class=\"motion-picker {{ className() }}\"\n (focusout)=\"onRootFocusOut($event)\"\n >\n <input type=\"hidden\" [name]=\"name()\" [value]=\"value()\" />\n\n <lily-icon-button\n #buttonEl\n [label]=\"label()\"\n baseClass=\"motion-picker-button\"\n ariaHaspopup=\"listbox\"\n [ariaExpanded]=\"open()\"\n [ariaControls]=\"listId\"\n (click)=\"toggle()\"\n (keydown)=\"onButtonKeydown($event)\"\n >\n @if (iconTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"childContext()\"\n />\n } @else {\n <svg\n class=\"motion-picker-icon\"\n viewBox=\"0 0 16 16\"\n width=\"1.05rem\"\n height=\"1.05rem\"\n aria-hidden=\"true\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.6\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"M5 3v10M11 3v10\" />\n </svg>\n }\n </lily-icon-button>\n\n <lily-listbox\n #listEl\n [label]=\"label()\"\n baseClass=\"motion-picker-list\"\n [elementId]=\"listId\"\n navigation=\"active-descendant\"\n [clamp]=\"true\"\n [typeahead]=\"true\"\n [pageSize]=\"10\"\n [(activeIndex)]=\"activeIndex\"\n [hidden]=\"!open()\"\n (activate)=\"choose($event)\"\n (escape)=\"closeList()\"\n (tabOut)=\"onListTabOut()\"\n >\n @for (motion of motions(); track motion; let i = $index) {\n <li\n class=\"motion-picker-option\"\n [id]=\"optionId(i)\"\n role=\"option\"\n [attr.aria-selected]=\"motion === value()\"\n [attr.data-active]=\"i === activeIndex() ? '' : null\"\n (click)=\"choose(i)\"\n >\n {{ labelFor(motion) }}\n </li>\n }\n </lily-listbox>\n </div>\n `,\n})\nexport class MotionPicker {\n readonly label = input.required<string>();\n readonly motions = input.required<string[]>();\n readonly value = model<string>(\"\");\n readonly defaultValue = input<string>(\"\");\n readonly storageKey = input<string>(\"\");\n readonly name = input<string>(\"motion\");\n readonly target = input<HTMLElement | null>(null);\n readonly motionLabels = input<Record<string, string>>({});\n readonly className = input<string>(\"\");\n readonly motionChange = output<string>();\n\n /** Projected icon template; replaces the default icon when supplied. */\n protected readonly iconTemplate = contentChild(TemplateRef);\n\n private readonly rootRef =\n viewChild.required<ElementRef<HTMLDivElement>>(\"rootEl\");\n // Angular resolves a template-ref-variable on a component tag to the\n // component INSTANCE by default (not its ElementRef) — exactly what's\n // needed to call the headless components' own public `focus()` methods.\n private readonly buttonRef = viewChild.required<IconButton>(\"buttonEl\");\n private readonly listRef = viewChild.required<Listbox>(\"listEl\");\n\n private readonly baseId = nextMotionPickerId();\n protected readonly listId = `${this.baseId}-list`;\n\n protected readonly open = signal(false);\n protected readonly activeIndex = signal(-1);\n\n protected readonly childContext = computed(() => {\n const args: ChildArgs = {\n value: this.value(),\n open: this.open(),\n labelFor: (motion: string) => this.labelFor(motion),\n };\n return { $implicit: args, ...args };\n });\n\n private initialised = false;\n\n constructor() {\n effect(() => {\n const current = this.value();\n\n if (!this.initialised) {\n this.initialised = true;\n let initial = current;\n\n const sk = this.storageKey();\n if (!initial && sk) {\n try {\n initial =\n (typeof localStorage !== \"undefined\"\n ? localStorage.getItem(sk)\n : null) ?? \"\";\n } catch {\n // ignore privacy errors\n }\n }\n\n if (!initial) {\n // Unlike text-size-picker's \"medium\" default, motion has a\n // real external signal to defer to: the platform's own\n // (prefers-reduced-motion: reduce) media query.\n const motions = this.motions();\n const dv = this.defaultValue();\n const osPreferred = prefersReducedMotion() ? \"reduce\" : \"no-preference\";\n initial =\n dv ||\n (motions.includes(osPreferred) ? osPreferred : undefined) ||\n motions[0] ||\n \"\";\n }\n\n if (initial && initial !== current) {\n this.value.set(initial);\n return;\n }\n }\n\n if (current) this.applyMotion(current);\n });\n }\n\n protected optionId(index: number): string {\n return `${this.baseId}-option-${index}`;\n }\n\n labelFor(motion: string): string {\n const labels = this.motionLabels();\n if (motion in labels) return labels[motion];\n return motionName(motion);\n }\n\n // ---------------------------------------------------------------\n // Open / close\n // ---------------------------------------------------------------\n\n /** Under zoneless change detection the signal write that un-hides the\n * list has not reached the DOM when a microtask runs, so focusing the\n * still-hidden list fails silently and Escape lands on the button.\n * Flushing detection first is the Angular equivalent of Svelte's\n * synchronous update (same fix the date-time-picker port recorded). */\n private readonly cdr = inject(ChangeDetectorRef);\n\n protected toggle(): void {\n if (this.open()) this.closeList();\n else this.openList();\n }\n\n /** Open the listbox, activating `startIndex` (default: the selection). */\n openList(startIndex?: number): void {\n const selected = this.motions().indexOf(this.value());\n // An empty list has no option to activate; -1 keeps\n // aria-activedescendant off rather than pointing at an id that\n // does not exist.\n this.activeIndex.set(\n this.motions().length === 0\n ? -1\n : (startIndex ?? (selected >= 0 ? selected : 0)),\n );\n this.open.set(true);\n // Focus moves to the listbox; the active option is conveyed via\n // aria-activedescendant, per the APG listbox pattern.\n this.cdr.detectChanges();\n queueMicrotask(() => {\n this.listRef().focus({ preventScroll: true });\n this.scrollActiveIntoView();\n });\n }\n\n /** Close the listbox; `refocus` returns focus to the button. */\n closeList(refocus = true): void {\n if (!this.open()) return;\n this.open.set(false);\n this.activeIndex.set(-1);\n if (refocus) queueMicrotask(() => this.buttonRef().focus({ preventScroll: true }));\n }\n\n protected choose(index: number): void {\n const slug = this.motions()[index];\n if (slug) this.value.set(slug);\n this.closeList();\n }\n\n private scrollActiveIntoView(): void {\n const i = this.activeIndex();\n if (i < 0) return;\n // getElementById rather than reaching into the composed Listbox's\n // DOM: Listbox exposes behaviour (focus()) and state (activeIndex),\n // not its rendered children, the same \"consumer owns the option\n // elements\" division of responsibility the headless component\n // documents for itself.\n const el = document.getElementById(this.optionId(i));\n // jsdom does not implement scrollIntoView; call it only if present.\n el?.scrollIntoView?.({ block: \"nearest\" });\n }\n\n // Arrow/Home/End/PageUp/PageDown/typeahead/Escape/Tab keyboard handling\n // inside the open list is owned by the composed `lily-listbox`'s\n // `navigation=\"active-descendant\"` mode (see\n // @lilydesignsystem/angular-headless); this component only decides\n // what open/close/choose/scroll mean, and keeps the highlighted option\n // in view whenever activeIndex changes.\n private readonly scrollOnActiveIndexChange = effect(() => {\n this.activeIndex();\n this.scrollActiveIntoView();\n });\n\n // ---------------------------------------------------------------\n // Keyboard\n // ---------------------------------------------------------------\n\n protected onButtonKeydown(event: KeyboardEvent): void {\n switch (event.key) {\n case \"ArrowDown\":\n case \"Enter\":\n case \" \":\n event.preventDefault();\n this.openList();\n break;\n case \"ArrowUp\":\n event.preventDefault();\n this.openList(this.motions().length - 1);\n break;\n }\n }\n\n /** `lily-listbox` Tab handling never prevents the key — focus goes to\n * the button FIRST so the browser's default Tab move computes from\n * the picker's position rather than from <body> (hiding the focused\n * list drops focus there, and the browser would otherwise compute the\n * default Tab move from the top of the document). */\n protected onListTabOut(): void {\n this.buttonRef().focus({ preventScroll: true });\n this.closeList(false);\n }\n\n protected onRootFocusOut(event: FocusEvent): void {\n const next = event.relatedTarget as Node | null;\n if (next && this.rootRef().nativeElement.contains(next)) return;\n this.closeList(false);\n }\n\n protected onDocumentClick(event: Event): void {\n if (!this.open()) return;\n const t = event.target as Node | null;\n if (t && !this.rootRef().nativeElement.contains(t)) this.closeList(false);\n }\n\n // ---------------------------------------------------------------\n // Apply\n // ---------------------------------------------------------------\n\n private applyMotion(slug: string): void {\n if (typeof document === \"undefined\" || !slug) return;\n (this.target() ?? document.documentElement).setAttribute(\n \"data-motion\",\n slug,\n );\n\n const sk = this.storageKey();\n if (sk) {\n try {\n localStorage.setItem(sk, slug);\n } catch {\n // ignore quota / privacy errors\n }\n }\n this.motionChange.emit(slug);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,10 +1,14 @@
1
1
  {
2
2
  "name": "@lilydesignsystem/angular-motion-picker",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "engines": {
5
5
  "node": "=26"
6
6
  },
7
7
  "description": "Lily Design System Angular 20 motion (reduced-motion) picker: an icon button opening an APG listbox that sets data-motion on the document, defaulting to the OS's own prefers-reduced-motion signal. Headless, SSR-safe, no CSS.",
8
+ "dependencies": {
9
+ "@lilydesignsystem/angular-headless": "^0.2.0",
10
+ "tslib": "^2.3.0"
11
+ },
8
12
  "peerDependencies": {
9
13
  "@angular/core": ">=20.0.0 <23.0.0",
10
14
  "@angular/common": ">=20.0.0 <23.0.0"
@@ -41,8 +45,5 @@
41
45
  "default": "./fesm2022/lilydesignsystem-angular-motion-picker.mjs"
42
46
  }
43
47
  },
44
- "type": "module",
45
- "dependencies": {
46
- "tslib": "^2.3.0"
47
- }
48
+ "type": "module"
48
49
  }
@@ -87,8 +87,6 @@ declare class MotionPicker {
87
87
  protected readonly listId: string;
88
88
  protected readonly open: _angular_core.WritableSignal<boolean>;
89
89
  protected readonly activeIndex: _angular_core.WritableSignal<number>;
90
- /** `aria-activedescendant` is only meaningful while the listbox is open. */
91
- protected readonly activeDescendant: _angular_core.Signal<string | null>;
92
90
  protected readonly childContext: _angular_core.Signal<{
93
91
  /** Currently selected motion slug. */
94
92
  value: string;
@@ -98,8 +96,6 @@ declare class MotionPicker {
98
96
  labelFor: (motion: string) => string;
99
97
  $implicit: ChildArgs;
100
98
  }>;
101
- private typeahead;
102
- private typeaheadTimer;
103
99
  private initialised;
104
100
  constructor();
105
101
  protected optionId(index: number): string;
@@ -117,10 +113,14 @@ declare class MotionPicker {
117
113
  closeList(refocus?: boolean): void;
118
114
  protected choose(index: number): void;
119
115
  private scrollActiveIntoView;
120
- private moveActive;
121
- private runTypeahead;
116
+ private readonly scrollOnActiveIndexChange;
122
117
  protected onButtonKeydown(event: KeyboardEvent): void;
123
- protected onListKeydown(event: KeyboardEvent): void;
118
+ /** `lily-listbox` Tab handling never prevents the key — focus goes to
119
+ * the button FIRST so the browser's default Tab move computes from
120
+ * the picker's position rather than from <body> (hiding the focused
121
+ * list drops focus there, and the browser would otherwise compute the
122
+ * default Tab move from the top of the document). */
123
+ protected onListTabOut(): void;
124
124
  protected onRootFocusOut(event: FocusEvent): void;
125
125
  protected onDocumentClick(event: Event): void;
126
126
  private applyMotion;
@@ -1 +1 @@
1
- {"version":3,"file":"lilydesignsystem-angular-motion-picker.d.ts","sources":["../../motion-picker.component.ts"],"mappings":";;;AAoBA;;;;;;;;AAQG;AAEH;AACM,KAAM,SAAS;;;;;;AAMnB;;AAGF;;;;;AAKG;AACH,iBAAgB,UAAU;AAO1B;;;;AAIG;AACH,iBAAgB,oBAAoB;AAYpC;AACA,iBAAgB,kBAAkB;AAKlC;;;;;;;;;;;;AAYG;AACH,cAIa,gBAAgB;AAC3B,wCACQ,gBAAgB,0BAEb,SAAS;mBAAgB,SAAS;AAAE;oDAJpC,gBAAgB;sDAAhB,gBAAgB;AAO5B;AAED;;;;;;;;;;;;AAYG;AACH,cA6Ea,YAAY;oBACT,aAAA,CAAA,WAAA;sBACE,aAAA,CAAA,WAAA;oBACF,aAAA,CAAA,WAAA;2BACO,aAAA,CAAA,WAAA;yBACF,aAAA,CAAA,WAAA;mBACN,aAAA,CAAA,WAAA;qBACE,aAAA,CAAA,WAAA,CAAA,WAAA;2BACM,aAAA,CAAA,WAAA,CAAA,MAAA;wBACH,aAAA,CAAA,WAAA;2BACG,aAAA,CAAA,gBAAA;;AAGrB,qCAA+B,aAAA,CAAA,MAAA,CAAA,WAAA;AAE/B;AAEA;AAEA;AAGA;AACA;AAEA,6BAAuB,aAAA,CAAA,cAAA;AACvB,oCAA8B,aAAA,CAAA,cAAA;;AAG9B,yCAAmC,aAAA,CAAA,MAAA;qCAKJ,aAAA,CAAA,MAAA;;;;;;AA7LrB;;AAoMP;;;;;AAsDH;AAIA;AAUA;;;;AAIuE;AACvE;;;AAQA;;AAqBA;AAOA;AAMA;AASA;AAUA;AA+BA,qCAAiC,aAAa;AAe9C,mCAA+B,aAAa;AA+D5C,oCAAgC,UAAU;AAM1C,qCAAiC,KAAK;AAUtC;oDA5SW,YAAY;sDAAZ,YAAY;AA6TxB;;","names":[]}
1
+ {"version":3,"file":"lilydesignsystem-angular-motion-picker.d.ts","sources":["../../motion-picker.component.ts"],"mappings":";;;AAoBA;;;;;;;;AAQG;AAEH;AACM,KAAM,SAAS;;;;;;AAMnB;;AAGF;;;;;AAKG;AACH,iBAAgB,UAAU;AAO1B;;;;AAIG;AACH,iBAAgB,oBAAoB;AAYpC;AACA,iBAAgB,kBAAkB;AAKlC;;;;;;;;;;;;AAYG;AACH,cAIa,gBAAgB;AAC3B,wCACQ,gBAAgB,0BAEb,SAAS;mBAAgB,SAAS;AAAE;oDAJpC,gBAAgB;sDAAhB,gBAAgB;AAO5B;AAED;;;;;;;;;;;;AAYG;AACH,cAgFa,YAAY;oBACT,aAAA,CAAA,WAAA;sBACE,aAAA,CAAA,WAAA;oBACF,aAAA,CAAA,WAAA;2BACO,aAAA,CAAA,WAAA;yBACF,aAAA,CAAA,WAAA;mBACN,aAAA,CAAA,WAAA;qBACE,aAAA,CAAA,WAAA,CAAA,WAAA;2BACM,aAAA,CAAA,WAAA,CAAA,MAAA;wBACH,aAAA,CAAA,WAAA;2BACG,aAAA,CAAA,gBAAA;;AAGrB,qCAA+B,aAAA,CAAA,MAAA,CAAA,WAAA;AAE/B;AAKA;AACA;AAEA;AACA;AAEA,6BAAuB,aAAA,CAAA,cAAA;AACvB,oCAA8B,aAAA,CAAA,cAAA;qCAEC,aAAA,CAAA,MAAA;;;;;;AA3LrB;;AAkMP;;;AAgDH;AAIA;AAUA;;;;AAIuE;AACvE;;;AAQA;;AAqBA;AAOA;AAMA;AAmBA;AASA,qCAAiC,aAAa;AAe9C;;;;AAIqD;;AAMrD,oCAAgC,UAAU;AAM1C,qCAAiC,KAAK;AAUtC;oDAtNW,YAAY;sDAAZ,YAAY;AAuOxB;;","names":[]}