@forgeroute/ui 0.4.28 → 0.4.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgeroute/ui",
3
- "version": "0.4.28",
3
+ "version": "0.4.30",
4
4
  "peerDependencies": {
5
5
  "@angular/common": ">=22.0.0",
6
6
  "@angular/core": ">=22.0.0",
@@ -2491,7 +2491,7 @@ export declare class AudioPlayerComponent {
2491
2491
  /** Accessible name of the player. */
2492
2492
  readonly ariaLabel: import("@angular/core").InputSignal<string>;
2493
2493
  /** How much to load before play: 'metadata' (default), 'auto' or 'none'. */
2494
- readonly preload: import("@angular/core").InputSignal<"metadata" | "none" | "auto">;
2494
+ readonly preload: import("@angular/core").InputSignal<"none" | "auto" | "metadata">;
2495
2495
  /** Fires when the browser can't load or decode the source. */
2496
2496
  readonly playbackError: import("@angular/core").OutputEmitterRef<void>;
2497
2497
  protected readonly failed: import("@angular/core").WritableSignal<boolean>;
@@ -3766,7 +3766,7 @@ export declare class ButtonComponent {
3766
3766
  * `ghost-start`: a ghost button for list / section toggles - a full-width row with its content aligned to the start, transparent
3767
3767
  * background, hover tint and the normal focus ring. Pair it with `ariaExpanded` / `ariaPressed`; the label may contain a heading element.
3768
3768
  */
3769
- readonly variant: import("@angular/core").InputSignal<"link" | "primary" | "secondary" | "danger" | "warning" | "ghost" | "ghost-start" | "tab" | "icon" | "icon-danger">;
3769
+ readonly variant: import("@angular/core").InputSignal<"primary" | "secondary" | "danger" | "warning" | "link" | "ghost" | "ghost-start" | "tab" | "icon" | "icon-danger">;
3770
3770
  /** Renders an anchor to this route instead of a button (use with `variant="link"` for an inline text link). `['/settings']` or `'/settings'`. While `disabled` or `loading` it renders a non-clickable `<span role="link" aria-disabled>` with no href (like `href`). */
3771
3771
  readonly routerLink: import("@angular/core").InputSignal<string | readonly unknown[] | null>;
3772
3772
  /** Query params of the `routerLink` (e.g. `{ section: 'deal-defaults' }`). */
@@ -3787,7 +3787,7 @@ export declare class ButtonComponent {
3787
3787
  */
3788
3788
  readonly href: import("@angular/core").InputSignal<string | null>;
3789
3789
  /** Where to open the `href` (`_blank` = a new tab). */
3790
- readonly target: import("@angular/core").InputSignal<"_blank" | "_self" | "_parent" | "_top" | null>;
3790
+ readonly target: import("@angular/core").InputSignal<"_self" | "_blank" | "_parent" | "_top" | null>;
3791
3791
  /** `rel` of the `href` link. A `_blank` target always gets `noopener noreferrer` added. */
3792
3792
  readonly rel: import("@angular/core").InputSignal<string | null>;
3793
3793
  /** The href when it is allowed (http, https, mailto, tel, sms or a relative URL), else null. */
@@ -3812,7 +3812,7 @@ export declare class ButtonComponent {
3812
3812
  readonly color: import("@angular/core").InputSignal<string | undefined>;
3813
3813
  readonly active: import("@angular/core").InputSignal<boolean>;
3814
3814
  readonly size: import("@angular/core").InputSignal<"sm" | "md" | "lg" | "xs">;
3815
- readonly type: import("@angular/core").InputSignal<"button" | "reset" | "submit">;
3815
+ readonly type: import("@angular/core").InputSignal<"reset" | "submit" | "button">;
3816
3816
  readonly disabled: import("@angular/core").InputSignal<boolean>;
3817
3817
  readonly loading: import("@angular/core").InputSignal<boolean>;
3818
3818
  readonly fullWidth: import("@angular/core").InputSignal<boolean>;
@@ -4427,7 +4427,7 @@ export declare class TextInputComponent implements ControlValueAccessor, Validat
4427
4427
  readonly passwordRequirements: import("@angular/core").InputSignal<boolean>;
4428
4428
  /** Rules for `passwordRequirements`; default = the library's five rules (8+ characters, upper, lower, digit, special). */
4429
4429
  readonly passwordRules: import("@angular/core").InputSignal<readonly PasswordRule[] | undefined>;
4430
- readonly type: import("@angular/core").InputSignal<"number" | "text" | "password" | "email" | "tel" | "url">;
4430
+ readonly type: import("@angular/core").InputSignal<"number" | "text" | "email" | "tel" | "password" | "url">;
4431
4431
  readonly maxlength: import("@angular/core").InputSignal<number | undefined>;
4432
4432
  readonly required: import("@angular/core").InputSignal<boolean>;
4433
4433
  readonly autocomplete: import("@angular/core").InputSignal<string>;
@@ -4436,7 +4436,7 @@ export declare class TextInputComponent implements ControlValueAccessor, Validat
4436
4436
  * `search`, `text`, `none`). Empty (default) = the browser's default for the field, except `type="tel"`, which keeps `tel`.
4437
4437
  * It only picks the keyboard; it does not restrict what can be typed or pasted.
4438
4438
  */
4439
- readonly inputmode: import("@angular/core").InputSignal<"" | "text" | "email" | "search" | "none" | "tel" | "url" | "decimal" | "numeric">;
4439
+ readonly inputmode: import("@angular/core").InputSignal<"" | "none" | "text" | "search" | "email" | "tel" | "url" | "decimal" | "numeric">;
4440
4440
  readonly readonly: import("@angular/core").InputSignal<boolean>;
4441
4441
  readonly hint: import("@angular/core").InputSignal<string>;
4442
4442
  readonly error: import("@angular/core").InputSignal<string>;
@@ -4500,7 +4500,7 @@ export declare class TextInputComponent implements ControlValueAccessor, Validat
4500
4500
  protected readonly _stepAttr: import("@angular/core").Signal<string | null>;
4501
4501
  protected readonly _revealable: import("@angular/core").Signal<boolean>;
4502
4502
  protected readonly _shown: import("@angular/core").WritableSignal<boolean>;
4503
- protected readonly _inputType: import("@angular/core").Signal<"number" | "text" | "password" | "email" | "tel" | "url">;
4503
+ protected readonly _inputType: import("@angular/core").Signal<"number" | "text" | "email" | "tel" | "password" | "url">;
4504
4504
  protected readonly _eyePath: string;
4505
4505
  protected readonly _eyeOffPath: string;
4506
4506
  protected readonly _showRequirements: import("@angular/core").Signal<boolean>;
@@ -5848,7 +5848,7 @@ export declare class DropdownComponent {
5848
5848
  readonly loader: import("@angular/core").InputSignal<(() => Promise<DropdownItem[]>) | null>;
5849
5849
  readonly active: import("@angular/core").InputSignal<boolean>;
5850
5850
  readonly activeValue: import("@angular/core").InputSignal<string>;
5851
- readonly variant: import("@angular/core").InputSignal<"button" | "badge">;
5851
+ readonly variant: import("@angular/core").InputSignal<"badge" | "button">;
5852
5852
  readonly menuWidth: import("@angular/core").InputSignal<string>;
5853
5853
  /**
5854
5854
  * Accessible name. Set it when the trigger has no meaningful visible text. Applied as `aria-label` on the
@@ -6046,7 +6046,7 @@ export declare class FormRowComponent {
6046
6046
  * child simply takes the full width. Keep a numeric span within the row's `cols` (use `all` for the whole row).
6047
6047
  */
6048
6048
  export declare class FormColSpanDirective {
6049
- readonly uiColSpan: import("@angular/core").InputSignal<"" | "all" | 2 | "2" | 3 | "3">;
6049
+ readonly uiColSpan: import("@angular/core").InputSignal<"" | "2" | "3" | 2 | 3 | "all">;
6050
6050
  protected readonly _class: import("@angular/core").Signal<string>;
6051
6051
  static ɵfac: i0.ɵɵFactoryDeclaration<FormColSpanDirective, never>;
6052
6052
  static ɵdir: i0.ɵɵDirectiveDeclaration<FormColSpanDirective, "[uiColSpan]", never, {
@@ -7076,7 +7076,7 @@ export declare class ChipGroupComponent {
7076
7076
  readonly spaceAbove: import("@angular/core").InputSignal<"none" | "sm" | "md">;
7077
7077
  /** Space below the chip group. */
7078
7078
  readonly spaceBelow: import("@angular/core").InputSignal<"none" | "sm" | "md">;
7079
- protected readonly _gap: import("@angular/core").Signal<"8px" | "6px" | "12px">;
7079
+ protected readonly _gap: import("@angular/core").Signal<"6px" | "12px" | "8px">;
7080
7080
  protected readonly _spaceAbove: import("@angular/core").Signal<"0" | "6px" | "12px">;
7081
7081
  protected readonly _spaceBelow: import("@angular/core").Signal<"0" | "6px" | "12px">;
7082
7082
  static ɵfac: i0.ɵɵFactoryDeclaration<ChipGroupComponent, never>;
@@ -11242,6 +11242,8 @@ export declare class SetupWizardComponent {
11242
11242
  protected isConnected(i: SetupIntegration): boolean;
11243
11243
  protected detailFor(i: SetupIntegration): string;
11244
11244
  protected connect(i: SetupIntegration): void;
11245
+ /** SVG path of a library icon (the buttons' leading / trailing glyph). */
11246
+ protected iconD(name: string): string;
11245
11247
  protected areaCodeLabelFor(i: SetupIntegration): string;
11246
11248
  /** Sends the browser to the provider's authorization page. */
11247
11249
  protected redirect(url: string): void;
@@ -11361,14 +11363,12 @@ type EditorTab = 'edit' | 'preview' | 'mobile';
11361
11363
  type RightTab = 'blocks' | 'variables' | 'video';
11362
11364
  /**
11363
11365
  * `ui-email-template-editor` - the full-page, block-based email template editor (details, assignments, block canvas with drag and drop,
11364
- * rich-text toolbar, preview / mobile preview, content blocks, merge variables, video library, test send and AI draft).
11366
+ * rich-text toolbar, preview / mobile preview, content blocks, merge variables, video library, test send).
11365
11367
  *
11366
11368
  * The component owns the editing behaviour and holds no HTTP: the host loads the template (`[template]`), supplies what differs per app
11367
11369
  * (`categories`, `assignmentOptions`, `variableGroups`, `blockGroups`, `videos`, `videoDefaults`, `maxBodyLength`, permissions) and reacts to
11368
- * `(save)`, `(testSend)`, `(aiGenerate)`, `(videosRequested)`, `(back)` and `(useAsMine)`. After a request the host calls back:
11369
- * `markSaved(id)` (a create succeeded), `closeTestSend()` and `addGeneratedHtml(html)`.
11370
- *
11371
- * Project an element with the `uiSubjectAction` attribute to put a host control (e.g. an AI copy button) at the end of the Subject label line.
11370
+ * `(save)`, `(testSend)`, `(videosRequested)` and `(useAsMine)`. After a request the host calls back:
11371
+ * `markSaved(id)` (a create succeeded) and `closeTestSend()`.
11372
11372
  *
11373
11373
  * Message limit: `maxBodyLength` is measured on the serialised HTML. An untouched stored body that is already longer than the limit is
11374
11374
  * accepted unchanged (legacy rows), exactly as the server does.
@@ -11388,12 +11388,8 @@ export declare class EmailTemplateEditorComponent {
11388
11388
  readonly textEnabled: import("@angular/core").InputSignal<boolean>;
11389
11389
  readonly variableGroups: import("@angular/core").InputSignal<EmailTemplateVariableGroup[]>;
11390
11390
  readonly blockGroups: import("@angular/core").InputSignal<EmailTemplateBlockGroup[]>;
11391
- readonly aiExamples: import("@angular/core").InputSignal<string[]>;
11392
11391
  readonly namePlaceholder: import("@angular/core").InputSignal<string>;
11393
11392
  readonly subjectPlaceholder: import("@angular/core").InputSignal<string>;
11394
- readonly backLabel: import("@angular/core").InputSignal<string>;
11395
- readonly createLabel: import("@angular/core").InputSignal<string>;
11396
- readonly editLabel: import("@angular/core").InputSignal<string>;
11397
11393
  readonly systemNote: import("@angular/core").InputSignal<string>;
11398
11394
  readonly readOnlyNote: import("@angular/core").InputSignal<string>;
11399
11395
  /** Prefix of the ids of the field errors the editor renders (`<prefix>-body-error`). */
@@ -11405,21 +11401,24 @@ export declare class EmailTemplateEditorComponent {
11405
11401
  readonly fullAccess: import("@angular/core").InputSignal<boolean>;
11406
11402
  /** A built-in template: shown read-only; a member with full access can copy it ((useAsMine)). */
11407
11403
  readonly systemTemplate: import("@angular/core").InputSignal<boolean>;
11408
- readonly aiEnabled: import("@angular/core").InputSignal<boolean>;
11409
11404
  readonly testSendEnabled: import("@angular/core").InputSignal<boolean>;
11410
- readonly videosEnabled: import("@angular/core").InputSignal<boolean>;
11411
11405
  readonly saving: import("@angular/core").InputSignal<boolean>;
11412
11406
  readonly testSending: import("@angular/core").InputSignal<boolean>;
11413
- readonly aiGenerating: import("@angular/core").InputSignal<boolean>;
11414
- readonly videos: import("@angular/core").InputSignal<EmailTemplateVideo[]>;
11407
+ /**
11408
+ * The host's videos. `null` (the default) = this app has no videos: the Video tab is not shown at all. Any list - even an empty one while the
11409
+ * host loads it on demand (see `(videosRequested)`) - turns the Video tab on.
11410
+ */
11411
+ readonly videos: import("@angular/core").InputSignal<EmailTemplateVideo[] | null>;
11412
+ /** Hides the Video tab completely even when `videos` is passed. */
11413
+ readonly hideVideos: import("@angular/core").InputSignal<boolean>;
11414
+ readonly videosEnabled: import("@angular/core").Signal<boolean>;
11415
+ protected readonly videoList: import("@angular/core").Signal<EmailTemplateVideo[]>;
11415
11416
  readonly videosLoading: import("@angular/core").InputSignal<boolean>;
11416
11417
  readonly videoDefaults: import("@angular/core").InputSignal<EmailTemplateVideoDefaults>;
11417
11418
  readonly videoLink: import("@angular/core").InputSignal<EmailTemplateVideoLinkFn>;
11418
11419
  readonly noVideosHint: import("@angular/core").InputSignal<string>;
11419
- readonly back: import("@angular/core").OutputEmitterRef<void>;
11420
11420
  readonly save: import("@angular/core").OutputEmitterRef<EmailTemplateSavePayload>;
11421
11421
  readonly testSend: import("@angular/core").OutputEmitterRef<string>;
11422
- readonly aiGenerate: import("@angular/core").OutputEmitterRef<string>;
11423
11422
  /** The Video tab was opened (the host loads the library once). */
11424
11423
  readonly videosRequested: import("@angular/core").OutputEmitterRef<void>;
11425
11424
  readonly useAsMine: import("@angular/core").OutputEmitterRef<void>;
@@ -11472,8 +11471,6 @@ export declare class EmailTemplateEditorComponent {
11472
11471
  readonly imageAlt: import("@angular/core").WritableSignal<string>;
11473
11472
  readonly showTestSendDialog: import("@angular/core").WritableSignal<boolean>;
11474
11473
  readonly testSendEmail: import("@angular/core").WritableSignal<string>;
11475
- readonly showAiPanel: import("@angular/core").WritableSignal<boolean>;
11476
- readonly aiPrompt: import("@angular/core").WritableSignal<string>;
11477
11474
  readonly showAssignmentConfig: import("@angular/core").WritableSignal<boolean>;
11478
11475
  readonly configTarget: import("@angular/core").WritableSignal<AssignmentView | null>;
11479
11476
  readonly configEmail: import("@angular/core").WritableSignal<boolean>;
@@ -11507,7 +11504,15 @@ export declare class EmailTemplateEditorComponent {
11507
11504
  readonly bodyError: import("@angular/core").Signal<string>;
11508
11505
  /** "Active From / To" only apply when the template is not "all year"; when both are given To may not be before From. */
11509
11506
  readonly datesError: import("@angular/core").Signal<"" | "Active To must be on or after Active From.">;
11507
+ /** Errors are held back until the user has had a chance to act: a field is blurred, the message is edited, or Save was clicked while something is invalid. */
11508
+ readonly submitAttempted: import("@angular/core").WritableSignal<boolean>;
11509
+ readonly nameTouched: import("@angular/core").WritableSignal<boolean>;
11510
+ readonly subjectTouched: import("@angular/core").WritableSignal<boolean>;
11511
+ readonly nameErrorShown: import("@angular/core").Signal<string>;
11512
+ readonly subjectErrorShown: import("@angular/core").Signal<string>;
11513
+ readonly bodyErrorShown: import("@angular/core").Signal<string>;
11510
11514
  readonly canSave: import("@angular/core").Signal<boolean>;
11515
+ /** Save is offered to members who can write; clicking it while a rule fails shows the messages instead of sending anything. */
11511
11516
  readonly saveDisabled: import("@angular/core").Signal<boolean>;
11512
11517
  private normalizedLink;
11513
11518
  /** Why Insert is disabled: nothing for an empty field, a message for an address that is not a link. */
@@ -11519,20 +11524,16 @@ export declare class EmailTemplateEditorComponent {
11519
11524
  readonly canSendTest: import("@angular/core").Signal<boolean>;
11520
11525
  /** The test email is the SAVED template: true when the editor holds changes that were not saved yet. */
11521
11526
  readonly hasUnsavedChanges: import("@angular/core").Signal<boolean>;
11522
- readonly canGenerate: import("@angular/core").Signal<boolean>;
11523
11527
  constructor();
11524
11528
  /** A create succeeded: the template now has an id (Test Send becomes available) and what is on screen is the saved version. */
11525
11529
  markSaved(id?: string | null): void;
11526
11530
  /** The test email was sent: close the dialog. */
11527
11531
  closeTestSend(): void;
11528
- /** The AI draft arrived: add it as a new block and close the AI panel. */
11529
- addGeneratedHtml(html: string): void;
11530
11532
  private applyTemplate;
11531
11533
  onSave(): void;
11532
11534
  private snapshot;
11533
11535
  private serialize;
11534
11536
  private htmlToBlocks;
11535
- private stripBlockWrappers;
11536
11537
  private genId;
11537
11538
  private editorFor;
11538
11539
  /** Copies each block's HTML from its DOM element (before saving / preview). */
@@ -11598,8 +11599,6 @@ export declare class EmailTemplateEditorComponent {
11598
11599
  insertImageFromUrl(): void;
11599
11600
  openTestSendDialog(): void;
11600
11601
  onTestSend(): void;
11601
- openAiPanel(): void;
11602
- onAiGenerate(): void;
11603
11602
  openVideoTab(): void;
11604
11603
  insertVideo(video: EmailTemplateVideo): void;
11605
11604
  formatDuration(seconds: number | null | undefined): string;
@@ -11658,11 +11657,6 @@ export declare class EmailTemplateEditorComponent {
11658
11657
  "required": false;
11659
11658
  "isSignal": true;
11660
11659
  };
11661
- "aiExamples": {
11662
- "alias": "aiExamples";
11663
- "required": false;
11664
- "isSignal": true;
11665
- };
11666
11660
  "namePlaceholder": {
11667
11661
  "alias": "namePlaceholder";
11668
11662
  "required": false;
@@ -11673,21 +11667,6 @@ export declare class EmailTemplateEditorComponent {
11673
11667
  "required": false;
11674
11668
  "isSignal": true;
11675
11669
  };
11676
- "backLabel": {
11677
- "alias": "backLabel";
11678
- "required": false;
11679
- "isSignal": true;
11680
- };
11681
- "createLabel": {
11682
- "alias": "createLabel";
11683
- "required": false;
11684
- "isSignal": true;
11685
- };
11686
- "editLabel": {
11687
- "alias": "editLabel";
11688
- "required": false;
11689
- "isSignal": true;
11690
- };
11691
11670
  "systemNote": {
11692
11671
  "alias": "systemNote";
11693
11672
  "required": false;
@@ -11723,21 +11702,11 @@ export declare class EmailTemplateEditorComponent {
11723
11702
  "required": false;
11724
11703
  "isSignal": true;
11725
11704
  };
11726
- "aiEnabled": {
11727
- "alias": "aiEnabled";
11728
- "required": false;
11729
- "isSignal": true;
11730
- };
11731
11705
  "testSendEnabled": {
11732
11706
  "alias": "testSendEnabled";
11733
11707
  "required": false;
11734
11708
  "isSignal": true;
11735
11709
  };
11736
- "videosEnabled": {
11737
- "alias": "videosEnabled";
11738
- "required": false;
11739
- "isSignal": true;
11740
- };
11741
11710
  "saving": {
11742
11711
  "alias": "saving";
11743
11712
  "required": false;
@@ -11748,13 +11717,13 @@ export declare class EmailTemplateEditorComponent {
11748
11717
  "required": false;
11749
11718
  "isSignal": true;
11750
11719
  };
11751
- "aiGenerating": {
11752
- "alias": "aiGenerating";
11720
+ "videos": {
11721
+ "alias": "videos";
11753
11722
  "required": false;
11754
11723
  "isSignal": true;
11755
11724
  };
11756
- "videos": {
11757
- "alias": "videos";
11725
+ "hideVideos": {
11726
+ "alias": "hideVideos";
11758
11727
  "required": false;
11759
11728
  "isSignal": true;
11760
11729
  };
@@ -11779,13 +11748,11 @@ export declare class EmailTemplateEditorComponent {
11779
11748
  "isSignal": true;
11780
11749
  };
11781
11750
  }, {
11782
- "back": "back";
11783
11751
  "save": "save";
11784
11752
  "testSend": "testSend";
11785
- "aiGenerate": "aiGenerate";
11786
11753
  "videosRequested": "videosRequested";
11787
11754
  "useAsMine": "useAsMine";
11788
- }, never, ["[uiSubjectAction]"], true, never>;
11755
+ }, never, never, true, never>;
11789
11756
  }
11790
11757
  /** The text a new template's first block starts with: it is a prompt, not content, so a template still holding only this has no message. */
11791
11758
  export declare const EMAIL_TEMPLATE_START_TEXT = "Start writing your email...";