@h-k-dev/angular-email-editor 0.0.0-stage → 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 -2
  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 +133 -3
  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,285 @@
1
+ import { NgTemplateOutlet } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { inject, TemplateRef, Directive, input, booleanAttribute, output, numberAttribute, signal, DestroyRef, contentChild, computed, Component } from '@angular/core';
4
+ import { ADDRESS_RULES } from '@h-k-dev/angular-email-editor/address';
5
+
6
+ /**
7
+ * The chip's remove slot: mark an `ng-template` with it (or use the `*`
8
+ * shorthand on the element itself) and the chip renders it in place of the
9
+ * default button, in its `trailing` box. Not rendered at all when the chip
10
+ * is not `removable`.
11
+ */
12
+ class AddressChipRemove {
13
+ template = inject(TemplateRef);
14
+ static ngTemplateContextGuard(_directive, context) {
15
+ return true;
16
+ }
17
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: AddressChipRemove, deps: [], target: i0.ɵɵFactoryTarget.Directive });
18
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.6", type: AddressChipRemove, isStandalone: true, selector: "ng-template[emailAddressChipRemove]", ngImport: i0 });
19
+ }
20
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: AddressChipRemove, decorators: [{
21
+ type: Directive,
22
+ args: [{ selector: 'ng-template[emailAddressChipRemove]' }]
23
+ }] });
24
+
25
+ /** The copy command, on a throwaway selection: the fallback for a clipboard
26
+ API that refuses. True when the browser reports the copy done. */
27
+ function copyWithCommand(text) {
28
+ const scratch = document.createElement('textarea');
29
+ scratch.value = text;
30
+ scratch.setAttribute('readonly', '');
31
+ scratch.style.position = 'fixed';
32
+ scratch.style.top = '-1000px';
33
+ document.body.append(scratch);
34
+ scratch.select();
35
+ let done = false;
36
+ try {
37
+ done = document.execCommand('copy');
38
+ }
39
+ catch {
40
+ done = false;
41
+ }
42
+ scratch.remove();
43
+ return done;
44
+ }
45
+ /**
46
+ * One address as a chip, the way Gmail draws it: the display name when the
47
+ * mailbox has one, the address otherwise, and the whole header form in the
48
+ * title. An address that is not one is flagged, not refused — the user
49
+ * sees the typo, the host decides what to do about it.
50
+ *
51
+ * **Parts are `data-slot`s** — `name`, `note`, `trailing`, `remove` — and
52
+ * that is how it is styled from outside: theme it through the `--email-*` tokens, and reach
53
+ * a part for a structural override with two attributes
54
+ * (`[email-address-chip] [data-slot='name'][data-slot]`).
55
+ *
56
+ * **State on the host.** `data-invalid` marks a malformed address (a list
57
+ * item may not carry `aria-invalid`; the flag is spoken through the `note`
58
+ * slot instead). `data-pending` marks a placeholder a template fills in
59
+ * later (`{{contact.email}}`) — no mistake, so never flagged as one; the
60
+ * note says what it is. `aria-disabled` takes the remove button out of play, and
61
+ * `aria-current` marks the chip a list's keys act on; each is an input under
62
+ * its ARIA name, reflected back, and a styling hook too.
63
+ *
64
+ * **The remove control is a template slot.** The chip draws its own ×
65
+ * button by default; an `ng-template[emailAddressChipRemove]` in the chip's
66
+ * content — or handed down as `removeTemplate` by the address input —
67
+ * replaces it, rendered in the `trailing` box with the address, the
68
+ * accessible name, the disabled state and a `remove()` to call.
69
+ *
70
+ * **Hold to copy.** A press held on the chip for the length of a phone's
71
+ * touch-and-hold — 500ms, `holdDelay` — copies the address, in header form,
72
+ * to the clipboard, mouse or touch alike; a release or a drag before that
73
+ * does not, and the browser's own context menu stays away. The chip shows
74
+ * and says "Copied" for a moment (`data-copied`, a live `status` slot) and
75
+ * emits `copied`.
76
+ *
77
+ * **It asks, it does not do.** The chip holds no list: `removed` is a
78
+ * request the host answers by dropping the address. The address input
79
+ * renders these for its value; a host can render them on its own for a
80
+ * read-only header (a sent copy, a preview).
81
+ *
82
+ * Meant for an `<li>`. No Material, no icon font, no headless library.
83
+ */
84
+ class AddressChip {
85
+ /** The mailbox in header form: `Ada <ada@example.com>` or `ada@example.com`. */
86
+ address = input.required(/* @ts-ignore */
87
+ ...(ngDevMode ? [{ debugName: "address" }] : /* istanbul ignore next */ []));
88
+ /** Set false for a read-only list: the chip stays, the remove button goes. */
89
+ removable = input(true, { ...(ngDevMode ? { debugName: "removable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
90
+ /** Draw the address as text in a line, not as a chip: no surface, no
91
+ room after the address, and the remove control out of the flow and out
92
+ of sight — still focusable, so the keyboard reaches it and a list can
93
+ turn back into chips when it does. The address input sets it while it
94
+ is not focused. Reflected as `data-inline`. */
95
+ inline = input(false, { ...(ngDevMode ? { debugName: "inline" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
96
+ /** The list's highlight is on this chip: the one the arrow keys reached,
97
+ or a click picked, while the caret stays in the list's input. An input
98
+ under its ARIA name, like `aria-disabled`, reflected back as
99
+ `aria-current="true"` — the list names the same chip in its input's
100
+ `aria-activedescendant` — and drawn as a focus ring. */
101
+ ariaCurrent = input(false, { ...(ngDevMode ? { debugName: "ariaCurrent" } : /* istanbul ignore next */ {}), transform: booleanAttribute, alias: 'aria-current' });
102
+ /** Whether the remove button is in the tab order. A list that reaches
103
+ its chips with the arrow keys sets false: Tab goes straight to its
104
+ input, and the button stays clickable and focusable by script. */
105
+ removeTabbable = input(true, { ...(ngDevMode ? { debugName: "removeTabbable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
106
+ /** Prefix for the remove button's accessible name — "Remove ada@example.com". */
107
+ removeLabel = input('Remove', /* @ts-ignore */
108
+ ...(ngDevMode ? [{ debugName: "removeLabel" }] : /* istanbul ignore next */ []));
109
+ /** What the flag on a malformed address says, for the title and for
110
+ assistive tech. */
111
+ invalidLabel = input('not an email address', /* @ts-ignore */
112
+ ...(ngDevMode ? [{ debugName: "invalidLabel" }] : /* istanbul ignore next */ []));
113
+ /** What the note on a placeholder says, for the title and for assistive
114
+ tech. */
115
+ pendingLabel = input('filled in when sent', /* @ts-ignore */
116
+ ...(ngDevMode ? [{ debugName: "pendingLabel" }] : /* istanbul ignore next */ []));
117
+ /** The remove button is out of play — e.g. while the message sends. */
118
+ ariaDisabled = input(false, { ...(ngDevMode ? { debugName: "ariaDisabled" } : /* istanbul ignore next */ {}), alias: 'aria-disabled', transform: booleanAttribute });
119
+ /** The remove button was pressed. The host drops the address; the chip
120
+ goes when the host's list no longer has it. */
121
+ removed = output();
122
+ /** How long a press must be held to copy the address, in milliseconds:
123
+ the length of a phone's touch-and-hold, 500ms, so the gesture is the
124
+ one a thumb already knows. 0 turns the hold off. */
125
+ holdDelay = input(500, { ...(ngDevMode ? { debugName: "holdDelay" } : /* istanbul ignore next */ {}), transform: numberAttribute });
126
+ /** What the chip says, for a moment, once a hold has copied the address
127
+ — "Copied", followed by the address. */
128
+ copiedLabel = input('Copied', /* @ts-ignore */
129
+ ...(ngDevMode ? [{ debugName: "copiedLabel" }] : /* istanbul ignore next */ []));
130
+ /** A hold copied the address to the clipboard, in header form. */
131
+ copied = output();
132
+ /** The address was just copied: shown for a moment, and said. Reflected
133
+ as `data-copied`. */
134
+ justCopied = signal(false, /* @ts-ignore */
135
+ ...(ngDevMode ? [{ debugName: "justCopied" }] : /* istanbul ignore next */ []));
136
+ #hold = null;
137
+ #copiedTimer;
138
+ constructor() {
139
+ inject(DestroyRef).onDestroy(() => {
140
+ this.cancelHold();
141
+ clearTimeout(this.#copiedTimer);
142
+ });
143
+ }
144
+ /** A press begins a hold — a primary pointer, mouse or touch alike, on
145
+ the chip itself and not its remove control. */
146
+ onPointerdown(event) {
147
+ if (event.button !== 0 || event.isPrimary === false || !this.holdDelay())
148
+ return;
149
+ if (event.target?.closest('[data-slot=trailing]'))
150
+ return;
151
+ this.cancelHold();
152
+ this.#hold = {
153
+ id: event.pointerId,
154
+ x: event.clientX,
155
+ y: event.clientY,
156
+ timer: setTimeout(() => void this.copy(), this.holdDelay()),
157
+ };
158
+ }
159
+ /** A hold that moves is a drag or a scroll, not a hold. */
160
+ onPointermove(event) {
161
+ const hold = this.#hold;
162
+ if (!hold || (event.pointerId ?? hold.id) !== hold.id)
163
+ return;
164
+ if (Math.hypot(event.clientX - hold.x, event.clientY - hold.y) > 10)
165
+ this.cancelHold();
166
+ }
167
+ /** Released, cancelled or gone before the time: no copy. */
168
+ cancelHold() {
169
+ if (!this.#hold)
170
+ return;
171
+ clearTimeout(this.#hold.timer);
172
+ this.#hold = null;
173
+ }
174
+ /** The hold is the chip's, not the browser's: no context menu on it, on
175
+ a phone or under a right button. */
176
+ onContextmenu(event) {
177
+ if (this.#hold || this.justCopied())
178
+ event.preventDefault();
179
+ }
180
+ /** Writes the address to the clipboard: the async API first, and when
181
+ it refuses — an unfocused document, a context without it — the old
182
+ copy command on a selection made for the purpose. */
183
+ /** Copies the address, in header form, to the clipboard — what a hold
184
+ does, and what a host's own control may ask for — and shows and says
185
+ so. The async API first; when it refuses (an unfocused document, a
186
+ context without it), the old copy command on a selection made for the
187
+ purpose. Resolves to whether the copy went. */
188
+ async copy() {
189
+ this.#hold = null;
190
+ const text = this.address();
191
+ if (!document.hasFocus())
192
+ window.focus();
193
+ const modern = navigator.clipboard
194
+ ? navigator.clipboard.writeText(text).then(() => true)
195
+ : Promise.resolve(false);
196
+ const done = (await modern.catch(() => false)) || copyWithCommand(text);
197
+ if (!done)
198
+ return false;
199
+ this.justCopied.set(true);
200
+ this.copied.emit(text);
201
+ clearTimeout(this.#copiedTimer);
202
+ this.#copiedTimer = setTimeout(() => this.justCopied.set(false), 1500);
203
+ return true;
204
+ }
205
+ /** A remove template handed down by a list that renders the chip — the
206
+ address input passes its own `emailAddressChipRemove` slot here. One
207
+ in the chip's own content wins. */
208
+ removeTemplate = input(null, /* @ts-ignore */
209
+ ...(ngDevMode ? [{ debugName: "removeTemplate" }] : /* istanbul ignore next */ []));
210
+ removeSlot = contentChild(AddressChipRemove, /* @ts-ignore */
211
+ ...(ngDevMode ? [{ debugName: "removeSlot" }] : /* istanbul ignore next */ []));
212
+ /** The template the trailing box renders, or null for the default button. */
213
+ removeOutlet = computed(() => this.removeSlot()?.template ?? this.removeTemplate(), /* @ts-ignore */
214
+ ...(ngDevMode ? [{ debugName: "removeOutlet" }] : /* istanbul ignore next */ []));
215
+ /** The address rule in force — ours, or the host's. */
216
+ #rules = inject(ADDRESS_RULES);
217
+ mailbox = computed(() => this.#rules.parse(this.address()), /* @ts-ignore */
218
+ ...(ngDevMode ? [{ debugName: "mailbox" }] : /* istanbul ignore next */ []));
219
+ /** Whether the address part is well-formed. Public so a list can count. */
220
+ valid = computed(() => this.#rules.isValid(this.mailbox().address), /* @ts-ignore */
221
+ ...(ngDevMode ? [{ debugName: "valid" }] : /* istanbul ignore next */ []));
222
+ /** Whether the address is a placeholder a template fills in later —
223
+ neither valid nor invalid yet. */
224
+ pending = computed(() => this.#rules.isPlaceholder(this.mailbox().address), /* @ts-ignore */
225
+ ...(ngDevMode ? [{ debugName: "pending" }] : /* istanbul ignore next */ []));
226
+ /** The remove control's accessible name — "Remove ada@example.com". */
227
+ removeName = computed(() => `${this.removeLabel()} ${this.address()}`, /* @ts-ignore */
228
+ ...(ngDevMode ? [{ debugName: "removeName" }] : /* istanbul ignore next */ []));
229
+ removeContext = computed(() => ({
230
+ $implicit: this.address(),
231
+ mailbox: this.mailbox(),
232
+ valid: this.valid(),
233
+ pending: this.pending(),
234
+ disabled: this.ariaDisabled(),
235
+ tabbable: this.removeTabbable(),
236
+ label: this.removeName(),
237
+ remove: this.remove,
238
+ }), /* @ts-ignore */
239
+ ...(ngDevMode ? [{ debugName: "removeContext" }] : /* istanbul ignore next */ []));
240
+ /** Asks to be removed, unless the control is out of play. One function
241
+ for the chip's life, so a template holding it never sees it change. */
242
+ remove = () => {
243
+ if (!this.ariaDisabled())
244
+ this.removed.emit();
245
+ };
246
+ /** The note the chip carries, if any — said, and shown in the title. */
247
+ note = computed(() => this.valid() ? null : this.pending() ? this.pendingLabel() : this.invalidLabel(), /* @ts-ignore */
248
+ ...(ngDevMode ? [{ debugName: "note" }] : /* istanbul ignore next */ []));
249
+ title = computed(() => {
250
+ const note = this.note();
251
+ return note ? `${this.address()} — ${note}` : this.address();
252
+ }, /* @ts-ignore */
253
+ ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
254
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: AddressChip, deps: [], target: i0.ɵɵFactoryTarget.Component });
255
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: AddressChip, isStandalone: true, selector: "[email-address-chip]", inputs: { address: { classPropertyName: "address", publicName: "address", isSignal: true, isRequired: true, transformFunction: null }, removable: { classPropertyName: "removable", publicName: "removable", isSignal: true, isRequired: false, transformFunction: null }, inline: { classPropertyName: "inline", publicName: "inline", isSignal: true, isRequired: false, transformFunction: null }, ariaCurrent: { classPropertyName: "ariaCurrent", publicName: "aria-current", isSignal: true, isRequired: false, transformFunction: null }, removeTabbable: { classPropertyName: "removeTabbable", publicName: "removeTabbable", isSignal: true, isRequired: false, transformFunction: null }, removeLabel: { classPropertyName: "removeLabel", publicName: "removeLabel", isSignal: true, isRequired: false, transformFunction: null }, invalidLabel: { classPropertyName: "invalidLabel", publicName: "invalidLabel", isSignal: true, isRequired: false, transformFunction: null }, pendingLabel: { classPropertyName: "pendingLabel", publicName: "pendingLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaDisabled: { classPropertyName: "ariaDisabled", publicName: "aria-disabled", isSignal: true, isRequired: false, transformFunction: null }, holdDelay: { classPropertyName: "holdDelay", publicName: "holdDelay", isSignal: true, isRequired: false, transformFunction: null }, copiedLabel: { classPropertyName: "copiedLabel", publicName: "copiedLabel", isSignal: true, isRequired: false, transformFunction: null }, removeTemplate: { classPropertyName: "removeTemplate", publicName: "removeTemplate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { removed: "removed", copied: "copied" }, host: { listeners: { "pointerdown": "onPointerdown($event)", "pointermove": "onPointermove($event)", "pointerup": "cancelHold()", "pointercancel": "cancelHold()", "pointerleave": "cancelHold()", "contextmenu": "onContextmenu($event)" }, properties: { "attr.data-invalid": "valid() || pending() ? null : true", "attr.data-pending": "pending() || null", "attr.aria-disabled": "ariaDisabled() || null", "attr.data-inline": "inline() || null", "attr.aria-current": "ariaCurrent() || null", "attr.data-copied": "justCopied() || null", "title": "title()" } }, queries: [{ propertyName: "removeSlot", first: true, predicate: AddressChipRemove, descendants: true, isSignal: true }], ngImport: i0, template: "<!-- The name when there is one, the address otherwise; the header form is\n the host's title, which is where a lone name recovers its address. -->\n<span data-slot=\"name\">{{ mailbox().name ?? mailbox().address }}</span>\n\n@if (note(); as note) {\n <!-- Spoken, not shown: the state is drawn by data-invalid or data-pending\n on the host, and a list item may not say aria-invalid \u2014 so the note\n says it. -->\n <span data-slot=\"note\">({{ note }})</span>\n}\n\n<!-- Spoken, not shown: a hold copied the address. The region is always\n there, so the text arriving in it is announced. -->\n<span data-slot=\"status\" role=\"status\">{{\n justCopied() ? copiedLabel() + ' ' + address() : ''\n}}</span>\n\n@if (removable()) {\n <!-- The box at the chip's end: the host's remove template when it gave\n one, the chip's own button otherwise. -->\n <span data-slot=\"trailing\">\n @if (removeOutlet(); as template) {\n <ng-container *ngTemplateOutlet=\"template; context: removeContext()\" />\n } @else {\n <button\n type=\"button\"\n data-slot=\"remove\"\n [disabled]=\"ariaDisabled()\"\n [attr.tabindex]=\"removeTabbable() ? null : -1\"\n [attr.aria-label]=\"removeName()\"\n (click)=\"remove()\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M4.5 4.5l7 7 M11.5 4.5l-7 7\" />\n </svg>\n </button>\n }\n </span>\n}\n", styles: [":host{position:relative;isolation:isolate;display:inline-flex;align-items:center;gap:var(--email-address-chip-gap, 4px);max-width:var(--email-address-chip-max-width, 100%);min-width:0;height:var(--email-address-chip-height, 24px);margin:0;padding-block:0;padding-inline:var(--email-address-chip-padding-start, 8px) var(--email-address-chip-padding-end, 2px);box-sizing:border-box;border-radius:var(--email-address-chip-corner, var(--mat-sys-corner-full, 12px));color:var(--email-address-chip-color, var(--mat-sys-on-surface, light-dark(#1f1f1f, #e3e3e3)));font-family:var(--email-address-chip-font-family, var(--mat-sys-body-medium-font, system-ui, sans-serif));font-size:var(--email-address-chip-font-size, var(--mat-sys-body-medium-size, .875rem));font-weight:var(--email-address-chip-font-weight, var(--mat-sys-body-medium-weight, 400));line-height:var(--email-address-chip-line-height, var(--mat-sys-body-medium-line-height, 1.25rem));letter-spacing:var(--email-address-chip-letter-spacing, var(--mat-sys-body-medium-tracking, .016rem));cursor:default;-webkit-user-select:none;user-select:none;-webkit-touch-callout:none;touch-action:manipulation;transition:opacity .15s ease}:host(:not(:has([data-slot=trailing]))){padding-inline-end:var(--email-address-chip-padding-start, 8px)}:host:before{content:\"\";position:absolute;z-index:-1;inset:0;box-sizing:border-box;pointer-events:none;background-color:var(--email-address-chip-background, var(--mat-sys-surface-container, light-dark(#f1f3f4, #303134)));border:1px solid var(--email-address-chip-border-color, transparent);border-radius:var(--email-address-chip-corner, var(--mat-sys-corner-full, 12px))}:host([data-invalid]){color:var(--email-address-chip-invalid-color, var(--mat-sys-error, light-dark(#b3261e, #f2b8b5)))}:host([data-invalid]):before{background-color:var(--email-address-chip-invalid-background, var(--email-address-chip-background, var(--mat-sys-surface-container, light-dark(#f1f3f4, #303134))));border-color:var(--email-address-chip-invalid-border-color, var(--email-address-chip-border-color, transparent))}:host([data-pending]):before{background-color:var(--email-address-chip-pending-background, var(--email-merge-tag-background, color-mix(in srgb, var(--mat-sys-primary, #4c6ef5) 12%, transparent)))}:host([data-pending][data-inline]):before{content:\"\";inset:2px -3px;border-radius:var(--email-address-chip-pending-corner, 4px)}:host([aria-disabled=true]){opacity:.6}:host([aria-current=true]){outline:2px solid var(--email-address-chip-focus-ring, var(--mat-sys-primary, light-dark(#0b57d0, #a8c7fa)));outline-offset:1px}:host([aria-current=true]):before{background-color:var(--email-address-chip-active-background, var(--mat-sys-surface-container-highest, light-dark(#e3e3e3, #444746)))}:host([data-inline]){gap:0;padding-inline:0}:host([data-inline]):before{content:none}:host([data-inline]) :where([data-slot=trailing]){position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}:host([data-inline]):after{flex:none;line-height:var(--email-address-chip-height, 24px);white-space:nowrap}:host :where([data-slot=name]){min-width:0;line-height:var(--email-address-chip-height, 24px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host([data-copied]){color:var(--email-address-chip-copied-color, var(--mat-sys-on-primary-container, light-dark(#041e49, #d3e3fd)))}:host([data-copied][data-inline]){color:var(--email-address-chip-copied-color, var(--mat-sys-primary, light-dark(#0b57d0, #a8c7fa)))}:host([data-copied]):before{background-color:var(--email-address-chip-copied-background, var(--mat-sys-primary-container, light-dark(#d3e3fd, #0842a0)))}:host :where([data-slot=note],[data-slot=status]){position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}:host :where([data-slot=trailing]){flex:none;display:flex;align-items:center;justify-content:center}:host :where([data-slot=remove]){position:relative;flex:none;display:flex;align-items:center;justify-content:center;width:var(--email-address-chip-remove-size, 18px);height:var(--email-address-chip-remove-size, 18px);margin:0;padding:0;border:none;border-radius:var(--email-address-chip-remove-corner, var(--mat-sys-corner-full, 50%));cursor:pointer;background-color:var(--email-address-chip-remove-background, transparent);color:var(--email-address-chip-remove-color, var(--mat-sys-on-surface-variant, light-dark(#444746, #c4c7c5)));transition:background-color .15s ease,color .15s ease}:host :where([data-slot=remove]):before{content:\"\";position:absolute;top:50%;left:50%;width:var(--email-address-chip-remove-target-size, 32px);height:var(--email-address-chip-remove-target-size, 32px);transform:translate(-50%,-50%)}:host :where([data-slot=remove] svg){width:12px;height:12px;fill:none;stroke:currentColor;stroke-width:1.75;stroke-linecap:round}:host :where([data-slot=remove]:hover:not(:disabled),[data-slot=remove]:focus-visible){background-color:var(--email-address-chip-remove-hover-background, var(--mat-sys-surface-container-highest, light-dark(#e3e3e3, #444746)));color:var(--email-address-chip-remove-hover-color, var(--mat-sys-on-surface, light-dark(#1f1f1f, #e3e3e3)))}:host :where([data-slot=remove]:focus-visible){outline:2px solid var(--email-address-chip-focus-ring, var(--mat-sys-primary, light-dark(#0b57d0, #a8c7fa)));outline-offset:1px}:host :where([data-slot=remove]:disabled){cursor:default}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
256
+ }
257
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: AddressChip, decorators: [{
258
+ type: Component,
259
+ args: [{ selector: '[email-address-chip]', imports: [NgTemplateOutlet], host: {
260
+ '[attr.data-invalid]': 'valid() || pending() ? null : true',
261
+ '[attr.data-pending]': 'pending() || null',
262
+ '[attr.aria-disabled]': 'ariaDisabled() || null',
263
+ '[attr.data-inline]': 'inline() || null',
264
+ '[attr.aria-current]': 'ariaCurrent() || null',
265
+ '[attr.data-copied]': 'justCopied() || null',
266
+ '[title]': 'title()',
267
+ '(pointerdown)': 'onPointerdown($event)',
268
+ '(pointermove)': 'onPointermove($event)',
269
+ '(pointerup)': 'cancelHold()',
270
+ '(pointercancel)': 'cancelHold()',
271
+ '(pointerleave)': 'cancelHold()',
272
+ '(contextmenu)': 'onContextmenu($event)',
273
+ }, template: "<!-- The name when there is one, the address otherwise; the header form is\n the host's title, which is where a lone name recovers its address. -->\n<span data-slot=\"name\">{{ mailbox().name ?? mailbox().address }}</span>\n\n@if (note(); as note) {\n <!-- Spoken, not shown: the state is drawn by data-invalid or data-pending\n on the host, and a list item may not say aria-invalid \u2014 so the note\n says it. -->\n <span data-slot=\"note\">({{ note }})</span>\n}\n\n<!-- Spoken, not shown: a hold copied the address. The region is always\n there, so the text arriving in it is announced. -->\n<span data-slot=\"status\" role=\"status\">{{\n justCopied() ? copiedLabel() + ' ' + address() : ''\n}}</span>\n\n@if (removable()) {\n <!-- The box at the chip's end: the host's remove template when it gave\n one, the chip's own button otherwise. -->\n <span data-slot=\"trailing\">\n @if (removeOutlet(); as template) {\n <ng-container *ngTemplateOutlet=\"template; context: removeContext()\" />\n } @else {\n <button\n type=\"button\"\n data-slot=\"remove\"\n [disabled]=\"ariaDisabled()\"\n [attr.tabindex]=\"removeTabbable() ? null : -1\"\n [attr.aria-label]=\"removeName()\"\n (click)=\"remove()\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M4.5 4.5l7 7 M11.5 4.5l-7 7\" />\n </svg>\n </button>\n }\n </span>\n}\n", styles: [":host{position:relative;isolation:isolate;display:inline-flex;align-items:center;gap:var(--email-address-chip-gap, 4px);max-width:var(--email-address-chip-max-width, 100%);min-width:0;height:var(--email-address-chip-height, 24px);margin:0;padding-block:0;padding-inline:var(--email-address-chip-padding-start, 8px) var(--email-address-chip-padding-end, 2px);box-sizing:border-box;border-radius:var(--email-address-chip-corner, var(--mat-sys-corner-full, 12px));color:var(--email-address-chip-color, var(--mat-sys-on-surface, light-dark(#1f1f1f, #e3e3e3)));font-family:var(--email-address-chip-font-family, var(--mat-sys-body-medium-font, system-ui, sans-serif));font-size:var(--email-address-chip-font-size, var(--mat-sys-body-medium-size, .875rem));font-weight:var(--email-address-chip-font-weight, var(--mat-sys-body-medium-weight, 400));line-height:var(--email-address-chip-line-height, var(--mat-sys-body-medium-line-height, 1.25rem));letter-spacing:var(--email-address-chip-letter-spacing, var(--mat-sys-body-medium-tracking, .016rem));cursor:default;-webkit-user-select:none;user-select:none;-webkit-touch-callout:none;touch-action:manipulation;transition:opacity .15s ease}:host(:not(:has([data-slot=trailing]))){padding-inline-end:var(--email-address-chip-padding-start, 8px)}:host:before{content:\"\";position:absolute;z-index:-1;inset:0;box-sizing:border-box;pointer-events:none;background-color:var(--email-address-chip-background, var(--mat-sys-surface-container, light-dark(#f1f3f4, #303134)));border:1px solid var(--email-address-chip-border-color, transparent);border-radius:var(--email-address-chip-corner, var(--mat-sys-corner-full, 12px))}:host([data-invalid]){color:var(--email-address-chip-invalid-color, var(--mat-sys-error, light-dark(#b3261e, #f2b8b5)))}:host([data-invalid]):before{background-color:var(--email-address-chip-invalid-background, var(--email-address-chip-background, var(--mat-sys-surface-container, light-dark(#f1f3f4, #303134))));border-color:var(--email-address-chip-invalid-border-color, var(--email-address-chip-border-color, transparent))}:host([data-pending]):before{background-color:var(--email-address-chip-pending-background, var(--email-merge-tag-background, color-mix(in srgb, var(--mat-sys-primary, #4c6ef5) 12%, transparent)))}:host([data-pending][data-inline]):before{content:\"\";inset:2px -3px;border-radius:var(--email-address-chip-pending-corner, 4px)}:host([aria-disabled=true]){opacity:.6}:host([aria-current=true]){outline:2px solid var(--email-address-chip-focus-ring, var(--mat-sys-primary, light-dark(#0b57d0, #a8c7fa)));outline-offset:1px}:host([aria-current=true]):before{background-color:var(--email-address-chip-active-background, var(--mat-sys-surface-container-highest, light-dark(#e3e3e3, #444746)))}:host([data-inline]){gap:0;padding-inline:0}:host([data-inline]):before{content:none}:host([data-inline]) :where([data-slot=trailing]){position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}:host([data-inline]):after{flex:none;line-height:var(--email-address-chip-height, 24px);white-space:nowrap}:host :where([data-slot=name]){min-width:0;line-height:var(--email-address-chip-height, 24px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host([data-copied]){color:var(--email-address-chip-copied-color, var(--mat-sys-on-primary-container, light-dark(#041e49, #d3e3fd)))}:host([data-copied][data-inline]){color:var(--email-address-chip-copied-color, var(--mat-sys-primary, light-dark(#0b57d0, #a8c7fa)))}:host([data-copied]):before{background-color:var(--email-address-chip-copied-background, var(--mat-sys-primary-container, light-dark(#d3e3fd, #0842a0)))}:host :where([data-slot=note],[data-slot=status]){position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}:host :where([data-slot=trailing]){flex:none;display:flex;align-items:center;justify-content:center}:host :where([data-slot=remove]){position:relative;flex:none;display:flex;align-items:center;justify-content:center;width:var(--email-address-chip-remove-size, 18px);height:var(--email-address-chip-remove-size, 18px);margin:0;padding:0;border:none;border-radius:var(--email-address-chip-remove-corner, var(--mat-sys-corner-full, 50%));cursor:pointer;background-color:var(--email-address-chip-remove-background, transparent);color:var(--email-address-chip-remove-color, var(--mat-sys-on-surface-variant, light-dark(#444746, #c4c7c5)));transition:background-color .15s ease,color .15s ease}:host :where([data-slot=remove]):before{content:\"\";position:absolute;top:50%;left:50%;width:var(--email-address-chip-remove-target-size, 32px);height:var(--email-address-chip-remove-target-size, 32px);transform:translate(-50%,-50%)}:host :where([data-slot=remove] svg){width:12px;height:12px;fill:none;stroke:currentColor;stroke-width:1.75;stroke-linecap:round}:host :where([data-slot=remove]:hover:not(:disabled),[data-slot=remove]:focus-visible){background-color:var(--email-address-chip-remove-hover-background, var(--mat-sys-surface-container-highest, light-dark(#e3e3e3, #444746)));color:var(--email-address-chip-remove-hover-color, var(--mat-sys-on-surface, light-dark(#1f1f1f, #e3e3e3)))}:host :where([data-slot=remove]:focus-visible){outline:2px solid var(--email-address-chip-focus-ring, var(--mat-sys-primary, light-dark(#0b57d0, #a8c7fa)));outline-offset:1px}:host :where([data-slot=remove]:disabled){cursor:default}\n"] }]
274
+ }], ctorParameters: () => [], propDecorators: { address: [{ type: i0.Input, args: [{ isSignal: true, alias: "address", required: true }] }], removable: [{ type: i0.Input, args: [{ isSignal: true, alias: "removable", required: false }] }], inline: [{ type: i0.Input, args: [{ isSignal: true, alias: "inline", required: false }] }], ariaCurrent: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-current", required: false }] }], removeTabbable: [{ type: i0.Input, args: [{ isSignal: true, alias: "removeTabbable", required: false }] }], removeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "removeLabel", required: false }] }], invalidLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalidLabel", required: false }] }], pendingLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "pendingLabel", required: false }] }], ariaDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-disabled", required: false }] }], removed: [{ type: i0.Output, args: ["removed"] }], holdDelay: [{ type: i0.Input, args: [{ isSignal: true, alias: "holdDelay", required: false }] }], copiedLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "copiedLabel", required: false }] }], copied: [{ type: i0.Output, args: ["copied"] }], removeTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "removeTemplate", required: false }] }], removeSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => AddressChipRemove), { isSignal: true }] }] } });
275
+
276
+ /*
277
+ * Public API Surface of @h-k-dev/angular-email-editor/address-chip
278
+ */
279
+
280
+ /**
281
+ * Generated bundle index. Do not edit.
282
+ */
283
+
284
+ export { AddressChip, AddressChipRemove };
285
+ //# sourceMappingURL=h-k-dev-angular-email-editor-address-chip.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"h-k-dev-angular-email-editor-address-chip.mjs","sources":["../../../projects/angular-email-editor/address-chip/address-chip.slots.ts","../../../projects/angular-email-editor/address-chip/address-chip.ts","../../../projects/angular-email-editor/address-chip/address-chip.html","../../../projects/angular-email-editor/address-chip/public-api.ts","../../../projects/angular-email-editor/address-chip/h-k-dev-angular-email-editor-address-chip.ts"],"sourcesContent":["import { Directive, TemplateRef, inject } from '@angular/core';\nimport { Mailbox } from '@h-k-dev/angular-email-editor/address';\n\n/**\n * The chip's template slot. A host that wants its own remove control — a\n * Material icon button, a text link, a different glyph — hands the chip a\n * template, and the chip renders it in the box at its end where the default\n * button sits. The template gets everything the default button uses, so it\n * can be just as accessible.\n *\n * <div email-address-input [formField]=\"envelope.to\">\n * <button\n * *emailAddressChipRemove=\"let remove = remove; let label = label; let disabled = disabled\"\n * type=\"button\"\n * [attr.aria-label]=\"label\"\n * [disabled]=\"disabled\"\n * (click)=\"remove()\"\n * >\n * <mat-icon inline>close</mat-icon>\n * </button>\n * </div>\n *\n * On an address input it applies to every chip the input renders; on a chip\n * of the host's own it applies to that chip.\n */\n\n/** What the remove template is given. */\nexport interface AddressChipRemoveContext {\n /** The mailbox in header form, as the chip holds it. */\n readonly $implicit: string;\n readonly mailbox: Mailbox;\n /** Whether the address is well-formed. */\n readonly valid: boolean;\n /** Whether the address is a placeholder a template fills in later. */\n readonly pending: boolean;\n /** The remove control is out of play — the chip is `aria-disabled`. */\n readonly disabled: boolean;\n /** Whether the control belongs in the tab order. False in a list that\n reaches its chips with the arrow keys instead; give it tabindex -1. */\n readonly tabbable: boolean;\n /** The control's accessible name — \"Remove ada@example.com\". */\n readonly label: string;\n /** Asks the host to drop the address; does nothing while disabled. */\n readonly remove: () => void;\n}\n\n/**\n * The chip's remove slot: mark an `ng-template` with it (or use the `*`\n * shorthand on the element itself) and the chip renders it in place of the\n * default button, in its `trailing` box. Not rendered at all when the chip\n * is not `removable`.\n */\n@Directive({ selector: 'ng-template[emailAddressChipRemove]' })\nexport class AddressChipRemove {\n readonly template = inject<TemplateRef<AddressChipRemoveContext>>(TemplateRef);\n\n static ngTemplateContextGuard(\n _directive: AddressChipRemove,\n context: unknown,\n ): context is AddressChipRemoveContext {\n return true;\n }\n}\n","import { NgTemplateOutlet } from '@angular/common';\nimport {\n Component,\n DestroyRef,\n TemplateRef,\n booleanAttribute,\n computed,\n contentChild,\n inject,\n input,\n numberAttribute,\n output,\n signal,\n} from '@angular/core';\nimport { ADDRESS_RULES } from '@h-k-dev/angular-email-editor/address';\nimport { AddressChipRemove, AddressChipRemoveContext } from './address-chip.slots';\n\n/** The copy command, on a throwaway selection: the fallback for a clipboard\n API that refuses. True when the browser reports the copy done. */\nfunction copyWithCommand(text: string): boolean {\n const scratch = document.createElement('textarea');\n scratch.value = text;\n scratch.setAttribute('readonly', '');\n scratch.style.position = 'fixed';\n scratch.style.top = '-1000px';\n document.body.append(scratch);\n scratch.select();\n let done = false;\n try {\n done = document.execCommand('copy');\n } catch {\n done = false;\n }\n scratch.remove();\n return done;\n}\n\n/**\n * One address as a chip, the way Gmail draws it: the display name when the\n * mailbox has one, the address otherwise, and the whole header form in the\n * title. An address that is not one is flagged, not refused — the user\n * sees the typo, the host decides what to do about it.\n *\n * **Parts are `data-slot`s** — `name`, `note`, `trailing`, `remove` — and\n * that is how it is styled from outside: theme it through the `--email-*` tokens, and reach\n * a part for a structural override with two attributes\n * (`[email-address-chip] [data-slot='name'][data-slot]`).\n *\n * **State on the host.** `data-invalid` marks a malformed address (a list\n * item may not carry `aria-invalid`; the flag is spoken through the `note`\n * slot instead). `data-pending` marks a placeholder a template fills in\n * later (`{{contact.email}}`) — no mistake, so never flagged as one; the\n * note says what it is. `aria-disabled` takes the remove button out of play, and\n * `aria-current` marks the chip a list's keys act on; each is an input under\n * its ARIA name, reflected back, and a styling hook too.\n *\n * **The remove control is a template slot.** The chip draws its own ×\n * button by default; an `ng-template[emailAddressChipRemove]` in the chip's\n * content — or handed down as `removeTemplate` by the address input —\n * replaces it, rendered in the `trailing` box with the address, the\n * accessible name, the disabled state and a `remove()` to call.\n *\n * **Hold to copy.** A press held on the chip for the length of a phone's\n * touch-and-hold — 500ms, `holdDelay` — copies the address, in header form,\n * to the clipboard, mouse or touch alike; a release or a drag before that\n * does not, and the browser's own context menu stays away. The chip shows\n * and says \"Copied\" for a moment (`data-copied`, a live `status` slot) and\n * emits `copied`.\n *\n * **It asks, it does not do.** The chip holds no list: `removed` is a\n * request the host answers by dropping the address. The address input\n * renders these for its value; a host can render them on its own for a\n * read-only header (a sent copy, a preview).\n *\n * Meant for an `<li>`. No Material, no icon font, no headless library.\n */\n@Component({\n selector: '[email-address-chip]',\n imports: [NgTemplateOutlet],\n templateUrl: './address-chip.html',\n styleUrl: './address-chip.scss',\n host: {\n '[attr.data-invalid]': 'valid() || pending() ? null : true',\n '[attr.data-pending]': 'pending() || null',\n '[attr.aria-disabled]': 'ariaDisabled() || null',\n '[attr.data-inline]': 'inline() || null',\n '[attr.aria-current]': 'ariaCurrent() || null',\n '[attr.data-copied]': 'justCopied() || null',\n '[title]': 'title()',\n '(pointerdown)': 'onPointerdown($event)',\n '(pointermove)': 'onPointermove($event)',\n '(pointerup)': 'cancelHold()',\n '(pointercancel)': 'cancelHold()',\n '(pointerleave)': 'cancelHold()',\n '(contextmenu)': 'onContextmenu($event)',\n },\n})\nexport class AddressChip {\n /** The mailbox in header form: `Ada <ada@example.com>` or `ada@example.com`. */\n readonly address = input.required<string>();\n\n /** Set false for a read-only list: the chip stays, the remove button goes. */\n readonly removable = input(true, { transform: booleanAttribute });\n\n /** Draw the address as text in a line, not as a chip: no surface, no\n room after the address, and the remove control out of the flow and out\n of sight — still focusable, so the keyboard reaches it and a list can\n turn back into chips when it does. The address input sets it while it\n is not focused. Reflected as `data-inline`. */\n readonly inline = input(false, { transform: booleanAttribute });\n\n /** The list's highlight is on this chip: the one the arrow keys reached,\n or a click picked, while the caret stays in the list's input. An input\n under its ARIA name, like `aria-disabled`, reflected back as\n `aria-current=\"true\"` — the list names the same chip in its input's\n `aria-activedescendant` — and drawn as a focus ring. */\n readonly ariaCurrent = input(false, { transform: booleanAttribute, alias: 'aria-current' });\n\n /** Whether the remove button is in the tab order. A list that reaches\n its chips with the arrow keys sets false: Tab goes straight to its\n input, and the button stays clickable and focusable by script. */\n readonly removeTabbable = input(true, { transform: booleanAttribute });\n\n /** Prefix for the remove button's accessible name — \"Remove ada@example.com\". */\n readonly removeLabel = input('Remove');\n\n /** What the flag on a malformed address says, for the title and for\n assistive tech. */\n readonly invalidLabel = input('not an email address');\n\n /** What the note on a placeholder says, for the title and for assistive\n tech. */\n readonly pendingLabel = input('filled in when sent');\n\n /** The remove button is out of play — e.g. while the message sends. */\n readonly ariaDisabled = input(false, { alias: 'aria-disabled', transform: booleanAttribute });\n\n /** The remove button was pressed. The host drops the address; the chip\n goes when the host's list no longer has it. */\n readonly removed = output<void>();\n\n /** How long a press must be held to copy the address, in milliseconds:\n the length of a phone's touch-and-hold, 500ms, so the gesture is the\n one a thumb already knows. 0 turns the hold off. */\n readonly holdDelay = input(500, { transform: numberAttribute });\n\n /** What the chip says, for a moment, once a hold has copied the address\n — \"Copied\", followed by the address. */\n readonly copiedLabel = input('Copied');\n\n /** A hold copied the address to the clipboard, in header form. */\n readonly copied = output<string>();\n\n /** The address was just copied: shown for a moment, and said. Reflected\n as `data-copied`. */\n protected readonly justCopied = signal(false);\n\n #hold: {\n id: number | undefined;\n x: number;\n y: number;\n timer: ReturnType<typeof setTimeout>;\n } | null = null;\n #copiedTimer: ReturnType<typeof setTimeout> | undefined;\n\n constructor() {\n inject(DestroyRef).onDestroy(() => {\n this.cancelHold();\n clearTimeout(this.#copiedTimer);\n });\n }\n\n /** A press begins a hold — a primary pointer, mouse or touch alike, on\n the chip itself and not its remove control. */\n protected onPointerdown(event: PointerEvent): void {\n if (event.button !== 0 || event.isPrimary === false || !this.holdDelay()) return;\n if ((event.target as Element | null)?.closest('[data-slot=trailing]')) return;\n this.cancelHold();\n this.#hold = {\n id: event.pointerId,\n x: event.clientX,\n y: event.clientY,\n timer: setTimeout(() => void this.copy(), this.holdDelay()),\n };\n }\n\n /** A hold that moves is a drag or a scroll, not a hold. */\n protected onPointermove(event: PointerEvent): void {\n const hold = this.#hold;\n if (!hold || (event.pointerId ?? hold.id) !== hold.id) return;\n if (Math.hypot(event.clientX - hold.x, event.clientY - hold.y) > 10) this.cancelHold();\n }\n\n /** Released, cancelled or gone before the time: no copy. */\n protected cancelHold(): void {\n if (!this.#hold) return;\n clearTimeout(this.#hold.timer);\n this.#hold = null;\n }\n\n /** The hold is the chip's, not the browser's: no context menu on it, on\n a phone or under a right button. */\n protected onContextmenu(event: Event): void {\n if (this.#hold || this.justCopied()) event.preventDefault();\n }\n\n /** Writes the address to the clipboard: the async API first, and when\n it refuses — an unfocused document, a context without it — the old\n copy command on a selection made for the purpose. */\n /** Copies the address, in header form, to the clipboard — what a hold\n does, and what a host's own control may ask for — and shows and says\n so. The async API first; when it refuses (an unfocused document, a\n context without it), the old copy command on a selection made for the\n purpose. Resolves to whether the copy went. */\n async copy(): Promise<boolean> {\n this.#hold = null;\n const text = this.address();\n if (!document.hasFocus()) window.focus();\n const modern = navigator.clipboard\n ? navigator.clipboard.writeText(text).then(() => true)\n : Promise.resolve(false);\n const done = (await modern.catch(() => false)) || copyWithCommand(text);\n if (!done) return false;\n this.justCopied.set(true);\n this.copied.emit(text);\n clearTimeout(this.#copiedTimer);\n this.#copiedTimer = setTimeout(() => this.justCopied.set(false), 1500);\n return true;\n }\n\n /** A remove template handed down by a list that renders the chip — the\n address input passes its own `emailAddressChipRemove` slot here. One\n in the chip's own content wins. */\n readonly removeTemplate = input<TemplateRef<AddressChipRemoveContext> | null>(null);\n\n protected readonly removeSlot = contentChild(AddressChipRemove);\n\n /** The template the trailing box renders, or null for the default button. */\n protected readonly removeOutlet = computed(\n () => this.removeSlot()?.template ?? this.removeTemplate(),\n );\n\n /** The address rule in force — ours, or the host's. */\n readonly #rules = inject(ADDRESS_RULES);\n\n protected readonly mailbox = computed(() => this.#rules.parse(this.address()));\n\n /** Whether the address part is well-formed. Public so a list can count. */\n readonly valid = computed(() => this.#rules.isValid(this.mailbox().address));\n\n /** Whether the address is a placeholder a template fills in later —\n neither valid nor invalid yet. */\n readonly pending = computed(() => this.#rules.isPlaceholder(this.mailbox().address));\n\n /** The remove control's accessible name — \"Remove ada@example.com\". */\n protected readonly removeName = computed(() => `${this.removeLabel()} ${this.address()}`);\n\n protected readonly removeContext = computed<AddressChipRemoveContext>(() => ({\n $implicit: this.address(),\n mailbox: this.mailbox(),\n valid: this.valid(),\n pending: this.pending(),\n disabled: this.ariaDisabled(),\n tabbable: this.removeTabbable(),\n label: this.removeName(),\n remove: this.remove,\n }));\n\n /** Asks to be removed, unless the control is out of play. One function\n for the chip's life, so a template holding it never sees it change. */\n protected readonly remove = (): void => {\n if (!this.ariaDisabled()) this.removed.emit();\n };\n\n /** The note the chip carries, if any — said, and shown in the title. */\n protected readonly note = computed(() =>\n this.valid() ? null : this.pending() ? this.pendingLabel() : this.invalidLabel(),\n );\n\n protected readonly title = computed(() => {\n const note = this.note();\n return note ? `${this.address()} — ${note}` : this.address();\n });\n}\n","<!-- The name when there is one, the address otherwise; the header form is\n the host's title, which is where a lone name recovers its address. -->\n<span data-slot=\"name\">{{ mailbox().name ?? mailbox().address }}</span>\n\n@if (note(); as note) {\n <!-- Spoken, not shown: the state is drawn by data-invalid or data-pending\n on the host, and a list item may not say aria-invalid — so the note\n says it. -->\n <span data-slot=\"note\">({{ note }})</span>\n}\n\n<!-- Spoken, not shown: a hold copied the address. The region is always\n there, so the text arriving in it is announced. -->\n<span data-slot=\"status\" role=\"status\">{{\n justCopied() ? copiedLabel() + ' ' + address() : ''\n}}</span>\n\n@if (removable()) {\n <!-- The box at the chip's end: the host's remove template when it gave\n one, the chip's own button otherwise. -->\n <span data-slot=\"trailing\">\n @if (removeOutlet(); as template) {\n <ng-container *ngTemplateOutlet=\"template; context: removeContext()\" />\n } @else {\n <button\n type=\"button\"\n data-slot=\"remove\"\n [disabled]=\"ariaDisabled()\"\n [attr.tabindex]=\"removeTabbable() ? null : -1\"\n [attr.aria-label]=\"removeName()\"\n (click)=\"remove()\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M4.5 4.5l7 7 M11.5 4.5l-7 7\" />\n </svg>\n </button>\n }\n </span>\n}\n","/*\n * Public API Surface of @h-k-dev/angular-email-editor/address-chip\n */\n\nexport * from './address-chip';\nexport * from './address-chip.slots';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}