@forgeroute/ui 0.4.33 → 0.4.35

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.33",
3
+ "version": "0.4.35",
4
4
  "peerDependencies": {
5
5
  "@angular/common": ">=22.0.0",
6
6
  "@angular/core": ">=22.0.0",
@@ -1999,7 +1999,7 @@ export declare class SidePanelComponent implements OnDestroy {
1999
1999
  readonly cancelLabel: import("@angular/core").InputSignal<string>;
2000
2000
  /** Right-aligned primary action button label. Empty string = hidden. */
2001
2001
  readonly primaryLabel: import("@angular/core").InputSignal<string>;
2002
- readonly primaryVariant: import("@angular/core").InputSignal<"danger" | "warning" | "primary">;
2002
+ readonly primaryVariant: import("@angular/core").InputSignal<"primary" | "danger" | "warning">;
2003
2003
  readonly primaryLoading: import("@angular/core").InputSignal<boolean>;
2004
2004
  readonly primaryDisabled: import("@angular/core").InputSignal<boolean>;
2005
2005
  /** `data-testid` of the Cancel button; none when empty (default). */
@@ -2571,6 +2571,8 @@ export declare class SkeletonComponent {
2571
2571
  /**
2572
2572
  * Hover / focus tooltip for any element: `<span uiTooltip="Saved">`.
2573
2573
  * Empty text shows nothing. Line breaks (`\n`) are kept. Escape or leaving the element closes it.
2574
+ * Only one tooltip is open at a time anywhere on the page: opening one closes the previous (e.g. a tooltip on a container and on a control inside
2575
+ * it never both show; the innermost one the pointer entered last wins).
2574
2576
  * The bubble is rendered on `document.body` (position: fixed) so table/panel overflow never clips it.
2575
2577
  */
2576
2578
  export declare class TooltipDirective {
@@ -2581,6 +2583,8 @@ export declare class TooltipDirective {
2581
2583
  private readonly host;
2582
2584
  private readonly doc;
2583
2585
  private bubble;
2586
+ /** The host's own `aria-describedby` (e.g. an error id) before the tooltip was added, put back when it closes. */
2587
+ private previousDescribedBy;
2584
2588
  private readonly id;
2585
2589
  constructor();
2586
2590
  protected show(): void;
@@ -2741,7 +2745,7 @@ export declare class CheckboxComponent implements ControlValueAccessor, Validato
2741
2745
  * clicking it still toggles the box - e.g. a product name that is both the heading of its card and the label of its checkbox.
2742
2746
  * Default null: plain label text, exactly as before. Single mode only.
2743
2747
  */
2744
- readonly headingLevel: import("@angular/core").InputSignal<1 | 2 | 6 | 3 | 5 | 4 | null>;
2748
+ readonly headingLevel: import("@angular/core").InputSignal<1 | 2 | 3 | 4 | 5 | 6 | null>;
2745
2749
  readonly indeterminate: import("@angular/core").InputSignal<boolean>;
2746
2750
  readonly checked: import("@angular/core").ModelSignal<boolean>;
2747
2751
  /**
@@ -3139,7 +3143,7 @@ export declare class StepperComponent {
3139
3143
  * its circle, one continuous line behind the circles that fills as steps complete; a complete step shows a check, the current step an
3140
3144
  * accent ring with a dot, an upcoming step an empty circle (no numbers). Circles are 40px (32px with `size="sm"`).
3141
3145
  */
3142
- readonly labelPosition: import("@angular/core").InputSignal<"below" | "beside">;
3146
+ readonly labelPosition: import("@angular/core").InputSignal<"beside" | "below">;
3143
3147
  /** Viewport width in px below which a horizontal stepper is drawn vertical (labels beside the circles), e.g. `640`. null (default) = never. */
3144
3148
  readonly stackBelow: import("@angular/core").InputSignal<number | null>;
3145
3149
  /** CSS length (e.g. `10.5rem`): horizontal only - the list gets `min-width: calc(<step count> * <value>)`. Empty (default) = no minimum. */
@@ -3766,7 +3770,7 @@ export declare class ButtonComponent {
3766
3770
  * `ghost-start`: a ghost button for list / section toggles - a full-width row with its content aligned to the start, transparent
3767
3771
  * background, hover tint and the normal focus ring. Pair it with `ariaExpanded` / `ariaPressed`; the label may contain a heading element.
3768
3772
  */
3769
- readonly variant: import("@angular/core").InputSignal<"link" | "danger" | "icon" | "warning" | "primary" | "secondary" | "ghost" | "ghost-start" | "tab" | "icon-danger">;
3773
+ readonly variant: import("@angular/core").InputSignal<"primary" | "secondary" | "danger" | "warning" | "link" | "ghost" | "ghost-start" | "tab" | "icon" | "icon-danger">;
3770
3774
  /** 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
3775
  readonly routerLink: import("@angular/core").InputSignal<string | readonly unknown[] | null>;
3772
3776
  /** Query params of the `routerLink` (e.g. `{ section: 'deal-defaults' }`). */
@@ -3963,6 +3967,11 @@ export declare class ToggleBadgeComponent {
3963
3967
  readonly active: import("@angular/core").InputSignal<boolean>;
3964
3968
  /** Icon buttons shown inside the pill after the label. Empty (default) = a plain pill, exactly as before. */
3965
3969
  readonly actions: import("@angular/core").InputSignal<ToggleBadgeAction[]>;
3970
+ /**
3971
+ * Hover / focus tooltip of the LABEL button only. Use this instead of `uiTooltip` on `<ui-toggle-badge>` when the badge has `actions`:
3972
+ * a tooltip on the host would also open while the pointer is over an icon button and stack on top of that icon's own tooltip.
3973
+ */
3974
+ readonly tooltip: import("@angular/core").InputSignal<string>;
3966
3975
  readonly toggle: import("@angular/core").OutputEmitterRef<void>;
3967
3976
  /** An icon button was clicked: its `id`. Never emits `toggle`. */
3968
3977
  readonly action: import("@angular/core").OutputEmitterRef<string>;
@@ -3983,6 +3992,11 @@ export declare class ToggleBadgeComponent {
3983
3992
  "required": false;
3984
3993
  "isSignal": true;
3985
3994
  };
3995
+ "tooltip": {
3996
+ "alias": "tooltip";
3997
+ "required": false;
3998
+ "isSignal": true;
3999
+ };
3986
4000
  }, {
3987
4001
  "toggle": "toggle";
3988
4002
  "action": "action";
@@ -4234,7 +4248,7 @@ export declare class BulkMessagePanelComponent {
4234
4248
  readonly sending: import("@angular/core").InputSignal<boolean>;
4235
4249
  readonly closed: import("@angular/core").OutputEmitterRef<void>;
4236
4250
  readonly send: import("@angular/core").OutputEmitterRef<BulkMessageSendEvent>;
4237
- readonly channel: import("@angular/core").WritableSignal<"email" | "text">;
4251
+ readonly channel: import("@angular/core").WritableSignal<"text" | "email">;
4238
4252
  private readonly _subject;
4239
4253
  private readonly _body;
4240
4254
  get subject(): string;
@@ -4510,7 +4524,7 @@ export declare class TextInputComponent implements ControlValueAccessor, Validat
4510
4524
  readonly passwordRequirements: import("@angular/core").InputSignal<boolean>;
4511
4525
  /** Rules for `passwordRequirements`; default = the library's five rules (8+ characters, upper, lower, digit, special). */
4512
4526
  readonly passwordRules: import("@angular/core").InputSignal<readonly PasswordRule[] | undefined>;
4513
- readonly type: import("@angular/core").InputSignal<"number" | "email" | "text" | "password" | "tel" | "url">;
4527
+ readonly type: import("@angular/core").InputSignal<"number" | "text" | "email" | "tel" | "password" | "url">;
4514
4528
  readonly maxlength: import("@angular/core").InputSignal<number | undefined>;
4515
4529
  readonly required: import("@angular/core").InputSignal<boolean>;
4516
4530
  readonly autocomplete: import("@angular/core").InputSignal<string>;
@@ -4519,7 +4533,7 @@ export declare class TextInputComponent implements ControlValueAccessor, Validat
4519
4533
  * `search`, `text`, `none`). Empty (default) = the browser's default for the field, except `type="tel"`, which keeps `tel`.
4520
4534
  * It only picks the keyboard; it does not restrict what can be typed or pasted.
4521
4535
  */
4522
- readonly inputmode: import("@angular/core").InputSignal<"" | "email" | "none" | "text" | "tel" | "url" | "decimal" | "numeric" | "search">;
4536
+ readonly inputmode: import("@angular/core").InputSignal<"" | "none" | "text" | "search" | "email" | "tel" | "url" | "decimal" | "numeric">;
4523
4537
  readonly readonly: import("@angular/core").InputSignal<boolean>;
4524
4538
  readonly hint: import("@angular/core").InputSignal<string>;
4525
4539
  readonly error: import("@angular/core").InputSignal<string>;
@@ -4583,7 +4597,7 @@ export declare class TextInputComponent implements ControlValueAccessor, Validat
4583
4597
  protected readonly _stepAttr: import("@angular/core").Signal<string | null>;
4584
4598
  protected readonly _revealable: import("@angular/core").Signal<boolean>;
4585
4599
  protected readonly _shown: import("@angular/core").WritableSignal<boolean>;
4586
- protected readonly _inputType: import("@angular/core").Signal<"number" | "email" | "text" | "password" | "tel" | "url">;
4600
+ protected readonly _inputType: import("@angular/core").Signal<"number" | "text" | "email" | "tel" | "password" | "url">;
4587
4601
  protected readonly _eyePath: string;
4588
4602
  protected readonly _eyeOffPath: string;
4589
4603
  protected readonly _showRequirements: import("@angular/core").Signal<boolean>;
@@ -6129,7 +6143,7 @@ export declare class FormRowComponent {
6129
6143
  * child simply takes the full width. Keep a numeric span within the row's `cols` (use `all` for the whole row).
6130
6144
  */
6131
6145
  export declare class FormColSpanDirective {
6132
- readonly uiColSpan: import("@angular/core").InputSignal<"" | 2 | 3 | "2" | "3" | "all">;
6146
+ readonly uiColSpan: import("@angular/core").InputSignal<"" | "2" | "3" | 2 | 3 | "all">;
6133
6147
  protected readonly _class: import("@angular/core").Signal<string>;
6134
6148
  static ɵfac: i0.ɵɵFactoryDeclaration<FormColSpanDirective, never>;
6135
6149
  static ɵdir: i0.ɵɵDirectiveDeclaration<FormColSpanDirective, "[uiColSpan]", never, {
@@ -6179,7 +6193,7 @@ export type ListItemDetail = string | {
6179
6193
  export type ListItemIconColor = 'slate' | 'amber' | 'red' | 'blue' | 'green' | 'purple' | 'teal' | 'orange';
6180
6194
  export declare class ListItemComponent {
6181
6195
  /** Show a radio button, checkbox, or nothing on the left. */
6182
- readonly leading: import("@angular/core").InputSignal<"radio" | "checkbox" | "none">;
6196
+ readonly leading: import("@angular/core").InputSignal<"none" | "checkbox" | "radio">;
6183
6197
  /** `name` attribute for the radio/checkbox (groups radios together). */
6184
6198
  readonly inputName: import("@angular/core").InputSignal<string>;
6185
6199
  /** `id` attribute for the radio/checkbox (links the label). */
@@ -7156,9 +7170,9 @@ export declare class ChipGroupComponent {
7156
7170
  /** Space between each chip. */
7157
7171
  readonly gap: import("@angular/core").InputSignal<"sm" | "md" | "lg">;
7158
7172
  /** Space above the chip group. */
7159
- readonly spaceAbove: import("@angular/core").InputSignal<"sm" | "md" | "none">;
7173
+ readonly spaceAbove: import("@angular/core").InputSignal<"none" | "sm" | "md">;
7160
7174
  /** Space below the chip group. */
7161
- readonly spaceBelow: import("@angular/core").InputSignal<"sm" | "md" | "none">;
7175
+ readonly spaceBelow: import("@angular/core").InputSignal<"none" | "sm" | "md">;
7162
7176
  protected readonly _gap: import("@angular/core").Signal<"6px" | "12px" | "8px">;
7163
7177
  protected readonly _spaceAbove: import("@angular/core").Signal<"0" | "6px" | "12px">;
7164
7178
  protected readonly _spaceBelow: import("@angular/core").Signal<"0" | "6px" | "12px">;
@@ -7490,7 +7504,7 @@ export declare class IconComponent {
7490
7504
  readonly ariaLabel: import("@angular/core").InputSignal<string>;
7491
7505
  /** Icon size in px. */
7492
7506
  readonly size: import("@angular/core").InputSignal<number>;
7493
- readonly variant: import("@angular/core").InputSignal<"default" | "danger">;
7507
+ readonly variant: import("@angular/core").InputSignal<"danger" | "default">;
7494
7508
  readonly disabled: import("@angular/core").InputSignal<boolean>;
7495
7509
  /** The host is running the request this icon started: a spinner replaces the picture and the button is disabled (no click) until it is false again. */
7496
7510
  readonly loading: import("@angular/core").InputSignal<boolean>;
@@ -7602,7 +7616,7 @@ export declare class TimelineComponent {
7602
7616
  /** Any CSS length (e.g. `600px`). When set, the list scrolls inside a keyboard-focusable region of at most this height. Empty = no wrapper. */
7603
7617
  readonly maxHeight: import("@angular/core").InputSignal<string>;
7604
7618
  /** `below` (default): the time is under the title / description. `end`: the time sits right-aligned on the title row. */
7605
- readonly timePosition: import("@angular/core").InputSignal<"below" | "end">;
7619
+ readonly timePosition: import("@angular/core").InputSignal<"end" | "below">;
7606
7620
  /**
7607
7621
  * Group the items under a heading per calendar day of `dateTime` ("Today", "Yesterday", "Oct 3", "Oct 3, 2025"). Default false = no headings,
7608
7622
  * DOM unchanged. The given order is kept: consecutive items of one day share a heading, the same day appearing again later starts a new group.
@@ -8932,7 +8946,7 @@ export declare class PdfHotspotComponent {
8932
8946
  /** `ready`: clickable; `blocked`: disabled (a prerequisite is missing); `signed`: shows `image`. */
8933
8947
  readonly state: import("@angular/core").InputSignal<"ready" | "blocked" | "signed">;
8934
8948
  /** `warning` gives the ready state an amber look (e.g. for initials), default is blue. */
8935
- readonly tone: import("@angular/core").InputSignal<"warning" | "primary">;
8949
+ readonly tone: import("@angular/core").InputSignal<"primary" | "warning">;
8936
8950
  /** Text of the ready button (e.g. "Sign Here"). */
8937
8951
  readonly label: import("@angular/core").InputSignal<string>;
8938
8952
  /** Text of the blocked button (e.g. "Add signature above"). */
@@ -9066,7 +9080,7 @@ export declare class DetailCardComponent {
9066
9080
  /** true (default) = the title is displayed in capitals (CSS only); false = shown as written. The text, aria-label and heading name are never changed. */
9067
9081
  readonly uppercaseTitle: import("@angular/core").InputSignal<boolean>;
9068
9082
  /** 1-6 makes the title a real heading for screen readers (role=heading, aria-level); null keeps it plain text. */
9069
- readonly headingLevel: import("@angular/core").InputSignal<1 | 2 | 6 | 3 | 5 | 4 | null>;
9083
+ readonly headingLevel: import("@angular/core").InputSignal<1 | 2 | 3 | 4 | 5 | 6 | null>;
9070
9084
  /** Optional id for the title text; the card is then labelled by it. Without it the title is the card's aria-label. */
9071
9085
  readonly titleId: import("@angular/core").InputSignal<string>;
9072
9086
  /** Accessible name of the card when the header gives none (no header, or no title). A visible title wins; blank = no name. */
@@ -11142,8 +11156,12 @@ export interface SetupIntegration {
11142
11156
  title: string;
11143
11157
  /** Shown while the integration is not connected. */
11144
11158
  description: string;
11145
- /** Library `ui-list-item` icon name (default 'link'). */
11159
+ /** Library icon name drawn in the tile when there is no `letter` (default 'link'). */
11146
11160
  icon?: string;
11161
+ /** One letter shown in the coloured tile (like the source cards of an import step); takes the place of `icon`. */
11162
+ letter?: string;
11163
+ /** Background of the tile (any CSS colour; default slate). */
11164
+ color?: string;
11147
11165
  /** GET answering `{ connected: boolean, ... }` (relative to the API base URL of `FORGEROUTE_CONFIG`). */
11148
11166
  statusEndpoint: string;
11149
11167
  /** When one status endpoint answers several providers (`{ gmail: {...}, outlook: {...} }`), the property that belongs to this one. */
@@ -11206,7 +11224,7 @@ export interface SetupStep {
11206
11224
  cardTitle?: string;
11207
11225
  /** The paragraph under the heading. */
11208
11226
  description?: string;
11209
- /** `custom` shows the app's own `<ng-template uiSetupStep="key">`; `import` and `integrations` are built in. */
11227
+ /** `custom` shows the app's own `<ng-template uiSetupStep="key">`; `import` and `integrations` are built in (an app template with the step's key is shown above the built-in content). */
11210
11228
  kind: 'custom' | 'import' | 'integrations';
11211
11229
  /** Shows "Skip for now" (default true). The first step of a wizard usually sets this to false. */
11212
11230
  skippable?: boolean;
@@ -11235,6 +11253,8 @@ export interface SetupWizardApi {
11235
11253
  saveAndContinue(): Promise<void>;
11236
11254
  /** True while a save is running. */
11237
11255
  readonly busy: () => boolean;
11256
+ /** Marks steps as done (check mark, counted in the progress) without the user visiting them; unknown keys are ignored. For example a live sync that already brought in the data of later steps. */
11257
+ markCompleted(...keys: string[]): void;
11238
11258
  }
11239
11259
  /**
11240
11260
  * The integrations every app can offer in a setup step. The endpoints follow the convention the apps' backends already use (`GET <x>/status`,
@@ -11251,6 +11271,8 @@ export declare function setupIntegrations(...keys: (keyof typeof SETUP_INTEGRATI
11251
11271
  * <ng-template uiSetupStep="profile"> …the app's own form… </ng-template>
11252
11272
  * </ui-setup-wizard>
11253
11273
  *
11274
+ * An `import` or `integrations` step with a template of its key shows it ABOVE the built-in content (for example a "connect your CRM" panel above the source cards).
11275
+ *
11254
11276
  * A step with `ownActions: true` draws its own continue button: `<ng-template uiSetupStep="profile" let-wizard>` … `(click)="wizard.saveAndContinue()"`.
11255
11277
  */
11256
11278
  export declare class SetupWizardStepDirective {
@@ -11373,6 +11395,8 @@ export declare class SetupWizardComponent {
11373
11395
  private advanceFrom;
11374
11396
  protected back(): void;
11375
11397
  protected skip(): void;
11398
+ /** Marks the given steps done (ignoring keys that are not steps of this wizard). */
11399
+ private markStepsCompleted;
11376
11400
  private markCompleted;
11377
11401
  /** "Save & Continue" of a custom or integrations step: runs the step's hook, then moves on. */
11378
11402
  saveAndContinue(): Promise<void>;
@@ -11422,12 +11446,6 @@ export interface EmailTemplateAssignmentOption {
11422
11446
  name: string;
11423
11447
  description: string;
11424
11448
  }
11425
- /** The channels a template is assigned to for one trigger. */
11426
- export interface EmailTemplateAssignmentChannels {
11427
- key: string;
11428
- email: boolean;
11429
- text: boolean;
11430
- }
11431
11449
  /** A template as the editor shows it. `id` is null/absent for a template that is not stored yet (new, or a copy of another). */
11432
11450
  export interface EmailTemplateValue {
11433
11451
  id?: string | null;
@@ -11443,8 +11461,8 @@ export interface EmailTemplateValue {
11443
11461
  body: string;
11444
11462
  videoId?: string | null;
11445
11463
  videoTitle?: string | null;
11446
- /** The channels per trigger; a trigger that is missing is not assigned. */
11447
- assignments?: EmailTemplateAssignmentChannels[];
11464
+ /** The keys of the triggers (`EmailTemplateAssignmentOption.key`) this template is assigned to. */
11465
+ assignments?: string[];
11448
11466
  }
11449
11467
  /** What `(save)` emits. Dates are the raw `YYYY-MM-DD` values (null while "all year" is on); the host maps them to its API. */
11450
11468
  export interface EmailTemplateSavePayload {
@@ -11456,8 +11474,8 @@ export interface EmailTemplateSavePayload {
11456
11474
  activeFrom: string | null;
11457
11475
  activeTo: string | null;
11458
11476
  videoId: string | null;
11459
- /** Only the triggers with at least one channel on. */
11460
- assignments: EmailTemplateAssignmentChannels[];
11477
+ /** The keys of the triggers this template is assigned to. */
11478
+ assignments: string[];
11461
11479
  }
11462
11480
  export interface EmailTemplateBlock {
11463
11481
  key: string;
@@ -11506,8 +11524,7 @@ interface EmailBlock {
11506
11524
  spacing?: string;
11507
11525
  }
11508
11526
  interface AssignmentView extends EmailTemplateAssignmentOption {
11509
- email: boolean;
11510
- text: boolean;
11527
+ assigned: boolean;
11511
11528
  }
11512
11529
  type EditorTab = 'edit' | 'preview' | 'mobile';
11513
11530
  type RightTab = 'blocks' | 'variables' | 'video';
@@ -11526,7 +11543,6 @@ type RightTab = 'blocks' | 'variables' | 'video';
11526
11543
  export declare class EmailTemplateEditorComponent {
11527
11544
  private readonly host;
11528
11545
  private readonly injector;
11529
- private readonly toast;
11530
11546
  /** The template to edit. `null` = a blank new template. An object without `id` is a new template that starts from those values (e.g. a copy). */
11531
11547
  readonly template: import("@angular/core").InputSignal<EmailTemplateValue | null>;
11532
11548
  readonly categories: import("@angular/core").InputSignal<SelectOption<string>[]>;
@@ -11535,9 +11551,10 @@ export declare class EmailTemplateEditorComponent {
11535
11551
  /** The assignment shown first (e.g. the trigger the user came from). */
11536
11552
  readonly featuredAssignmentKey: import("@angular/core").InputSignal<string | null>;
11537
11553
  readonly assignmentsDescription: import("@angular/core").InputSignal<string>;
11538
- readonly textEnabled: import("@angular/core").InputSignal<boolean>;
11539
11554
  readonly variableGroups: import("@angular/core").InputSignal<EmailTemplateVariableGroup[]>;
11540
11555
  readonly blockGroups: import("@angular/core").InputSignal<EmailTemplateBlockGroup[]>;
11556
+ /** Page heading shown at the start of the actions row (e.g. "Create Your Template"). Empty (default) = no heading; the buttons stay right aligned. */
11557
+ readonly heading: import("@angular/core").InputSignal<string>;
11541
11558
  readonly namePlaceholder: import("@angular/core").InputSignal<string>;
11542
11559
  readonly subjectPlaceholder: import("@angular/core").InputSignal<string>;
11543
11560
  readonly systemNote: import("@angular/core").InputSignal<string>;
@@ -11582,8 +11599,8 @@ export declare class EmailTemplateEditorComponent {
11582
11599
  readonly activeTo: import("@angular/core").WritableSignal<string>;
11583
11600
  readonly videoId: import("@angular/core").WritableSignal<string | null>;
11584
11601
  readonly videoTitle: import("@angular/core").WritableSignal<string | null>;
11585
- /** The channels the template has per trigger; the rows shown are `assignmentOptions` with these applied. */
11586
- private readonly assignmentChannels;
11602
+ /** The triggers this template is assigned to (an email template is only ever assigned for email); the rows shown are `assignmentOptions` with this applied. */
11603
+ private readonly assignedKeys;
11587
11604
  private readonly assignmentState;
11588
11605
  readonly blocks: import("@angular/core").WritableSignal<EmailBlock[]>;
11589
11606
  readonly selectedBlockId: import("@angular/core").WritableSignal<string | null>;
@@ -11621,10 +11638,6 @@ export declare class EmailTemplateEditorComponent {
11621
11638
  readonly imageAlt: import("@angular/core").WritableSignal<string>;
11622
11639
  readonly showTestSendDialog: import("@angular/core").WritableSignal<boolean>;
11623
11640
  readonly testSendEmail: import("@angular/core").WritableSignal<string>;
11624
- readonly showAssignmentConfig: import("@angular/core").WritableSignal<boolean>;
11625
- readonly configTarget: import("@angular/core").WritableSignal<AssignmentView | null>;
11626
- readonly configEmail: import("@angular/core").WritableSignal<boolean>;
11627
- readonly configText: import("@angular/core").WritableSignal<boolean>;
11628
11641
  readonly activeEditorTab: import("@angular/core").WritableSignal<EditorTab>;
11629
11642
  readonly activeRightTab: import("@angular/core").WritableSignal<RightTab>;
11630
11643
  readonly blockSearch: import("@angular/core").WritableSignal<string>;
@@ -11661,9 +11674,16 @@ export declare class EmailTemplateEditorComponent {
11661
11674
  readonly nameErrorShown: import("@angular/core").Signal<string>;
11662
11675
  readonly subjectErrorShown: import("@angular/core").Signal<string>;
11663
11676
  readonly bodyErrorShown: import("@angular/core").Signal<string>;
11677
+ /** A template must be assigned to at least one automation (only in an app that offers assignments at all). */
11678
+ readonly assignmentError: import("@angular/core").Signal<"" | "Assign this template to at least one automation.">;
11679
+ /** True once the user has changed an assignment (or tried to save), so the message never shows on a page nobody has touched yet. */
11680
+ readonly assignmentsTouched: import("@angular/core").WritableSignal<boolean>;
11681
+ readonly assignmentErrorShown: import("@angular/core").Signal<"" | "Assign this template to at least one automation.">;
11664
11682
  readonly canSave: import("@angular/core").Signal<boolean>;
11665
- /** Save is offered to members who can write; clicking it while a rule fails shows the messages instead of sending anything. */
11683
+ /** Save is offered to members who can write and stays disabled until every rule passes: a name, a subject, a message with some design (text or an image) and at least one assignment. */
11666
11684
  readonly saveDisabled: import("@angular/core").Signal<boolean>;
11685
+ /** What is still missing, shown as the tooltip of the disabled Save button ('' when Save is available or the member cannot write). */
11686
+ readonly saveHint: import("@angular/core").Signal<string>;
11667
11687
  private normalizedLink;
11668
11688
  /** Why Insert is disabled: nothing for an empty field, a message for an address that is not a link. */
11669
11689
  readonly linkUrlError: import("@angular/core").Signal<"" | "Enter a valid email address." | "Enter a valid web address, like https://example.com.">;
@@ -11753,8 +11773,8 @@ export declare class EmailTemplateEditorComponent {
11753
11773
  insertVideo(video: EmailTemplateVideo): void;
11754
11774
  formatDuration(seconds: number | null | undefined): string;
11755
11775
  categoryLabel(cat: string | null | undefined): string;
11756
- openAssignmentConfig(item: AssignmentView): void;
11757
- saveAssignmentConfig(): void;
11776
+ /** Click / Enter / Space on a row: assign this template to that automation, or take it off again. */
11777
+ toggleAssignment(item: AssignmentView): void;
11758
11778
  isOpen(id: string): boolean;
11759
11779
  toggleGroup(id: string): void;
11760
11780
  insertBlock(block: EmailTemplateBlock): void;
@@ -11792,11 +11812,6 @@ export declare class EmailTemplateEditorComponent {
11792
11812
  "required": false;
11793
11813
  "isSignal": true;
11794
11814
  };
11795
- "textEnabled": {
11796
- "alias": "textEnabled";
11797
- "required": false;
11798
- "isSignal": true;
11799
- };
11800
11815
  "variableGroups": {
11801
11816
  "alias": "variableGroups";
11802
11817
  "required": false;
@@ -11807,6 +11822,11 @@ export declare class EmailTemplateEditorComponent {
11807
11822
  "required": false;
11808
11823
  "isSignal": true;
11809
11824
  };
11825
+ "heading": {
11826
+ "alias": "heading";
11827
+ "required": false;
11828
+ "isSignal": true;
11829
+ };
11810
11830
  "namePlaceholder": {
11811
11831
  "alias": "namePlaceholder";
11812
11832
  "required": false;