@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.
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 +383 -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 +1106 -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 +78 -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 +18820 -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 +234 -0
  38. package/types/h-k-dev-angular-email-editor-address-input.d.ts +449 -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 +46 -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
package/package.json CHANGED
@@ -1,6 +1,136 @@
1
1
  {
2
2
  "name": "@h-k-dev/angular-email-editor",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.0.2",
4
+ "description": "An email editor for Angular on ProseMirror — a visual composer and an HTML source editor over one schema that is the vocabulary of email HTML.",
5
+ "license": "MIT",
6
+ "author": "h-k-dev",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/h-k-dev/angular-email-editor.git",
10
+ "directory": "projects/angular-email-editor"
11
+ },
12
+ "homepage": "https://h-k-dev.github.io/angular-email-editor/",
13
+ "bugs": "https://github.com/h-k-dev/angular-email-editor/issues",
14
+ "keywords": [
15
+ "angular",
16
+ "email",
17
+ "email-editor",
18
+ "prosemirror",
19
+ "wysiwyg",
20
+ "html-email",
21
+ "rich-text",
22
+ "signals"
23
+ ],
24
+ "peerDependencies": {
25
+ "@angular/common": "^22.0.0",
26
+ "@angular/core": "^22.0.0",
27
+ "@angular/forms": "^22.0.0",
28
+ "@angular/material": "^22.0.0",
29
+ "@chenglou/pretext": "^1.0.0",
30
+ "prosemirror-commands": "^1.7.1",
31
+ "prosemirror-gapcursor": "^1.4.1",
32
+ "prosemirror-history": "^1.5.0",
33
+ "prosemirror-inputrules": "^1.5.1",
34
+ "prosemirror-keymap": "^1.2.3",
35
+ "prosemirror-model": "^1.25.9",
36
+ "prosemirror-schema-list": "^1.5.1",
37
+ "prosemirror-state": "^1.4.4",
38
+ "prosemirror-tables": "^1.8.5",
39
+ "prosemirror-transform": "^1.12.0",
40
+ "prosemirror-view": "^1.41.9",
41
+ "rxjs": "^7.4.0"
42
+ },
43
+ "peerDependenciesMeta": {
44
+ "@angular/forms": {
45
+ "optional": true
46
+ },
47
+ "@angular/material": {
48
+ "optional": true
49
+ },
50
+ "@chenglou/pretext": {
51
+ "optional": true
52
+ },
53
+ "rxjs": {
54
+ "optional": true
55
+ }
56
+ },
57
+ "dependencies": {
58
+ "tslib": "^2.3.0"
59
+ },
60
+ "exports": {
61
+ "./styles/*": {
62
+ "sass": "./styles/_*.scss"
63
+ },
64
+ "./package.json": {
65
+ "default": "./package.json"
66
+ },
67
+ ".": {
68
+ "types": "./types/h-k-dev-angular-email-editor.d.ts",
69
+ "default": "./fesm2022/h-k-dev-angular-email-editor.mjs"
70
+ },
71
+ "./actions": {
72
+ "types": "./types/h-k-dev-angular-email-editor-actions.d.ts",
73
+ "default": "./fesm2022/h-k-dev-angular-email-editor-actions.mjs"
74
+ },
75
+ "./address": {
76
+ "types": "./types/h-k-dev-angular-email-editor-address.d.ts",
77
+ "default": "./fesm2022/h-k-dev-angular-email-editor-address.mjs"
78
+ },
79
+ "./address-chip": {
80
+ "types": "./types/h-k-dev-angular-email-editor-address-chip.d.ts",
81
+ "default": "./fesm2022/h-k-dev-angular-email-editor-address-chip.mjs"
82
+ },
83
+ "./address-input": {
84
+ "types": "./types/h-k-dev-angular-email-editor-address-input.d.ts",
85
+ "default": "./fesm2022/h-k-dev-angular-email-editor-address-input.mjs"
86
+ },
87
+ "./anchor": {
88
+ "types": "./types/h-k-dev-angular-email-editor-anchor.d.ts",
89
+ "default": "./fesm2022/h-k-dev-angular-email-editor-anchor.mjs"
90
+ },
91
+ "./attachment-chip": {
92
+ "types": "./types/h-k-dev-angular-email-editor-attachment-chip.d.ts",
93
+ "default": "./fesm2022/h-k-dev-angular-email-editor-attachment-chip.mjs"
94
+ },
95
+ "./attachment-chips": {
96
+ "types": "./types/h-k-dev-angular-email-editor-attachment-chips.d.ts",
97
+ "default": "./fesm2022/h-k-dev-angular-email-editor-attachment-chips.mjs"
98
+ },
99
+ "./chat-input": {
100
+ "types": "./types/h-k-dev-angular-email-editor-chat-input.d.ts",
101
+ "default": "./fesm2022/h-k-dev-angular-email-editor-chat-input.mjs"
102
+ },
103
+ "./focus": {
104
+ "types": "./types/h-k-dev-angular-email-editor-focus.d.ts",
105
+ "default": "./fesm2022/h-k-dev-angular-email-editor-focus.mjs"
106
+ },
107
+ "./image": {
108
+ "types": "./types/h-k-dev-angular-email-editor-image.d.ts",
109
+ "default": "./fesm2022/h-k-dev-angular-email-editor-image.mjs"
110
+ },
111
+ "./material": {
112
+ "types": "./types/h-k-dev-angular-email-editor-material.d.ts",
113
+ "default": "./fesm2022/h-k-dev-angular-email-editor-material.mjs"
114
+ },
115
+ "./metrics": {
116
+ "types": "./types/h-k-dev-angular-email-editor-metrics.d.ts",
117
+ "default": "./fesm2022/h-k-dev-angular-email-editor-metrics.mjs"
118
+ },
119
+ "./palette": {
120
+ "types": "./types/h-k-dev-angular-email-editor-palette.d.ts",
121
+ "default": "./fesm2022/h-k-dev-angular-email-editor-palette.mjs"
122
+ },
123
+ "./proposal": {
124
+ "types": "./types/h-k-dev-angular-email-editor-proposal.d.ts",
125
+ "default": "./fesm2022/h-k-dev-angular-email-editor-proposal.mjs"
126
+ },
127
+ "./suggestion-menu": {
128
+ "types": "./types/h-k-dev-angular-email-editor-suggestion-menu.d.ts",
129
+ "default": "./fesm2022/h-k-dev-angular-email-editor-suggestion-menu.mjs"
130
+ }
131
+ },
132
+ "sideEffects": false,
133
+ "module": "fesm2022/h-k-dev-angular-email-editor.mjs",
134
+ "typings": "types/h-k-dev-angular-email-editor.d.ts",
135
+ "type": "module"
6
136
  }
@@ -0,0 +1,101 @@
1
+ // The library's scrollbar, for anything that scrolls — after iusta-core's
2
+ // `.iusta-scroll`, on our own tokens. A thin, round thumb on no track, out
3
+ // of sight until the pointer is over the scroller or the focus is in it
4
+ // (always there where nothing hovers: a touch screen); a stable gutter, so
5
+ // the thumb coming and going never moves the content; sideways overscroll
6
+ // kept in the scroller, so a pane scrolled to its edge never swipes the
7
+ // page back a page.
8
+ //
9
+ // @use '@h-k-dev/angular-email-editor/styles/scroll';
10
+ //
11
+ // .pane {
12
+ // overflow-y: auto;
13
+ // @include scroll.scroll;
14
+ // }
15
+ //
16
+ // A mixin, not a class: nothing reaches a stylesheet that does not include
17
+ // it. Tokens — `--email-scroll-*` first, then Material's system token of the
18
+ // same meaning, then a fallback:
19
+ //
20
+ // - `--email-scroll-size` — the thumb's thickness and the gutter's (6px).
21
+ // - `--email-scroll-thumb` — the thumb at rest (the outline variant at 50%).
22
+ // - `--email-scroll-thumb-hover` — under the pointer (at 75%).
23
+ // - `--email-scroll-track` — the track (transparent).
24
+ //
25
+ // **Smooth scrolling is opt-in** (`$smooth: true`, and then only without
26
+ // reduced motion). Most of what scrolls here is driven by a caret or by keys —
27
+ // an editor following its caret, a menu following its active row — and a
28
+ // smooth scroll there animates every one of those nudges: arrowing past the
29
+ // edge reads as lag, a held key queues animations behind the cursor. iusta
30
+ // turned it on everywhere and turns it off one scroller at a time; here it
31
+ // is the other way round. Only a scroller a person drags or jumps through
32
+ // by link wants it.
33
+ //
34
+ // Webkit and Blink draw the thumb from `::-webkit-scrollbar`; Firefox, which
35
+ // has no pseudo-elements for it, gets the standard `scrollbar-width` and
36
+ // `scrollbar-color` — kept to Firefox, because in Chrome either one switches
37
+ // the pseudo-elements, and so the thickness and the round thumb, off.
38
+
39
+ $_outline-variant: var(--mat-sys-outline-variant, light-dark(#c4c7c5, #444746));
40
+
41
+ @mixin scroll($smooth: false) {
42
+ --_email-scroll-size: var(--email-scroll-size, 6px);
43
+ --_email-scroll-thumb: var(
44
+ --email-scroll-thumb,
45
+ color-mix(in srgb, #{$_outline-variant} 50%, transparent)
46
+ );
47
+ --_email-scroll-thumb-hover: var(
48
+ --email-scroll-thumb-hover,
49
+ color-mix(in srgb, #{$_outline-variant} 75%, transparent)
50
+ );
51
+ // The thumb's colour now: none until the scroller is in use. Declared on
52
+ // every scroller, so a nested one starts out of sight however its outer
53
+ // one stands.
54
+ --_email-scroll-current: transparent;
55
+
56
+ scrollbar-gutter: stable;
57
+ overscroll-behavior-x: contain;
58
+
59
+ @supports (-moz-appearance: none) {
60
+ scrollbar-width: thin;
61
+ scrollbar-color: var(--_email-scroll-current) var(--email-scroll-track, transparent);
62
+ }
63
+
64
+ &::-webkit-scrollbar {
65
+ width: var(--_email-scroll-size);
66
+ height: var(--_email-scroll-size);
67
+ }
68
+
69
+ &::-webkit-scrollbar-track {
70
+ background: var(--email-scroll-track, transparent);
71
+ }
72
+
73
+ &::-webkit-scrollbar-corner {
74
+ background: transparent;
75
+ }
76
+
77
+ &::-webkit-scrollbar-thumb {
78
+ background-color: var(--_email-scroll-current);
79
+ border-radius: 1e3px;
80
+ }
81
+
82
+ &:hover,
83
+ &:focus-within {
84
+ --_email-scroll-current: var(--_email-scroll-thumb);
85
+
86
+ &::-webkit-scrollbar-thumb:hover {
87
+ background-color: var(--_email-scroll-thumb-hover);
88
+ }
89
+ }
90
+
91
+ // Nothing hovers on a touch screen: the thumb is simply there.
92
+ @media (hover: none) {
93
+ --_email-scroll-current: var(--_email-scroll-thumb);
94
+ }
95
+
96
+ @if $smooth {
97
+ @media (prefers-reduced-motion: no-preference) {
98
+ scroll-behavior: smooth;
99
+ }
100
+ }
101
+ }
@@ -0,0 +1,147 @@
1
+ import * as _angular_core from '@angular/core';
2
+ import { Signal, Injector } from '@angular/core';
3
+ import { EditorState } from 'prosemirror-state';
4
+ import { EditorAction, Editor } from '@h-k-dev/angular-email-editor';
5
+
6
+ /**
7
+ * An editor's state as a signal: the text, the selection, the stored marks —
8
+ * whatever changed, the signal has the new one. It follows the editor it is
9
+ * given, so handing it "the source pane in code view, the email editor
10
+ * otherwise" is one `computed`; `undefined` while there is none.
11
+ *
12
+ * It listens through `editor.subscribe`, so the editor can already exist:
13
+ * nothing has to be in the kit before the editor is made.
14
+ *
15
+ * Call in an injection context, or hand it an injector.
16
+ */
17
+ declare function editorState(editor: () => Editor | undefined, options?: {
18
+ injector?: Injector;
19
+ }): Signal<EditorState | undefined>;
20
+ /**
21
+ * An action of the host's own: something that opens its UI — a link dialog,
22
+ * a colour or table picker — rather than running a command. It has a `run`,
23
+ * not a `command`, on purpose: a command is *asked* whether it would apply
24
+ * (called without a dispatch), and a function that opens a dialog must never
25
+ * be called to ask.
26
+ *
27
+ * Two ways in, by what it is:
28
+ * - `host` — an action of its own, there for every editor: "Link", which
29
+ * no extension can offer because it needs the host's dialog;
30
+ * - `override` — the host's `run` *over* an action the kit declares, by its
31
+ * id: a toolbar's "Insert table" that opens a size picker instead of
32
+ * inserting one. It exists only where the kit's action does (so not in a
33
+ * code view), and what it leaves out — when it is on, whether it can run —
34
+ * is still the extension's answer.
35
+ */
36
+ interface HostAction {
37
+ id: string;
38
+ /** What triggering it does. Focus is the host's from here on: the caret
39
+ is not sent back to the editor after it. */
40
+ run: () => void;
41
+ /** On at the selection — makes it a toggle. */
42
+ isActive?: (state: EditorState) => boolean;
43
+ /** Can run right now. Absent: the extension action's answer when it
44
+ stands over one, else yes. */
45
+ isEnabled?: (state: EditorState) => boolean;
46
+ }
47
+ /** An action bound to a live editor: what a button needs. */
48
+ interface BoundAction {
49
+ /** The action as its extension declared it — id, title, icon, keywords;
50
+ `undefined` for a {@link HostAction} that stands alone. */
51
+ readonly action: EditorAction | undefined;
52
+ readonly id: string;
53
+ /** It is the host's `run`: what happens after — where focus goes — is the
54
+ host's too. */
55
+ readonly external: boolean;
56
+ /** On at the selection. Always false for an action with no "on". */
57
+ readonly pressed: Signal<boolean>;
58
+ /** Whether it is a toggle at all — whether `pressed` means anything. */
59
+ readonly toggles: boolean;
60
+ /** Cannot run right now. */
61
+ readonly disabled: Signal<boolean>;
62
+ /** Runs it on the editor; false when it did not apply. */
63
+ run(): boolean;
64
+ /** Puts the caret back in the editor it acts on. */
65
+ focus(): void;
66
+ }
67
+ /** The actions of the editor being watched. */
68
+ interface EditorActions {
69
+ /** The action of that id — `undefined` while there is no editor, and when
70
+ the editor's kit has no such action: a button for it has nothing to do.
71
+ Reactive: it follows the editor. */
72
+ get(id: string): BoundAction | undefined;
73
+ /** Every action of the current editor, in kit order. */
74
+ readonly all: Signal<readonly BoundAction[]>;
75
+ /** The watched editor's state. */
76
+ readonly state: Signal<EditorState | undefined>;
77
+ }
78
+ /**
79
+ * The actions of an editor, bound and live — for a toolbar, a bubble menu,
80
+ * a menu:
81
+ *
82
+ * readonly actions = injectActions(() => this.editor());
83
+ *
84
+ * <button [emailAction]="actions.get('bold')">…</button>
85
+ *
86
+ * `editor` is read reactively: when it changes — a composer switching to its
87
+ * code view — the actions are the new editor's. Its kit decides what exists:
88
+ * the source pane offers the marks, so there a list button has no action
89
+ * and shows disabled, with no routing written anywhere.
90
+ *
91
+ * `host` and `override` bring in the host's own — see {@link HostAction} — so
92
+ * that one list serves a surface whose buttons are not all the editor's.
93
+ *
94
+ * Call in an injection context, or hand it an injector.
95
+ */
96
+ declare function injectActions(editor: () => Editor | undefined, options?: {
97
+ host?: readonly HostAction[];
98
+ override?: Readonly<Record<string, Omit<HostAction, 'id'>>>;
99
+ injector?: Injector;
100
+ }): EditorActions;
101
+
102
+ /**
103
+ * Makes the host's own control the trigger of an editor action: a click runs
104
+ * it, and the control says what the action's state is.
105
+ *
106
+ * <button mat-icon-button [emailAction]="actions.get('bold')" #bold="emailAction"
107
+ * [disabled]="bold.disabled()">
108
+ * <mat-icon>format_bold</mat-icon>
109
+ * </button>
110
+ *
111
+ * Behaviour only, and ARIA only where the host's widget has not spoken:
112
+ * - `aria-pressed` for a toggle, and `aria-disabled` when it cannot run —
113
+ * both off with `[emailActionAria]="false"` for a widget that writes its own
114
+ * (a Material button toggle);
115
+ * - never the native `disabled`, a class or a style: how disabled *looks* is
116
+ * the host's, bound from `disabled()` as above. A control that is only
117
+ * `aria-disabled` stays reachable by keyboard, which is what a toolbar wants;
118
+ * - a click on a disabled trigger runs nothing.
119
+ *
120
+ * After running, the caret goes back to the editor the action ran on — a
121
+ * no-op for a pointer press under `emailKeepFocus`, the way back for a key
122
+ * press in a toolbar. `[emailActionFocus]="false"` leaves focus on the
123
+ * control, as the ARIA toolbar pattern has it. A host's own action (a link
124
+ * dialog, a picker) never has focus taken from what it opened.
125
+ */
126
+ declare class ActionTrigger {
127
+ /** The action to trigger — `actions.get(id)`. `undefined` (no editor yet,
128
+ or a kit without it) is a trigger with nothing to do: disabled. */
129
+ readonly action: _angular_core.InputSignal<BoundAction | undefined>;
130
+ /** Whether this directive writes `aria-pressed` and `aria-disabled`. */
131
+ readonly aria: _angular_core.InputSignalWithTransform<boolean, unknown>;
132
+ /** Whether the caret returns to the editor after the action ran. */
133
+ readonly returnFocus: _angular_core.InputSignalWithTransform<boolean, unknown>;
134
+ /** On at the selection. */
135
+ readonly pressed: _angular_core.Signal<boolean>;
136
+ /** Cannot run right now — or has no action at all. */
137
+ readonly disabled: _angular_core.Signal<boolean>;
138
+ protected readonly ariaPressed: _angular_core.Signal<boolean | null>;
139
+ protected readonly ariaDisabled: _angular_core.Signal<true | null>;
140
+ /** Runs the action, as a click does. False when it did not apply. */
141
+ run(): boolean;
142
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ActionTrigger, never>;
143
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<ActionTrigger, "[emailAction]", ["emailAction"], { "action": { "alias": "emailAction"; "required": true; "isSignal": true; }; "aria": { "alias": "emailActionAria"; "required": false; "isSignal": true; }; "returnFocus": { "alias": "emailActionFocus"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
144
+ }
145
+
146
+ export { ActionTrigger, editorState, injectActions };
147
+ export type { BoundAction, EditorActions, HostAction };
@@ -0,0 +1,234 @@
1
+ import * as _h_k_dev_angular_email_editor_address from '@h-k-dev/angular-email-editor/address';
2
+ import { Mailbox } from '@h-k-dev/angular-email-editor/address';
3
+ import * as _angular_core from '@angular/core';
4
+ import { TemplateRef } from '@angular/core';
5
+
6
+ /**
7
+ * The chip's template slot. A host that wants its own remove control — a
8
+ * Material icon button, a text link, a different glyph — hands the chip a
9
+ * template, and the chip renders it in the box at its end where the default
10
+ * button sits. The template gets everything the default button uses, so it
11
+ * can be just as accessible.
12
+ *
13
+ * <div email-address-input [formField]="envelope.to">
14
+ * <button
15
+ * *emailAddressChipRemove="let remove = remove; let label = label; let disabled = disabled"
16
+ * type="button"
17
+ * [attr.aria-label]="label"
18
+ * [disabled]="disabled"
19
+ * (click)="remove()"
20
+ * >
21
+ * <mat-icon inline>close</mat-icon>
22
+ * </button>
23
+ * </div>
24
+ *
25
+ * On an address input it applies to every chip the input renders; on a chip
26
+ * of the host's own it applies to that chip.
27
+ */
28
+ /** What the remove template is given. */
29
+ interface AddressChipRemoveContext {
30
+ /** The mailbox in header form, as the chip holds it. */
31
+ readonly $implicit: string;
32
+ readonly mailbox: Mailbox;
33
+ /** Whether the address is well-formed. */
34
+ readonly valid: boolean;
35
+ /** Whether the address is a placeholder a template fills in later. */
36
+ readonly pending: boolean;
37
+ /** The remove control is out of play — the chip is `aria-disabled`. */
38
+ readonly disabled: boolean;
39
+ /** Whether the control belongs in the tab order. False in a list that
40
+ reaches its chips with the arrow keys instead; give it tabindex -1. */
41
+ readonly tabbable: boolean;
42
+ /** The control's accessible name — "Remove ada@example.com". */
43
+ readonly label: string;
44
+ /** Asks the host to drop the address; does nothing while disabled. */
45
+ readonly remove: () => void;
46
+ }
47
+ /**
48
+ * The chip's remove slot: mark an `ng-template` with it (or use the `*`
49
+ * shorthand on the element itself) and the chip renders it in place of the
50
+ * default button, in its `trailing` box. Not rendered at all when the chip
51
+ * is not `removable`.
52
+ */
53
+ declare class AddressChipRemove {
54
+ readonly template: TemplateRef<AddressChipRemoveContext>;
55
+ static ngTemplateContextGuard(_directive: AddressChipRemove, context: unknown): context is AddressChipRemoveContext;
56
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<AddressChipRemove, never>;
57
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<AddressChipRemove, "ng-template[emailAddressChipRemove]", never, {}, {}, never, never, true, never>;
58
+ }
59
+
60
+ /** The drag data type that carries a chip's address between address
61
+ inputs — one entry, in header form, exactly as the list holds it. It is
62
+ the only thing a chip's drag carries: no plain text, so no text field,
63
+ editor or page drop zone can take it — a chip lands in an address input
64
+ or nowhere. */
65
+ declare const ADDRESS_DRAG_TYPE = "application/x-email-address";
66
+ /**
67
+ * One address as a chip, the way Gmail draws it: the display name when the
68
+ * mailbox has one, the address otherwise, and the whole header form in the
69
+ * title. An address that is not one is flagged, not refused — the user
70
+ * sees the typo, the host decides what to do about it.
71
+ *
72
+ * **Parts are `data-slot`s** — `name`, `note`, `trailing`, `remove` — and
73
+ * that is how it is styled from outside: theme it through the `--email-*` tokens, and reach
74
+ * a part for a structural override with two attributes
75
+ * (`[email-address-chip] [data-slot='name'][data-slot]`).
76
+ *
77
+ * **State on the host.** `data-invalid` marks a malformed address (a list
78
+ * item may not carry `aria-invalid`; the flag is spoken through the `note`
79
+ * slot instead). `data-pending` marks a placeholder a template fills in
80
+ * later (`{{contact.email}}`) — no mistake, so never flagged as one; the
81
+ * note says what it is. `aria-disabled` takes the remove button out of play, and
82
+ * `aria-current` marks the chip a list's keys act on; each is an input under
83
+ * its ARIA name, reflected back, and a styling hook too.
84
+ *
85
+ * **The remove control is a template slot.** The chip draws its own ×
86
+ * button by default; an `ng-template[emailAddressChipRemove]` in the chip's
87
+ * content — or handed down as `removeTemplate` by the address input —
88
+ * replaces it, rendered in the `trailing` box with the address, the
89
+ * accessible name, the disabled state and a `remove()` to call.
90
+ *
91
+ * **Hold to copy.** A press held on the chip for the length of a phone's
92
+ * touch-and-hold — 500ms, `holdDelay` — copies the address, in header form,
93
+ * to the clipboard, mouse or touch alike; a release or a drag before that
94
+ * does not, and the browser's own context menu stays away. The chip shows
95
+ * and says "Copied" for a moment (`data-copied`, a live `status` slot) and
96
+ * emits `copied`. A list that copies the chip itself — the address input
97
+ * on Ctrl+C or Cmd+C — shows the same through `showCopied()`.
98
+ *
99
+ * **Drag, when asked.** Marked `draggable`, the chip can be dragged — by a
100
+ * mouse or a pen; a touch keeps the press for the hold, so a phone's
101
+ * long-press never lifts the chip instead of copying it. The drag carries
102
+ * the header form as `ADDRESS_DRAG_TYPE` and nothing else — no text for
103
+ * another field to take — allows copy and move, and cancels a hold in progress: the browser starts a drag only
104
+ * once the press has moved, so the two gestures never both happen. A drag
105
+ * from the remove control is no drag. What travels under the pointer is
106
+ * the chip as a pill, whole — a copy with room around it, so no engine
107
+ * clips its edge or a ring it wears. `data-dragging` marks the chip while
108
+ * it is in flight. Where the drag lands, and whether the chip leaves its
109
+ * list, is the list's business — the address input does both.
110
+ *
111
+ * **It asks, it does not do.** The chip holds no list: `removed` is a
112
+ * request the host answers by dropping the address. The address input
113
+ * renders these for its value; a host can render them on its own for a
114
+ * read-only header (a sent copy, a preview).
115
+ *
116
+ * Meant for an `<li>`. No Material, no icon font, no headless library.
117
+ */
118
+ declare class AddressChip {
119
+ #private;
120
+ /** The mailbox in header form: `Ada <ada@example.com>` or `ada@example.com`. */
121
+ readonly address: _angular_core.InputSignal<string>;
122
+ /** Set false for a read-only list: the chip stays, the remove button goes. */
123
+ readonly removable: _angular_core.InputSignalWithTransform<boolean, unknown>;
124
+ /** Draw the address as text in a line, not as a chip: no surface, no
125
+ room after the address, and the remove control out of the flow and out
126
+ of sight — still focusable, so the keyboard reaches it and a list can
127
+ turn back into chips when it does. The address input sets it while it
128
+ is not focused. Reflected as `data-inline`. */
129
+ readonly inline: _angular_core.InputSignalWithTransform<boolean, unknown>;
130
+ /** The list's highlight is on this chip: the one the arrow keys reached,
131
+ or a click picked, while the caret stays in the list's input. An input
132
+ under its ARIA name, like `aria-disabled`, reflected back as
133
+ `aria-current="true"` — the list names the same chip in its input's
134
+ `aria-activedescendant` — and drawn as a focus ring. */
135
+ readonly ariaCurrent: _angular_core.InputSignalWithTransform<boolean, unknown>;
136
+ /** Whether the remove button is in the tab order. A list that reaches
137
+ its chips with the arrow keys sets false: Tab goes straight to its
138
+ input, and the button stays clickable and focusable by script. */
139
+ readonly removeTabbable: _angular_core.InputSignalWithTransform<boolean, unknown>;
140
+ /** Prefix for the remove button's accessible name — "Remove ada@example.com". */
141
+ readonly removeLabel: _angular_core.InputSignal<string>;
142
+ /** What the flag on a malformed address says, for the title and for
143
+ assistive tech. */
144
+ readonly invalidLabel: _angular_core.InputSignal<string>;
145
+ /** What the note on a placeholder says, for the title and for assistive
146
+ tech. */
147
+ readonly pendingLabel: _angular_core.InputSignal<string>;
148
+ /** The remove button is out of play — e.g. while the message sends. */
149
+ readonly ariaDisabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
150
+ /** The remove button was pressed. The host drops the address; the chip
151
+ goes when the host's list no longer has it. */
152
+ readonly removed: _angular_core.OutputEmitterRef<void>;
153
+ /** How long a press must be held to copy the address, in milliseconds:
154
+ the length of a phone's touch-and-hold, 500ms, so the gesture is the
155
+ one a thumb already knows. 0 turns the hold off. */
156
+ readonly holdDelay: _angular_core.InputSignalWithTransform<number, unknown>;
157
+ /** What the chip says, for a moment, once a hold has copied the address
158
+ — "Copied", followed by the address. */
159
+ readonly copiedLabel: _angular_core.InputSignal<string>;
160
+ /** A hold copied the address to the clipboard, in header form. */
161
+ readonly copied: _angular_core.OutputEmitterRef<string>;
162
+ /** The address was just copied: shown for a moment, and said. Reflected
163
+ as `data-copied`. */
164
+ protected readonly justCopied: _angular_core.WritableSignal<boolean>;
165
+ /** What the `status` slot says for a moment — a copy, or a list's
166
+ `announce()`. */
167
+ protected readonly status: _angular_core.WritableSignal<string>;
168
+ /** The chip may be dragged — to another address input, or as text into
169
+ any field. Off by default: a chip on its own has no list to leave; the
170
+ address input turns it on for an editable list. Reflected as
171
+ `draggable` for a mouse or pen, never for a touch. */
172
+ readonly draggable: _angular_core.InputSignalWithTransform<boolean, unknown>;
173
+ /** The last press was a touch: no drag, so the press stays the hold's. */
174
+ protected readonly touching: _angular_core.WritableSignal<boolean>;
175
+ /** The chip is being dragged. Reflected as `data-dragging`. */
176
+ protected readonly dragging: _angular_core.WritableSignal<boolean>;
177
+ constructor();
178
+ /** A press begins a hold — a primary pointer, mouse or touch alike, on
179
+ the chip itself and not its remove control. */
180
+ protected onPointerdown(event: PointerEvent): void;
181
+ /** A hold that moves is a drag or a scroll, not a hold. */
182
+ protected onPointermove(event: PointerEvent): void;
183
+ /** Released, cancelled or gone before the time: no copy. */
184
+ protected cancelHold(): void;
185
+ /** A drag begins: the address goes along in header form, as the
186
+ library's own type only, and a hold in progress is over.
187
+ Not from the remove control. */
188
+ protected onDragstart(event: DragEvent): void;
189
+ /** The hold is the chip's, not the browser's: no context menu on it, on
190
+ a phone or under a right button. */
191
+ protected onContextmenu(event: Event): void;
192
+ /** Copies the address, in header form, to the clipboard — what a hold
193
+ does, and what a host's own control may ask for — and shows and says
194
+ so. The async API first; when it refuses (an unfocused document, a
195
+ context without it), the old copy command on a selection made for the
196
+ purpose. Resolves to whether the copy went. */
197
+ copy(): Promise<boolean>;
198
+ /** Shows and says, for a moment, that the address is on the clipboard,
199
+ and emits `copied` — what a copy does once the clipboard has it. For a
200
+ list that put it there itself: the address input's Ctrl+C, which
201
+ writes the clipboard in the copy event, synchronously. */
202
+ showCopied(): void;
203
+ /** Says something about the chip through its `status` slot, for a
204
+ moment — what a copy says, and what a list says of a chip it just
205
+ received ("Moved to Cc: …"). */
206
+ announce(message: string): void;
207
+ /** A remove template handed down by a list that renders the chip — the
208
+ address input passes its own `emailAddressChipRemove` slot here. One
209
+ in the chip's own content wins. */
210
+ readonly removeTemplate: _angular_core.InputSignal<TemplateRef<AddressChipRemoveContext> | null>;
211
+ protected readonly removeSlot: _angular_core.Signal<AddressChipRemove | undefined>;
212
+ /** The template the trailing box renders, or null for the default button. */
213
+ protected readonly removeOutlet: _angular_core.Signal<TemplateRef<AddressChipRemoveContext> | null>;
214
+ protected readonly mailbox: _angular_core.Signal<_h_k_dev_angular_email_editor_address.Mailbox>;
215
+ /** Whether the address part is well-formed. Public so a list can count. */
216
+ readonly valid: _angular_core.Signal<boolean>;
217
+ /** Whether the address is a placeholder a template fills in later —
218
+ neither valid nor invalid yet. */
219
+ readonly pending: _angular_core.Signal<boolean>;
220
+ /** The remove control's accessible name — "Remove ada@example.com". */
221
+ protected readonly removeName: _angular_core.Signal<string>;
222
+ protected readonly removeContext: _angular_core.Signal<AddressChipRemoveContext>;
223
+ /** Asks to be removed, unless the control is out of play. One function
224
+ for the chip's life, so a template holding it never sees it change. */
225
+ protected readonly remove: () => void;
226
+ /** The note the chip carries, if any — said, and shown in the title. */
227
+ protected readonly note: _angular_core.Signal<string | null>;
228
+ protected readonly title: _angular_core.Signal<string>;
229
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<AddressChip, never>;
230
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<AddressChip, "[email-address-chip]", never, { "address": { "alias": "address"; "required": true; "isSignal": true; }; "removable": { "alias": "removable"; "required": false; "isSignal": true; }; "inline": { "alias": "inline"; "required": false; "isSignal": true; }; "ariaCurrent": { "alias": "aria-current"; "required": false; "isSignal": true; }; "removeTabbable": { "alias": "removeTabbable"; "required": false; "isSignal": true; }; "removeLabel": { "alias": "removeLabel"; "required": false; "isSignal": true; }; "invalidLabel": { "alias": "invalidLabel"; "required": false; "isSignal": true; }; "pendingLabel": { "alias": "pendingLabel"; "required": false; "isSignal": true; }; "ariaDisabled": { "alias": "aria-disabled"; "required": false; "isSignal": true; }; "holdDelay": { "alias": "holdDelay"; "required": false; "isSignal": true; }; "copiedLabel": { "alias": "copiedLabel"; "required": false; "isSignal": true; }; "draggable": { "alias": "draggable"; "required": false; "isSignal": true; }; "removeTemplate": { "alias": "removeTemplate"; "required": false; "isSignal": true; }; }, { "removed": "removed"; "copied": "copied"; }, ["removeSlot"], never, true, never>;
231
+ }
232
+
233
+ export { ADDRESS_DRAG_TYPE, AddressChip, AddressChipRemove };
234
+ export type { AddressChipRemoveContext };