@bitforgehq/ui 0.4.0 → 0.5.0-next.3

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.
Files changed (80) hide show
  1. package/CHANGELOG.md +303 -0
  2. package/README.md +12 -12
  3. package/ai/MANIFEST.index.json +65 -2
  4. package/ai/MANIFEST.json +2221 -737
  5. package/ai/REFERENCE.index.json +161 -0
  6. package/ai/REFERENCE.json +1336 -27
  7. package/ai/UI_GUIDE.md +61 -20
  8. package/ai/components/app-frame.md +1 -1
  9. package/ai/components/chat-composer.md +357 -0
  10. package/ai/components/chat-message.md +251 -0
  11. package/ai/components/chat-thread.md +521 -0
  12. package/ai/components/checkbox.md +7 -0
  13. package/ai/components/combobox.md +12 -0
  14. package/ai/components/date-input.md +9 -0
  15. package/ai/components/datetime-input.md +2 -0
  16. package/ai/components/field.md +10 -0
  17. package/ai/components/file-input.md +498 -0
  18. package/ai/components/header.md +1 -1
  19. package/ai/components/image-input.md +368 -0
  20. package/ai/components/image-viewer.md +309 -0
  21. package/ai/components/media-card.md +478 -0
  22. package/ai/components/menu-button.md +2 -0
  23. package/ai/components/menu.md +10 -0
  24. package/ai/components/multi-combobox.md +7 -0
  25. package/ai/components/nav-group.md +10 -3
  26. package/ai/components/nav-item.md +11 -2
  27. package/ai/components/number-input.md +2 -7
  28. package/ai/components/popover.md +13 -0
  29. package/ai/components/radio-group.md +6 -0
  30. package/ai/components/segmented.md +14 -2
  31. package/ai/components/select.md +100 -42
  32. package/ai/components/sidebar-toggle.md +16 -9
  33. package/ai/components/sidebar.md +41 -13
  34. package/ai/components/tab-group.md +18 -2
  35. package/ai/components/table.md +17 -2
  36. package/ai/components/theme-menu-button.md +1 -0
  37. package/ai/components/time-input.md +2 -0
  38. package/ai/components/toolbar.md +7 -18
  39. package/ai/reference/bf-chat-composer-harness.md +64 -0
  40. package/ai/reference/bf-chat-message-role.md +27 -0
  41. package/ai/reference/bf-chat-message-status.md +28 -0
  42. package/ai/reference/bf-fade-in-directive.md +18 -0
  43. package/ai/reference/bf-file-input-harness.md +67 -0
  44. package/ai/reference/bf-file-input-variant.md +27 -0
  45. package/ai/reference/bf-image-input-harness.md +67 -0
  46. package/ai/reference/bf-image-input-shape.md +27 -0
  47. package/ai/reference/bf-image-viewer-actions-context.md +29 -0
  48. package/ai/reference/bf-image-viewer-harness.md +87 -0
  49. package/ai/reference/bf-image-viewer-image.md +25 -0
  50. package/ai/reference/bf-image-viewer-options.md +33 -0
  51. package/ai/reference/bf-image-viewer-origin.md +23 -0
  52. package/ai/reference/bf-image-viewer-ref.md +55 -0
  53. package/ai/reference/bf-image-viewer-service.md +32 -0
  54. package/ai/reference/bf-labels.md +105 -75
  55. package/ai/reference/bf-media-card-aspect.md +28 -0
  56. package/ai/reference/bf-media-card-harness.md +66 -0
  57. package/ai/reference/bf-motion-list-directive.md +22 -0
  58. package/ai/reference/bf-prose-directive.md +16 -0
  59. package/ai/reference/bf-rejected-file-reason.md +28 -0
  60. package/ai/reference/bf-rejected-file.md +28 -0
  61. package/ai/reference/bf-select-harness.md +14 -5
  62. package/ai/reference/bf-select-option.md +2 -0
  63. package/ai/reference/bf-size.md +3 -0
  64. package/ai/reference/bf_table_sort_select.md +23 -0
  65. package/ai/reference/provide-bf-labels.md +10 -3
  66. package/ai/reference/provide-bf-select.md +20 -0
  67. package/bin/bf-ui.js +1 -0
  68. package/fesm2022/bitforgehq-ui-overlay.mjs +2649 -606
  69. package/fesm2022/bitforgehq-ui-overlay.mjs.map +1 -1
  70. package/fesm2022/bitforgehq-ui-testing.mjs +356 -10
  71. package/fesm2022/bitforgehq-ui-testing.mjs.map +1 -1
  72. package/fesm2022/bitforgehq-ui.mjs +7531 -3989
  73. package/fesm2022/bitforgehq-ui.mjs.map +1 -1
  74. package/llms-full.txt +4151 -130
  75. package/llms.txt +31 -1
  76. package/package.json +1 -1
  77. package/theme.css +60 -0
  78. package/types/bitforgehq-ui-overlay.d.ts +684 -7
  79. package/types/bitforgehq-ui-testing.d.ts +434 -6
  80. package/types/bitforgehq-ui.d.ts +1473 -182
@@ -1,7 +1,7 @@
1
1
  import * as i0 from "@angular/core";
2
2
  import { ComponentRef, DestroyRef, EnvironmentProviders, InjectionToken, Injector, Signal, StaticProvider, TemplateRef, Type } from "@angular/core";
3
3
  import { LiveAnnouncer } from "@angular/cdk/a11y";
4
- import { BfCalendarDisabledDate, BfColorScheme, BfCompareWith, BfSize } from "@bitforgehq/ui";
4
+ import { BfCalendarDisabledDate, BfColorScheme, BfCompareWith, BfSelectOption, BfSelectOption as BfSelectOption$1, BfSize } from "@bitforgehq/ui";
5
5
  import * as i1 from "@angular/cdk/menu";
6
6
  import { FormValueControl, ValidationError } from "@angular/forms/signals";
7
7
  /**
@@ -390,7 +390,6 @@ declare function firstTabbable(panel: HTMLElement): HTMLElement | null;
390
390
  * @since 0.2.0
391
391
  */
392
392
  export declare class BfOverlayService {
393
- private readonly overlay;
394
393
  private readonly injector;
395
394
  private readonly document;
396
395
  private readonly stack;
@@ -1162,7 +1161,6 @@ interface BfDesktopToastOptions {
1162
1161
  * @summary Shows brief desktop notifications in a stack at a corner of the viewport
1163
1162
  */
1164
1163
  export declare class BfDesktopToastService {
1165
- private readonly overlay;
1166
1164
  private readonly injector;
1167
1165
  private readonly document;
1168
1166
  private readonly config;
@@ -1244,6 +1242,7 @@ export declare class BfMenuComponent {
1244
1242
  readonly resolvedSize: import("@angular/core").Signal<BfSize>;
1245
1243
  private readonly triggerId;
1246
1244
  protected readonly labelledBy: import("@angular/core").Signal<string | null>;
1245
+ constructor();
1247
1246
  static ɵfac: i0.ɵɵFactoryDeclaration<BfMenuComponent, never>;
1248
1247
  static ɵcmp: i0.ɵɵComponentDeclaration<BfMenuComponent, "bf-menu", never, {
1249
1248
  "size": {
@@ -1440,6 +1439,11 @@ export declare class BfMenuTriggerDirective {
1440
1439
  * item, at the inline end, and ignores it. */
1441
1440
  readonly bfMenuPlacement: import("@angular/core").InputSignal<BfMenuPlacement>;
1442
1441
  constructor();
1442
+ /**
1443
+ * The trigger element, which the opening menu grows out of.
1444
+ * @internal
1445
+ */
1446
+ anchorElement(): HTMLElement;
1443
1447
  /** The trigger element's id (assigned when it has none), which names the menu it opens. It is
1444
1448
  * read when the menu opens: an `[id]` bound on the trigger that changes while the menu is open
1445
1449
  * names the menu from its next opening. */
@@ -1694,6 +1698,158 @@ export declare class BfSidebarToggleComponent {
1694
1698
  };
1695
1699
  }, {}, never, ["[bfSlot=collapse-icon]", "[bfSlot=expand-icon]"], true, never>;
1696
1700
  }
1701
+ type BfChatComposerAction = 'send' | 'stop';
1702
+ /**
1703
+ * The input row of a chat: a `bf-textarea` that grows with the text (`autoResize`, up to
1704
+ * `maxRows`, then it scrolls), a start and an end slot for extra controls, and one button that
1705
+ * sends the text or, while a reply is `running`, stops it. `Enter` sends and `Shift+Enter`
1706
+ * starts a new line; nothing sends during IME composition or
1707
+ * when the text is only whitespace. After a send the text clears and focus stays in the field.
1708
+ * `disabled` with a `disabledReason` shows the reason as the button's tooltip and as the empty
1709
+ * field's placeholder.
1710
+ *
1711
+ * @example
1712
+ * <bf-chat-composer [(value)]="draft" [running]="replying()" (sent)="ask($event)" (stopped)="cancel()">
1713
+ * <bf-icon bfSlot="send-icon" [icon]="ArrowUp" />
1714
+ * <bf-icon bfSlot="stop-icon" [icon]="Square" />
1715
+ * </bf-chat-composer>
1716
+ * @summary Chat input that grows with the text and sends on Enter, or stops a reply
1717
+ * @category Forms
1718
+ * @since 0.5.0
1719
+ * @slot start Controls before the field, such as a preset picker or an attach button.
1720
+ * @slot end Controls between the field and the send button.
1721
+ * @slot send-icon The send button's icon; without one the button shows its text.
1722
+ * @slot stop-icon The stop button's icon; without one the button shows its text.
1723
+ */
1724
+ export declare class BfChatComposerComponent {
1725
+ private readonly defaults;
1726
+ private readonly labels;
1727
+ /** The text being written; two-way bindable. Cleared after a send. */
1728
+ readonly value: import("@angular/core").ModelSignal<string>;
1729
+ /** A reply is running: the button stops it (`stopped`) instead of sending. */
1730
+ readonly running: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
1731
+ /** Nothing can be written or sent. */
1732
+ readonly disabled: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
1733
+ /** Why it is disabled, shown as the button's tooltip and the empty field's placeholder. */
1734
+ readonly disabledReason: import("@angular/core").InputSignal<string>;
1735
+ /** Visible label above the field; usually left empty in a chat. */
1736
+ readonly label: import("@angular/core").InputSignal<string>;
1737
+ /** The field's accessible name without a visible label; empty uses the `chatComposer` label
1738
+ * ("Message"). */
1739
+ readonly ariaLabel: import("@angular/core").InputSignal<string>;
1740
+ /** Id(s) of the element(s) that name the field without a visible label. */
1741
+ readonly ariaLabelledby: import("@angular/core").InputSignal<string>;
1742
+ /** Example text shown while the field is empty. */
1743
+ readonly placeholder: import("@angular/core").InputSignal<string>;
1744
+ /** The lines the field grows to at most; past them it scrolls. */
1745
+ readonly maxRows: import("@angular/core").InputSignal<number>;
1746
+ /** Maximum number of characters (native `maxlength`). */
1747
+ readonly maxLength: import("@angular/core").InputSignal<number | undefined>;
1748
+ /** Native name attribute of the textarea. */
1749
+ readonly name: import("@angular/core").InputSignal<string>;
1750
+ /** Padding, type and button step: sm, md, lg; unset follows `provideBfDefaults()`
1751
+ * (`fieldSize`, `md` by default). */
1752
+ readonly size: import("@angular/core").InputSignal<BfSize | undefined>;
1753
+ /** Emitted with the new text right after the user changes it (each keystroke or paste). */
1754
+ readonly changed: import("@angular/core").OutputEmitterRef<string>;
1755
+ /** Emitted with the trimmed text when the user sends it (Enter or the send button). */
1756
+ readonly sent: import("@angular/core").OutputEmitterRef<string>;
1757
+ /** Emitted when the user presses the stop button while `running`. */
1758
+ readonly stopped: import("@angular/core").OutputEmitterRef<void>;
1759
+ private readonly field;
1760
+ protected readonly resolvedSize: import("@angular/core").Signal<BfSize>;
1761
+ /** The field's one-line height, matched to the button. */
1762
+ protected readonly fieldClasses: import("@angular/core").Signal<string>;
1763
+ protected readonly action: import("@angular/core").Signal<BfChatComposerAction>;
1764
+ /** Disabled with a reason: focusable, `aria-disabled`, with the reason as a tooltip. */
1765
+ protected readonly blocked: import("@angular/core").Signal<boolean>;
1766
+ /** Native `disabled`: disabled without a reason, or nothing to send yet. */
1767
+ protected readonly nativeDisabled: import("@angular/core").Signal<boolean>;
1768
+ protected readonly buttonName: import("@angular/core").Signal<string>;
1769
+ /** The field's `aria-label`: `ariaLabel`, else the label key while nothing else names it. */
1770
+ protected readonly fieldName: import("@angular/core").Signal<string>;
1771
+ protected readonly fieldPlaceholder: import("@angular/core").Signal<string>;
1772
+ protected readonly buttonClasses: import("@angular/core").Signal<string>;
1773
+ /** Sends the text: emits `sent` with it trimmed, clears the field and keeps focus in it. Does
1774
+ * nothing while disabled or running, or when the text is only whitespace. */
1775
+ send(): void;
1776
+ /** Focuses the text field. */
1777
+ focus(): void;
1778
+ /** @internal */
1779
+ onChanged(text: string): void;
1780
+ /** @internal */
1781
+ onButton(): void;
1782
+ /** @internal */
1783
+ onKeydown(event: KeyboardEvent): void;
1784
+ static ɵfac: i0.ɵɵFactoryDeclaration<BfChatComposerComponent, never>;
1785
+ static ɵcmp: i0.ɵɵComponentDeclaration<BfChatComposerComponent, "bf-chat-composer", never, {
1786
+ "value": {
1787
+ "alias": "value";
1788
+ "required": false;
1789
+ "isSignal": true;
1790
+ };
1791
+ "running": {
1792
+ "alias": "running";
1793
+ "required": false;
1794
+ "isSignal": true;
1795
+ };
1796
+ "disabled": {
1797
+ "alias": "disabled";
1798
+ "required": false;
1799
+ "isSignal": true;
1800
+ };
1801
+ "disabledReason": {
1802
+ "alias": "disabledReason";
1803
+ "required": false;
1804
+ "isSignal": true;
1805
+ };
1806
+ "label": {
1807
+ "alias": "label";
1808
+ "required": false;
1809
+ "isSignal": true;
1810
+ };
1811
+ "ariaLabel": {
1812
+ "alias": "ariaLabel";
1813
+ "required": false;
1814
+ "isSignal": true;
1815
+ };
1816
+ "ariaLabelledby": {
1817
+ "alias": "ariaLabelledby";
1818
+ "required": false;
1819
+ "isSignal": true;
1820
+ };
1821
+ "placeholder": {
1822
+ "alias": "placeholder";
1823
+ "required": false;
1824
+ "isSignal": true;
1825
+ };
1826
+ "maxRows": {
1827
+ "alias": "maxRows";
1828
+ "required": false;
1829
+ "isSignal": true;
1830
+ };
1831
+ "maxLength": {
1832
+ "alias": "maxLength";
1833
+ "required": false;
1834
+ "isSignal": true;
1835
+ };
1836
+ "name": {
1837
+ "alias": "name";
1838
+ "required": false;
1839
+ "isSignal": true;
1840
+ };
1841
+ "size": {
1842
+ "alias": "size";
1843
+ "required": false;
1844
+ "isSignal": true;
1845
+ };
1846
+ }, {
1847
+ "value": "valueChange";
1848
+ "changed": "changed";
1849
+ "sent": "sent";
1850
+ "stopped": "stopped";
1851
+ }, never, ["[bfSlot=start]", "[bfSlot=end]", "[bfSlot=send-icon]", "[bfSlot=stop-icon]"], true, never>;
1852
+ }
1697
1853
  /**
1698
1854
  * Layout of a side panel opened with `BfSidePanelService`: a title row with a close button, a
1699
1855
  * scrolling body and a footer of actions kept at the bottom. The panel is labelled by the title,
@@ -1932,7 +2088,7 @@ type BfTooltipPlacement = 'top' | 'bottom' | 'start' | 'end';
1932
2088
  * @since 0.3.1
1933
2089
  */
1934
2090
  export declare class BfTooltipDirective {
1935
- private readonly overlay;
2091
+ private readonly environment;
1936
2092
  private readonly document;
1937
2093
  private readonly injector;
1938
2094
  private readonly dir;
@@ -2037,7 +2193,7 @@ type BfPopoverPlacement = 'below-start' | 'below-end' | 'above-start' | 'above-e
2037
2193
  * @since 0.3.1
2038
2194
  */
2039
2195
  export declare class BfPopoverTriggerDirective {
2040
- private readonly overlay;
2196
+ private readonly environment;
2041
2197
  private readonly document;
2042
2198
  private readonly injector;
2043
2199
  private readonly viewContainer;
@@ -2060,6 +2216,7 @@ export declare class BfPopoverTriggerDirective {
2060
2216
  /** The id the `bf-popover` panel takes, which the trigger's `aria-controls` points at. */
2061
2217
  readonly panelId: string;
2062
2218
  private anchored;
2219
+ private readonly motion;
2063
2220
  private subscriptions;
2064
2221
  private stopNavigation;
2065
2222
  private stopHeading;
@@ -2089,6 +2246,10 @@ export declare class BfPopoverTriggerDirective {
2089
2246
  * the trigger.
2090
2247
  */
2091
2248
  private name;
2249
+ /**
2250
+ * Drops the listeners and closes the overlay: with its exit animation (it stays attached, inert,
2251
+ * until the exit has played), or at once when the directive is destroyed.
2252
+ */
2092
2253
  private teardown;
2093
2254
  static ɵfac: i0.ɵɵFactoryDeclaration<BfPopoverTriggerDirective, never>;
2094
2255
  static ɵdir: i0.ɵɵDirectiveDeclaration<BfPopoverTriggerDirective, "[bfPopoverTriggerFor]", ["bfPopoverTrigger"], {
@@ -2197,7 +2358,7 @@ interface ComboboxHost<T> {
2197
2358
  */
2198
2359
  declare class ComboboxCore<T> {
2199
2360
  readonly host: ComboboxHost<T>;
2200
- private readonly overlay;
2361
+ private readonly environment;
2201
2362
  private readonly injector;
2202
2363
  private readonly viewContainer;
2203
2364
  private readonly dir;
@@ -2220,6 +2381,9 @@ declare class ComboboxCore<T> {
2220
2381
  /** `aria-controls`: the listbox while it is rendered. */
2221
2382
  readonly controls: Signal<string | null>;
2222
2383
  private anchored;
2384
+ /** The listbox component's view, frozen while the panel plays its exit. */
2385
+ private panelView;
2386
+ private readonly motion;
2223
2387
  private subscriptions;
2224
2388
  constructor(host: ComboboxHost<T>);
2225
2389
  /**
@@ -2257,6 +2421,10 @@ declare class ComboboxCore<T> {
2257
2421
  /** Moves the active option by `step` through the enabled ones, wrapping at the ends. */
2258
2422
  private move;
2259
2423
  private firstEnabled;
2424
+ /**
2425
+ * Drops the listeners and closes the overlay: with its exit animation (the list frozen as it
2426
+ * was, so a reset query never reflows it while it fades), or at once on destroy.
2427
+ */
2260
2428
  private teardown;
2261
2429
  }
2262
2430
  /** Equality of an option's value and a picked value under `compareWith`, never for `null`. */
@@ -2777,6 +2945,271 @@ export declare class BfMultiComboboxComponent<T = string> implements FormValueCo
2777
2945
  "touch": "touch";
2778
2946
  }, never, never, true, never>;
2779
2947
  }
2948
+ /**
2949
+ * Single-choice dropdown styled like the text field, with a placeholder and Signal Forms binding.
2950
+ * Under a mouse it opens a styled list (the combobox's) that follows the select-only combobox
2951
+ * pattern: the arrow keys, `Home`, `End`, `PageUp`/`PageDown` and typeahead move through it,
2952
+ * `Enter`, `Space` or `Tab` pick. Under a coarse pointer (touch) it renders the platform's native
2953
+ * select instead. Generic over the option value (a string by default); `null` means nothing is
2954
+ * selected. Moved from `@bitforgehq/ui` to `@bitforgehq/ui/overlay` in 0.5.0.
2955
+ *
2956
+ * @example
2957
+ * <bf-select label="Project" placeholder="Pick a project" [options]="projects" [(value)]="projectId" />
2958
+ * <bf-select label="Team" placeholder="Pick a team" [options]="teams" [formField]="task.teamId" />
2959
+ * @summary Single-choice dropdown with a styled list, native under touch, with Signal Forms binding
2960
+ * @category Forms
2961
+ * @since 0.1.0
2962
+ */
2963
+ export declare class BfSelectComponent<T = string> implements FormValueControl<T | null> {
2964
+ /** The selected option's value, `null` while nothing is selected; two-way bindable and bound by
2965
+ * `[formField]`. */
2966
+ readonly value: import("@angular/core").ModelSignal<T | null>;
2967
+ /** The options to choose from. */
2968
+ readonly options: import("@angular/core").InputSignal<readonly BfSelectOption$1<T>[]>;
2969
+ /** Equality for matching `value` to an option (default `Object.is`); pass one for object values. */
2970
+ readonly compareWith: import("@angular/core").InputSignal<BfCompareWith<T>>;
2971
+ /** Height step: sm 32, md 40, lg 48 px; unset follows `provideBfDefaults()` (`fieldSize`, `md`
2972
+ * by default). */
2973
+ readonly size: import("@angular/core").InputSignal<BfSize | undefined>;
2974
+ /** Visible label above the control. */
2975
+ readonly label: import("@angular/core").InputSignal<string>;
2976
+ /**
2977
+ * Accessible name when no visible label is rendered (e.g. a table filter). Ignored when `label`
2978
+ * is set.
2979
+ */
2980
+ readonly ariaLabel: import("@angular/core").InputSignal<string>;
2981
+ /**
2982
+ * Id(s) of the element(s) that name the field when no visible label is rendered. Ignored when
2983
+ * `label` is set.
2984
+ */
2985
+ readonly ariaLabelledby: import("@angular/core").InputSignal<string>;
2986
+ /** Muted text shown while nothing is selected. */
2987
+ readonly placeholder: import("@angular/core").InputSignal<string>;
2988
+ /** Helper text under the control. */
2989
+ readonly helpText: import("@angular/core").InputSignal<string>;
2990
+ /** Explicit error text; takes precedence over Signal Forms errors. */
2991
+ readonly errorMessage: import("@angular/core").InputSignal<string>;
2992
+ /** Disables the control. */
2993
+ readonly disabled: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
2994
+ /** Shows the value but blocks changes; the select stays focusable and exposes `aria-readonly`. */
2995
+ readonly readonly: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
2996
+ /** Marks the control required. */
2997
+ readonly required: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
2998
+ /** Field validity from Signal Forms. */
2999
+ readonly invalid: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
3000
+ /** Whether the user has left the field. */
3001
+ readonly touched: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
3002
+ /** Validation errors from Signal Forms. */
3003
+ readonly errors: import("@angular/core").InputSignal<readonly ValidationError.WithOptionalFieldTree[]>;
3004
+ /** Form name: of a hidden input carrying the selected option's key, or of the native select. */
3005
+ readonly name: import("@angular/core").InputSignal<string>;
3006
+ /** Control id; generated when not set. */
3007
+ readonly id: import("@angular/core").InputSignal<string>;
3008
+ /** Emitted with the chosen value after a user change (never `null`). */
3009
+ readonly changed: import("@angular/core").OutputEmitterRef<T>;
3010
+ /** Emitted when the control gains focus. */
3011
+ readonly focused: import("@angular/core").OutputEmitterRef<FocusEvent>;
3012
+ /** Emitted when the control loses focus. */
3013
+ readonly blurred: import("@angular/core").OutputEmitterRef<FocusEvent>;
3014
+ /** Emitted on blur so Signal Forms marks the field touched. */
3015
+ readonly touch: import("@angular/core").OutputEmitterRef<void>;
3016
+ private readonly fieldEl;
3017
+ private readonly anchorEl;
3018
+ private readonly nativeSelect;
3019
+ private readonly defaults;
3020
+ private readonly labels;
3021
+ private readonly document;
3022
+ /** Whether the primary pointer is coarse (touch), from `matchMedia`: the native mode. */
3023
+ protected readonly coarse: import("@angular/core").WritableSignal<boolean>;
3024
+ /** The size in effect: `size`, else the app's `fieldSize` default. */
3025
+ protected readonly resolvedSize: import("@angular/core").Signal<BfSize>;
3026
+ /** Index of the option matching `value`, `-1` while nothing (or `null`) is selected. */
3027
+ protected readonly selectedIndex: import("@angular/core").Signal<number>;
3028
+ /** The selected option's label, null while nothing is selected. */
3029
+ protected readonly selectedLabel: import("@angular/core").Signal<string | null>;
3030
+ /** The hidden input's value: the selected option's key (its index for an object), else `''`. */
3031
+ protected readonly formKey: import("@angular/core").Signal<string>;
3032
+ /** The options as the list shows them; the selected one is matched by identity. */
3033
+ private readonly listOptions;
3034
+ protected readonly core: ComboboxCore<T>;
3035
+ protected readonly errorText: import("@angular/core").Signal<string>;
3036
+ protected readonly showError: import("@angular/core").Signal<boolean>;
3037
+ protected readonly describedBy: import("@angular/core").Signal<string | null>;
3038
+ /** `aria-errormessage`: the error paragraph while `aria-invalid` is set. */
3039
+ protected readonly errorMessageId: import("@angular/core").Signal<string | null>;
3040
+ /**
3041
+ * `aria-label` value: only when there is no visible label, so the label stays the sole accessible
3042
+ * name.
3043
+ */
3044
+ protected readonly ariaLabelAttr: import("@angular/core").Signal<string | null>;
3045
+ /**
3046
+ * `aria-labelledby` value while there is no visible label; with one, the field points
3047
+ * `aria-labelledby` at the label's id instead (a `<label for>` cannot name a div).
3048
+ */
3049
+ protected readonly ariaLabelledbyAttr: import("@angular/core").Signal<string | null>;
3050
+ protected readonly wrapperClasses: import("@angular/core").Signal<string>;
3051
+ protected readonly classes: import("@angular/core").Signal<string>;
3052
+ /** The typed typeahead text and when its last key came. */
3053
+ private typeahead;
3054
+ private typedAt;
3055
+ constructor();
3056
+ /**
3057
+ * Focuses the field, or the native select under a coarse pointer; Signal Forms calls it from
3058
+ * `focusBoundControl()`.
3059
+ * @param options Passed on to the native `focus()`, such as `{ preventScroll: true }`.
3060
+ */
3061
+ focus(options?: FocusOptions): void;
3062
+ /**
3063
+ * A click on the label focuses the field (a label's `for` cannot name a div).
3064
+ * @internal
3065
+ */
3066
+ onHostClick(event: MouseEvent): void;
3067
+ /**
3068
+ * A click on the field opens or closes the list.
3069
+ * @internal
3070
+ */
3071
+ onClick(): void;
3072
+ /**
3073
+ * The select-only combobox keys: `Enter`, `Space` and the arrows open; while open the arrows,
3074
+ * `Home`, `End`, `PageUp` and `PageDown` move, `Enter`, `Space` and `Tab` pick; printable keys
3075
+ * search by label. `Escape` on an open list is the panel's: it closes it.
3076
+ * @internal
3077
+ */
3078
+ onKeydown(event: KeyboardEvent): void;
3079
+ /**
3080
+ * Leaving the field closes the list, emits `blurred` and marks the field touched. A press in the
3081
+ * list never moves the focus (the listbox cancels it), so this runs only when the field is left.
3082
+ * @internal
3083
+ */
3084
+ onBlur(event: FocusEvent): void;
3085
+ /** Opens the list with the selected option active, else the first enabled one. */
3086
+ private openList;
3087
+ /** Picks an option: the value and `changed` when it differs, and the list closes. */
3088
+ private pick;
3089
+ private pickActive;
3090
+ /** Moves the active option by `step` enabled options, stopping at the ends. */
3091
+ private page;
3092
+ /** Whether a typeahead search is still being typed. */
3093
+ private typing;
3094
+ /**
3095
+ * Adds a key to the typeahead search, opens the list and activates the next enabled option whose
3096
+ * label starts with the search, ignoring case and accents. One letter repeated cycles through the
3097
+ * options starting with it; a longer search keeps the active option while it still matches.
3098
+ */
3099
+ private search;
3100
+ /** The list's enabled options in list order. */
3101
+ private enabledItems;
3102
+ static ɵfac: i0.ɵɵFactoryDeclaration<BfSelectComponent<any>, never>;
3103
+ static ɵcmp: i0.ɵɵComponentDeclaration<BfSelectComponent<any>, "bf-select", never, {
3104
+ "value": {
3105
+ "alias": "value";
3106
+ "required": false;
3107
+ "isSignal": true;
3108
+ };
3109
+ "options": {
3110
+ "alias": "options";
3111
+ "required": true;
3112
+ "isSignal": true;
3113
+ };
3114
+ "compareWith": {
3115
+ "alias": "compareWith";
3116
+ "required": false;
3117
+ "isSignal": true;
3118
+ };
3119
+ "size": {
3120
+ "alias": "size";
3121
+ "required": false;
3122
+ "isSignal": true;
3123
+ };
3124
+ "label": {
3125
+ "alias": "label";
3126
+ "required": false;
3127
+ "isSignal": true;
3128
+ };
3129
+ "ariaLabel": {
3130
+ "alias": "ariaLabel";
3131
+ "required": false;
3132
+ "isSignal": true;
3133
+ };
3134
+ "ariaLabelledby": {
3135
+ "alias": "ariaLabelledby";
3136
+ "required": false;
3137
+ "isSignal": true;
3138
+ };
3139
+ "placeholder": {
3140
+ "alias": "placeholder";
3141
+ "required": false;
3142
+ "isSignal": true;
3143
+ };
3144
+ "helpText": {
3145
+ "alias": "helpText";
3146
+ "required": false;
3147
+ "isSignal": true;
3148
+ };
3149
+ "errorMessage": {
3150
+ "alias": "errorMessage";
3151
+ "required": false;
3152
+ "isSignal": true;
3153
+ };
3154
+ "disabled": {
3155
+ "alias": "disabled";
3156
+ "required": false;
3157
+ "isSignal": true;
3158
+ };
3159
+ "readonly": {
3160
+ "alias": "readonly";
3161
+ "required": false;
3162
+ "isSignal": true;
3163
+ };
3164
+ "required": {
3165
+ "alias": "required";
3166
+ "required": false;
3167
+ "isSignal": true;
3168
+ };
3169
+ "invalid": {
3170
+ "alias": "invalid";
3171
+ "required": false;
3172
+ "isSignal": true;
3173
+ };
3174
+ "touched": {
3175
+ "alias": "touched";
3176
+ "required": false;
3177
+ "isSignal": true;
3178
+ };
3179
+ "errors": {
3180
+ "alias": "errors";
3181
+ "required": false;
3182
+ "isSignal": true;
3183
+ };
3184
+ "name": {
3185
+ "alias": "name";
3186
+ "required": false;
3187
+ "isSignal": true;
3188
+ };
3189
+ "id": {
3190
+ "alias": "id";
3191
+ "required": false;
3192
+ "isSignal": true;
3193
+ };
3194
+ }, {
3195
+ "value": "valueChange";
3196
+ "changed": "changed";
3197
+ "focused": "focused";
3198
+ "blurred": "blurred";
3199
+ "touch": "touch";
3200
+ }, never, never, true, never>;
3201
+ }
3202
+ /**
3203
+ * Makes the library's built-in selects use the styled `bf-select`: a stacked `bf-table` then
3204
+ * renders its Sort by select as a `bf-select` (the native `<select>` under a coarse pointer, as
3205
+ * `bf-select` itself does) instead of the plain native one. Add it to
3206
+ * `ApplicationConfig.providers`.
3207
+ *
3208
+ * @returns The providers that fill `BF_TABLE_SORT_SELECT` with `BfSelectComponent`.
3209
+ * @summary Makes a stacked table's Sort by select the styled bf-select, app-wide
3210
+ * @since 0.5.0
3211
+ */
3212
+ export declare function provideBfSelect(): EnvironmentProviders;
2780
3213
  /**
2781
3214
  * How `bf-date-input` picks a date besides typing: `calendar` adds a button that opens a
2782
3215
  * `bf-calendar` in a popover, `none` keeps the field typed only.
@@ -3399,4 +3832,248 @@ export declare class BfDateTimeInputComponent implements FormValueControl<string
3399
3832
  "touch": "touch";
3400
3833
  }, never, never, true, never>;
3401
3834
  }
3402
- export { type BfComboboxFilter, type BfComboboxOption, type BfConfirmButton, type BfConfirmButtonRole, type BfConfirmOptions, type BfConfirmSize, type BfDateInputPicker, type BfDateTimeInputPicker, type BfDesktopToastConfig, type BfDesktopToastOptions, type BfDesktopToastPosition, type BfDialogConfig, type BfDialogRole, type BfDialogSize, type BfDialogTemplateContext, type BfMenuButtonVariant, type BfMenuPlacement, type BfOverlayAutoFocus, type BfOverlayController, type BfOverlayHost, type BfOverlayOptions, type BfOverlayPlacement, type BfOverlayPosition, type BfOverlayRole, type BfOverlayStackOptions, type BfOverlayTemplateContext, type BfPopoverPlacement, type BfSidePanelConfig, type BfSidePanelTemplateContext, type BfSidePanelWidth, type BfStackedOverlay, type BfTimeInputHourCycle, type BfTimeInputPicker, type BfToastAction, type BfToastController, type BfToastDismissReason, type BfTooltipPlacement, type ɵBfToastHandle, type ɵBfToastPresenter, type ɵBfToastQueueOptions, type ɵBfToastRequest, type ɵBfToastView, labelPanelByHeading as ɵlabelPanelByHeading, watchPanelHeading as ɵwatchPanelHeading };
3835
+ /**
3836
+ * One image of a `BfImageViewerService.open()` set.
3837
+ *
3838
+ * @summary One image the viewer shows: its URL, its name as alternative text and a caption
3839
+ * @since 0.5.0
3840
+ */
3841
+ interface BfImageViewerImage {
3842
+ /** URL of the full-size image. */
3843
+ src: string;
3844
+ /**
3845
+ * The image's name: its alternative text (`alt`), read when it shows and announced after a step
3846
+ * (required: the viewer shows nothing else that names it).
3847
+ */
3848
+ label: string;
3849
+ /** Text shown under the image; none shows no caption. */
3850
+ caption?: string;
3851
+ }
3852
+ /**
3853
+ * Handle to an open image viewer, returned by `BfImageViewerService.open()` and injectable in
3854
+ * its actions template (`inject(BfImageViewerRef)`). `index` is the shown image; `next()`,
3855
+ * `previous()` and `goTo()` step through the set (wrapping at both ends), `close()` plays the
3856
+ * exit motion and resolves `afterClosed`.
3857
+ *
3858
+ * @example
3859
+ * const ref = inject(BfImageViewerService).open({ images, index: 2, origin: thumb });
3860
+ * ref.next();
3861
+ * await ref.afterClosed;
3862
+ * @summary Handle on an open image viewer that steps through its images and closes it
3863
+ * @since 0.5.0
3864
+ */
3865
+ export declare class BfImageViewerRef {
3866
+ private readonly overlayRef;
3867
+ /** Unique id of the viewer (its overlay's `bf-overlay-N`). */
3868
+ readonly id: string;
3869
+ /** The images of the set, in order. */
3870
+ readonly images: readonly BfImageViewerImage[];
3871
+ /** Resolves once the viewer has closed and left the page. */
3872
+ readonly afterClosed: Promise<void>;
3873
+ /** True once the viewer has been detached. */
3874
+ readonly closed: Signal<boolean>;
3875
+ private readonly current;
3876
+ /** Index (0-based) of the image the viewer shows. */
3877
+ readonly index: Signal<number>;
3878
+ /** @internal Created by `BfImageViewerService`; apps never construct it. */
3879
+ constructor(overlayRef: BfOverlayRef<unknown>, images: readonly BfImageViewerImage[], start: number);
3880
+ /** Shows the next image; after the last one, the first. */
3881
+ next(): void;
3882
+ /** Shows the previous image; before the first one, the last. */
3883
+ previous(): void;
3884
+ /**
3885
+ * Shows the image at `index`, wrapped into the set (`-1` is the last one).
3886
+ * @param index The 0-based index of the image to show.
3887
+ */
3888
+ goTo(index: number): void;
3889
+ /** Closes the viewer: it zooms back into its thumbnail, or fades out. Ignored once closing. */
3890
+ close(): void;
3891
+ }
3892
+ /**
3893
+ * Where the viewer zooms from and back to: the thumbnail that was clicked, or a function that
3894
+ * returns the thumbnail of any image of the set (so closing after stepping zooms into the right
3895
+ * one), `null` for none.
3896
+ *
3897
+ * @summary The thumbnail an image viewer zooms from and back into, or a lookup per image
3898
+ * @since 0.5.0
3899
+ */
3900
+ type BfImageViewerOrigin = HTMLElement | ((index: number) => HTMLElement | null | undefined) | null;
3901
+ /**
3902
+ * Context of the `actions` template of `BfImageViewerService.open()`: `let-image`,
3903
+ * `let-index="index"` and `let-ref="ref"`.
3904
+ *
3905
+ * @summary What the viewer's actions template receives: the shown image, its index and the ref
3906
+ * @since 0.5.0
3907
+ */
3908
+ interface BfImageViewerActionsContext {
3909
+ /** The image the viewer shows (`let-image`). */
3910
+ $implicit: BfImageViewerImage;
3911
+ /** Its 0-based index in the set (`let-index="index"`). */
3912
+ index: number;
3913
+ /** The viewer's ref (`let-ref="ref"`), to close it or step. */
3914
+ ref: BfImageViewerRef;
3915
+ }
3916
+ /** @internal What `BfImageViewerService` hands the `bf-image-viewer` it opens. */
3917
+ interface BfImageViewerContext {
3918
+ readonly start: number;
3919
+ /** The viewer's accessible name. */
3920
+ readonly label: string;
3921
+ readonly origin: BfImageViewerOrigin;
3922
+ readonly actions: TemplateRef<BfImageViewerActionsContext> | null;
3923
+ /** Called by the viewer as it is created, so the overlay's enter and exit can run its motion. */
3924
+ readonly register: (motion: BfImageViewerMotion) => void;
3925
+ }
3926
+ /** @internal The open and close motion of a `bf-image-viewer`. */
3927
+ interface BfImageViewerMotion {
3928
+ enter(): Animation;
3929
+ exit(): Animation;
3930
+ }
3931
+ /** @internal Provided by `BfImageViewerService` to the viewer it opens. */
3932
+ declare const BF_IMAGE_VIEWER_CONTEXT: InjectionToken<BfImageViewerContext>;
3933
+ /** How far one image got. */
3934
+ type ImageStatus = 'pending' | 'loaded' | 'error';
3935
+ /**
3936
+ * The full-screen image viewer (lightbox) `BfImageViewerService.open()` renders: one image large
3937
+ * over a dark scrim, with a counter, previous and next buttons, a close button, an optional
3938
+ * caption and actions. It opens with a zoom out of the clicked thumbnail and steps with a
3939
+ * cross-fade. Apps do not place it in templates; open it with the service.
3940
+ *
3941
+ * @example
3942
+ * const ref = inject(BfImageViewerService).open({
3943
+ * images: [{ src: '/art/1.jpg', label: 'Neon skyline', caption: 'Concept 1' }],
3944
+ * index: 0,
3945
+ * origin: thumbnailElement,
3946
+ * });
3947
+ * @summary Full-screen image viewer that zooms from a thumbnail and steps through a set
3948
+ * @category Overlays
3949
+ * @since 0.5.0
3950
+ */
3951
+ export declare class BfImageViewerComponent implements BfImageViewerMotion {
3952
+ protected readonly labels: import("@bitforgehq/ui").BfLabels;
3953
+ protected readonly ref: BfImageViewerRef;
3954
+ protected readonly context: BfImageViewerContext;
3955
+ protected readonly injector: Injector;
3956
+ private readonly document;
3957
+ private readonly host;
3958
+ protected readonly closeIcon: import("lucide").IconNode;
3959
+ protected readonly previousIcon: import("lucide").IconNode;
3960
+ protected readonly nextIcon: import("lucide").IconNode;
3961
+ protected readonly previousClasses = "pointer-events-auto absolute top-1/2 -translate-y-1/2 start-3";
3962
+ protected readonly nextClasses = "pointer-events-auto absolute top-1/2 -translate-y-1/2 end-3";
3963
+ private readonly frame;
3964
+ private readonly scrim;
3965
+ private readonly top;
3966
+ private readonly bottom;
3967
+ /** The progress of every image shown so far, by index. */
3968
+ private readonly statuses;
3969
+ /** The aspect ratio (width / height) of every image that has loaded, by index. */
3970
+ private readonly ratios;
3971
+ protected readonly count: import("@angular/core").Signal<number>;
3972
+ protected readonly index: import("@angular/core").Signal<number>;
3973
+ protected readonly image: import("@angular/core").Signal<BfImageViewerImage>;
3974
+ protected readonly status: import("@angular/core").Signal<ImageStatus>;
3975
+ /** The shown image, keyed by index so a step replaces the element (and cross-fades); none
3976
+ * once it failed, so the failure message is not drawn over a broken-image box. */
3977
+ protected readonly shown: import("@angular/core").Signal<{
3978
+ index: number;
3979
+ image: BfImageViewerImage;
3980
+ }[]>;
3981
+ protected readonly counter: import("@angular/core").Signal<string>;
3982
+ /** Announced politely after each step (never on open, where the dialog itself is read). */
3983
+ private readonly stepped;
3984
+ protected readonly announcement: import("@angular/core").Signal<string>;
3985
+ /** The thumbnail of the shown image, under it until the large one arrives. */
3986
+ protected readonly placeholder: import("@angular/core").Signal<string | null>;
3987
+ /** Width / height of the shown image: once loaded its own, before that its thumbnail's. */
3988
+ protected readonly ratio: import("@angular/core").Signal<number | null>;
3989
+ protected readonly frameClasses: import("@angular/core").Signal<"relative max-h-full max-w-full aspect-(--bf-image-viewer-ratio) w-[min(100cqw,calc(100cqh*var(--bf-image-viewer-ratio)))]" | "relative max-h-full max-w-full size-full">;
3990
+ protected readonly actionsContext: import("@angular/core").Signal<BfImageViewerActionsContext>;
3991
+ constructor();
3992
+ /** @internal Plays the open motion; called by `BfImageViewerService` as the overlay's enter. */
3993
+ enter(): Animation;
3994
+ /** @internal Plays the close motion; called by `BfImageViewerService` as the overlay's exit. */
3995
+ exit(): Animation;
3996
+ /** @internal An image loaded: it shows, and the frame takes its aspect ratio. */
3997
+ imageLoaded(event: Event, index: number): void;
3998
+ /** @internal An image failed: the frame shows the failure message. */
3999
+ imageFailed(index: number): void;
4000
+ /** @internal Arrow keys step (mirrored under rtl), Home and End jump to the ends. */
4001
+ keydown(event: KeyboardEvent): void;
4002
+ /** @internal A click on the scrim or around the image closes the viewer. */
4003
+ clicked(event: MouseEvent): void;
4004
+ /** The thumbnail of image `index`, when the opener gave one. */
4005
+ private originFor;
4006
+ private parts;
4007
+ private reduced;
4008
+ static ɵfac: i0.ɵɵFactoryDeclaration<BfImageViewerComponent, never>;
4009
+ static ɵcmp: i0.ɵɵComponentDeclaration<BfImageViewerComponent, "bf-image-viewer", never, {}, {}, never, never, true, never>;
4010
+ }
4011
+ /**
4012
+ * Options of `BfImageViewerService.open()`.
4013
+ *
4014
+ * @summary Options of an image viewer: its images, the first one, its thumbnail and actions
4015
+ * @since 0.5.0
4016
+ */
4017
+ interface BfImageViewerOptions {
4018
+ /** The images to step through, in order; at least one. */
4019
+ images: readonly BfImageViewerImage[];
4020
+ /**
4021
+ * Index of the image shown first, wrapped into the set.
4022
+ * @default 0
4023
+ */
4024
+ index?: number;
4025
+ /**
4026
+ * The thumbnail the viewer zooms out of and back into (or a function returning the thumbnail
4027
+ * of an image by index); none fades the viewer in and out.
4028
+ */
4029
+ origin?: BfImageViewerOrigin;
4030
+ /**
4031
+ * Actions under the image, beside its caption (a download link, a "Use this one" button); its
4032
+ * context is `{ $implicit: image, index, ref }`.
4033
+ */
4034
+ actions?: TemplateRef<BfImageViewerActionsContext>;
4035
+ /** Accessible name of the viewer; empty uses `provideBfLabels()`'s `imageViewer`. */
4036
+ ariaLabel?: string;
4037
+ /**
4038
+ * A navigation to another URL path closes the viewer (default true).
4039
+ * @default true
4040
+ */
4041
+ closeOnNavigation?: boolean;
4042
+ /**
4043
+ * Parent of the viewer's injector and source of its labels (default the root injector). Pass
4044
+ * the opener's `inject(Injector)` so a feature-level `provideBfLabels()` reaches the viewer.
4045
+ */
4046
+ injector?: Injector;
4047
+ }
4048
+ /**
4049
+ * Opens a full-screen image viewer (a lightbox) on the overlay core: one image large over a dark
4050
+ * scrim, previous and next buttons, the arrow keys, Home and End to step through the set
4051
+ * (wrapping at both ends), a counter, the caption and optional actions. It zooms out of the
4052
+ * clicked thumbnail (`origin`) and back into it, cross-fades between images, and fades when there
4053
+ * is no thumbnail on screen; reduced motion only fades. It is a modal dialog: focus moves to its
4054
+ * close button and is trapped, `Escape`, the close button and a click on the scrim close it, the
4055
+ * page behind it is `inert` and does not scroll, and focus returns to the opener.
4056
+ *
4057
+ * @example
4058
+ * const ref = inject(BfImageViewerService).open({
4059
+ * images: shots.map((s) => ({ src: s.full, label: s.alt, caption: s.title })),
4060
+ * index: 2,
4061
+ * origin: (i) => thumbnails[i],
4062
+ * });
4063
+ * await ref.afterClosed;
4064
+ * @summary Opens a full-screen image viewer that zooms from a thumbnail and steps through a set
4065
+ * @since 0.5.0
4066
+ */
4067
+ export declare class BfImageViewerService {
4068
+ private readonly overlays;
4069
+ private readonly injector;
4070
+ /**
4071
+ * Opens the viewer on `options.images`, showing `options.index` first.
4072
+ * @param options The images, the first index, the thumbnail, the actions and the injector.
4073
+ * @returns The viewer's ref.
4074
+ */
4075
+ open(options: BfImageViewerOptions): BfImageViewerRef;
4076
+ static ɵfac: i0.ɵɵFactoryDeclaration<BfImageViewerService, never>;
4077
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
4078
+ }
4079
+ export { type BfComboboxFilter, type BfComboboxOption, type BfConfirmButton, type BfConfirmButtonRole, type BfConfirmOptions, type BfConfirmSize, type BfDateInputPicker, type BfDateTimeInputPicker, type BfDesktopToastConfig, type BfDesktopToastOptions, type BfDesktopToastPosition, type BfDialogConfig, type BfDialogRole, type BfDialogSize, type BfDialogTemplateContext, type BfImageViewerActionsContext, type BfImageViewerImage, type BfImageViewerOptions, type BfImageViewerOrigin, type BfMenuButtonVariant, type BfMenuPlacement, type BfOverlayAutoFocus, type BfOverlayController, type BfOverlayHost, type BfOverlayOptions, type BfOverlayPlacement, type BfOverlayPosition, type BfOverlayRole, type BfOverlayStackOptions, type BfOverlayTemplateContext, type BfPopoverPlacement, type BfSelectOption, type BfSidePanelConfig, type BfSidePanelTemplateContext, type BfSidePanelWidth, type BfStackedOverlay, type BfTimeInputHourCycle, type BfTimeInputPicker, type BfToastAction, type BfToastController, type BfToastDismissReason, type BfTooltipPlacement, type ɵBfToastHandle, type ɵBfToastPresenter, type ɵBfToastQueueOptions, type ɵBfToastRequest, type ɵBfToastView, labelPanelByHeading as ɵlabelPanelByHeading, watchPanelHeading as ɵwatchPanelHeading };