@h-k-dev/angular-email-editor 0.0.0-stage → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +127 -2
- package/fesm2022/h-k-dev-angular-email-editor-actions.mjs +179 -0
- package/fesm2022/h-k-dev-angular-email-editor-actions.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-address-chip.mjs +383 -0
- package/fesm2022/h-k-dev-angular-email-editor-address-chip.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-address-input.mjs +1106 -0
- package/fesm2022/h-k-dev-angular-email-editor-address-input.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-address.mjs +489 -0
- package/fesm2022/h-k-dev-angular-email-editor-address.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-anchor.mjs +64 -0
- package/fesm2022/h-k-dev-angular-email-editor-anchor.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-attachment-chip.mjs +387 -0
- package/fesm2022/h-k-dev-angular-email-editor-attachment-chip.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-attachment-chips.mjs +80 -0
- package/fesm2022/h-k-dev-angular-email-editor-attachment-chips.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-chat-input.mjs +346 -0
- package/fesm2022/h-k-dev-angular-email-editor-chat-input.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-focus.mjs +78 -0
- package/fesm2022/h-k-dev-angular-email-editor-focus.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-image.mjs +132 -0
- package/fesm2022/h-k-dev-angular-email-editor-image.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-material.mjs +124 -0
- package/fesm2022/h-k-dev-angular-email-editor-material.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-metrics.mjs +243 -0
- package/fesm2022/h-k-dev-angular-email-editor-metrics.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-palette.mjs +87 -0
- package/fesm2022/h-k-dev-angular-email-editor-palette.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-proposal.mjs +307 -0
- package/fesm2022/h-k-dev-angular-email-editor-proposal.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-suggestion-menu.mjs +392 -0
- package/fesm2022/h-k-dev-angular-email-editor-suggestion-menu.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor.mjs +18820 -0
- package/fesm2022/h-k-dev-angular-email-editor.mjs.map +1 -0
- package/package.json +133 -3
- package/styles/_scroll.scss +101 -0
- package/types/h-k-dev-angular-email-editor-actions.d.ts +147 -0
- package/types/h-k-dev-angular-email-editor-address-chip.d.ts +234 -0
- package/types/h-k-dev-angular-email-editor-address-input.d.ts +449 -0
- package/types/h-k-dev-angular-email-editor-address.d.ts +125 -0
- package/types/h-k-dev-angular-email-editor-anchor.d.ts +44 -0
- package/types/h-k-dev-angular-email-editor-attachment-chip.d.ts +271 -0
- package/types/h-k-dev-angular-email-editor-attachment-chips.d.ts +48 -0
- package/types/h-k-dev-angular-email-editor-chat-input.d.ts +101 -0
- package/types/h-k-dev-angular-email-editor-focus.d.ts +46 -0
- package/types/h-k-dev-angular-email-editor-image.d.ts +66 -0
- package/types/h-k-dev-angular-email-editor-material.d.ts +65 -0
- package/types/h-k-dev-angular-email-editor-metrics.d.ts +110 -0
- package/types/h-k-dev-angular-email-editor-palette.d.ts +51 -0
- package/types/h-k-dev-angular-email-editor-proposal.d.ts +170 -0
- package/types/h-k-dev-angular-email-editor-suggestion-menu.d.ts +177 -0
- package/types/h-k-dev-angular-email-editor.d.ts +3959 -0
|
@@ -0,0 +1,383 @@
|
|
|
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, ElementRef, 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 drag data type that carries a chip's address between address
|
|
26
|
+
inputs — one entry, in header form, exactly as the list holds it. It is
|
|
27
|
+
the only thing a chip's drag carries: no plain text, so no text field,
|
|
28
|
+
editor or page drop zone can take it — a chip lands in an address input
|
|
29
|
+
or nowhere. */
|
|
30
|
+
const ADDRESS_DRAG_TYPE = 'application/x-email-address';
|
|
31
|
+
/** The copy command, on a throwaway selection: the fallback for a clipboard
|
|
32
|
+
API that refuses. True when the browser reports the copy done. */
|
|
33
|
+
function copyWithCommand(text) {
|
|
34
|
+
const scratch = document.createElement('textarea');
|
|
35
|
+
scratch.value = text;
|
|
36
|
+
scratch.setAttribute('readonly', '');
|
|
37
|
+
scratch.style.position = 'fixed';
|
|
38
|
+
scratch.style.top = '-1000px';
|
|
39
|
+
document.body.append(scratch);
|
|
40
|
+
scratch.select();
|
|
41
|
+
let done = false;
|
|
42
|
+
try {
|
|
43
|
+
done = document.execCommand('copy');
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
done = false;
|
|
47
|
+
}
|
|
48
|
+
scratch.remove();
|
|
49
|
+
return done;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* One address as a chip, the way Gmail draws it: the display name when the
|
|
53
|
+
* mailbox has one, the address otherwise, and the whole header form in the
|
|
54
|
+
* title. An address that is not one is flagged, not refused — the user
|
|
55
|
+
* sees the typo, the host decides what to do about it.
|
|
56
|
+
*
|
|
57
|
+
* **Parts are `data-slot`s** — `name`, `note`, `trailing`, `remove` — and
|
|
58
|
+
* that is how it is styled from outside: theme it through the `--email-*` tokens, and reach
|
|
59
|
+
* a part for a structural override with two attributes
|
|
60
|
+
* (`[email-address-chip] [data-slot='name'][data-slot]`).
|
|
61
|
+
*
|
|
62
|
+
* **State on the host.** `data-invalid` marks a malformed address (a list
|
|
63
|
+
* item may not carry `aria-invalid`; the flag is spoken through the `note`
|
|
64
|
+
* slot instead). `data-pending` marks a placeholder a template fills in
|
|
65
|
+
* later (`{{contact.email}}`) — no mistake, so never flagged as one; the
|
|
66
|
+
* note says what it is. `aria-disabled` takes the remove button out of play, and
|
|
67
|
+
* `aria-current` marks the chip a list's keys act on; each is an input under
|
|
68
|
+
* its ARIA name, reflected back, and a styling hook too.
|
|
69
|
+
*
|
|
70
|
+
* **The remove control is a template slot.** The chip draws its own ×
|
|
71
|
+
* button by default; an `ng-template[emailAddressChipRemove]` in the chip's
|
|
72
|
+
* content — or handed down as `removeTemplate` by the address input —
|
|
73
|
+
* replaces it, rendered in the `trailing` box with the address, the
|
|
74
|
+
* accessible name, the disabled state and a `remove()` to call.
|
|
75
|
+
*
|
|
76
|
+
* **Hold to copy.** A press held on the chip for the length of a phone's
|
|
77
|
+
* touch-and-hold — 500ms, `holdDelay` — copies the address, in header form,
|
|
78
|
+
* to the clipboard, mouse or touch alike; a release or a drag before that
|
|
79
|
+
* does not, and the browser's own context menu stays away. The chip shows
|
|
80
|
+
* and says "Copied" for a moment (`data-copied`, a live `status` slot) and
|
|
81
|
+
* emits `copied`. A list that copies the chip itself — the address input
|
|
82
|
+
* on Ctrl+C or Cmd+C — shows the same through `showCopied()`.
|
|
83
|
+
*
|
|
84
|
+
* **Drag, when asked.** Marked `draggable`, the chip can be dragged — by a
|
|
85
|
+
* mouse or a pen; a touch keeps the press for the hold, so a phone's
|
|
86
|
+
* long-press never lifts the chip instead of copying it. The drag carries
|
|
87
|
+
* the header form as `ADDRESS_DRAG_TYPE` and nothing else — no text for
|
|
88
|
+
* another field to take — allows copy and move, and cancels a hold in progress: the browser starts a drag only
|
|
89
|
+
* once the press has moved, so the two gestures never both happen. A drag
|
|
90
|
+
* from the remove control is no drag. What travels under the pointer is
|
|
91
|
+
* the chip as a pill, whole — a copy with room around it, so no engine
|
|
92
|
+
* clips its edge or a ring it wears. `data-dragging` marks the chip while
|
|
93
|
+
* it is in flight. Where the drag lands, and whether the chip leaves its
|
|
94
|
+
* list, is the list's business — the address input does both.
|
|
95
|
+
*
|
|
96
|
+
* **It asks, it does not do.** The chip holds no list: `removed` is a
|
|
97
|
+
* request the host answers by dropping the address. The address input
|
|
98
|
+
* renders these for its value; a host can render them on its own for a
|
|
99
|
+
* read-only header (a sent copy, a preview).
|
|
100
|
+
*
|
|
101
|
+
* Meant for an `<li>`. No Material, no icon font, no headless library.
|
|
102
|
+
*/
|
|
103
|
+
class AddressChip {
|
|
104
|
+
/** The mailbox in header form: `Ada <ada@example.com>` or `ada@example.com`. */
|
|
105
|
+
address = input.required(/* @ts-ignore */
|
|
106
|
+
...(ngDevMode ? [{ debugName: "address" }] : /* istanbul ignore next */ []));
|
|
107
|
+
/** Set false for a read-only list: the chip stays, the remove button goes. */
|
|
108
|
+
removable = input(true, { ...(ngDevMode ? { debugName: "removable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
109
|
+
/** Draw the address as text in a line, not as a chip: no surface, no
|
|
110
|
+
room after the address, and the remove control out of the flow and out
|
|
111
|
+
of sight — still focusable, so the keyboard reaches it and a list can
|
|
112
|
+
turn back into chips when it does. The address input sets it while it
|
|
113
|
+
is not focused. Reflected as `data-inline`. */
|
|
114
|
+
inline = input(false, { ...(ngDevMode ? { debugName: "inline" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
115
|
+
/** The list's highlight is on this chip: the one the arrow keys reached,
|
|
116
|
+
or a click picked, while the caret stays in the list's input. An input
|
|
117
|
+
under its ARIA name, like `aria-disabled`, reflected back as
|
|
118
|
+
`aria-current="true"` — the list names the same chip in its input's
|
|
119
|
+
`aria-activedescendant` — and drawn as a focus ring. */
|
|
120
|
+
ariaCurrent = input(false, { ...(ngDevMode ? { debugName: "ariaCurrent" } : /* istanbul ignore next */ {}), transform: booleanAttribute, alias: 'aria-current' });
|
|
121
|
+
/** Whether the remove button is in the tab order. A list that reaches
|
|
122
|
+
its chips with the arrow keys sets false: Tab goes straight to its
|
|
123
|
+
input, and the button stays clickable and focusable by script. */
|
|
124
|
+
removeTabbable = input(true, { ...(ngDevMode ? { debugName: "removeTabbable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
125
|
+
/** Prefix for the remove button's accessible name — "Remove ada@example.com". */
|
|
126
|
+
removeLabel = input('Remove', /* @ts-ignore */
|
|
127
|
+
...(ngDevMode ? [{ debugName: "removeLabel" }] : /* istanbul ignore next */ []));
|
|
128
|
+
/** What the flag on a malformed address says, for the title and for
|
|
129
|
+
assistive tech. */
|
|
130
|
+
invalidLabel = input('not an email address', /* @ts-ignore */
|
|
131
|
+
...(ngDevMode ? [{ debugName: "invalidLabel" }] : /* istanbul ignore next */ []));
|
|
132
|
+
/** What the note on a placeholder says, for the title and for assistive
|
|
133
|
+
tech. */
|
|
134
|
+
pendingLabel = input('filled in when sent', /* @ts-ignore */
|
|
135
|
+
...(ngDevMode ? [{ debugName: "pendingLabel" }] : /* istanbul ignore next */ []));
|
|
136
|
+
/** The remove button is out of play — e.g. while the message sends. */
|
|
137
|
+
ariaDisabled = input(false, { ...(ngDevMode ? { debugName: "ariaDisabled" } : /* istanbul ignore next */ {}), alias: 'aria-disabled', transform: booleanAttribute });
|
|
138
|
+
/** The remove button was pressed. The host drops the address; the chip
|
|
139
|
+
goes when the host's list no longer has it. */
|
|
140
|
+
removed = output();
|
|
141
|
+
/** How long a press must be held to copy the address, in milliseconds:
|
|
142
|
+
the length of a phone's touch-and-hold, 500ms, so the gesture is the
|
|
143
|
+
one a thumb already knows. 0 turns the hold off. */
|
|
144
|
+
holdDelay = input(500, { ...(ngDevMode ? { debugName: "holdDelay" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
145
|
+
/** What the chip says, for a moment, once a hold has copied the address
|
|
146
|
+
— "Copied", followed by the address. */
|
|
147
|
+
copiedLabel = input('Copied', /* @ts-ignore */
|
|
148
|
+
...(ngDevMode ? [{ debugName: "copiedLabel" }] : /* istanbul ignore next */ []));
|
|
149
|
+
/** A hold copied the address to the clipboard, in header form. */
|
|
150
|
+
copied = output();
|
|
151
|
+
/** The address was just copied: shown for a moment, and said. Reflected
|
|
152
|
+
as `data-copied`. */
|
|
153
|
+
justCopied = signal(false, /* @ts-ignore */
|
|
154
|
+
...(ngDevMode ? [{ debugName: "justCopied" }] : /* istanbul ignore next */ []));
|
|
155
|
+
/** What the `status` slot says for a moment — a copy, or a list's
|
|
156
|
+
`announce()`. */
|
|
157
|
+
status = signal('', /* @ts-ignore */
|
|
158
|
+
...(ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []));
|
|
159
|
+
/** The chip may be dragged — to another address input, or as text into
|
|
160
|
+
any field. Off by default: a chip on its own has no list to leave; the
|
|
161
|
+
address input turns it on for an editable list. Reflected as
|
|
162
|
+
`draggable` for a mouse or pen, never for a touch. */
|
|
163
|
+
draggable = input(false, { ...(ngDevMode ? { debugName: "draggable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
164
|
+
/** The last press was a touch: no drag, so the press stays the hold's. */
|
|
165
|
+
touching = signal(false, /* @ts-ignore */
|
|
166
|
+
...(ngDevMode ? [{ debugName: "touching" }] : /* istanbul ignore next */ []));
|
|
167
|
+
/** The chip is being dragged. Reflected as `data-dragging`. */
|
|
168
|
+
dragging = signal(false, /* @ts-ignore */
|
|
169
|
+
...(ngDevMode ? [{ debugName: "dragging" }] : /* istanbul ignore next */ []));
|
|
170
|
+
#hold = null;
|
|
171
|
+
#copiedTimer;
|
|
172
|
+
constructor() {
|
|
173
|
+
inject(DestroyRef).onDestroy(() => {
|
|
174
|
+
this.cancelHold();
|
|
175
|
+
clearTimeout(this.#copiedTimer);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
/** A press begins a hold — a primary pointer, mouse or touch alike, on
|
|
179
|
+
the chip itself and not its remove control. */
|
|
180
|
+
onPointerdown(event) {
|
|
181
|
+
this.touching.set(event.pointerType === 'touch');
|
|
182
|
+
if (event.button !== 0 || event.isPrimary === false || !this.holdDelay())
|
|
183
|
+
return;
|
|
184
|
+
if (event.target?.closest('[data-slot=trailing]'))
|
|
185
|
+
return;
|
|
186
|
+
this.cancelHold();
|
|
187
|
+
this.#hold = {
|
|
188
|
+
id: event.pointerId,
|
|
189
|
+
x: event.clientX,
|
|
190
|
+
y: event.clientY,
|
|
191
|
+
timer: setTimeout(() => void this.copy(), this.holdDelay()),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
/** A hold that moves is a drag or a scroll, not a hold. */
|
|
195
|
+
onPointermove(event) {
|
|
196
|
+
const hold = this.#hold;
|
|
197
|
+
if (!hold || (event.pointerId ?? hold.id) !== hold.id)
|
|
198
|
+
return;
|
|
199
|
+
if (Math.hypot(event.clientX - hold.x, event.clientY - hold.y) > 10)
|
|
200
|
+
this.cancelHold();
|
|
201
|
+
}
|
|
202
|
+
/** Released, cancelled or gone before the time: no copy. */
|
|
203
|
+
cancelHold() {
|
|
204
|
+
if (!this.#hold)
|
|
205
|
+
return;
|
|
206
|
+
clearTimeout(this.#hold.timer);
|
|
207
|
+
this.#hold = null;
|
|
208
|
+
}
|
|
209
|
+
/** A drag begins: the address goes along in header form, as the
|
|
210
|
+
library's own type only, and a hold in progress is over.
|
|
211
|
+
Not from the remove control. */
|
|
212
|
+
onDragstart(event) {
|
|
213
|
+
if (event.target?.closest('[data-slot=trailing]')) {
|
|
214
|
+
event.preventDefault();
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
this.cancelHold();
|
|
218
|
+
const transfer = event.dataTransfer;
|
|
219
|
+
if (transfer) {
|
|
220
|
+
transfer.setData(ADDRESS_DRAG_TYPE, this.address());
|
|
221
|
+
transfer.effectAllowed = 'copyMove';
|
|
222
|
+
this.#dragImage(transfer, event);
|
|
223
|
+
}
|
|
224
|
+
this.dragging.set(true);
|
|
225
|
+
}
|
|
226
|
+
/** The drag image: an engine snapshots the chip's own box, cutting off
|
|
227
|
+
whatever it draws outside it — a picked chip's ring. So it gets a copy,
|
|
228
|
+
as a plain pill (not inline, not picked), inside a frame with room
|
|
229
|
+
around it, laid over the chip exactly for the snapshot and gone on
|
|
230
|
+
the next task. On screen, not off it: WebKit snapshots only what it
|
|
231
|
+
can paint. */
|
|
232
|
+
#dragImage(transfer, event) {
|
|
233
|
+
if (typeof transfer.setDragImage !== 'function')
|
|
234
|
+
return;
|
|
235
|
+
const host = this.#host.nativeElement;
|
|
236
|
+
const box = host.getBoundingClientRect();
|
|
237
|
+
const room = 4;
|
|
238
|
+
const frame = document.createElement('div');
|
|
239
|
+
frame.style.cssText =
|
|
240
|
+
`position: fixed; left: ${box.left - room}px; top: ${box.top - room}px; ` +
|
|
241
|
+
`padding: ${room}px; z-index: 2147483647; pointer-events: none;`;
|
|
242
|
+
const copy = host.cloneNode(true);
|
|
243
|
+
for (const name of ['id', 'data-inline', 'aria-current', 'data-dragging', 'data-copied']) {
|
|
244
|
+
copy.removeAttribute(name);
|
|
245
|
+
}
|
|
246
|
+
copy.style.display = 'inline-flex';
|
|
247
|
+
frame.append(copy);
|
|
248
|
+
document.body.append(frame);
|
|
249
|
+
transfer.setDragImage(frame, event.clientX - box.left + room, event.clientY - box.top + room);
|
|
250
|
+
setTimeout(() => frame.remove());
|
|
251
|
+
}
|
|
252
|
+
/** The hold is the chip's, not the browser's: no context menu on it, on
|
|
253
|
+
a phone or under a right button. */
|
|
254
|
+
onContextmenu(event) {
|
|
255
|
+
if (this.#hold || this.justCopied())
|
|
256
|
+
event.preventDefault();
|
|
257
|
+
}
|
|
258
|
+
/** Copies the address, in header form, to the clipboard — what a hold
|
|
259
|
+
does, and what a host's own control may ask for — and shows and says
|
|
260
|
+
so. The async API first; when it refuses (an unfocused document, a
|
|
261
|
+
context without it), the old copy command on a selection made for the
|
|
262
|
+
purpose. Resolves to whether the copy went. */
|
|
263
|
+
async copy() {
|
|
264
|
+
this.#hold = null;
|
|
265
|
+
const text = this.address();
|
|
266
|
+
if (!document.hasFocus())
|
|
267
|
+
window.focus();
|
|
268
|
+
const modern = navigator.clipboard
|
|
269
|
+
? navigator.clipboard.writeText(text).then(() => true)
|
|
270
|
+
: Promise.resolve(false);
|
|
271
|
+
const done = (await modern.catch(() => false)) || copyWithCommand(text);
|
|
272
|
+
if (!done)
|
|
273
|
+
return false;
|
|
274
|
+
this.showCopied();
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
/** Shows and says, for a moment, that the address is on the clipboard,
|
|
278
|
+
and emits `copied` — what a copy does once the clipboard has it. For a
|
|
279
|
+
list that put it there itself: the address input's Ctrl+C, which
|
|
280
|
+
writes the clipboard in the copy event, synchronously. */
|
|
281
|
+
showCopied() {
|
|
282
|
+
const text = this.address();
|
|
283
|
+
this.announce(`${this.copiedLabel()} ${text}`);
|
|
284
|
+
this.justCopied.set(true);
|
|
285
|
+
this.copied.emit(text);
|
|
286
|
+
}
|
|
287
|
+
/** Says something about the chip through its `status` slot, for a
|
|
288
|
+
moment — what a copy says, and what a list says of a chip it just
|
|
289
|
+
received ("Moved to Cc: …"). */
|
|
290
|
+
announce(message) {
|
|
291
|
+
this.status.set(message);
|
|
292
|
+
clearTimeout(this.#copiedTimer);
|
|
293
|
+
this.#copiedTimer = setTimeout(() => {
|
|
294
|
+
this.status.set('');
|
|
295
|
+
this.justCopied.set(false);
|
|
296
|
+
}, 1500);
|
|
297
|
+
}
|
|
298
|
+
/** A remove template handed down by a list that renders the chip — the
|
|
299
|
+
address input passes its own `emailAddressChipRemove` slot here. One
|
|
300
|
+
in the chip's own content wins. */
|
|
301
|
+
removeTemplate = input(null, /* @ts-ignore */
|
|
302
|
+
...(ngDevMode ? [{ debugName: "removeTemplate" }] : /* istanbul ignore next */ []));
|
|
303
|
+
removeSlot = contentChild(AddressChipRemove, /* @ts-ignore */
|
|
304
|
+
...(ngDevMode ? [{ debugName: "removeSlot" }] : /* istanbul ignore next */ []));
|
|
305
|
+
/** The template the trailing box renders, or null for the default button. */
|
|
306
|
+
removeOutlet = computed(() => this.removeSlot()?.template ?? this.removeTemplate(), /* @ts-ignore */
|
|
307
|
+
...(ngDevMode ? [{ debugName: "removeOutlet" }] : /* istanbul ignore next */ []));
|
|
308
|
+
/** The address rule in force — ours, or the host's. */
|
|
309
|
+
#rules = inject(ADDRESS_RULES);
|
|
310
|
+
#host = inject(ElementRef);
|
|
311
|
+
mailbox = computed(() => this.#rules.parse(this.address()), /* @ts-ignore */
|
|
312
|
+
...(ngDevMode ? [{ debugName: "mailbox" }] : /* istanbul ignore next */ []));
|
|
313
|
+
/** Whether the address part is well-formed. Public so a list can count. */
|
|
314
|
+
valid = computed(() => this.#rules.isValid(this.mailbox().address), /* @ts-ignore */
|
|
315
|
+
...(ngDevMode ? [{ debugName: "valid" }] : /* istanbul ignore next */ []));
|
|
316
|
+
/** Whether the address is a placeholder a template fills in later —
|
|
317
|
+
neither valid nor invalid yet. */
|
|
318
|
+
pending = computed(() => this.#rules.isPlaceholder(this.mailbox().address), /* @ts-ignore */
|
|
319
|
+
...(ngDevMode ? [{ debugName: "pending" }] : /* istanbul ignore next */ []));
|
|
320
|
+
/** The remove control's accessible name — "Remove ada@example.com". */
|
|
321
|
+
removeName = computed(() => `${this.removeLabel()} ${this.address()}`, /* @ts-ignore */
|
|
322
|
+
...(ngDevMode ? [{ debugName: "removeName" }] : /* istanbul ignore next */ []));
|
|
323
|
+
removeContext = computed(() => ({
|
|
324
|
+
$implicit: this.address(),
|
|
325
|
+
mailbox: this.mailbox(),
|
|
326
|
+
valid: this.valid(),
|
|
327
|
+
pending: this.pending(),
|
|
328
|
+
disabled: this.ariaDisabled(),
|
|
329
|
+
tabbable: this.removeTabbable(),
|
|
330
|
+
label: this.removeName(),
|
|
331
|
+
remove: this.remove,
|
|
332
|
+
}), /* @ts-ignore */
|
|
333
|
+
...(ngDevMode ? [{ debugName: "removeContext" }] : /* istanbul ignore next */ []));
|
|
334
|
+
/** Asks to be removed, unless the control is out of play. One function
|
|
335
|
+
for the chip's life, so a template holding it never sees it change. */
|
|
336
|
+
remove = () => {
|
|
337
|
+
if (!this.ariaDisabled())
|
|
338
|
+
this.removed.emit();
|
|
339
|
+
};
|
|
340
|
+
/** The note the chip carries, if any — said, and shown in the title. */
|
|
341
|
+
note = computed(() => this.valid() ? null : this.pending() ? this.pendingLabel() : this.invalidLabel(), /* @ts-ignore */
|
|
342
|
+
...(ngDevMode ? [{ debugName: "note" }] : /* istanbul ignore next */ []));
|
|
343
|
+
title = computed(() => {
|
|
344
|
+
const note = this.note();
|
|
345
|
+
return note ? `${this.address()} — ${note}` : this.address();
|
|
346
|
+
}, /* @ts-ignore */
|
|
347
|
+
...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
|
|
348
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: AddressChip, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
349
|
+
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 }, draggable: { classPropertyName: "draggable", publicName: "draggable", 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)", "dragstart": "onDragstart($event)", "dragend": "dragging.set(false)" }, 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", "attr.draggable": "draggable() && !touching() ? \"true\" : null", "attr.data-dragging": "dragging() || 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, or the list said\n something of the chip (`announce()`). The region is always there, so\n the text arriving in it is announced. -->\n<span data-slot=\"status\" role=\"status\">{{ status() }}</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-high, light-dark(#e8eaed, #3c4043)));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-high, light-dark(#e8eaed, #3c4043))));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, color-mix(in oklab, var(--mat-sys-primary, light-dark(#0b57d0, #a8c7fa)) 12%, var(--mat-sys-surface, light-dark(#ffffff, #1f1f1f))))}: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([data-dragging]){opacity:var(--email-address-chip-dragging-opacity, .4)}:host([draggable=true]){cursor:grab}: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"] }] });
|
|
350
|
+
}
|
|
351
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: AddressChip, decorators: [{
|
|
352
|
+
type: Component,
|
|
353
|
+
args: [{ selector: '[email-address-chip]', imports: [NgTemplateOutlet], host: {
|
|
354
|
+
'[attr.data-invalid]': 'valid() || pending() ? null : true',
|
|
355
|
+
'[attr.data-pending]': 'pending() || null',
|
|
356
|
+
'[attr.aria-disabled]': 'ariaDisabled() || null',
|
|
357
|
+
'[attr.data-inline]': 'inline() || null',
|
|
358
|
+
'[attr.aria-current]': 'ariaCurrent() || null',
|
|
359
|
+
'[attr.data-copied]': 'justCopied() || null',
|
|
360
|
+
'[attr.draggable]': 'draggable() && !touching() ? "true" : null',
|
|
361
|
+
'[attr.data-dragging]': 'dragging() || null',
|
|
362
|
+
'[title]': 'title()',
|
|
363
|
+
'(pointerdown)': 'onPointerdown($event)',
|
|
364
|
+
'(pointermove)': 'onPointermove($event)',
|
|
365
|
+
'(pointerup)': 'cancelHold()',
|
|
366
|
+
'(pointercancel)': 'cancelHold()',
|
|
367
|
+
'(pointerleave)': 'cancelHold()',
|
|
368
|
+
'(contextmenu)': 'onContextmenu($event)',
|
|
369
|
+
'(dragstart)': 'onDragstart($event)',
|
|
370
|
+
'(dragend)': 'dragging.set(false)',
|
|
371
|
+
}, 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, or the list said\n something of the chip (`announce()`). The region is always there, so\n the text arriving in it is announced. -->\n<span data-slot=\"status\" role=\"status\">{{ status() }}</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-high, light-dark(#e8eaed, #3c4043)));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-high, light-dark(#e8eaed, #3c4043))));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, color-mix(in oklab, var(--mat-sys-primary, light-dark(#0b57d0, #a8c7fa)) 12%, var(--mat-sys-surface, light-dark(#ffffff, #1f1f1f))))}: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([data-dragging]){opacity:var(--email-address-chip-dragging-opacity, .4)}:host([draggable=true]){cursor:grab}: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"] }]
|
|
372
|
+
}], 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"] }], draggable: [{ type: i0.Input, args: [{ isSignal: true, alias: "draggable", required: false }] }], removeTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "removeTemplate", required: false }] }], removeSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => AddressChipRemove), { isSignal: true }] }] } });
|
|
373
|
+
|
|
374
|
+
/*
|
|
375
|
+
* Public API Surface of @h-k-dev/angular-email-editor/address-chip
|
|
376
|
+
*/
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Generated bundle index. Do not edit.
|
|
380
|
+
*/
|
|
381
|
+
|
|
382
|
+
export { ADDRESS_DRAG_TYPE, AddressChip, AddressChipRemove };
|
|
383
|
+
//# 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 ElementRef,\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 drag data type that carries a chip's address between address\n inputs — one entry, in header form, exactly as the list holds it. It is\n the only thing a chip's drag carries: no plain text, so no text field,\n editor or page drop zone can take it — a chip lands in an address input\n or nowhere. */\nexport const ADDRESS_DRAG_TYPE = 'application/x-email-address';\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`. A list that copies the chip itself — the address input\n * on Ctrl+C or Cmd+C — shows the same through `showCopied()`.\n *\n * **Drag, when asked.** Marked `draggable`, the chip can be dragged — by a\n * mouse or a pen; a touch keeps the press for the hold, so a phone's\n * long-press never lifts the chip instead of copying it. The drag carries\n * the header form as `ADDRESS_DRAG_TYPE` and nothing else — no text for\n * another field to take — allows copy and move, and cancels a hold in progress: the browser starts a drag only\n * once the press has moved, so the two gestures never both happen. A drag\n * from the remove control is no drag. What travels under the pointer is\n * the chip as a pill, whole — a copy with room around it, so no engine\n * clips its edge or a ring it wears. `data-dragging` marks the chip while\n * it is in flight. Where the drag lands, and whether the chip leaves its\n * list, is the list's business — the address input does both.\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 '[attr.draggable]': 'draggable() && !touching() ? \"true\" : null',\n '[attr.data-dragging]': 'dragging() || 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 '(dragstart)': 'onDragstart($event)',\n '(dragend)': 'dragging.set(false)',\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 /** What the `status` slot says for a moment — a copy, or a list's\n `announce()`. */\n protected readonly status = signal('');\n\n /** The chip may be dragged — to another address input, or as text into\n any field. Off by default: a chip on its own has no list to leave; the\n address input turns it on for an editable list. Reflected as\n `draggable` for a mouse or pen, never for a touch. */\n readonly draggable = input(false, { transform: booleanAttribute });\n\n /** The last press was a touch: no drag, so the press stays the hold's. */\n protected readonly touching = signal(false);\n\n /** The chip is being dragged. Reflected as `data-dragging`. */\n protected readonly dragging = 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 this.touching.set(event.pointerType === 'touch');\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 /** A drag begins: the address goes along in header form, as the\n library's own type only, and a hold in progress is over.\n Not from the remove control. */\n protected onDragstart(event: DragEvent): void {\n if ((event.target as Element | null)?.closest('[data-slot=trailing]')) {\n event.preventDefault();\n return;\n }\n this.cancelHold();\n const transfer = event.dataTransfer;\n if (transfer) {\n transfer.setData(ADDRESS_DRAG_TYPE, this.address());\n transfer.effectAllowed = 'copyMove';\n this.#dragImage(transfer, event);\n }\n this.dragging.set(true);\n }\n\n /** The drag image: an engine snapshots the chip's own box, cutting off\n whatever it draws outside it — a picked chip's ring. So it gets a copy,\n as a plain pill (not inline, not picked), inside a frame with room\n around it, laid over the chip exactly for the snapshot and gone on\n the next task. On screen, not off it: WebKit snapshots only what it\n can paint. */\n #dragImage(transfer: DataTransfer, event: DragEvent): void {\n if (typeof transfer.setDragImage !== 'function') return;\n const host = this.#host.nativeElement;\n const box = host.getBoundingClientRect();\n const room = 4;\n const frame = document.createElement('div');\n frame.style.cssText =\n `position: fixed; left: ${box.left - room}px; top: ${box.top - room}px; ` +\n `padding: ${room}px; z-index: 2147483647; pointer-events: none;`;\n const copy = host.cloneNode(true) as HTMLElement;\n for (const name of ['id', 'data-inline', 'aria-current', 'data-dragging', 'data-copied']) {\n copy.removeAttribute(name);\n }\n copy.style.display = 'inline-flex';\n frame.append(copy);\n document.body.append(frame);\n transfer.setDragImage(frame, event.clientX - box.left + room, event.clientY - box.top + room);\n setTimeout(() => frame.remove());\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 /** 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.showCopied();\n return true;\n }\n\n /** Shows and says, for a moment, that the address is on the clipboard,\n and emits `copied` — what a copy does once the clipboard has it. For a\n list that put it there itself: the address input's Ctrl+C, which\n writes the clipboard in the copy event, synchronously. */\n showCopied(): void {\n const text = this.address();\n this.announce(`${this.copiedLabel()} ${text}`);\n this.justCopied.set(true);\n this.copied.emit(text);\n }\n\n /** Says something about the chip through its `status` slot, for a\n moment — what a copy says, and what a list says of a chip it just\n received (\"Moved to Cc: …\"). */\n announce(message: string): void {\n this.status.set(message);\n clearTimeout(this.#copiedTimer);\n this.#copiedTimer = setTimeout(() => {\n this.status.set('');\n this.justCopied.set(false);\n }, 1500);\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 readonly #host = inject<ElementRef<HTMLElement>>(ElementRef);\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, or the list said\n something of the chip (`announce()`). The region is always there, so\n the text arriving in it is announced. -->\n<span data-slot=\"status\" role=\"status\">{{ status() }}</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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"}
|