@h-k-dev/angular-email-editor 0.0.0-placeholder.0 → 0.0.1

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 (51) hide show
  1. package/README.md +127 -3
  2. package/fesm2022/h-k-dev-angular-email-editor-actions.mjs +179 -0
  3. package/fesm2022/h-k-dev-angular-email-editor-actions.mjs.map +1 -0
  4. package/fesm2022/h-k-dev-angular-email-editor-address-chip.mjs +285 -0
  5. package/fesm2022/h-k-dev-angular-email-editor-address-chip.mjs.map +1 -0
  6. package/fesm2022/h-k-dev-angular-email-editor-address-input.mjs +686 -0
  7. package/fesm2022/h-k-dev-angular-email-editor-address-input.mjs.map +1 -0
  8. package/fesm2022/h-k-dev-angular-email-editor-address.mjs +489 -0
  9. package/fesm2022/h-k-dev-angular-email-editor-address.mjs.map +1 -0
  10. package/fesm2022/h-k-dev-angular-email-editor-anchor.mjs +64 -0
  11. package/fesm2022/h-k-dev-angular-email-editor-anchor.mjs.map +1 -0
  12. package/fesm2022/h-k-dev-angular-email-editor-attachment-chip.mjs +387 -0
  13. package/fesm2022/h-k-dev-angular-email-editor-attachment-chip.mjs.map +1 -0
  14. package/fesm2022/h-k-dev-angular-email-editor-attachment-chips.mjs +80 -0
  15. package/fesm2022/h-k-dev-angular-email-editor-attachment-chips.mjs.map +1 -0
  16. package/fesm2022/h-k-dev-angular-email-editor-chat-input.mjs +346 -0
  17. package/fesm2022/h-k-dev-angular-email-editor-chat-input.mjs.map +1 -0
  18. package/fesm2022/h-k-dev-angular-email-editor-focus.mjs +68 -0
  19. package/fesm2022/h-k-dev-angular-email-editor-focus.mjs.map +1 -0
  20. package/fesm2022/h-k-dev-angular-email-editor-image.mjs +132 -0
  21. package/fesm2022/h-k-dev-angular-email-editor-image.mjs.map +1 -0
  22. package/fesm2022/h-k-dev-angular-email-editor-material.mjs +124 -0
  23. package/fesm2022/h-k-dev-angular-email-editor-material.mjs.map +1 -0
  24. package/fesm2022/h-k-dev-angular-email-editor-metrics.mjs +243 -0
  25. package/fesm2022/h-k-dev-angular-email-editor-metrics.mjs.map +1 -0
  26. package/fesm2022/h-k-dev-angular-email-editor-palette.mjs +87 -0
  27. package/fesm2022/h-k-dev-angular-email-editor-palette.mjs.map +1 -0
  28. package/fesm2022/h-k-dev-angular-email-editor-proposal.mjs +307 -0
  29. package/fesm2022/h-k-dev-angular-email-editor-proposal.mjs.map +1 -0
  30. package/fesm2022/h-k-dev-angular-email-editor-suggestion-menu.mjs +392 -0
  31. package/fesm2022/h-k-dev-angular-email-editor-suggestion-menu.mjs.map +1 -0
  32. package/fesm2022/h-k-dev-angular-email-editor.mjs +18818 -0
  33. package/fesm2022/h-k-dev-angular-email-editor.mjs.map +1 -0
  34. package/package.json +130 -6
  35. package/styles/_scroll.scss +101 -0
  36. package/types/h-k-dev-angular-email-editor-actions.d.ts +147 -0
  37. package/types/h-k-dev-angular-email-editor-address-chip.d.ts +193 -0
  38. package/types/h-k-dev-angular-email-editor-address-input.d.ts +328 -0
  39. package/types/h-k-dev-angular-email-editor-address.d.ts +125 -0
  40. package/types/h-k-dev-angular-email-editor-anchor.d.ts +44 -0
  41. package/types/h-k-dev-angular-email-editor-attachment-chip.d.ts +271 -0
  42. package/types/h-k-dev-angular-email-editor-attachment-chips.d.ts +48 -0
  43. package/types/h-k-dev-angular-email-editor-chat-input.d.ts +101 -0
  44. package/types/h-k-dev-angular-email-editor-focus.d.ts +40 -0
  45. package/types/h-k-dev-angular-email-editor-image.d.ts +66 -0
  46. package/types/h-k-dev-angular-email-editor-material.d.ts +65 -0
  47. package/types/h-k-dev-angular-email-editor-metrics.d.ts +110 -0
  48. package/types/h-k-dev-angular-email-editor-palette.d.ts +51 -0
  49. package/types/h-k-dev-angular-email-editor-proposal.d.ts +170 -0
  50. package/types/h-k-dev-angular-email-editor-suggestion-menu.d.ts +177 -0
  51. package/types/h-k-dev-angular-email-editor.d.ts +3959 -0
@@ -0,0 +1,271 @@
1
+ import * as _angular_core from '@angular/core';
2
+ import { TemplateRef, InjectionToken, OnInit } from '@angular/core';
3
+
4
+ /**
5
+ * What a chip needs to draw an attachment: a name and, if they are known, a
6
+ * MIME type and a size. Deliberately structural rather than a class — a
7
+ * browser `File` already satisfies it, so a host that holds `File[]` binds it
8
+ * straight in with no adapter, and one that holds parsed MIME parts
9
+ * (postal-mime hands back a name, a `mimeType` and the content, not a
10
+ * `File`) maps three fields.
11
+ */
12
+ interface Attachment {
13
+ readonly name: string;
14
+ /** MIME type, e.g. `application/pdf`. Only the icon reads it. */
15
+ readonly type?: string;
16
+ /** Size in bytes. Shown under the name, and what simulated progress
17
+ paces itself by. */
18
+ readonly size?: number;
19
+ }
20
+ /**
21
+ * Where an attachment's transfer is, in the stages uploaders agree on —
22
+ * Uppy, tus and their kin name them the same way. `preprocessing` is the
23
+ * work before the bytes move (a fingerprint, a virus scan) and has no
24
+ * number; `queued` is accepted and waiting for a connection, with nothing
25
+ * happening yet; `uploading` is the bytes moving, with a number;
26
+ * `postprocessing` is the work after (a transcode, a scan on the far side),
27
+ * again with none; `complete` is done. A chip draws a sweep for the stages
28
+ * where work runs without a number, a bar for the one with, and keeps
29
+ * quiet while a transfer only waits.
30
+ *
31
+ * The set is a vocabulary, not a sequence: which stages a pipeline has, and
32
+ * in what order, is the host's — a store that scans on the far side queues
33
+ * before it preprocesses, one that caps its connections queues after.
34
+ */
35
+ type AttachmentStatus = 'preprocessing' | 'queued' | 'uploading' | 'postprocessing' | 'complete';
36
+ /** The stages during which the transfer is not done — a waiting one
37
+ included, since the message is no more sendable for it. */
38
+ declare function attachmentInFlight(status: AttachmentStatus | null | undefined): boolean;
39
+ /** The families a chip draws a distinct icon for. */
40
+ type AttachmentKind = 'image' | 'video' | 'audio' | 'pdf' | 'archive' | 'spreadsheet' | 'presentation' | 'document' | 'message' | 'file';
41
+ /** The kind of file a MIME type stands for; `file` when the type is unknown
42
+ or matches nothing above. Case-insensitive — a type off the wire may be
43
+ shouted. */
44
+ declare function attachmentKind(type: string | undefined): AttachmentKind;
45
+ /**
46
+ * A byte count the way a mail client prints it: binary steps, one decimal
47
+ * below ten ("2.5 MB", "15 MB"), none for bytes. The number follows the
48
+ * locale ("2,5 MB" in German); the unit labels are the ones every mail
49
+ * client uses, not CLDR's ("kB", "byte").
50
+ */
51
+ declare function formatAttachmentSize(bytes: number, locale?: string): string;
52
+
53
+ /** What the host's `data-scanning` reads: `'true'` while the band sweeps,
54
+ `'leaving'` while it fades out, `null` when there is no band. */
55
+ type ScanningState = 'true' | 'leaving' | null;
56
+
57
+ /**
58
+ * The chip's template slots. A host that wants its own icon or its own
59
+ * progress readout hands the chip a template, and the chip renders it
60
+ * inside the box it already sizes — so a Material icon, a font icon or a
61
+ * custom SVG lands in the same 1em square the default glyph does, and the
62
+ * stylesheet never has to reach into the host's markup.
63
+ *
64
+ * <li email-attachment-chip [attachment]="file">
65
+ * <mat-icon *emailAttachmentIcon="let kind = kind" inline>
66
+ * {{ kind === 'pdf' ? 'picture_as_pdf' : 'attach_file' }}
67
+ * </mat-icon>
68
+ * </li>
69
+ */
70
+ /** What the icon template is given: the attachment, and the kind the chip
71
+ worked out from its MIME type. */
72
+ interface AttachmentChipIconContext {
73
+ readonly $implicit: Attachment;
74
+ readonly kind: AttachmentKind;
75
+ }
76
+ /** Which progress the chip is drawing: a bar with a number, a sweep with
77
+ none, the chip's own fake fill, or nothing yet while the transfer waits. */
78
+ type AttachmentProgressMode = 'determinate' | 'indeterminate' | 'simulated' | 'queued';
79
+ /** What the progress template is given: the attachment and everything the
80
+ chip knows about its transfer. `percent` is `progress` as a whole number
81
+ of 0–100, clamped. */
82
+ interface AttachmentChipProgressContext {
83
+ readonly $implicit: Attachment;
84
+ readonly status: AttachmentStatus | null;
85
+ readonly progress: number | null;
86
+ readonly percent: number;
87
+ readonly mode: AttachmentProgressMode;
88
+ }
89
+ /**
90
+ * The chip's icon slot: mark an `ng-template` with it (or use the `*`
91
+ * shorthand on the element itself) and the chip renders it in place of the
92
+ * default glyph, in the icon box. The box is `--email-attachment-chip-icon-size`
93
+ * square and sets `font-size` and `color` to match, so a `mat-icon` with
94
+ * `inline`, a font icon, or an SVG drawn at `1em` all fit without a rule.
95
+ */
96
+ declare class AttachmentChipIcon {
97
+ readonly template: TemplateRef<AttachmentChipIconContext>;
98
+ static ngTemplateContextGuard(_directive: AttachmentChipIcon, context: unknown): context is AttachmentChipIconContext;
99
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<AttachmentChipIcon, never>;
100
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<AttachmentChipIcon, "ng-template[emailAttachmentIcon]", never, {}, {}, never, never, true, never>;
101
+ }
102
+ /**
103
+ * The chip's progress slot: the readout in the trailing square, rendered
104
+ * from the host's template instead of the default percentage. The bar along
105
+ * the chip's edge and the scanning sweep stay the chip's — this replaces
106
+ * only what the square shows.
107
+ */
108
+ declare class AttachmentChipProgress {
109
+ readonly template: TemplateRef<AttachmentChipProgressContext>;
110
+ static ngTemplateContextGuard(_directive: AttachmentChipProgress, context: unknown): context is AttachmentChipProgressContext;
111
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<AttachmentChipProgress, never>;
112
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<AttachmentChipProgress, "ng-template[emailAttachmentProgress]", never, {}, {}, never, never, true, never>;
113
+ }
114
+
115
+ /** How simulated progress paces itself. */
116
+ interface AttachmentChipOptions {
117
+ /** The connection a simulated transfer pretends to have, in bytes per
118
+ second. A fast line by default — the point is the gesture, not a wait. */
119
+ readonly bytesPerSecond: number;
120
+ /** Floor in ms, so a tiny file still shows a visible fill. */
121
+ readonly minDuration: number;
122
+ /** Ceiling in ms, so a large file never makes anyone wait on a fake. */
123
+ readonly maxDuration: number;
124
+ /** Duration in ms for an attachment whose size is unknown. */
125
+ readonly fallbackDuration: number;
126
+ /** How long the full bar stays before the chip settles, in ms — so 100%
127
+ is something the eye gets to see, not a frame on the way out. */
128
+ readonly linger: number;
129
+ }
130
+ /** Overrides for simulated progress, app-wide or per subtree:
131
+ `{ provide: ATTACHMENT_CHIP_OPTIONS, useValue: { bytesPerSecond: 2e6 } }`.
132
+ Anything left out keeps its default. */
133
+ declare const ATTACHMENT_CHIP_OPTIONS: InjectionToken<Partial<AttachmentChipOptions>>;
134
+ /** How long a simulated transfer of `size` bytes runs, in ms: size over the
135
+ assumed speed, clamped to the floor and ceiling. The linger after it is
136
+ not part of this — it is the same for every size. */
137
+ declare function simulatedDuration(size: number | undefined, options?: Omit<AttachmentChipOptions, 'linger'>): number;
138
+ /**
139
+ * One attachment, laid out like a list row with an avatar:
140
+ *
141
+ * [icon] | name | [progress / ×]
142
+ * | size |
143
+ * ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
144
+ *
145
+ * Progress is a bar along the bottom edge that fills with the number; with
146
+ * no number the whole chip is swept by a band of light instead, the way a
147
+ * scanner passes over a page — and when the number arrives mid-sweep the
148
+ * band fades out on its way rather than vanishing. The square at the end is
149
+ * shared: while work is in flight it holds the readout (the percentage,
150
+ * nothing while there is none), and the remove button paints over it on
151
+ * hover or focus, always on a touch screen. The chip owns all of that
152
+ * motion: the stylesheet draws it and `attachment-chip.animation.ts` times
153
+ * it; a host only says where the transfer is.
154
+ *
155
+ * **Stages are `status`.** A host with an upload pipeline binds the stage
156
+ * it is in — `preprocessing`, `queued`, `uploading`, `postprocessing`,
157
+ * `complete`, the vocabulary of Uppy and tus — and `progress` (0–1) while
158
+ * uploading. The stages where work runs without a number sweep, the one
159
+ * with a number fills, and `queued` keeps quiet: busy, no bar, no sweep,
160
+ * announced as waiting. The progress names itself after the stage
161
+ * ("Preparing report.pdf") unless `progressLabel` says otherwise. The stage
162
+ * is reflected as `data-status` on the host. Which stages a host has, and
163
+ * in what order, is the host's pipeline; the chip only draws the one it is
164
+ * given.
165
+ *
166
+ * **State is ARIA.** `aria-busy` is reflected while any stage is in flight,
167
+ * and is also an input for a host with no stages to tell: busy with a
168
+ * `progress` fills, busy with `null` sweeps. `aria-disabled` takes the
169
+ * remove button out of play. Both inputs go under their ARIA names, so a
170
+ * host writes `[aria-busy]="upload.pending"` exactly as it would on any
171
+ * element, and the chip reflects them back onto itself — they are the
172
+ * styling hooks too (`[email-attachment-chip][aria-busy='true']`).
173
+ *
174
+ * **`simulateProgress`** is for hosts that already have the bytes and only
175
+ * want the "attaching" moment: the chip runs a fill paced by `size` over
176
+ * `ATTACHMENT_CHIP_OPTIONS`, is busy while it runs and for a moment after
177
+ * the bar is full, and settles on its own.
178
+ * It plays once, when the chip is created; it is skipped for reduced motion
179
+ * and on the server; and a real `status` or `aria-busy` from the host always wins. It
180
+ * never emits and never gates anything — don't use it for work that is
181
+ * really happening but untracked; that is `aria-busy` with no `progress`.
182
+ * The fake announces no number to assistive tech, only that it is busy.
183
+ *
184
+ * **Parts are `data-slot`s.** Every part carries one — `icon`, `label`,
185
+ * `name`, `size`, `trailing`, `progress` (the readout, with
186
+ * `progress-value` inside), `progress-track`, `progress-indicator` (the
187
+ * bar), `remove` — and that is how it is styled from outside:
188
+ * `[email-attachment-chip] [data-slot='name'] { … }`. Theme it through the
189
+ * `--email-*` tokens; a structural override reaches a part with two
190
+ * attributes (`[email-attachment-chip] [data-slot='name'][data-slot]`).
191
+ *
192
+ * **Slots are templates.** `icon` and `progress` are boxes the chip sizes;
193
+ * a host fills either with its own template by marking an `ng-template`
194
+ * `emailAttachmentIcon` / `emailAttachmentProgress` (or the `*` shorthand on
195
+ * the element). The icon box is `--email-attachment-chip-icon-size` square
196
+ * and sets `font-size` and `color` to match, so a `mat-icon` with `inline`, a
197
+ * font icon or an SVG drawn at `1em` all fit with no rule of their own; the
198
+ * template gets the attachment and its `kind`. The progress template gets
199
+ * the attachment, `status`, `progress`, `percent` and `mode`, and replaces
200
+ * only the readout — the bar and the sweep stay the chip's.
201
+ *
202
+ * **It asks, it does not do.** The chip holds no list: `removed` is a
203
+ * request the host answers by dropping the item (and cancelling its upload).
204
+ * Picking, dropping, uploading and assembling the MIME are the host's.
205
+ *
206
+ * Opt-in enter/leave animations ship with it:
207
+ * `animate.enter="email-attachment-chip-enter"` and
208
+ * `animate.leave="email-attachment-chip-leave"` on the chip's element.
209
+ *
210
+ * Meant for an `<li>` inside `<ul email-attachment-chips>`. No Material, no
211
+ * icon font, no headless library.
212
+ */
213
+ declare class AttachmentChip implements OnInit {
214
+ #private;
215
+ /** The file this chip stands for. A `File` satisfies it as it is. */
216
+ readonly attachment: _angular_core.InputSignal<Attachment>;
217
+ /** Set false for a read-only message (a sent copy, a preview): the chip
218
+ stays, the remove button goes. */
219
+ readonly removable: _angular_core.InputSignalWithTransform<boolean, unknown>;
220
+ /** Prefix for the remove button's accessible name — "Remove report.pdf". */
221
+ readonly removeLabel: _angular_core.InputSignal<string>;
222
+ /** Which stage the transfer is in. `null` for a host with no stages to
223
+ tell — then `aria-busy` and `progress` alone describe the work. */
224
+ readonly status: _angular_core.InputSignal<AttachmentStatus | null>;
225
+ /** Work is in flight for this attachment (an upload, a download, a scan),
226
+ for a host that has no stages to tell. */
227
+ readonly ariaBusy: _angular_core.InputSignalWithTransform<boolean, unknown>;
228
+ /** How far the work is, 0–1, while uploading. `null` when there is no
229
+ number. */
230
+ readonly progress: _angular_core.InputSignal<number | null>;
231
+ /** The remove button is out of play — e.g. while the message sends. */
232
+ readonly ariaDisabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
233
+ /** Run a fake transfer once, paced by the attachment's size. */
234
+ readonly simulateProgress: _angular_core.InputSignalWithTransform<boolean, unknown>;
235
+ /** Prefix for the progress's accessible name — "Uploading report.pdf".
236
+ Left out, it follows the stage: Preparing, Uploading, Processing. */
237
+ readonly progressLabel: _angular_core.InputSignal<string | undefined>;
238
+ /** The remove button was pressed. The host drops the attachment; the chip
239
+ goes when the host's list no longer has it. */
240
+ readonly removed: _angular_core.OutputEmitterRef<void>;
241
+ /** The host's own icon, if it handed one in. */
242
+ protected readonly iconTemplate: _angular_core.Signal<AttachmentChipIcon | undefined>;
243
+ /** The host's own progress readout, if it handed one in. */
244
+ protected readonly progressTemplate: _angular_core.Signal<AttachmentChipProgress | undefined>;
245
+ /** Busy for real (a stage in flight, or the host said so) or for show (a
246
+ simulation running). Public so the container can tell whether any of
247
+ its chips is. */
248
+ readonly busy: _angular_core.Signal<boolean>;
249
+ /** Which progress to draw, if any: a sweep for a stage with no number, a
250
+ bar for one with, nothing yet for one that waits, and the host's real
251
+ state beats a simulation. Reflected on the host as `data-mode`. */
252
+ protected readonly mode: _angular_core.Signal<AttachmentProgressMode | null>;
253
+ /** The scanning band's state on the host — `true` while there is no
254
+ number, `leaving` for a moment after one arrives. */
255
+ protected readonly scanning: _angular_core.Signal<ScanningState>;
256
+ /** What the progress is called: the host's word, or the stage's. */
257
+ protected readonly label: _angular_core.Signal<string>;
258
+ protected readonly percent: _angular_core.Signal<number>;
259
+ protected readonly kind: _angular_core.Signal<AttachmentKind>;
260
+ protected readonly icon: _angular_core.Signal<string>;
261
+ protected readonly iconContext: _angular_core.Signal<AttachmentChipIconContext>;
262
+ protected readonly progressContext: _angular_core.Signal<AttachmentChipProgressContext>;
263
+ protected readonly size: _angular_core.Signal<string>;
264
+ protected readonly duration: _angular_core.Signal<number>;
265
+ ngOnInit(): void;
266
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<AttachmentChip, never>;
267
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<AttachmentChip, "[email-attachment-chip]", never, { "attachment": { "alias": "attachment"; "required": true; "isSignal": true; }; "removable": { "alias": "removable"; "required": false; "isSignal": true; }; "removeLabel": { "alias": "removeLabel"; "required": false; "isSignal": true; }; "status": { "alias": "status"; "required": false; "isSignal": true; }; "ariaBusy": { "alias": "aria-busy"; "required": false; "isSignal": true; }; "progress": { "alias": "progress"; "required": false; "isSignal": true; }; "ariaDisabled": { "alias": "aria-disabled"; "required": false; "isSignal": true; }; "simulateProgress": { "alias": "simulateProgress"; "required": false; "isSignal": true; }; "progressLabel": { "alias": "progressLabel"; "required": false; "isSignal": true; }; }, { "removed": "removed"; }, ["iconTemplate", "progressTemplate"], never, true, never>;
268
+ }
269
+
270
+ export { ATTACHMENT_CHIP_OPTIONS, AttachmentChip, AttachmentChipIcon, AttachmentChipProgress, attachmentInFlight, attachmentKind, formatAttachmentSize, simulatedDuration };
271
+ export type { Attachment, AttachmentChipIconContext, AttachmentChipOptions, AttachmentChipProgressContext, AttachmentKind, AttachmentProgressMode, AttachmentStatus };
@@ -0,0 +1,48 @@
1
+ import * as _angular_core from '@angular/core';
2
+ import { FormValueControl } from '@angular/forms/signals';
3
+ import { Attachment, AttachmentChip } from '@h-k-dev/angular-email-editor/attachment-chip';
4
+
5
+ /**
6
+ * The row a message's attachment chips sit in — under the body, in Gmail's
7
+ * spot: the strip belongs to the message, not to the envelope.
8
+ *
9
+ * **The strip is the form's control.** It holds the list as its `value` —
10
+ * a `FormValueControl` over the attachments, bound with `[formField]` or
11
+ * two-way as `[(value)]` — and nothing else: the host renders one
12
+ * `<li email-attachment-chip>` per item into it and decides, chip by chip
13
+ * in its own template, what each one shows (real progress, a simulated one,
14
+ * none). That is why the pair is two components. The chips register by
15
+ * being content: each one's `removed` is relayed into the value for as long
16
+ * as it is in the strip, so a chip's × takes its item out of the message
17
+ * with no wiring on the host's side. Identity, not name — the host's items
18
+ * are the value's, so the chip that asked is exactly the item that goes.
19
+ * `disabled` (the form's, or the host's) holds the value still.
20
+ *
21
+ * `aria-busy` is reflected while any chip is busy, so a host can style or
22
+ * gate on the strip as a whole (`[email-attachment-chips][aria-busy='true']`,
23
+ * or a disabled Send).
24
+ *
25
+ * Meant for a `<ul>`. `role="list"` is set anyway, because `list-style: none`
26
+ * costs a list its semantics in Safari. Hidden while it holds no chips, so a
27
+ * host can place it unconditionally.
28
+ */
29
+ declare class AttachmentChips<T extends Attachment = Attachment> implements FormValueControl<T[]> {
30
+ /** Names the list for assistive tech. */
31
+ readonly label: _angular_core.InputSignal<string>;
32
+ /** The attachments the strip stands for — the form's value. The host
33
+ renders a chip per item; a chip's removal takes its item out of here. */
34
+ readonly value: _angular_core.ModelSignal<T[]>;
35
+ /** Nothing may leave the value — e.g. while the message sends. */
36
+ readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
37
+ protected readonly chips: _angular_core.Signal<readonly AttachmentChip[]>;
38
+ /** Any chip in the strip is busy — uploading, downloading, simulating. */
39
+ readonly busy: _angular_core.Signal<boolean>;
40
+ constructor();
41
+ /** Takes one attachment out of the value, by identity. A no-op while
42
+ disabled. */
43
+ remove(attachment: T): void;
44
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<AttachmentChips<any>, never>;
45
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<AttachmentChips<any>, "[email-attachment-chips]", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; }, ["chips"], ["*"], true, never>;
46
+ }
47
+
48
+ export { AttachmentChips };
@@ -0,0 +1,101 @@
1
+ import * as _angular_core from '@angular/core';
2
+ import { FormValueControl } from '@angular/forms/signals';
3
+ import { Node } from 'prosemirror-model';
4
+ import { Editor } from '@h-k-dev/angular-email-editor';
5
+
6
+ /** The text as it is sent: a line per block, a list item as a dash line —
7
+ the writer's points, kept as points. */
8
+ declare function chatInputText(doc: Node): string;
9
+ /**
10
+ * The behaviour of a chat input, on an element of the host's own: a field
11
+ * to ask an assistant with, the way a chat's message box works — Enter
12
+ * sends, Shift-Enter breaks the line — that also takes **lists**, so a list
13
+ * of points reaches the assistant as points: `- ` or `1. ` at a line's
14
+ * start begins one, Enter goes on to the next item, Enter on an empty item
15
+ * leaves the list, Tab and Shift-Tab nest and lift. Ctrl-Enter sends from
16
+ * anywhere, a list included. Nothing else: no headings, no bold — an
17
+ * assistant reads words. No look of its own: the host's element, the
18
+ * host's styles (`ChatInput` is the styled one).
19
+ *
20
+ * <div emailChatInput placeholder="Tell the assistant what to change…"
21
+ * [(value)]="instructions" (sent)="ask($event)" (escaped)="close()"></div>
22
+ *
23
+ * `value` is the text as it is sent — a line per block, list items as
24
+ * dash lines (`chatInputText`) — two-way: set it and the field shows it
25
+ * (a dash line becomes an item again). `sent` carries the same text;
26
+ * `clear()` empties the field, `focus()` puts the caret in it. A
27
+ * ProseMirror editor is mounted inside the element (the library's own,
28
+ * `.aee-editor`), so a host's editor styles reach it.
29
+ *
30
+ * **A signal-forms control, optionally.** It is a `FormValueControl` for
31
+ * `string`: bind a field with `[formField]="chat.message"` and the
32
+ * directive drives `value` both ways, pushes `disabled` and `readonly`
33
+ * in, hears `touch` when focus leaves the field, and `reset()` empties
34
+ * it. The form holds the *message*; the ask is still `sent` — an action
35
+ * with a stream behind it is the host's, not the form's submit.
36
+ */
37
+ declare class ChatInputField implements FormValueControl<string> {
38
+ #private;
39
+ /** What the field is for, while it is empty. */
40
+ readonly placeholder: _angular_core.InputSignal<string>;
41
+ /** The field's name for assistive technology. */
42
+ readonly label: _angular_core.InputSignal<string>;
43
+ /** Whether typing is refused. */
44
+ readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
45
+ /** Whether the text is shown but not edited. */
46
+ readonly readonly: _angular_core.InputSignalWithTransform<boolean, unknown>;
47
+ /** The text, as it is sent: lines, and `- ` dash lines for list items. */
48
+ readonly value: _angular_core.ModelSignal<string>;
49
+ /** Enter (outside a list) or Ctrl-Enter: the text, as `value` has it.
50
+ Nothing is sent empty. The field keeps its text — the host clears it
51
+ (`clear()`) once the ask is on its way. */
52
+ readonly sent: _angular_core.OutputEmitterRef<string>;
53
+ /** Escape, in the field. */
54
+ readonly escaped: _angular_core.OutputEmitterRef<void>;
55
+ /** Focus left the field — a form marks it touched on this. */
56
+ readonly touch: _angular_core.OutputEmitterRef<void>;
57
+ /** The editor inside, once mounted — for a host that wants more of it
58
+ than the text: its selection, a command, its `view`. */
59
+ readonly editor: _angular_core.Signal<Editor | null>;
60
+ constructor();
61
+ /** Empties the field. */
62
+ clear(): void;
63
+ /** A form's reset: the field, empty. */
64
+ reset(): void;
65
+ focus(): void;
66
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ChatInputField, never>;
67
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<ChatInputField, "[emailChatInput]", ["emailChatInput"], { "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "sent": "sent"; "escaped": "escaped"; "touch": "touch"; }, never, never, true, never>;
68
+ }
69
+ /**
70
+ * The chat input, styled: `ChatInputField`'s behaviour in a box of its
71
+ * own — outlined on focus, growing and shrinking with its text in a short
72
+ * transition, scrolling past a height, disabled dimmed — the look
73
+ * tokenized (`--email-chat-input-*`, Material's system tokens beneath).
74
+ * The same inputs and outputs, forwarded; the same signal-forms contract.
75
+ *
76
+ * <div email-chat-input placeholder="Tell the assistant what to change…"
77
+ * [(value)]="instructions" (sent)="ask($event)" (escaped)="close()"></div>
78
+ *
79
+ * `field` is the directive underneath; `editor`, `clear()` and `focus()`
80
+ * are its, at hand.
81
+ *
82
+ * **The growing** (chat-input.animation.ts): the box follows the text's
83
+ * measured height through a `height` transition — `--email-chat-input-motion`
84
+ * is its duration and easing (`150ms ease`); reduced motion turns it off. The
85
+ * text grows to `--email-chat-input-max-height` (`5lh`, five lines) and
86
+ * then scrolls inside the box.
87
+ */
88
+ declare class ChatInput {
89
+ /** The behaviour underneath: the field itself. */
90
+ readonly field: ChatInputField;
91
+ /** The editor inside, once mounted. */
92
+ readonly editor: _angular_core.Signal<Editor | null>;
93
+ constructor();
94
+ /** Empties the field. */
95
+ clear(): void;
96
+ focus(): void;
97
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ChatInput, never>;
98
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<ChatInput, "div[email-chat-input]", never, {}, {}, never, never, true, [{ directive: typeof ChatInputField; inputs: { "placeholder": "placeholder"; "label": "label"; "disabled": "disabled"; "readonly": "readonly"; "value": "value"; }; outputs: { "valueChange": "valueChange"; "sent": "sent"; "escaped": "escaped"; "touch": "touch"; }; }]>;
99
+ }
100
+
101
+ export { ChatInput, ChatInputField, chatInputText };
@@ -0,0 +1,40 @@
1
+ import * as _angular_core from '@angular/core';
2
+
3
+ /** Where a press may still take focus by default: real text entry. A field
4
+ inside a toolbar has to be typed in; everything else there is a tool. */
5
+ declare const KEEP_FOCUS_EXCEPT = "input, textarea, select, [contenteditable]:not([contenteditable=\"false\"])";
6
+ /**
7
+ * A press on this element never takes focus from wherever it is — the
8
+ * editor keeps its caret and its selection while a toolbar button, a menu
9
+ * row or a swatch is clicked, and whatever depends on the editor's focus (a
10
+ * bubble menu, a suggestion list) stays open through the click.
11
+ *
12
+ * Behaviour only: it renders nothing and sets no attribute, so it goes on a
13
+ * host's own elements — a Material button, an Aria toolbar, a CDK overlay's
14
+ * panel — or into a host's own component through `hostDirectives`:
15
+ *
16
+ * <div role="toolbar" emailKeepFocus> … <button mat-icon-button>…</button> … </div>
17
+ *
18
+ * @Component({ …, hostDirectives: [KeepFocus] })
19
+ *
20
+ * One on a container covers everything in it: the press bubbles. Only the
21
+ * pointer is kept out — Tab and the arrow keys still reach the tools, which
22
+ * is what makes them usable from the keyboard — and only the press: the
23
+ * click that follows runs as ever.
24
+ *
25
+ * It needs no editor: what keeps focus is whichever element has it, so the
26
+ * same directive serves a composer's address rows.
27
+ */
28
+ declare class KeepFocus {
29
+ #private;
30
+ /** Off with `[emailKeepFocus]="false"`; on by its presence. */
31
+ readonly enabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
32
+ /** What inside this element may still take focus on a press — a selector.
33
+ Text entry by default ({@link KEEP_FOCUS_EXCEPT}); `''` for nothing. */
34
+ readonly except: _angular_core.InputSignal<string>;
35
+ protected onMousedown(event: MouseEvent): void;
36
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<KeepFocus, never>;
37
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<KeepFocus, "[emailKeepFocus]", never, { "enabled": { "alias": "emailKeepFocus"; "required": false; "isSignal": true; }; "except": { "alias": "emailKeepFocusExcept"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
38
+ }
39
+
40
+ export { KEEP_FOCUS_EXCEPT, KeepFocus };
@@ -0,0 +1,66 @@
1
+ import * as _angular_core from '@angular/core';
2
+ import { Editor } from '@h-k-dev/angular-email-editor';
3
+
4
+ /**
5
+ * Makes the host's own text field the alt text of the editor's selected
6
+ * image — the image a click selected, or a drag over it alone
7
+ * (`selectedImage`). Everything but the field and its words is done:
8
+ *
9
+ * <input dir="auto" [emailImageAlt]="editor" #alt="emailImageAlt"
10
+ * (emailImageAltClosed)="open.set(false)" />
11
+ * <button (click)="alt.apply()">Apply</button>
12
+ *
13
+ * - the field shows the image's alt, and again whenever another image is
14
+ * selected; it is focused and selected as it appears, so typing replaces
15
+ * it (`[emailImageAltAutoSelect]="false"` for a field that is always
16
+ * there, a side panel's);
17
+ * - Enter applies — trimmed, and an emptied field takes the alt off — and
18
+ * Escape puts the alt back as it was; both hand the caret back to the
19
+ * editor (`[emailImageAltFocus]="false"` keeps it) and say so through
20
+ * `emailImageAltClosed`: `true` applied, `false` not — the host's cue to
21
+ * close its popover. Neither fires mid-composition: an IME's own Enter
22
+ * and Escape stay the IME's;
23
+ * - `apply()` and `cancel()` are there for the host's buttons, `value()`
24
+ * and `active()` for its bindings (an Apply that waits for a change).
25
+ *
26
+ * The field's words and direction are the host's: its label, its
27
+ * placeholder, and `dir="auto"` so an alt in a right-to-left script reads
28
+ * the right way round. The image stays selected throughout — the editor
29
+ * keeps its selection while the field has focus — so the popover can sit
30
+ * where the host likes.
31
+ */
32
+ declare class ImageAlt {
33
+ #private;
34
+ /** The editor whose selected image this field describes. */
35
+ readonly editor: _angular_core.InputSignal<Editor | undefined>;
36
+ /** Whether the field takes focus, its text selected, as it appears. */
37
+ readonly autoSelect: _angular_core.InputSignalWithTransform<boolean, unknown>;
38
+ /** Whether the caret returns to the editor after an apply or a cancel. */
39
+ readonly returnFocus: _angular_core.InputSignalWithTransform<boolean, unknown>;
40
+ /** Done with the field: `true` when the alt was applied, `false` when
41
+ not — cancelled, or no image to apply it to. */
42
+ readonly closed: _angular_core.OutputEmitterRef<boolean>;
43
+ /** Whether an image is selected at all — there is nothing to describe
44
+ otherwise, and `apply()` does nothing. */
45
+ readonly active: _angular_core.Signal<boolean>;
46
+ /**
47
+ * What the field says: the selected image's alt, until typing changes it
48
+ * — the draft. It starts over from the image whenever another one is
49
+ * selected or its alt changes elsewhere; typing changes no editor state,
50
+ * so it is never written over. Deliberately no write-back setter: the
51
+ * draft reaches the document once, on `apply()`, as one undo step — not
52
+ * a transaction per keystroke.
53
+ */
54
+ readonly value: _angular_core.WritableSignal<string>;
55
+ constructor();
56
+ /** Writes the draft to the selected image's alt. False when no image is
57
+ selected. */
58
+ apply(): boolean;
59
+ /** Leaves the alt as it was, and the field with it. */
60
+ cancel(): void;
61
+ protected onKey(event: Event, apply: boolean): void;
62
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ImageAlt, never>;
63
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<ImageAlt, "input[emailImageAlt], textarea[emailImageAlt]", ["emailImageAlt"], { "editor": { "alias": "emailImageAlt"; "required": true; "isSignal": true; }; "autoSelect": { "alias": "emailImageAltAutoSelect"; "required": false; "isSignal": true; }; "returnFocus": { "alias": "emailImageAltFocus"; "required": false; "isSignal": true; }; }, { "closed": "emailImageAltClosed"; }, never, never, true, never>;
64
+ }
65
+
66
+ export { ImageAlt };
@@ -0,0 +1,65 @@
1
+ import { MatFormFieldControl } from '@angular/material/form-field';
2
+ import { Subject } from 'rxjs';
3
+ import * as i0 from '@angular/core';
4
+
5
+ /**
6
+ * Makes an address input the control of an Angular Material form field —
7
+ * opt in per field, with the attribute:
8
+ *
9
+ * ```html
10
+ * <mat-form-field>
11
+ * <mat-label>To</mat-label>
12
+ * <div email-address-input emailMatFormField [formField]="envelope.to"></div>
13
+ * <mat-hint>Separate addresses with a comma</mat-hint>
14
+ * <mat-error>{{ envelope.to().errors()[0]?.message }}</mat-error>
15
+ * </mat-form-field>
16
+ * ```
17
+ *
18
+ * The directive is its own object, provided as the field's
19
+ * `MatFormFieldControl` — so the input's `value`, `disabled` and `required`
20
+ * stay the signal-forms contract they are, and nothing of Material reaches
21
+ * the input itself. What the field asks, it reads off the input:
22
+ *
23
+ * - **Label** — `for` the input's text field (`id`); it floats while the
24
+ * field is focused or not `empty`: no chips *and* nothing typed.
25
+ * - **Errors** — `errorState` is invalid *and* touched: `mat-error` shows at
26
+ * the moment the input starts flagging itself, never before.
27
+ * - **Hints and errors are read out** — their ids join the text field's
28
+ * `aria-describedby`, beside any the host set itself.
29
+ * - **A click on the field** — anywhere in its box — puts the caret in.
30
+ *
31
+ * No `NgControl`: signal forms bind through `[formField]`, which the field
32
+ * does not need to know.
33
+ *
34
+ * The field's value is the input's — one header-form entry per recipient.
35
+ * A backend that keeps the header as one string gets it with
36
+ * `joinAddresses(list)` and hands it back through `splitAddresses(text)`
37
+ * (`@h-k-dev/angular-email-editor/address`); the pair round-trips entry for entry.
38
+ */
39
+ declare class AddressFormField implements MatFormFieldControl<string[]> {
40
+ #private;
41
+ /** Tells the field to look again — whenever anything it reads moves. */
42
+ readonly stateChanges: Subject<void>;
43
+ readonly controlType = "email-address-input";
44
+ readonly ngControl: null;
45
+ constructor();
46
+ get value(): string[];
47
+ set value(value: string[] | null);
48
+ get id(): string;
49
+ get placeholder(): string;
50
+ get focused(): boolean;
51
+ get empty(): boolean;
52
+ get shouldLabelFloat(): boolean;
53
+ get required(): boolean;
54
+ get disabled(): boolean;
55
+ get errorState(): boolean;
56
+ /** The host's own `aria-describedby`, which the field keeps in its list. */
57
+ get userAriaDescribedBy(): string | undefined;
58
+ describedByIds: string[];
59
+ setDescribedByIds(ids: string[]): void;
60
+ onContainerClick(): void;
61
+ static ɵfac: i0.ɵɵFactoryDeclaration<AddressFormField, never>;
62
+ static ɵdir: i0.ɵɵDirectiveDeclaration<AddressFormField, "[email-address-input][emailMatFormField]", never, {}, {}, never, never, true, never>;
63
+ }
64
+
65
+ export { AddressFormField };