@h-k-dev/angular-email-editor 0.0.7 → 0.0.8
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 +2 -1
- package/fesm2022/h-k-dev-angular-email-editor-toggle-button-group.mjs +342 -0
- package/fesm2022/h-k-dev-angular-email-editor-toggle-button-group.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor.mjs +52 -8
- package/fesm2022/h-k-dev-angular-email-editor.mjs.map +1 -1
- package/package.json +5 -1
- package/types/h-k-dev-angular-email-editor-toggle-button-group.d.ts +172 -0
- package/types/h-k-dev-angular-email-editor.d.ts +29 -1
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ copy of each.
|
|
|
47
47
|
|
|
48
48
|
| Optional peer | Needed for |
|
|
49
49
|
| --- | --- |
|
|
50
|
-
| `@angular/forms` | `/address-input`, `/attachment-chips` (signal-forms controls) |
|
|
50
|
+
| `@angular/forms` | `/address-input`, `/attachment-chips`, `/toggle-button-group` (signal-forms controls) |
|
|
51
51
|
| `@angular/material`, `rxjs` | `/material` (the `mat-form-field` adapter) |
|
|
52
52
|
| `@chenglou/pretext` `^1.0.0` | `/metrics` (word, line and height metrics) |
|
|
53
53
|
|
|
@@ -96,6 +96,7 @@ Every UI piece is its own secondary entry point, so you only bundle what you imp
|
|
|
96
96
|
| `…/address-chip` | One address chip |
|
|
97
97
|
| `…/attachment-chips` | The attachment strip, a `FormValueControl` over the list |
|
|
98
98
|
| `…/attachment-chip` | One attachment chip with upload stages and progress |
|
|
99
|
+
| `…/toggle-button-group` | `<hk-tgb>` with `hkTgbIcon` (round) and `hkTgbText` (pill) items on any element, a sliding thumb, a `FormValueControl` |
|
|
99
100
|
| `…/suggestion-menu` | The `/` and `{{` menus' floating box, with roles and ids |
|
|
100
101
|
| `…/palette` | The colour palette (readable on light and dark) |
|
|
101
102
|
| `…/proposal` | A streamed proposal's state for your panel (accept / discard) |
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, input, booleanAttribute, inject, ElementRef, HostAttributeToken, computed, Directive, forwardRef, Component, model, output, contentChildren, signal, afterRenderEffect, DestroyRef, effect } from '@angular/core';
|
|
3
|
+
|
|
4
|
+
const TOGGLE_BUTTON_GROUP = new InjectionToken('ToggleButtonGroup');
|
|
5
|
+
/**
|
|
6
|
+
* What every choice of an `hk-tgb` is, whichever shape it wears — the
|
|
7
|
+
* group finds its items by this class. Use one of the shapes:
|
|
8
|
+
* `hkTgbIcon` (round) or `hkTgbText` (a pill).
|
|
9
|
+
*
|
|
10
|
+
* An item makes its element — a `<button>`, an `<a>`, anything — a `radio`
|
|
11
|
+
* that carries a `value` and is `aria-checked` while the group holds it.
|
|
12
|
+
* The element and its content stay the host's: a font icon, a `mat-icon`,
|
|
13
|
+
* an SVG, a word. A click picks it, and Space and Enter do too (the
|
|
14
|
+
* group's keys), so a `<div>` works as well as a button. A `<button>`
|
|
15
|
+
* without a `type` gets `type="button"`, so an item in a form never
|
|
16
|
+
* submits it. `aria-disabled` takes it out of the choices and the arrow
|
|
17
|
+
* keys' way — it stays in the track, so the others keep their places —
|
|
18
|
+
* and stops a disabled link from being followed.
|
|
19
|
+
*/
|
|
20
|
+
class ToggleButtonGroupItem {
|
|
21
|
+
/** What the group's value is while this item is picked. */
|
|
22
|
+
value = input.required(/* @ts-ignore */
|
|
23
|
+
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
24
|
+
/** This one can't be picked. */
|
|
25
|
+
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), alias: 'aria-disabled', transform: booleanAttribute });
|
|
26
|
+
group = inject(TOGGLE_BUTTON_GROUP);
|
|
27
|
+
element = inject(ElementRef).nativeElement;
|
|
28
|
+
/** `button` for a `<button>` the host gave no type; the host's own type
|
|
29
|
+
otherwise, and none on any other element. */
|
|
30
|
+
type = this.element.tagName === 'BUTTON'
|
|
31
|
+
? (inject(new HostAttributeToken('type'), { optional: true }) ?? 'button')
|
|
32
|
+
: null;
|
|
33
|
+
/** The group holds this item's value. */
|
|
34
|
+
checked = computed(() => this.group.value() === this.value(), /* @ts-ignore */
|
|
35
|
+
...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []));
|
|
36
|
+
/** Out of the choices: its own `aria-disabled`, or the group's. */
|
|
37
|
+
blocked = computed(() => this.disabled() || this.group.disabled(), /* @ts-ignore */
|
|
38
|
+
...(ngDevMode ? [{ debugName: "blocked" }] : /* istanbul ignore next */ []));
|
|
39
|
+
focus(options) {
|
|
40
|
+
this.element.focus(options);
|
|
41
|
+
}
|
|
42
|
+
onClick(event) {
|
|
43
|
+
if (this.blocked()) {
|
|
44
|
+
event.preventDefault();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
this.group.select(this.value());
|
|
48
|
+
}
|
|
49
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ToggleButtonGroupItem, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
50
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.6", type: ToggleButtonGroupItem, isStandalone: true, inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "aria-disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "radio" }, listeners: { "click": "onClick($event)" }, properties: { "attr.type": "type", "attr.aria-checked": "checked()", "attr.aria-disabled": "blocked() || null", "tabIndex": "group.tabStop() === this ? 0 : -1" } }, ngImport: i0 });
|
|
51
|
+
}
|
|
52
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ToggleButtonGroupItem, decorators: [{
|
|
53
|
+
type: Directive,
|
|
54
|
+
args: [{
|
|
55
|
+
host: {
|
|
56
|
+
role: 'radio',
|
|
57
|
+
'[attr.type]': 'type',
|
|
58
|
+
'[attr.aria-checked]': 'checked()',
|
|
59
|
+
'[attr.aria-disabled]': 'blocked() || null',
|
|
60
|
+
'[tabIndex]': 'group.tabStop() === this ? 0 : -1',
|
|
61
|
+
'(click)': 'onClick($event)',
|
|
62
|
+
},
|
|
63
|
+
}]
|
|
64
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-disabled", required: false }] }] } });
|
|
65
|
+
/**
|
|
66
|
+
* A round choice holding an icon: a circle as wide as the track is tall
|
|
67
|
+
* (`--hk-tgb-size`). The icon sits in a box of the item's own
|
|
68
|
+
* (`[data-slot=icon]`, `--hk-tgb-icon-size`), whose font size and colour it
|
|
69
|
+
* takes: a `mat-icon` marked `inline`, a font icon's `<i>`, or an SVG sized
|
|
70
|
+
* `1em` fits with no rule of the host's. It has no words, so name it:
|
|
71
|
+
* `aria-label` (and a `title` for a tooltip).
|
|
72
|
+
*
|
|
73
|
+
* ```html
|
|
74
|
+
* <button hkTgbIcon value="light" aria-label="Light"><mat-icon inline>light_mode</mat-icon></button>
|
|
75
|
+
* ```
|
|
76
|
+
*
|
|
77
|
+
* A component on the host's element, not a directive: it brings its own
|
|
78
|
+
* encapsulated look (`:host`), which nothing outside could give an element
|
|
79
|
+
* the host wrote.
|
|
80
|
+
*/
|
|
81
|
+
class ToggleButtonGroupIcon extends ToggleButtonGroupItem {
|
|
82
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ToggleButtonGroupIcon, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
83
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.6", type: ToggleButtonGroupIcon, isStandalone: true, selector: "[hkTgbIcon]", providers: [
|
|
84
|
+
{ provide: ToggleButtonGroupItem, useExisting: forwardRef(() => ToggleButtonGroupIcon) },
|
|
85
|
+
], exportAs: ["hkTgbIcon"], usesInheritance: true, ngImport: i0, template: "<!-- The icon box: sized by --hk-tgb-icon-size; the host's icon takes its\n font size and colour. -->\n<span data-slot=\"icon\"><ng-content /></span>\n", styles: [":host{position:relative;display:inline-flex;flex:none;align-items:center;justify-content:center;gap:var(--hk-tgb-item-gap, 4px);block-size:var(--hk-tgb-size, 32px);min-inline-size:var(--hk-tgb-size, 32px);margin:0;padding:0;box-sizing:border-box;border:0;border-radius:var(--hk-tgb-thumb-corner, var(--mat-sys-corner-full, 9999px));background:transparent;color:var(--hk-tgb-color, var(--mat-sys-on-surface-variant, light-dark(#444746, #c4c7c5)));font-family:var(--hk-tgb-font-family, var(--mat-sys-label-large-font, system-ui, sans-serif));font-size:var(--hk-tgb-font-size, var(--mat-sys-label-large-size, .875rem));font-weight:var(--hk-tgb-font-weight, var(--mat-sys-label-large-weight, 500));line-height:var(--hk-tgb-line-height, var(--mat-sys-label-large-line-height, 1.25rem));letter-spacing:var(--hk-tgb-letter-spacing, var(--mat-sys-label-large-tracking, .006rem));text-decoration:none;white-space:nowrap;cursor:pointer;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;touch-action:manipulation;transition:color .2s ease,background-color .15s ease}@media(hover:hover){:host(:hover:not([aria-checked=true],[aria-disabled=true])){color:var(--hk-tgb-hover-color, var(--mat-sys-on-surface, light-dark(#1f1f1f, #e3e3e3)));background-color:var(--hk-tgb-hover-background, color-mix(in oklch, var(--mat-sys-on-surface, light-dark(#1f1f1f, #e3e3e3)) 4%, transparent))}}:host([aria-checked=true]){color:var(--hk-tgb-checked-color, var(--mat-sys-on-surface, light-dark(#1f1f1f, #e3e3e3)));cursor:default}:host(:focus-visible){outline:2px solid var(--hk-tgb-focus-ring, var(--mat-sys-primary, light-dark(#0b57d0, #a8c7fa)));outline-offset:1px}:host([aria-disabled=true]){opacity:.38;cursor:default}@media(prefers-reduced-motion:reduce){:host{transition:none}}\n", ":host{inline-size:var(--hk-tgb-size, 32px)}:host :where([data-slot=icon]){display:inline-flex;align-items:center;justify-content:center;inline-size:var(--hk-tgb-icon-size, 18px);block-size:var(--hk-tgb-icon-size, 18px);font-size:var(--hk-tgb-icon-size, 18px);line-height:1}\n"] });
|
|
86
|
+
}
|
|
87
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ToggleButtonGroupIcon, decorators: [{
|
|
88
|
+
type: Component,
|
|
89
|
+
args: [{ selector: '[hkTgbIcon]', exportAs: 'hkTgbIcon', providers: [
|
|
90
|
+
{ provide: ToggleButtonGroupItem, useExisting: forwardRef(() => ToggleButtonGroupIcon) },
|
|
91
|
+
], template: "<!-- The icon box: sized by --hk-tgb-icon-size; the host's icon takes its\n font size and colour. -->\n<span data-slot=\"icon\"><ng-content /></span>\n", styles: [":host{position:relative;display:inline-flex;flex:none;align-items:center;justify-content:center;gap:var(--hk-tgb-item-gap, 4px);block-size:var(--hk-tgb-size, 32px);min-inline-size:var(--hk-tgb-size, 32px);margin:0;padding:0;box-sizing:border-box;border:0;border-radius:var(--hk-tgb-thumb-corner, var(--mat-sys-corner-full, 9999px));background:transparent;color:var(--hk-tgb-color, var(--mat-sys-on-surface-variant, light-dark(#444746, #c4c7c5)));font-family:var(--hk-tgb-font-family, var(--mat-sys-label-large-font, system-ui, sans-serif));font-size:var(--hk-tgb-font-size, var(--mat-sys-label-large-size, .875rem));font-weight:var(--hk-tgb-font-weight, var(--mat-sys-label-large-weight, 500));line-height:var(--hk-tgb-line-height, var(--mat-sys-label-large-line-height, 1.25rem));letter-spacing:var(--hk-tgb-letter-spacing, var(--mat-sys-label-large-tracking, .006rem));text-decoration:none;white-space:nowrap;cursor:pointer;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;touch-action:manipulation;transition:color .2s ease,background-color .15s ease}@media(hover:hover){:host(:hover:not([aria-checked=true],[aria-disabled=true])){color:var(--hk-tgb-hover-color, var(--mat-sys-on-surface, light-dark(#1f1f1f, #e3e3e3)));background-color:var(--hk-tgb-hover-background, color-mix(in oklch, var(--mat-sys-on-surface, light-dark(#1f1f1f, #e3e3e3)) 4%, transparent))}}:host([aria-checked=true]){color:var(--hk-tgb-checked-color, var(--mat-sys-on-surface, light-dark(#1f1f1f, #e3e3e3)));cursor:default}:host(:focus-visible){outline:2px solid var(--hk-tgb-focus-ring, var(--mat-sys-primary, light-dark(#0b57d0, #a8c7fa)));outline-offset:1px}:host([aria-disabled=true]){opacity:.38;cursor:default}@media(prefers-reduced-motion:reduce){:host{transition:none}}\n", ":host{inline-size:var(--hk-tgb-size, 32px)}:host :where([data-slot=icon]){display:inline-flex;align-items:center;justify-content:center;inline-size:var(--hk-tgb-icon-size, 18px);block-size:var(--hk-tgb-icon-size, 18px);font-size:var(--hk-tgb-icon-size, 18px);line-height:1}\n"] }]
|
|
92
|
+
}] });
|
|
93
|
+
/**
|
|
94
|
+
* A pill-shaped choice holding words — as wide as its text, padded by
|
|
95
|
+
* `--hk-tgb-padding`.
|
|
96
|
+
*
|
|
97
|
+
* ```html
|
|
98
|
+
* <button hkTgbText value="auto">Auto</button>
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
class ToggleButtonGroupText extends ToggleButtonGroupItem {
|
|
102
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ToggleButtonGroupText, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
103
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.6", type: ToggleButtonGroupText, isStandalone: true, selector: "[hkTgbText]", providers: [
|
|
104
|
+
{ provide: ToggleButtonGroupItem, useExisting: forwardRef(() => ToggleButtonGroupText) },
|
|
105
|
+
], exportAs: ["hkTgbText"], usesInheritance: true, ngImport: i0, template: "<ng-content />\n", styles: [":host{position:relative;display:inline-flex;flex:none;align-items:center;justify-content:center;gap:var(--hk-tgb-item-gap, 4px);block-size:var(--hk-tgb-size, 32px);min-inline-size:var(--hk-tgb-size, 32px);margin:0;padding:0;box-sizing:border-box;border:0;border-radius:var(--hk-tgb-thumb-corner, var(--mat-sys-corner-full, 9999px));background:transparent;color:var(--hk-tgb-color, var(--mat-sys-on-surface-variant, light-dark(#444746, #c4c7c5)));font-family:var(--hk-tgb-font-family, var(--mat-sys-label-large-font, system-ui, sans-serif));font-size:var(--hk-tgb-font-size, var(--mat-sys-label-large-size, .875rem));font-weight:var(--hk-tgb-font-weight, var(--mat-sys-label-large-weight, 500));line-height:var(--hk-tgb-line-height, var(--mat-sys-label-large-line-height, 1.25rem));letter-spacing:var(--hk-tgb-letter-spacing, var(--mat-sys-label-large-tracking, .006rem));text-decoration:none;white-space:nowrap;cursor:pointer;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;touch-action:manipulation;transition:color .2s ease,background-color .15s ease}@media(hover:hover){:host(:hover:not([aria-checked=true],[aria-disabled=true])){color:var(--hk-tgb-hover-color, var(--mat-sys-on-surface, light-dark(#1f1f1f, #e3e3e3)));background-color:var(--hk-tgb-hover-background, color-mix(in oklch, var(--mat-sys-on-surface, light-dark(#1f1f1f, #e3e3e3)) 4%, transparent))}}:host([aria-checked=true]){color:var(--hk-tgb-checked-color, var(--mat-sys-on-surface, light-dark(#1f1f1f, #e3e3e3)));cursor:default}:host(:focus-visible){outline:2px solid var(--hk-tgb-focus-ring, var(--mat-sys-primary, light-dark(#0b57d0, #a8c7fa)));outline-offset:1px}:host([aria-disabled=true]){opacity:.38;cursor:default}@media(prefers-reduced-motion:reduce){:host{transition:none}}\n", ":host{padding-inline:var(--hk-tgb-padding, 12px)}\n"] });
|
|
106
|
+
}
|
|
107
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ToggleButtonGroupText, decorators: [{
|
|
108
|
+
type: Component,
|
|
109
|
+
args: [{ selector: '[hkTgbText]', exportAs: 'hkTgbText', providers: [
|
|
110
|
+
{ provide: ToggleButtonGroupItem, useExisting: forwardRef(() => ToggleButtonGroupText) },
|
|
111
|
+
], template: "<ng-content />\n", styles: [":host{position:relative;display:inline-flex;flex:none;align-items:center;justify-content:center;gap:var(--hk-tgb-item-gap, 4px);block-size:var(--hk-tgb-size, 32px);min-inline-size:var(--hk-tgb-size, 32px);margin:0;padding:0;box-sizing:border-box;border:0;border-radius:var(--hk-tgb-thumb-corner, var(--mat-sys-corner-full, 9999px));background:transparent;color:var(--hk-tgb-color, var(--mat-sys-on-surface-variant, light-dark(#444746, #c4c7c5)));font-family:var(--hk-tgb-font-family, var(--mat-sys-label-large-font, system-ui, sans-serif));font-size:var(--hk-tgb-font-size, var(--mat-sys-label-large-size, .875rem));font-weight:var(--hk-tgb-font-weight, var(--mat-sys-label-large-weight, 500));line-height:var(--hk-tgb-line-height, var(--mat-sys-label-large-line-height, 1.25rem));letter-spacing:var(--hk-tgb-letter-spacing, var(--mat-sys-label-large-tracking, .006rem));text-decoration:none;white-space:nowrap;cursor:pointer;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;touch-action:manipulation;transition:color .2s ease,background-color .15s ease}@media(hover:hover){:host(:hover:not([aria-checked=true],[aria-disabled=true])){color:var(--hk-tgb-hover-color, var(--mat-sys-on-surface, light-dark(#1f1f1f, #e3e3e3)));background-color:var(--hk-tgb-hover-background, color-mix(in oklch, var(--mat-sys-on-surface, light-dark(#1f1f1f, #e3e3e3)) 4%, transparent))}}:host([aria-checked=true]){color:var(--hk-tgb-checked-color, var(--mat-sys-on-surface, light-dark(#1f1f1f, #e3e3e3)));cursor:default}:host(:focus-visible){outline:2px solid var(--hk-tgb-focus-ring, var(--mat-sys-primary, light-dark(#0b57d0, #a8c7fa)));outline-offset:1px}:host([aria-disabled=true]){opacity:.38;cursor:default}@media(prefers-reduced-motion:reduce){:host{transition:none}}\n", ":host{padding-inline:var(--hk-tgb-padding, 12px)}\n"] }]
|
|
112
|
+
}] });
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* A toggle button group: one of a few choices on a track, the picked one
|
|
116
|
+
* under a thumb that slides — and changes shape — to whichever is picked
|
|
117
|
+
* next. The pricing page's Monthly | Yearly; a theme's ☀ | ☾ | Auto.
|
|
118
|
+
*
|
|
119
|
+
* ```html
|
|
120
|
+
* <hk-tgb aria-label="Appearance" [(value)]="appearance">
|
|
121
|
+
* <button hkTgbIcon value="light" aria-label="Light"><i class="icon-sun"></i></button>
|
|
122
|
+
* <a hkTgbIcon value="dark" aria-label="Dark" href="?theme=dark"><mat-icon inline>dark_mode</mat-icon></a>
|
|
123
|
+
* <button hkTgbText value="auto">Auto</button>
|
|
124
|
+
* </hk-tgb>
|
|
125
|
+
* ```
|
|
126
|
+
*
|
|
127
|
+
* **The group is the control; the items are the host's own elements.** Any
|
|
128
|
+
* element becomes a choice by wearing one of two attributes — `hkTgbIcon`,
|
|
129
|
+
* a circle around an icon, or `hkTgbText`, a pill around words — and
|
|
130
|
+
* registers by being content, so the host writes the items as it would
|
|
131
|
+
* plain buttons or links. A pick is all a click does: a group inside a
|
|
132
|
+
* menu leaves the menu open.
|
|
133
|
+
*
|
|
134
|
+
* **A signal-forms control, optionally.** It implements `FormValueControl`:
|
|
135
|
+
* bind a field with `[formField]="settings.theme"` and the directive drives
|
|
136
|
+
* `value` both ways, pushes `disabled`, `readonly`, `required`, `invalid`
|
|
137
|
+
* and `touched` in, and hears `touch` when focus leaves the group. Without
|
|
138
|
+
* a form, `[(value)]` alone is the whole contract. The value is whatever
|
|
139
|
+
* the items carry — strings, an enum, objects — matched by identity; a
|
|
140
|
+
* value no item carries picks none.
|
|
141
|
+
*
|
|
142
|
+
* **Radio semantics.** The group is a `radiogroup`, each item a `radio`
|
|
143
|
+
* with `aria-checked`, and one tab stop: the picked item (the first
|
|
144
|
+
* enabled one while none is). The arrow keys of the group's `orientation`
|
|
145
|
+
* move the pick — Left and Right across, mirrored right to left; Up and
|
|
146
|
+
* Down down a vertical group — Home and End go to the ends, Space and
|
|
147
|
+
* Enter pick in place. The arrows across the other way are left alone for
|
|
148
|
+
* whatever the group sits in: a vertical menu moves on with them. A key
|
|
149
|
+
* the group acted on is spent: it does not travel on to a menu or toolbar
|
|
150
|
+
* around it, whose Space or Enter would act a second time.
|
|
151
|
+
*
|
|
152
|
+
* **The thumb follows the picked item's box.** Items differ in size — a
|
|
153
|
+
* circle, a pill as wide as its words — so the group reads the picked
|
|
154
|
+
* item's box once per pick, and again only when an item resizes (a font
|
|
155
|
+
* arriving, a language switched); the thumb slides there by CSS, its size
|
|
156
|
+
* and corner easing from one shape to the other, and stands still for
|
|
157
|
+
* reduced motion. It takes its first place without sliding.
|
|
158
|
+
*
|
|
159
|
+
* **Each part styles itself, encapsulated.** The group draws the track
|
|
160
|
+
* and the thumb; each item is a component on the host's element and brings
|
|
161
|
+
* its shape with it, so nothing reaches past an encapsulation boundary.
|
|
162
|
+
* Every look is a `--hk-tgb-*` token, defaulting to Material's system
|
|
163
|
+
* tokens, then a light-dark() pair.
|
|
164
|
+
*
|
|
165
|
+
* **State on the host:** `aria-disabled`, `aria-readonly`,
|
|
166
|
+
* `aria-required`, `aria-invalid` (invalid *and* touched, so nothing
|
|
167
|
+
* shouts early), `data-orientation`, and `data-empty` while nothing is
|
|
168
|
+
* picked. No Material, no icon font, no headless library.
|
|
169
|
+
*/
|
|
170
|
+
class ToggleButtonGroup {
|
|
171
|
+
/** The picked item's value. Two-way; the form's value. */
|
|
172
|
+
value = model.required(/* @ts-ignore */
|
|
173
|
+
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
174
|
+
/** Which way the items line up, and so which arrow keys are the
|
|
175
|
+
group's. */
|
|
176
|
+
orientation = input('horizontal', /* @ts-ignore */
|
|
177
|
+
...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
|
|
178
|
+
// ── the form contract: pushed in by the field directive ───────────────
|
|
179
|
+
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
180
|
+
/** The pick holds: the items can be reached and read, not changed. */
|
|
181
|
+
readonly = input(false, { ...(ngDevMode ? { debugName: "readonly" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
182
|
+
required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
183
|
+
invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
184
|
+
touched = input(false, { ...(ngDevMode ? { debugName: "touched" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
185
|
+
/** Focus left the group. The directive marks the field touched on it. */
|
|
186
|
+
touch = output();
|
|
187
|
+
/** The items, in the order they stand. */
|
|
188
|
+
items = contentChildren(ToggleButtonGroupItem, { ...(ngDevMode ? { debugName: "items" } : /* istanbul ignore next */ {}), descendants: true });
|
|
189
|
+
/** The item the group's value picks, if any. */
|
|
190
|
+
picked = computed(() => this.items().find((item) => item.checked()), /* @ts-ignore */
|
|
191
|
+
...(ngDevMode ? [{ debugName: "picked" }] : /* istanbul ignore next */ []));
|
|
192
|
+
/** The one item Tab lands on: the picked one, else the first that can be
|
|
193
|
+
picked — none while every one is disabled. */
|
|
194
|
+
tabStop = computed(() => {
|
|
195
|
+
const enabled = this.items().filter((item) => !item.blocked());
|
|
196
|
+
return enabled.find((item) => item.checked()) ?? enabled[0];
|
|
197
|
+
}, /* @ts-ignore */
|
|
198
|
+
...(ngDevMode ? [{ debugName: "tabStop" }] : /* istanbul ignore next */ []));
|
|
199
|
+
#host = inject(ElementRef).nativeElement;
|
|
200
|
+
/** Bumped when an item or the group resizes, for the thumb to read the
|
|
201
|
+
picked box again. */
|
|
202
|
+
#resized = signal(0, /* @ts-ignore */
|
|
203
|
+
...(ngDevMode ? [{ debugName: "#resized" }] : /* istanbul ignore next */ []));
|
|
204
|
+
constructor() {
|
|
205
|
+
this.#watchSizes();
|
|
206
|
+
afterRenderEffect({
|
|
207
|
+
earlyRead: () => {
|
|
208
|
+
this.#resized();
|
|
209
|
+
const element = this.picked()?.element;
|
|
210
|
+
return element
|
|
211
|
+
? {
|
|
212
|
+
x: element.offsetLeft,
|
|
213
|
+
y: element.offsetTop,
|
|
214
|
+
width: element.offsetWidth,
|
|
215
|
+
height: element.offsetHeight,
|
|
216
|
+
}
|
|
217
|
+
: null;
|
|
218
|
+
},
|
|
219
|
+
write: (box) => this.#place(box()),
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
/** Picks a value — an item's click, a key. A no-op while disabled or
|
|
223
|
+
read-only. */
|
|
224
|
+
select(value) {
|
|
225
|
+
if (this.disabled() || this.readonly())
|
|
226
|
+
return;
|
|
227
|
+
this.value.set(value);
|
|
228
|
+
}
|
|
229
|
+
/** Puts focus on the group's tab stop. */
|
|
230
|
+
focus(options) {
|
|
231
|
+
this.tabStop()?.focus(options);
|
|
232
|
+
}
|
|
233
|
+
/** Stands the thumb on the picked box. The first place is taken without
|
|
234
|
+
sliding: `data-placed` turns the motion on only after it. Nothing
|
|
235
|
+
picked keeps the last place, so the thumb fades out where it stood. */
|
|
236
|
+
#place(box) {
|
|
237
|
+
if (!box)
|
|
238
|
+
return;
|
|
239
|
+
const style = this.#host.style;
|
|
240
|
+
style.setProperty('--hk-tgb-thumb-x', `${box.x}px`);
|
|
241
|
+
style.setProperty('--hk-tgb-thumb-y', `${box.y}px`);
|
|
242
|
+
style.setProperty('--hk-tgb-thumb-width', `${box.width}px`);
|
|
243
|
+
style.setProperty('--hk-tgb-thumb-height', `${box.height}px`);
|
|
244
|
+
if (!this.#host.hasAttribute('data-placed') && typeof requestAnimationFrame === 'function') {
|
|
245
|
+
requestAnimationFrame(() => this.#host.setAttribute('data-placed', ''));
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
/** Watches the group and its items for size changes — the only thing
|
|
249
|
+
besides a pick that moves the picked box. */
|
|
250
|
+
#watchSizes() {
|
|
251
|
+
if (typeof ResizeObserver === 'undefined')
|
|
252
|
+
return;
|
|
253
|
+
const observer = new ResizeObserver(() => this.#resized.update((n) => n + 1));
|
|
254
|
+
inject(DestroyRef).onDestroy(() => observer.disconnect());
|
|
255
|
+
effect((onCleanup) => {
|
|
256
|
+
observer.observe(this.#host);
|
|
257
|
+
const items = this.items().map((item) => item.element);
|
|
258
|
+
for (const element of items)
|
|
259
|
+
observer.observe(element);
|
|
260
|
+
onCleanup(() => {
|
|
261
|
+
observer.unobserve(this.#host);
|
|
262
|
+
for (const element of items)
|
|
263
|
+
observer.unobserve(element);
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
onKeydown(event) {
|
|
268
|
+
const items = this.items().filter((item) => !item.blocked());
|
|
269
|
+
const current = items.findIndex((item) => item.element === event.target);
|
|
270
|
+
if (current < 0)
|
|
271
|
+
return;
|
|
272
|
+
const target = this.#target(event, current, items.length);
|
|
273
|
+
if (target === undefined)
|
|
274
|
+
return;
|
|
275
|
+
event.preventDefault();
|
|
276
|
+
event.stopPropagation();
|
|
277
|
+
const item = items[target];
|
|
278
|
+
item.focus();
|
|
279
|
+
this.select(item.value());
|
|
280
|
+
}
|
|
281
|
+
/** The index a key moves to, or `undefined` for a key that is not the
|
|
282
|
+
group's. Space and Enter pick the item in place — a button's own
|
|
283
|
+
click would too, but a menu around it hears the keydown first. */
|
|
284
|
+
#target(event, current, count) {
|
|
285
|
+
const vertical = this.orientation() === 'vertical';
|
|
286
|
+
const rtl = !vertical && getComputedStyle(this.#host).direction === 'rtl';
|
|
287
|
+
const step = (by) => (current + by + count) % count;
|
|
288
|
+
switch (event.key) {
|
|
289
|
+
case ' ':
|
|
290
|
+
case 'Enter':
|
|
291
|
+
return current;
|
|
292
|
+
case 'Home':
|
|
293
|
+
return 0;
|
|
294
|
+
case 'End':
|
|
295
|
+
return count - 1;
|
|
296
|
+
case 'ArrowLeft':
|
|
297
|
+
return vertical ? undefined : step(rtl ? 1 : -1);
|
|
298
|
+
case 'ArrowRight':
|
|
299
|
+
return vertical ? undefined : step(rtl ? -1 : 1);
|
|
300
|
+
case 'ArrowUp':
|
|
301
|
+
return vertical ? step(-1) : undefined;
|
|
302
|
+
case 'ArrowDown':
|
|
303
|
+
return vertical ? step(1) : undefined;
|
|
304
|
+
default:
|
|
305
|
+
return undefined;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
onFocusout(event) {
|
|
309
|
+
if (!this.#host.contains(event.relatedTarget))
|
|
310
|
+
this.touch.emit();
|
|
311
|
+
}
|
|
312
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ToggleButtonGroup, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
313
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.6", type: ToggleButtonGroup, isStandalone: true, selector: "hk-tgb", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touch: "touch" }, host: { attributes: { "role": "radiogroup" }, listeners: { "keydown": "onKeydown($event)", "focusout": "onFocusout($event)" }, properties: { "attr.aria-orientation": "orientation()", "attr.data-orientation": "orientation()", "attr.aria-disabled": "disabled() || null", "attr.aria-readonly": "readonly() || null", "attr.aria-required": "required() || null", "attr.aria-invalid": "(invalid() && touched()) || null", "attr.data-empty": "!picked() || null" } }, providers: [{ provide: TOGGLE_BUTTON_GROUP, useExisting: forwardRef(() => ToggleButtonGroup) }], queries: [{ propertyName: "items", predicate: ToggleButtonGroupItem, descendants: true, isSignal: true }], ngImport: i0, template: "<!-- The thumb: painted under the picked item, never laid out. The group\n places it from that item's box (`--hk-tgb-thumb-*`). -->\n<span data-slot=\"thumb\" aria-hidden=\"true\"></span>\n<ng-content />\n", styles: [":host{position:relative;isolation:isolate;display:inline-flex;align-items:center;gap:var(--hk-tgb-gap, 2px);padding:var(--hk-tgb-inset, 2px);box-sizing:border-box;border-radius:var(--hk-tgb-corner, var(--mat-sys-corner-full, 9999px));background-color:var(--hk-tgb-background, color-mix(in oklch, var(--mat-sys-on-surface, light-dark(#1f1f1f, #e3e3e3)) 4%, transparent));vertical-align:middle}:host([data-orientation=vertical]){flex-direction:column;align-items:stretch}:host :where([data-slot=thumb]){position:absolute;z-index:-1;top:0;left:0;width:var(--hk-tgb-thumb-width, 0);height:var(--hk-tgb-thumb-height, 0);box-sizing:border-box;border-radius:var(--hk-tgb-thumb-corner, var(--mat-sys-corner-full, 9999px));background-color:var(--hk-tgb-thumb, color-mix(in oklch, var(--mat-sys-on-surface, light-dark(#1f1f1f, #e3e3e3)) calc(var(--mat-sys-hover-state-layer-opacity, .08) * 100%), transparent));box-shadow:var(--hk-tgb-thumb-shadow, none);translate:var(--hk-tgb-thumb-x, 0) var(--hk-tgb-thumb-y, 0);pointer-events:none;transition:opacity .15s ease}:host([data-placed]) :where([data-slot=thumb]){--_duration: var(--hk-tgb-duration, .32s);--_easing: var(--hk-tgb-easing, cubic-bezier(.3, 1.2, .5, 1));transition:translate var(--_duration) var(--_easing),width var(--_duration) var(--_easing),height var(--_duration) var(--_easing),opacity .15s ease}:host([data-empty]) :where([data-slot=thumb]){opacity:0}:host([aria-disabled=true]) :where([data-slot=thumb]){opacity:.38}@media(prefers-reduced-motion:reduce){:host :where([data-slot=thumb]){transition:none}}\n"] });
|
|
314
|
+
}
|
|
315
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ToggleButtonGroup, decorators: [{
|
|
316
|
+
type: Component,
|
|
317
|
+
args: [{ selector: 'hk-tgb', providers: [{ provide: TOGGLE_BUTTON_GROUP, useExisting: forwardRef(() => ToggleButtonGroup) }], host: {
|
|
318
|
+
role: 'radiogroup',
|
|
319
|
+
'[attr.aria-orientation]': 'orientation()',
|
|
320
|
+
'[attr.data-orientation]': 'orientation()',
|
|
321
|
+
'[attr.aria-disabled]': 'disabled() || null',
|
|
322
|
+
'[attr.aria-readonly]': 'readonly() || null',
|
|
323
|
+
'[attr.aria-required]': 'required() || null',
|
|
324
|
+
'[attr.aria-invalid]': '(invalid() && touched()) || null',
|
|
325
|
+
'[attr.data-empty]': '!picked() || null',
|
|
326
|
+
'(keydown)': 'onKeydown($event)',
|
|
327
|
+
'(focusout)': 'onFocusout($event)',
|
|
328
|
+
}, template: "<!-- The thumb: painted under the picked item, never laid out. The group\n places it from that item's box (`--hk-tgb-thumb-*`). -->\n<span data-slot=\"thumb\" aria-hidden=\"true\"></span>\n<ng-content />\n", styles: [":host{position:relative;isolation:isolate;display:inline-flex;align-items:center;gap:var(--hk-tgb-gap, 2px);padding:var(--hk-tgb-inset, 2px);box-sizing:border-box;border-radius:var(--hk-tgb-corner, var(--mat-sys-corner-full, 9999px));background-color:var(--hk-tgb-background, color-mix(in oklch, var(--mat-sys-on-surface, light-dark(#1f1f1f, #e3e3e3)) 4%, transparent));vertical-align:middle}:host([data-orientation=vertical]){flex-direction:column;align-items:stretch}:host :where([data-slot=thumb]){position:absolute;z-index:-1;top:0;left:0;width:var(--hk-tgb-thumb-width, 0);height:var(--hk-tgb-thumb-height, 0);box-sizing:border-box;border-radius:var(--hk-tgb-thumb-corner, var(--mat-sys-corner-full, 9999px));background-color:var(--hk-tgb-thumb, color-mix(in oklch, var(--mat-sys-on-surface, light-dark(#1f1f1f, #e3e3e3)) calc(var(--mat-sys-hover-state-layer-opacity, .08) * 100%), transparent));box-shadow:var(--hk-tgb-thumb-shadow, none);translate:var(--hk-tgb-thumb-x, 0) var(--hk-tgb-thumb-y, 0);pointer-events:none;transition:opacity .15s ease}:host([data-placed]) :where([data-slot=thumb]){--_duration: var(--hk-tgb-duration, .32s);--_easing: var(--hk-tgb-easing, cubic-bezier(.3, 1.2, .5, 1));transition:translate var(--_duration) var(--_easing),width var(--_duration) var(--_easing),height var(--_duration) var(--_easing),opacity .15s ease}:host([data-empty]) :where([data-slot=thumb]){opacity:0}:host([aria-disabled=true]) :where([data-slot=thumb]){opacity:.38}@media(prefers-reduced-motion:reduce){:host :where([data-slot=thumb]){transition:none}}\n"] }]
|
|
329
|
+
}], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }, { type: i0.Output, args: ["valueChange"] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }], touch: [{ type: i0.Output, args: ["touch"] }], items: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => ToggleButtonGroupItem), { ...{
|
|
330
|
+
descendants: true,
|
|
331
|
+
}, isSignal: true }] }] } });
|
|
332
|
+
|
|
333
|
+
/*
|
|
334
|
+
* Public API Surface of @h-k-dev/angular-email-editor/toggle-button-group
|
|
335
|
+
*/
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Generated bundle index. Do not edit.
|
|
339
|
+
*/
|
|
340
|
+
|
|
341
|
+
export { TOGGLE_BUTTON_GROUP, ToggleButtonGroup, ToggleButtonGroupIcon, ToggleButtonGroupItem, ToggleButtonGroupText };
|
|
342
|
+
//# sourceMappingURL=h-k-dev-angular-email-editor-toggle-button-group.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"h-k-dev-angular-email-editor-toggle-button-group.mjs","sources":["../../../projects/angular-email-editor/toggle-button-group/toggle-button-group-item.ts","../../../projects/angular-email-editor/toggle-button-group/toggle-button-group-icon.html","../../../projects/angular-email-editor/toggle-button-group/toggle-button-group-text.html","../../../projects/angular-email-editor/toggle-button-group/toggle-button-group.ts","../../../projects/angular-email-editor/toggle-button-group/toggle-button-group.html","../../../projects/angular-email-editor/toggle-button-group/public-api.ts","../../../projects/angular-email-editor/toggle-button-group/h-k-dev-angular-email-editor-toggle-button-group.ts"],"sourcesContent":["import {\n Component,\n Directive,\n ElementRef,\n HostAttributeToken,\n InjectionToken,\n Signal,\n booleanAttribute,\n computed,\n forwardRef,\n inject,\n input,\n} from '@angular/core';\n\n/** What an item needs of the group it stands in — `hk-tgb` provides it, so\n an item finds its group without importing it. */\nexport interface ToggleButtonGroupHost<T = unknown> {\n readonly value: Signal<T>;\n readonly disabled: Signal<boolean>;\n readonly tabStop: Signal<ToggleButtonGroupItem<T> | undefined>;\n select(value: T): void;\n}\n\nexport const TOGGLE_BUTTON_GROUP = new InjectionToken<ToggleButtonGroupHost>('ToggleButtonGroup');\n\n/**\n * What every choice of an `hk-tgb` is, whichever shape it wears — the\n * group finds its items by this class. Use one of the shapes:\n * `hkTgbIcon` (round) or `hkTgbText` (a pill).\n *\n * An item makes its element — a `<button>`, an `<a>`, anything — a `radio`\n * that carries a `value` and is `aria-checked` while the group holds it.\n * The element and its content stay the host's: a font icon, a `mat-icon`,\n * an SVG, a word. A click picks it, and Space and Enter do too (the\n * group's keys), so a `<div>` works as well as a button. A `<button>`\n * without a `type` gets `type=\"button\"`, so an item in a form never\n * submits it. `aria-disabled` takes it out of the choices and the arrow\n * keys' way — it stays in the track, so the others keep their places —\n * and stops a disabled link from being followed.\n */\n@Directive({\n host: {\n role: 'radio',\n '[attr.type]': 'type',\n '[attr.aria-checked]': 'checked()',\n '[attr.aria-disabled]': 'blocked() || null',\n '[tabIndex]': 'group.tabStop() === this ? 0 : -1',\n '(click)': 'onClick($event)',\n },\n})\nexport abstract class ToggleButtonGroupItem<T = unknown> {\n /** What the group's value is while this item is picked. */\n readonly value = input.required<T>();\n\n /** This one can't be picked. */\n readonly disabled = input(false, { alias: 'aria-disabled', transform: booleanAttribute });\n\n protected readonly group = inject(TOGGLE_BUTTON_GROUP) as ToggleButtonGroupHost<T>;\n\n readonly element = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n\n /** `button` for a `<button>` the host gave no type; the host's own type\n otherwise, and none on any other element. */\n protected readonly type =\n this.element.tagName === 'BUTTON'\n ? (inject(new HostAttributeToken('type'), { optional: true }) ?? 'button')\n : null;\n\n /** The group holds this item's value. */\n readonly checked = computed(() => this.group.value() === this.value());\n\n /** Out of the choices: its own `aria-disabled`, or the group's. */\n readonly blocked = computed(() => this.disabled() || this.group.disabled());\n\n focus(options?: FocusOptions): void {\n this.element.focus(options);\n }\n\n protected onClick(event: MouseEvent): void {\n if (this.blocked()) {\n event.preventDefault();\n return;\n }\n this.group.select(this.value());\n }\n}\n\n/**\n * A round choice holding an icon: a circle as wide as the track is tall\n * (`--hk-tgb-size`). The icon sits in a box of the item's own\n * (`[data-slot=icon]`, `--hk-tgb-icon-size`), whose font size and colour it\n * takes: a `mat-icon` marked `inline`, a font icon's `<i>`, or an SVG sized\n * `1em` fits with no rule of the host's. It has no words, so name it:\n * `aria-label` (and a `title` for a tooltip).\n *\n * ```html\n * <button hkTgbIcon value=\"light\" aria-label=\"Light\"><mat-icon inline>light_mode</mat-icon></button>\n * ```\n *\n * A component on the host's element, not a directive: it brings its own\n * encapsulated look (`:host`), which nothing outside could give an element\n * the host wrote.\n */\n@Component({\n selector: '[hkTgbIcon]',\n exportAs: 'hkTgbIcon',\n templateUrl: './toggle-button-group-icon.html',\n styleUrls: ['./toggle-button-group-item.scss', './toggle-button-group-icon.scss'],\n providers: [\n { provide: ToggleButtonGroupItem, useExisting: forwardRef(() => ToggleButtonGroupIcon) },\n ],\n})\nexport class ToggleButtonGroupIcon<T = unknown> extends ToggleButtonGroupItem<T> {}\n\n/**\n * A pill-shaped choice holding words — as wide as its text, padded by\n * `--hk-tgb-padding`.\n *\n * ```html\n * <button hkTgbText value=\"auto\">Auto</button>\n * ```\n */\n@Component({\n selector: '[hkTgbText]',\n exportAs: 'hkTgbText',\n templateUrl: './toggle-button-group-text.html',\n styleUrls: ['./toggle-button-group-item.scss', './toggle-button-group-text.scss'],\n providers: [\n { provide: ToggleButtonGroupItem, useExisting: forwardRef(() => ToggleButtonGroupText) },\n ],\n})\nexport class ToggleButtonGroupText<T = unknown> extends ToggleButtonGroupItem<T> {}\n","<!-- The icon box: sized by --hk-tgb-icon-size; the host's icon takes its\n font size and colour. -->\n<span data-slot=\"icon\"><ng-content /></span>\n","<ng-content />\n","import {\n Component,\n DestroyRef,\n ElementRef,\n afterRenderEffect,\n booleanAttribute,\n computed,\n contentChildren,\n effect,\n forwardRef,\n inject,\n input,\n model,\n output,\n signal,\n} from '@angular/core';\nimport type { FormValueControl } from '@angular/forms/signals';\nimport { TOGGLE_BUTTON_GROUP, ToggleButtonGroupItem } from './toggle-button-group-item';\n\n/** The picked item's box, in the group's own coordinates. */\ninterface ThumbBox {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/**\n * A toggle button group: one of a few choices on a track, the picked one\n * under a thumb that slides — and changes shape — to whichever is picked\n * next. The pricing page's Monthly | Yearly; a theme's ☀ | ☾ | Auto.\n *\n * ```html\n * <hk-tgb aria-label=\"Appearance\" [(value)]=\"appearance\">\n * <button hkTgbIcon value=\"light\" aria-label=\"Light\"><i class=\"icon-sun\"></i></button>\n * <a hkTgbIcon value=\"dark\" aria-label=\"Dark\" href=\"?theme=dark\"><mat-icon inline>dark_mode</mat-icon></a>\n * <button hkTgbText value=\"auto\">Auto</button>\n * </hk-tgb>\n * ```\n *\n * **The group is the control; the items are the host's own elements.** Any\n * element becomes a choice by wearing one of two attributes — `hkTgbIcon`,\n * a circle around an icon, or `hkTgbText`, a pill around words — and\n * registers by being content, so the host writes the items as it would\n * plain buttons or links. A pick is all a click does: a group inside a\n * menu leaves the menu open.\n *\n * **A signal-forms control, optionally.** It implements `FormValueControl`:\n * bind a field with `[formField]=\"settings.theme\"` and the directive drives\n * `value` both ways, pushes `disabled`, `readonly`, `required`, `invalid`\n * and `touched` in, and hears `touch` when focus leaves the group. Without\n * a form, `[(value)]` alone is the whole contract. The value is whatever\n * the items carry — strings, an enum, objects — matched by identity; a\n * value no item carries picks none.\n *\n * **Radio semantics.** The group is a `radiogroup`, each item a `radio`\n * with `aria-checked`, and one tab stop: the picked item (the first\n * enabled one while none is). The arrow keys of the group's `orientation`\n * move the pick — Left and Right across, mirrored right to left; Up and\n * Down down a vertical group — Home and End go to the ends, Space and\n * Enter pick in place. The arrows across the other way are left alone for\n * whatever the group sits in: a vertical menu moves on with them. A key\n * the group acted on is spent: it does not travel on to a menu or toolbar\n * around it, whose Space or Enter would act a second time.\n *\n * **The thumb follows the picked item's box.** Items differ in size — a\n * circle, a pill as wide as its words — so the group reads the picked\n * item's box once per pick, and again only when an item resizes (a font\n * arriving, a language switched); the thumb slides there by CSS, its size\n * and corner easing from one shape to the other, and stands still for\n * reduced motion. It takes its first place without sliding.\n *\n * **Each part styles itself, encapsulated.** The group draws the track\n * and the thumb; each item is a component on the host's element and brings\n * its shape with it, so nothing reaches past an encapsulation boundary.\n * Every look is a `--hk-tgb-*` token, defaulting to Material's system\n * tokens, then a light-dark() pair.\n *\n * **State on the host:** `aria-disabled`, `aria-readonly`,\n * `aria-required`, `aria-invalid` (invalid *and* touched, so nothing\n * shouts early), `data-orientation`, and `data-empty` while nothing is\n * picked. No Material, no icon font, no headless library.\n */\n@Component({\n selector: 'hk-tgb',\n templateUrl: './toggle-button-group.html',\n styleUrl: './toggle-button-group.scss',\n providers: [{ provide: TOGGLE_BUTTON_GROUP, useExisting: forwardRef(() => ToggleButtonGroup) }],\n host: {\n role: 'radiogroup',\n '[attr.aria-orientation]': 'orientation()',\n '[attr.data-orientation]': 'orientation()',\n '[attr.aria-disabled]': 'disabled() || null',\n '[attr.aria-readonly]': 'readonly() || null',\n '[attr.aria-required]': 'required() || null',\n '[attr.aria-invalid]': '(invalid() && touched()) || null',\n '[attr.data-empty]': '!picked() || null',\n '(keydown)': 'onKeydown($event)',\n '(focusout)': 'onFocusout($event)',\n },\n})\nexport class ToggleButtonGroup<T = unknown> implements FormValueControl<T> {\n /** The picked item's value. Two-way; the form's value. */\n readonly value = model.required<T>();\n\n /** Which way the items line up, and so which arrow keys are the\n group's. */\n readonly orientation = input<'horizontal' | 'vertical'>('horizontal');\n\n // ── the form contract: pushed in by the field directive ───────────────\n\n readonly disabled = input(false, { transform: booleanAttribute });\n /** The pick holds: the items can be reached and read, not changed. */\n readonly readonly = input(false, { transform: booleanAttribute });\n readonly required = input(false, { transform: booleanAttribute });\n readonly invalid = input(false, { transform: booleanAttribute });\n readonly touched = input(false, { transform: booleanAttribute });\n\n /** Focus left the group. The directive marks the field touched on it. */\n readonly touch = output<void>();\n\n /** The items, in the order they stand. */\n protected readonly items = contentChildren<ToggleButtonGroupItem<T>>(ToggleButtonGroupItem, {\n descendants: true,\n });\n\n /** The item the group's value picks, if any. */\n protected readonly picked = computed(() => this.items().find((item) => item.checked()));\n\n /** The one item Tab lands on: the picked one, else the first that can be\n picked — none while every one is disabled. */\n readonly tabStop = computed(() => {\n const enabled = this.items().filter((item) => !item.blocked());\n return enabled.find((item) => item.checked()) ?? enabled[0];\n });\n\n readonly #host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n\n /** Bumped when an item or the group resizes, for the thumb to read the\n picked box again. */\n readonly #resized = signal(0);\n\n constructor() {\n this.#watchSizes();\n afterRenderEffect({\n earlyRead: (): ThumbBox | null => {\n this.#resized();\n const element = this.picked()?.element;\n return element\n ? {\n x: element.offsetLeft,\n y: element.offsetTop,\n width: element.offsetWidth,\n height: element.offsetHeight,\n }\n : null;\n },\n write: (box) => this.#place(box()),\n });\n }\n\n /** Picks a value — an item's click, a key. A no-op while disabled or\n read-only. */\n select(value: T): void {\n if (this.disabled() || this.readonly()) return;\n this.value.set(value);\n }\n\n /** Puts focus on the group's tab stop. */\n focus(options?: FocusOptions): void {\n this.tabStop()?.focus(options);\n }\n\n /** Stands the thumb on the picked box. The first place is taken without\n sliding: `data-placed` turns the motion on only after it. Nothing\n picked keeps the last place, so the thumb fades out where it stood. */\n #place(box: ThumbBox | null): void {\n if (!box) return;\n const style = this.#host.style;\n style.setProperty('--hk-tgb-thumb-x', `${box.x}px`);\n style.setProperty('--hk-tgb-thumb-y', `${box.y}px`);\n style.setProperty('--hk-tgb-thumb-width', `${box.width}px`);\n style.setProperty('--hk-tgb-thumb-height', `${box.height}px`);\n if (!this.#host.hasAttribute('data-placed') && typeof requestAnimationFrame === 'function') {\n requestAnimationFrame(() => this.#host.setAttribute('data-placed', ''));\n }\n }\n\n /** Watches the group and its items for size changes — the only thing\n besides a pick that moves the picked box. */\n #watchSizes(): void {\n if (typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(() => this.#resized.update((n) => n + 1));\n inject(DestroyRef).onDestroy(() => observer.disconnect());\n effect((onCleanup) => {\n observer.observe(this.#host);\n const items = this.items().map((item) => item.element);\n for (const element of items) observer.observe(element);\n onCleanup(() => {\n observer.unobserve(this.#host);\n for (const element of items) observer.unobserve(element);\n });\n });\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n const items = this.items().filter((item) => !item.blocked());\n const current = items.findIndex((item) => item.element === event.target);\n if (current < 0) return;\n\n const target = this.#target(event, current, items.length);\n if (target === undefined) return;\n event.preventDefault();\n event.stopPropagation();\n const item = items[target];\n item.focus();\n this.select(item.value());\n }\n\n /** The index a key moves to, or `undefined` for a key that is not the\n group's. Space and Enter pick the item in place — a button's own\n click would too, but a menu around it hears the keydown first. */\n #target(event: KeyboardEvent, current: number, count: number): number | undefined {\n const vertical = this.orientation() === 'vertical';\n const rtl = !vertical && getComputedStyle(this.#host).direction === 'rtl';\n const step = (by: number) => (current + by + count) % count;\n switch (event.key) {\n case ' ':\n case 'Enter':\n return current;\n case 'Home':\n return 0;\n case 'End':\n return count - 1;\n case 'ArrowLeft':\n return vertical ? undefined : step(rtl ? 1 : -1);\n case 'ArrowRight':\n return vertical ? undefined : step(rtl ? -1 : 1);\n case 'ArrowUp':\n return vertical ? step(-1) : undefined;\n case 'ArrowDown':\n return vertical ? step(1) : undefined;\n default:\n return undefined;\n }\n }\n\n protected onFocusout(event: FocusEvent): void {\n if (!this.#host.contains(event.relatedTarget as Node | null)) this.touch.emit();\n }\n}\n","<!-- The thumb: painted under the picked item, never laid out. The group\n places it from that item's box (`--hk-tgb-thumb-*`). -->\n<span data-slot=\"thumb\" aria-hidden=\"true\"></span>\n<ng-content />\n","/*\n * Public API Surface of @h-k-dev/angular-email-editor/toggle-button-group\n */\n\nexport * from './toggle-button-group';\nexport * from './toggle-button-group-item';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;MAuBa,mBAAmB,GAAG,IAAI,cAAc,CAAwB,mBAAmB;AAEhG;;;;;;;;;;;;;;AAcG;MAWmB,qBAAqB,CAAA;;IAEhC,KAAK,GAAG,KAAK,CAAC,QAAQ;8EAAK;;AAG3B,IAAA,QAAQ,GAAG,KAAK,CAAC,KAAK,EAAA,EAAA,IAAA,SAAA,GAAA,EAAA,SAAA,EAAA,UAAA,EAAA,8BAAA,EAAA,CAAA,EAAI,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,gBAAgB,GAAG;AAEtE,IAAA,KAAK,GAAG,MAAM,CAAC,mBAAmB,CAA6B;AAEzE,IAAA,OAAO,GAAG,MAAM,CAA0B,UAAU,CAAC,CAAC,aAAa;AAE5E;AACgD;AAC7B,IAAA,IAAI,GACrB,IAAI,CAAC,OAAO,CAAC,OAAO,KAAK;AACvB,WAAG,MAAM,CAAC,IAAI,kBAAkB,CAAC,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,IAAI,QAAQ;UACvE,IAAI;;AAGD,IAAA,OAAO,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,IAAI,CAAC,KAAK,EAAE;gFAAC;;AAG7D,IAAA,OAAO,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,QAAQ,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE;gFAAC;AAE3E,IAAA,KAAK,CAAC,OAAsB,EAAA;AAC1B,QAAA,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC;IAC7B;AAEU,IAAA,OAAO,CAAC,KAAiB,EAAA;AACjC,QAAA,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE;YAClB,KAAK,CAAC,cAAc,EAAE;YACtB;QACF;QACA,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;IACjC;uGAlCoB,qBAAqB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAArB,qBAAqB,EAAA,YAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,eAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,MAAA,EAAA,OAAA,EAAA,EAAA,SAAA,EAAA,EAAA,OAAA,EAAA,iBAAA,EAAA,EAAA,UAAA,EAAA,EAAA,WAAA,EAAA,MAAA,EAAA,mBAAA,EAAA,WAAA,EAAA,oBAAA,EAAA,mBAAA,EAAA,UAAA,EAAA,mCAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;2FAArB,qBAAqB,EAAA,UAAA,EAAA,CAAA;kBAV1C,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,IAAI,EAAE;AACJ,wBAAA,IAAI,EAAE,OAAO;AACb,wBAAA,aAAa,EAAE,MAAM;AACrB,wBAAA,qBAAqB,EAAE,WAAW;AAClC,wBAAA,sBAAsB,EAAE,mBAAmB;AAC3C,wBAAA,YAAY,EAAE,mCAAmC;AACjD,wBAAA,SAAS,EAAE,iBAAiB;AAC7B,qBAAA;AACF,iBAAA;;AAsCD;;;;;;;;;;;;;;;AAeG;AAUG,MAAO,qBAAmC,SAAQ,qBAAwB,CAAA;uGAAnE,qBAAqB,EAAA,IAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAArB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,qBAAqB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,aAAA,EAAA,SAAA,EAJrB;AACT,YAAA,EAAE,OAAO,EAAE,qBAAqB,EAAE,WAAW,EAAE,UAAU,CAAC,MAAM,qBAAqB,CAAC,EAAE;AACzF,SAAA,EAAA,QAAA,EAAA,CAAA,WAAA,CAAA,EAAA,eAAA,EAAA,IAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EC9GH,6JAGA,EAAA,MAAA,EAAA,CAAA,8vDAAA,EAAA,qRAAA,CAAA,EAAA,CAAA;;2FD6Ga,qBAAqB,EAAA,UAAA,EAAA,CAAA;kBATjC,SAAS;+BACE,aAAa,EAAA,QAAA,EACb,WAAW,EAAA,SAAA,EAGV;AACT,wBAAA,EAAE,OAAO,EAAE,qBAAqB,EAAE,WAAW,EAAE,UAAU,CAAC,MAAK,qBAAsB,CAAC,EAAE;AACzF,qBAAA,EAAA,QAAA,EAAA,6JAAA,EAAA,MAAA,EAAA,CAAA,8vDAAA,EAAA,qRAAA,CAAA,EAAA;;AAIH;;;;;;;AAOG;AAUG,MAAO,qBAAmC,SAAQ,qBAAwB,CAAA;uGAAnE,qBAAqB,EAAA,IAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAArB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,qBAAqB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,aAAA,EAAA,SAAA,EAJrB;AACT,YAAA,EAAE,OAAO,EAAE,qBAAqB,EAAE,WAAW,EAAE,UAAU,CAAC,MAAM,qBAAqB,CAAC,EAAE;AACzF,SAAA,EAAA,QAAA,EAAA,CAAA,WAAA,CAAA,EAAA,eAAA,EAAA,IAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EEjIH,kBACA,EAAA,MAAA,EAAA,CAAA,8vDAAA,EAAA,qDAAA,CAAA,EAAA,CAAA;;2FFkIa,qBAAqB,EAAA,UAAA,EAAA,CAAA;kBATjC,SAAS;+BACE,aAAa,EAAA,QAAA,EACb,WAAW,EAAA,SAAA,EAGV;AACT,wBAAA,EAAE,OAAO,EAAE,qBAAqB,EAAE,WAAW,EAAE,UAAU,CAAC,MAAK,qBAAsB,CAAC,EAAE;AACzF,qBAAA,EAAA,QAAA,EAAA,kBAAA,EAAA,MAAA,EAAA,CAAA,8vDAAA,EAAA,qDAAA,CAAA,EAAA;;;AGtGH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuDG;MAmBU,iBAAiB,CAAA;;IAEnB,KAAK,GAAG,KAAK,CAAC,QAAQ;8EAAK;AAEpC;AACe;IACN,WAAW,GAAG,KAAK,CAA4B,YAAY;oFAAC;;IAI5D,QAAQ,GAAG,KAAK,CAAC,KAAK,gFAAI,SAAS,EAAE,gBAAgB,EAAA,CAAG;;IAExD,QAAQ,GAAG,KAAK,CAAC,KAAK,gFAAI,SAAS,EAAE,gBAAgB,EAAA,CAAG;IACxD,QAAQ,GAAG,KAAK,CAAC,KAAK,gFAAI,SAAS,EAAE,gBAAgB,EAAA,CAAG;IACxD,OAAO,GAAG,KAAK,CAAC,KAAK,+EAAI,SAAS,EAAE,gBAAgB,EAAA,CAAG;IACvD,OAAO,GAAG,KAAK,CAAC,KAAK,+EAAI,SAAS,EAAE,gBAAgB,EAAA,CAAG;;IAGvD,KAAK,GAAG,MAAM,EAAQ;;IAGZ,KAAK,GAAG,eAAe,CAA2B,qBAAqB,6EACxF,WAAW,EAAE,IAAI,EAAA,CACjB;;IAGiB,MAAM,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC;+EAAC;AAEvF;AACiD;AACxC,IAAA,OAAO,GAAG,QAAQ,CAAC,MAAK;QAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;AAC9D,QAAA,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC;IAC7D,CAAC;gFAAC;AAEO,IAAA,KAAK,GAAG,MAAM,CAA0B,UAAU,CAAC,CAAC,aAAa;AAE1E;AACwB;IACf,QAAQ,GAAG,MAAM,CAAC,CAAC;iFAAC;AAE7B,IAAA,WAAA,GAAA;QACE,IAAI,CAAC,WAAW,EAAE;AAClB,QAAA,iBAAiB,CAAC;YAChB,SAAS,EAAE,MAAsB;gBAC/B,IAAI,CAAC,QAAQ,EAAE;gBACf,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,OAAO;AACtC,gBAAA,OAAO;AACL,sBAAE;wBACE,CAAC,EAAE,OAAO,CAAC,UAAU;wBACrB,CAAC,EAAE,OAAO,CAAC,SAAS;wBACpB,KAAK,EAAE,OAAO,CAAC,WAAW;wBAC1B,MAAM,EAAE,OAAO,CAAC,YAAY;AAC7B;sBACD,IAAI;YACV,CAAC;AACD,YAAA,KAAK,EAAE,CAAC,GAAG,KAAK,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;AACnC,SAAA,CAAC;IACJ;AAEA;AACiB;AACjB,IAAA,MAAM,CAAC,KAAQ,EAAA;QACb,IAAI,IAAI,CAAC,QAAQ,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE;YAAE;AACxC,QAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC;IACvB;;AAGA,IAAA,KAAK,CAAC,OAAsB,EAAA;QAC1B,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,OAAO,CAAC;IAChC;AAEA;;AAE0E;AAC1E,IAAA,MAAM,CAAC,GAAoB,EAAA;AACzB,QAAA,IAAI,CAAC,GAAG;YAAE;AACV,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK;QAC9B,KAAK,CAAC,WAAW,CAAC,kBAAkB,EAAE,CAAA,EAAG,GAAG,CAAC,CAAC,CAAA,EAAA,CAAI,CAAC;QACnD,KAAK,CAAC,WAAW,CAAC,kBAAkB,EAAE,CAAA,EAAG,GAAG,CAAC,CAAC,CAAA,EAAA,CAAI,CAAC;QACnD,KAAK,CAAC,WAAW,CAAC,sBAAsB,EAAE,CAAA,EAAG,GAAG,CAAC,KAAK,CAAA,EAAA,CAAI,CAAC;QAC3D,KAAK,CAAC,WAAW,CAAC,uBAAuB,EAAE,CAAA,EAAG,GAAG,CAAC,MAAM,CAAA,EAAA,CAAI,CAAC;AAC7D,QAAA,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,aAAa,CAAC,IAAI,OAAO,qBAAqB,KAAK,UAAU,EAAE;AAC1F,YAAA,qBAAqB,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;QACzE;IACF;AAEA;AACgD;IAChD,WAAW,GAAA;QACT,IAAI,OAAO,cAAc,KAAK,WAAW;YAAE;QAC3C,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;AAC7E,QAAA,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,MAAM,QAAQ,CAAC,UAAU,EAAE,CAAC;AACzD,QAAA,MAAM,CAAC,CAAC,SAAS,KAAI;AACnB,YAAA,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;AAC5B,YAAA,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,OAAO,CAAC;YACtD,KAAK,MAAM,OAAO,IAAI,KAAK;AAAE,gBAAA,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC;YACtD,SAAS,CAAC,MAAK;AACb,gBAAA,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC;gBAC9B,KAAK,MAAM,OAAO,IAAI,KAAK;AAAE,oBAAA,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC;AAC1D,YAAA,CAAC,CAAC;AACJ,QAAA,CAAC,CAAC;IACJ;AAEU,IAAA,SAAS,CAAC,KAAoB,EAAA;QACtC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;AAC5D,QAAA,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,OAAO,KAAK,KAAK,CAAC,MAAM,CAAC;QACxE,IAAI,OAAO,GAAG,CAAC;YAAE;AAEjB,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC;QACzD,IAAI,MAAM,KAAK,SAAS;YAAE;QAC1B,KAAK,CAAC,cAAc,EAAE;QACtB,KAAK,CAAC,eAAe,EAAE;AACvB,QAAA,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,KAAK,EAAE;QACZ,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;IAC3B;AAEA;;AAEqE;AACrE,IAAA,OAAO,CAAC,KAAoB,EAAE,OAAe,EAAE,KAAa,EAAA;QAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,KAAK,UAAU;AAClD,QAAA,MAAM,GAAG,GAAG,CAAC,QAAQ,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,KAAK,KAAK;AACzE,QAAA,MAAM,IAAI,GAAG,CAAC,EAAU,KAAK,CAAC,OAAO,GAAG,EAAE,GAAG,KAAK,IAAI,KAAK;AAC3D,QAAA,QAAQ,KAAK,CAAC,GAAG;AACf,YAAA,KAAK,GAAG;AACR,YAAA,KAAK,OAAO;AACV,gBAAA,OAAO,OAAO;AAChB,YAAA,KAAK,MAAM;AACT,gBAAA,OAAO,CAAC;AACV,YAAA,KAAK,KAAK;gBACR,OAAO,KAAK,GAAG,CAAC;AAClB,YAAA,KAAK,WAAW;gBACd,OAAO,QAAQ,GAAG,SAAS,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;AAClD,YAAA,KAAK,YAAY;gBACf,OAAO,QAAQ,GAAG,SAAS,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;AAClD,YAAA,KAAK,SAAS;AACZ,gBAAA,OAAO,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS;AACxC,YAAA,KAAK,WAAW;AACd,gBAAA,OAAO,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,SAAS;AACvC,YAAA;AACE,gBAAA,OAAO,SAAS;;IAEtB;AAEU,IAAA,UAAU,CAAC,KAAiB,EAAA;QACpC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,aAA4B,CAAC;AAAE,YAAA,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;IACjF;uGApJW,iBAAiB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAjB,iBAAiB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,QAAA,EAAA,MAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,WAAA,EAAA,EAAA,iBAAA,EAAA,aAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,KAAA,EAAA,aAAA,EAAA,KAAA,EAAA,OAAA,EAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,MAAA,EAAA,YAAA,EAAA,EAAA,SAAA,EAAA,EAAA,SAAA,EAAA,mBAAA,EAAA,UAAA,EAAA,oBAAA,EAAA,EAAA,UAAA,EAAA,EAAA,uBAAA,EAAA,eAAA,EAAA,uBAAA,EAAA,eAAA,EAAA,oBAAA,EAAA,oBAAA,EAAA,oBAAA,EAAA,oBAAA,EAAA,oBAAA,EAAA,oBAAA,EAAA,mBAAA,EAAA,kCAAA,EAAA,iBAAA,EAAA,mBAAA,EAAA,EAAA,EAAA,SAAA,EAdjB,CAAC,EAAE,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,UAAU,CAAC,MAAM,iBAAiB,CAAC,EAAE,CAAC,EAAA,OAAA,EAAA,CAAA,EAAA,YAAA,EAAA,OAAA,EAAA,SAAA,EAmC1B,qBAAqB,EAAA,WAAA,EAAA,IAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EC1H5F,mNAIA,EAAA,MAAA,EAAA,CAAA,8hDAAA,CAAA,EAAA,CAAA;;2FDiGa,iBAAiB,EAAA,UAAA,EAAA,CAAA;kBAlB7B,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,QAAQ,aAGP,CAAC,EAAE,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,UAAU,CAAC,uBAAuB,CAAC,EAAE,CAAC,EAAA,IAAA,EACzF;AACJ,wBAAA,IAAI,EAAE,YAAY;AAClB,wBAAA,yBAAyB,EAAE,eAAe;AAC1C,wBAAA,yBAAyB,EAAE,eAAe;AAC1C,wBAAA,sBAAsB,EAAE,oBAAoB;AAC5C,wBAAA,sBAAsB,EAAE,oBAAoB;AAC5C,wBAAA,sBAAsB,EAAE,oBAAoB;AAC5C,wBAAA,qBAAqB,EAAE,kCAAkC;AACzD,wBAAA,mBAAmB,EAAE,mBAAmB;AACxC,wBAAA,WAAW,EAAE,mBAAmB;AAChC,wBAAA,YAAY,EAAE,oBAAoB;AACnC,qBAAA,EAAA,QAAA,EAAA,mNAAA,EAAA,MAAA,EAAA,CAAA,8hDAAA,CAAA,EAAA;AAuBoE,SAAA,CAAA,EAAA,cAAA,EAAA,MAAA,EAAA,EAAA,cAAA,EAAA,EAAA,KAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,EAAA,CAAA,MAAA,EAAA,IAAA,EAAA,CAAA,aAAA,CAAA,EAAA,CAAA,EAAA,WAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,aAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,QAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,UAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,QAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,UAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,QAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,UAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,SAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,SAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,KAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,MAAA,EAAA,IAAA,EAAA,CAAA,OAAA,CAAA,EAAA,CAAA,EAAA,KAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,eAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,UAAA,CAAA,MAAA,qBAAqB,CAAA,EAAA,EAAA,GAAE;AAC1F,4BAAA,WAAW,EAAE,IAAI;AAClB,yBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,EAAA,CAAA;;AE5HH;;AAEG;;ACFH;;AAEG;;;;"}
|
|
@@ -7073,7 +7073,7 @@ function canonicalHead(head) {
|
|
|
7073
7073
|
if (node.nodeType === Node.TEXT_NODE) {
|
|
7074
7074
|
const text = (node.nodeValue ?? '').replace(/\s+/g, ' ').trim();
|
|
7075
7075
|
if (text)
|
|
7076
|
-
lines.push(escapeText$
|
|
7076
|
+
lines.push(escapeText$2(text));
|
|
7077
7077
|
continue;
|
|
7078
7078
|
}
|
|
7079
7079
|
if (!(node instanceof Element))
|
|
@@ -7090,7 +7090,7 @@ function canonicalHead(head) {
|
|
|
7090
7090
|
}
|
|
7091
7091
|
else {
|
|
7092
7092
|
const text = (node.textContent ?? '').replace(/\s+/g, ' ').trim();
|
|
7093
|
-
lines.push(`${open}${escapeText$
|
|
7093
|
+
lines.push(`${open}${escapeText$2(text)}</${tag}>`);
|
|
7094
7094
|
}
|
|
7095
7095
|
}
|
|
7096
7096
|
return lines.join('\n');
|
|
@@ -7152,7 +7152,7 @@ const BOOLEAN_ATTRIBUTES = new Set([
|
|
|
7152
7152
|
'reversed',
|
|
7153
7153
|
'selected',
|
|
7154
7154
|
]);
|
|
7155
|
-
function escapeText$
|
|
7155
|
+
function escapeText$2(text) {
|
|
7156
7156
|
return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
7157
7157
|
}
|
|
7158
7158
|
function escapeAttribute$2(value) {
|
|
@@ -8527,7 +8527,7 @@ function emailSizeBudget(html) {
|
|
|
8527
8527
|
}
|
|
8528
8528
|
|
|
8529
8529
|
/** Escapes text for an HTML text position. */
|
|
8530
|
-
function escapeText(text) {
|
|
8530
|
+
function escapeText$1(text) {
|
|
8531
8531
|
return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
8532
8532
|
}
|
|
8533
8533
|
/** Escapes a value for a double-quoted HTML attribute. */
|
|
@@ -9627,7 +9627,7 @@ const trimWhitespace = (text) => text.replace(/^ | $/g, '');
|
|
|
9627
9627
|
/** Text for the source pane: a Unicode space shows as its reference
|
|
9628
9628
|
(` `, ` `) — the raw character is invisible there and reads
|
|
9629
9629
|
as a plain space, or as nothing. */
|
|
9630
|
-
const sourceText = (text) => escapeText(text).replace(/[\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]/g, (ch) => (ch === '\u00a0' ? ' ' : `&#${ch.charCodeAt(0)};`));
|
|
9630
|
+
const sourceText = (text) => escapeText$1(text).replace(/[\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]/g, (ch) => (ch === '\u00a0' ? ' ' : `&#${ch.charCodeAt(0)};`));
|
|
9631
9631
|
|
|
9632
9632
|
/**
|
|
9633
9633
|
* Dark mode, written into the email: the palette's own dark variants
|
|
@@ -10090,6 +10090,27 @@ function emailPlainText(html) {
|
|
|
10090
10090
|
renderChildren(body, lines, '');
|
|
10091
10091
|
return lines.join('\n');
|
|
10092
10092
|
}
|
|
10093
|
+
/**
|
|
10094
|
+
* Text as a plain-text message's document — the way back from
|
|
10095
|
+
* {@link emailPlainText}: one email line (`<div>`) per line of text, an
|
|
10096
|
+
* empty line as `<div><br></div>`, every character escaped. What an editor
|
|
10097
|
+
* on `plainTextExtensions` takes when a message turns to plain text:
|
|
10098
|
+
*
|
|
10099
|
+
* ```ts
|
|
10100
|
+
* editor.setContent(plainTextHTML(emailPlainText(html)));
|
|
10101
|
+
* ```
|
|
10102
|
+
*
|
|
10103
|
+
* and `emailPlainText` reads it back as the same lines.
|
|
10104
|
+
*/
|
|
10105
|
+
function plainTextHTML(text) {
|
|
10106
|
+
return text
|
|
10107
|
+
.split(/\r\n?|\n/)
|
|
10108
|
+
.map((line) => (line ? `<div>${escapeText(line)}</div>` : '<div><br></div>'))
|
|
10109
|
+
.join('');
|
|
10110
|
+
}
|
|
10111
|
+
function escapeText(text) {
|
|
10112
|
+
return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
10113
|
+
}
|
|
10093
10114
|
function renderChildren(parent, lines, prefix) {
|
|
10094
10115
|
for (const child of Array.from(parent.children))
|
|
10095
10116
|
renderBlock(child, lines, prefix);
|
|
@@ -10222,7 +10243,7 @@ function emailDocument(html, options = {}) {
|
|
|
10222
10243
|
'<meta name="viewport" content="width=device-width, initial-scale=1">' +
|
|
10223
10244
|
'<meta name="x-apple-disable-message-reformatting">' +
|
|
10224
10245
|
(dark ? COLOR_SCHEME_META : '') +
|
|
10225
|
-
`<title>${escapeText(title ?? '')}</title>` +
|
|
10246
|
+
`<title>${escapeText$1(title ?? '')}</title>` +
|
|
10226
10247
|
OUTLOOK_SETTINGS +
|
|
10227
10248
|
`<style type="text/css">${STACK_RULE}</style>` +
|
|
10228
10249
|
(dark ? `<style type="text/css">${darkStyle}</style>` : '') +
|
|
@@ -10249,7 +10270,7 @@ const PREVIEW_STYLE = 'display: none; max-height: 0; overflow: hidden; mso-hide:
|
|
|
10249
10270
|
space per step — none renders, each counts as a character. */
|
|
10250
10271
|
const PREVIEW_FILLER = '͏‌ '.repeat(90);
|
|
10251
10272
|
function previewBlock(text) {
|
|
10252
|
-
return `<div style="${PREVIEW_STYLE}">${escapeText(text)}${PREVIEW_FILLER}</div>`;
|
|
10273
|
+
return `<div style="${PREVIEW_STYLE}">${escapeText$1(text)}${PREVIEW_FILLER}</div>`;
|
|
10253
10274
|
}
|
|
10254
10275
|
|
|
10255
10276
|
const word = (label) => (typeof label === 'function' ? label() : label);
|
|
@@ -15948,6 +15969,29 @@ const emailExtensions = [
|
|
|
15948
15969
|
// language swaps it for its own `createQuotedHistory({ labels })`.
|
|
15949
15970
|
QuotedHistory,
|
|
15950
15971
|
];
|
|
15972
|
+
/**
|
|
15973
|
+
* Plain text — a mail client's plain-text mode: the message as lines and
|
|
15974
|
+
* nothing else. No marks, no blocks, no images, no tokens in the schema, so
|
|
15975
|
+
* neither a shortcut (Mod-B has nothing to toggle) nor a paste (HTML is
|
|
15976
|
+
* read down to its text) can bring formatting in. Lines are the email
|
|
15977
|
+
* kit's `<div>` lines, Shift-Enter a `<br>` inside one, so what the editor
|
|
15978
|
+
* publishes is still email HTML — `emailPlainText` reads it back as exactly
|
|
15979
|
+
* the text written, the `text/plain` a plain-text message sends. The
|
|
15980
|
+
* quoted history is kept beside it, as in the email kit.
|
|
15981
|
+
*
|
|
15982
|
+
* A host switches a message between the two by mounting a new editor on
|
|
15983
|
+
* the other kit (a schema is fixed for an editor's life) with the content
|
|
15984
|
+
* carried over: `plainTextHTML(emailPlainText(html))` on the way in.
|
|
15985
|
+
*/
|
|
15986
|
+
const plainTextExtensions = [
|
|
15987
|
+
Document$1,
|
|
15988
|
+
EmailParagraph,
|
|
15989
|
+
Text,
|
|
15990
|
+
HardBreak,
|
|
15991
|
+
History,
|
|
15992
|
+
QuotedHistory,
|
|
15993
|
+
BaseKeymap,
|
|
15994
|
+
];
|
|
15951
15995
|
/**
|
|
15952
15996
|
* The parallel source-editor kit: the document is HTML source text, one
|
|
15953
15997
|
* `codeLine` per line, with highlighting, linting and formatting instead of
|
|
@@ -20521,5 +20565,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
20521
20565
|
* Generated bundle index. Do not edit.
|
|
20522
20566
|
*/
|
|
20523
20567
|
|
|
20524
|
-
export { AUTHORED_ATTR, AUTHORED_SPEC_KEY, AUTHORED_STYLE_DEFAULTS_KEY, AuthoredEnter, BLOCKQUOTE_STYLE, BLOCK_MARQUEE_CLASS, BLOCK_MENU_FOCUS_KEY, BLOCK_RELEASING_CLASS, BLOCK_SELECTED_CLASS, BUTTON_FILL, BUTTON_STYLE, BaseKeymap, BlockSelection, Blockquote, Bold, BulletList, Button, CARET_ROOM, CLIENT_LABELS, COLOR_SCHEME_META, COLOR_SCHEME_RULE, COLUMN_STARTING_LINES, COMMENT_TAG, CONTENT_ATTRS, CSS_SUPPORT, ClearFormatting, CodeLine, CodeLineIndent, Column, ColumnResize, Columns, ColumnsResize, DARK_FILL_CLASS, DARK_MODE_SURFACES, DARK_TEXT_CLASS, DOCUMENT_CLASS, DOCUMENT_TAGS, DUAL_BACKGROUND_MIN_RATIO, DUAL_CONTRAST_DARK, DUAL_CONTRAST_LIGHT, DUAL_CONTRAST_MIN_RATIO, Divider, Document$1 as Document, DocumentStyles, EMAIL_ATTRIBUTES, EMAIL_CSS_PROPERTIES, EMAIL_SAFE_STYLE_PROPERTIES, EMAIL_SAFE_TAGS, EMAIL_TAGS, EMAIL_TAG_ATTRIBUTES, EmailParagraph, FILE_CHOOSER_POINTER_WAIT, FILL_TEXT_COLOR, FILL_TEXT_COLOR_LIGHT, FILL_TEXT_COLOR_RGB, FOLD_WIDTH, FORMAT_WIDTH, GMAIL_CLIP_BYTES, GUIDES_ACTIVE_CLASS, GUIDES_PEEK_CLASS, GUIDES_PEEK_DELAY_MS, Gapcursor, HEADING_LEVELS, HEADING_SIZES, HEADING_SPACE_ABOVE, HEADING_SPACE_BELOW, HardBreak, Heading, History, HtmlComment, HtmlCommentInline, HtmlElement, HtmlInline, HtmlLanguage, HtmlRaw, HtmlTextElement, HtmlVoid, HtmlVoidBlock, IMPORT_VIEWPORT_WIDTH, INDENT_MAX, INDENT_STEP, INLINE_TAGS, INLINE_VOID_TAGS, IN_SELECTION_CLASS, Image, InlineAtomSelection, InlineImageStore, InlineImages, Italic, LIST_SPACE, LayoutGuides, Link, ListItem, MAX_COLUMNS, MAX_IMAGE_WIDTH, MAX_MERGE_TAG_LENGTH, MAX_UNBROKEN_RUN, MIN_COLUMN_CAP, MIN_COLUMN_PCT, MIN_FONT_SIZE, MIN_IMAGE_WIDTH, MIN_TABLE_PCT, MJML_WORLDLY_HTML, MergeTag, NoTextDrag, OrderedList, PARSE_SCOPE_KEY, PLACEHOLDER_WIDTH, Paragraph, PasteHygiene, QuotedHistory, REFUSED_TAGS, RENDERING_CLIENTS, SECTION_CONTENT_MAX, SECTION_INSET, SECTION_PADDING, STACK_CLASS, STACK_RULE, Section, SelectedBlocks, SplitKeepingMarks, TABLE_BLOCK_SPACING, TABLE_BORDER_COLOR, TABLE_CELL_PADDING, TABLE_SECTION_TAGS, TYPING_REST, Table, TableCell, TableRow, Text, TextStyle, UNSET_BUTTON_HREF, Underline, VOID_TAGS, acceptProposal, addColumn, addColumnAtEnd, addRowAtEnd, analyzeAngularExpression, angularDialect, angularRequiredFields, appendColumn, applyAuthoredAttributes, applyEmailVocabulary, attributeString, authoredAttribute, authoredMarkupOf, authoredViewAttributes, bareButtons, beginBlockMarquee, buttonStyle, canonicalHead, canonicalMergeTagInner, canonicalRender, caretCellPos, caretInsideMergeTag, claimedImageFiles, clearCells, clientList, columnCaps, columnWidths, completionContextAt, containerStyle, contrastRatio, correctStoredMistakes, createAngularExpressions, createBlockMarquee, createBlockMenu, createBubbleMenu, createButtonEdit, createContentProposal, createContentStream, createDOMParser, createEditor, createHtmlAutocomplete, createHtmlLanguage, createImageDrag, createInlineImages, createMergeTagResolution, createOffsetMapper, createPlaceholder, createQuotedHistory, createSchema, createSendIntent, createSourceMarks, createSuggestionMenu, createTableHandles, cssColorToHex, darkModeEditorCss, declarationProperty, declaredBackground, decodeDataUrl, defaultBubbleShouldShow, defineExtension, defineMark, defineNode, deleteColumns, deleteFullySelected, discardProposal, distributePadding, docText, dropDeadOfficeClasses, dropHidden, dropMediaQueries, dropZeroSizedWhitespace, duplicateColumn, duplicateRow, editQuotedHistory, effectiveWidths, emailBackgroundPalette, emailDarkModePalette, emailDocument, emailExtensions, emailPlainText, emailSizeBudget, emailTextPalette, embedsAsLinks, entitySpans, escapeAttribute$2 as escapeAttribute, escapeText$
|
|
20568
|
+
export { AUTHORED_ATTR, AUTHORED_SPEC_KEY, AUTHORED_STYLE_DEFAULTS_KEY, AuthoredEnter, BLOCKQUOTE_STYLE, BLOCK_MARQUEE_CLASS, BLOCK_MENU_FOCUS_KEY, BLOCK_RELEASING_CLASS, BLOCK_SELECTED_CLASS, BUTTON_FILL, BUTTON_STYLE, BaseKeymap, BlockSelection, Blockquote, Bold, BulletList, Button, CARET_ROOM, CLIENT_LABELS, COLOR_SCHEME_META, COLOR_SCHEME_RULE, COLUMN_STARTING_LINES, COMMENT_TAG, CONTENT_ATTRS, CSS_SUPPORT, ClearFormatting, CodeLine, CodeLineIndent, Column, ColumnResize, Columns, ColumnsResize, DARK_FILL_CLASS, DARK_MODE_SURFACES, DARK_TEXT_CLASS, DOCUMENT_CLASS, DOCUMENT_TAGS, DUAL_BACKGROUND_MIN_RATIO, DUAL_CONTRAST_DARK, DUAL_CONTRAST_LIGHT, DUAL_CONTRAST_MIN_RATIO, Divider, Document$1 as Document, DocumentStyles, EMAIL_ATTRIBUTES, EMAIL_CSS_PROPERTIES, EMAIL_SAFE_STYLE_PROPERTIES, EMAIL_SAFE_TAGS, EMAIL_TAGS, EMAIL_TAG_ATTRIBUTES, EmailParagraph, FILE_CHOOSER_POINTER_WAIT, FILL_TEXT_COLOR, FILL_TEXT_COLOR_LIGHT, FILL_TEXT_COLOR_RGB, FOLD_WIDTH, FORMAT_WIDTH, GMAIL_CLIP_BYTES, GUIDES_ACTIVE_CLASS, GUIDES_PEEK_CLASS, GUIDES_PEEK_DELAY_MS, Gapcursor, HEADING_LEVELS, HEADING_SIZES, HEADING_SPACE_ABOVE, HEADING_SPACE_BELOW, HardBreak, Heading, History, HtmlComment, HtmlCommentInline, HtmlElement, HtmlInline, HtmlLanguage, HtmlRaw, HtmlTextElement, HtmlVoid, HtmlVoidBlock, IMPORT_VIEWPORT_WIDTH, INDENT_MAX, INDENT_STEP, INLINE_TAGS, INLINE_VOID_TAGS, IN_SELECTION_CLASS, Image, InlineAtomSelection, InlineImageStore, InlineImages, Italic, LIST_SPACE, LayoutGuides, Link, ListItem, MAX_COLUMNS, MAX_IMAGE_WIDTH, MAX_MERGE_TAG_LENGTH, MAX_UNBROKEN_RUN, MIN_COLUMN_CAP, MIN_COLUMN_PCT, MIN_FONT_SIZE, MIN_IMAGE_WIDTH, MIN_TABLE_PCT, MJML_WORLDLY_HTML, MergeTag, NoTextDrag, OrderedList, PARSE_SCOPE_KEY, PLACEHOLDER_WIDTH, Paragraph, PasteHygiene, QuotedHistory, REFUSED_TAGS, RENDERING_CLIENTS, SECTION_CONTENT_MAX, SECTION_INSET, SECTION_PADDING, STACK_CLASS, STACK_RULE, Section, SelectedBlocks, SplitKeepingMarks, TABLE_BLOCK_SPACING, TABLE_BORDER_COLOR, TABLE_CELL_PADDING, TABLE_SECTION_TAGS, TYPING_REST, Table, TableCell, TableRow, Text, TextStyle, UNSET_BUTTON_HREF, Underline, VOID_TAGS, acceptProposal, addColumn, addColumnAtEnd, addRowAtEnd, analyzeAngularExpression, angularDialect, angularRequiredFields, appendColumn, applyAuthoredAttributes, applyEmailVocabulary, attributeString, authoredAttribute, authoredMarkupOf, authoredViewAttributes, bareButtons, beginBlockMarquee, buttonStyle, canonicalHead, canonicalMergeTagInner, canonicalRender, caretCellPos, caretInsideMergeTag, claimedImageFiles, clearCells, clientList, columnCaps, columnWidths, completionContextAt, containerStyle, contrastRatio, correctStoredMistakes, createAngularExpressions, createBlockMarquee, createBlockMenu, createBubbleMenu, createButtonEdit, createContentProposal, createContentStream, createDOMParser, createEditor, createHtmlAutocomplete, createHtmlLanguage, createImageDrag, createInlineImages, createMergeTagResolution, createOffsetMapper, createPlaceholder, createQuotedHistory, createSchema, createSendIntent, createSourceMarks, createSuggestionMenu, createTableHandles, cssColorToHex, darkModeEditorCss, declarationProperty, declaredBackground, decodeDataUrl, defaultBubbleShouldShow, defineExtension, defineMark, defineNode, deleteColumns, deleteFullySelected, discardProposal, distributePadding, docText, dropDeadOfficeClasses, dropHidden, dropMediaQueries, dropZeroSizedWhitespace, duplicateColumn, duplicateRow, editQuotedHistory, effectiveWidths, emailBackgroundPalette, emailDarkModePalette, emailDocument, emailExtensions, emailPlainText, emailSizeBudget, emailTextPalette, embedsAsLinks, entitySpans, escapeAttribute$2 as escapeAttribute, escapeText$2 as escapeText, expressionDialect, expressionDialectKey, extensionActions, extensionSuggestions, fillTextColor, filledPlaceholderAttrs, filterEmailStyle, findColumnContext, findColumnsContext, findCssIssues, findListDepth, findSectionContext, findTableContext, fitImageWidth, foldHTML, formatHTML, formatPct, forwardDocument, forwardQuote, hasBlockChildren$1 as hasBlockChildren, hasDocumentEnvelope, headingStyle, holdsBlock, holdsColumn, hrefProblem, htmlSourceExtensions, imageDropTarget, importLoss, importedDocument, inheritTextStyles, inheritedMarkup, inlineBootstrapTableBorders, inlineImageCids, inlineImageRegistry, inlineImagesKey, inlineStyles, insertHTML, insertMergeTag, isActionEnabled, isBlock$1 as isBlock, isBlockLevel, isBlockish, isBooleanAttribute, isEmailAttribute, isEmailCssProperty, isEmailTag, isFillTextColor, isInlineAtom, isMarkActive, isMergeTagExpression, isMergeTagPath, isNodeActive, isPadded, isProposing, isQuotedHistoryFolded, isSafeColor, isSafeFontFamily, isSafeFontSize, isSafeUrl$1 as isSafeUrl, isSectionImageUrl, isStreaming, italicStarInputRegex, italicStarPasteRegex, italicUnderscoreInputRegex, italicUnderscorePasteRegex, keepGmailRules, layoutBlockAt, linkClickPlugin, linkPastePlugin, linkRangeAt, lintHTML, listStyle, lowerInlinesAroundBlocks, markAttrsOf, markDarkMode, markStackingColumns, marksAcrossBreak, materializeComments, measureMenuPlacement, mediaMatches, mergeTagAt, mergeTagExpressions, mergeTagFields, mergeTagRanges, mergeTagResolutionPending, moveColumn, moveRow, normalizeCommentText, normalizeCss, normalizeHref, normalizeMergeTagText, noteOwnWidths, openFileChooser, openTag$1 as openTag, openTags, outlookColumns, paddingOf, pagedSource, parseAngularExpression, parseDocument, parseFontFamily, parseFontSize, parseHTML, parsePadding, parseRules, parseTextTransform, passesDualBackground, passesDualContrast, placeholderKey, plainTextExtensions, plainTextHTML, preserveExtensions, preservedAttributes, promoteInlineImages, promoteMergeTags, proposalRange, proposeContent, quotedHistory, readEnvelope, readImageFile, realizeComments, relativeLuminance, releaseBlockSelection, removeColumn, removeLastColumn, removeQuotedHistory, renderForClient, renderedPath, repairLinkSchemes, repairTables, replaceHTML, replyDocument, replyEnvelope, replyQuote, resolveColorScheme, reviseProposal, rewriteInlineImageSources, richTextExtensions, ruleForScope, ruleScope, sanitizePastedHTML, scanHTML, scanMergeTags, scopeOf$1 as scopeOf, scopedDocumentCss, selectColumn, selectRow, selectedButton, selectedImage, selectedImageAlt, selectedMarkAtom, selectionInsideMergeTag, serializeDocument, serializeToHTML, setCellBackground, setColumnBackground, setColumnBoundary, setColumnsBoundary, setPlaceholder, setQuotedHistory, setSectionBackground, setSectionImage, setTableBox, setTableWidth, soleInlineAtom, splitDeclarations$2 as splitDeclarations, splitListItemKeepMarks, starInputRegex, starPasteRegex, stopProposal, streamContent, streamedRange, streamingRange, suggestionSectionTitles, tableStyle, textOffsetAt, textRegions, toEmailSafeColor, toInboundMessage, toggleLayoutList, toggleList, trimInlineBoxes, underscoreInputRegex, underscorePasteRegex, unwrapLayoutTables, unwrapOfficeParagraphMarks, verbatimElement, videoPage, watchMenuScroll, withAttribute, withAuthoredMarkup, withQuoted, withQuotedHistory, withStyleDeclaration, withoutAttribute, withoutStyleDeclaration };
|
|
20525
20569
|
//# sourceMappingURL=h-k-dev-angular-email-editor.mjs.map
|