@h-k-dev/angular-email-editor 0.0.0-stage → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +127 -2
- package/fesm2022/h-k-dev-angular-email-editor-actions.mjs +179 -0
- package/fesm2022/h-k-dev-angular-email-editor-actions.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-address-chip.mjs +285 -0
- package/fesm2022/h-k-dev-angular-email-editor-address-chip.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-address-input.mjs +686 -0
- package/fesm2022/h-k-dev-angular-email-editor-address-input.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-address.mjs +489 -0
- package/fesm2022/h-k-dev-angular-email-editor-address.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-anchor.mjs +64 -0
- package/fesm2022/h-k-dev-angular-email-editor-anchor.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-attachment-chip.mjs +387 -0
- package/fesm2022/h-k-dev-angular-email-editor-attachment-chip.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-attachment-chips.mjs +80 -0
- package/fesm2022/h-k-dev-angular-email-editor-attachment-chips.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-chat-input.mjs +346 -0
- package/fesm2022/h-k-dev-angular-email-editor-chat-input.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-focus.mjs +68 -0
- package/fesm2022/h-k-dev-angular-email-editor-focus.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-image.mjs +132 -0
- package/fesm2022/h-k-dev-angular-email-editor-image.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-material.mjs +124 -0
- package/fesm2022/h-k-dev-angular-email-editor-material.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-metrics.mjs +243 -0
- package/fesm2022/h-k-dev-angular-email-editor-metrics.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-palette.mjs +87 -0
- package/fesm2022/h-k-dev-angular-email-editor-palette.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-proposal.mjs +307 -0
- package/fesm2022/h-k-dev-angular-email-editor-proposal.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor-suggestion-menu.mjs +392 -0
- package/fesm2022/h-k-dev-angular-email-editor-suggestion-menu.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor.mjs +18818 -0
- package/fesm2022/h-k-dev-angular-email-editor.mjs.map +1 -0
- package/package.json +133 -3
- package/styles/_scroll.scss +101 -0
- package/types/h-k-dev-angular-email-editor-actions.d.ts +147 -0
- package/types/h-k-dev-angular-email-editor-address-chip.d.ts +193 -0
- package/types/h-k-dev-angular-email-editor-address-input.d.ts +328 -0
- package/types/h-k-dev-angular-email-editor-address.d.ts +125 -0
- package/types/h-k-dev-angular-email-editor-anchor.d.ts +44 -0
- package/types/h-k-dev-angular-email-editor-attachment-chip.d.ts +271 -0
- package/types/h-k-dev-angular-email-editor-attachment-chips.d.ts +48 -0
- package/types/h-k-dev-angular-email-editor-chat-input.d.ts +101 -0
- package/types/h-k-dev-angular-email-editor-focus.d.ts +40 -0
- package/types/h-k-dev-angular-email-editor-image.d.ts +66 -0
- package/types/h-k-dev-angular-email-editor-material.d.ts +65 -0
- package/types/h-k-dev-angular-email-editor-metrics.d.ts +110 -0
- package/types/h-k-dev-angular-email-editor-palette.d.ts +51 -0
- package/types/h-k-dev-angular-email-editor-proposal.d.ts +170 -0
- package/types/h-k-dev-angular-email-editor-suggestion-menu.d.ts +177 -0
- package/types/h-k-dev-angular-email-editor.d.ts +3959 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"h-k-dev-angular-email-editor-address.mjs","sources":["../../../projects/angular-email-editor/address/rules.ts","../../../projects/angular-email-editor/address/address.ts","../../../projects/angular-email-editor/address/public-api.ts","../../../projects/angular-email-editor/address/h-k-dev-angular-email-editor-address.ts"],"sourcesContent":["import type { AddressRules, Mailbox } from './address';\n\n/**\n * **Our address rule** — the one place that decides what an email address\n * is, how a mailbox is read and written, and how typed or pasted text splits\n * into mailboxes. Everything else in the library asks through\n * {@link AddressRules}; a host that wants another rule provides its own\n * (`provideAddressRules`) and this file is simply not used.\n *\n * Kept as a living document: what it implements, and every place it\n * deliberately does less or more than the RFCs, is listed here — change the\n * list with the code.\n *\n * **Implements**\n * - RFC 5322 §3.4 `mailbox`: `addr-spec` or `name-addr` — a phrase of atoms\n * and quoted strings (quoted-pairs unescaped) in front of an `angle-addr`.\n * - RFC 5322 §3.2.2 comments, nested, with quoted-pairs: skipped — and a\n * comment after a bare address names it (`jane@x.io (Jane Doe)`), the\n * de facto reading of the old form.\n * - RFC 5322 §3.4 `group`: `Team: a@x.io, b@x.io;` yields its members; an\n * empty group (`undisclosed-recipients:;`) yields nothing.\n * - RFC 5322 §4.4 obsolete routes: `<@relay.io:jane@x.io>` is `jane@x.io`.\n * - RFC 5322 §3.4.1 `addr-spec`: a dot-atom or quoted-string local part.\n * - RFC 6532 / 6531: UTF-8 in local parts, names and domains (`jörg@müller.de`).\n * - RFC 5321 §4.1.2 / 4.1.3 domains: LDH labels of 1–63 characters, no\n * hyphen at either end; address literals `[192.0.2.1]` and `[IPv6:…]`.\n * - RFC 5321 §4.5.3.1 lengths, in octets (UTF-8): local part ≤ 64,\n * address ≤ 254 (the 256-octet path less its brackets, RFC 3696 errata).\n * - RFC 2047 encoded words in names (`=?UTF-8?B?…?=`, `Q` too, any charset\n * the platform's `TextDecoder` knows), adjacent words joined as §6.2 says.\n *\n * **Deliberately stricter than the RFCs** — the typo worth catching:\n * - A domain needs a dot (`hans@gmail` is legal, and a typo every time).\n * - A top-level label is not all digits (RFC 3696 §2) — `a@1.2.3.4` is an\n * IP address missing its brackets.\n * - General address literals (`[tag:content]`) are refused; only IPv4/IPv6.\n *\n * **Deliberately more lenient — people type these:**\n * - `,` `;` and line breaks all separate (RFC 5322: only `,`).\n * - A bare run is read as people type it: words in front of an address name\n * it (`Ada Lovelace ada@x.io`), addresses only whitespace separates are\n * several, words naming no address stay one token (one typo, not three).\n * - A group needs whitespace, `;`, `,` or the end after its colon, so\n * `hans:müller@x.de` stays one (invalid) address instead of silently\n * becoming `müller@x.de` under a group called `hans`.\n *\n * **Placeholders** — not an RFC matter, a template one: an address holding\n * a `{{…}}` token (`{{contact.email}}`, `\"{{contact.name}}\" <{{contact.email}}>`)\n * is filled in later, so it is neither valid nor invalid yet\n * (`isPlaceholder`), and a separator inside the braces is part of it\n * (`{{join contacts \", \"}}` is one entry). Only doubled braces count — a\n * lone `{` is atext and stays an ordinary character.\n */\n\n// ── characters ───────────────────────────────────────────────────────────\n\n/** RFC 5322 `atext`, plus RFC 6532's UTF-8 beyond ASCII. */\nconst ATEXT = \"A-Za-z0-9!#$%&'*+/=?^_`{|}~\\\\-\\\\u0080-\\\\u{10FFFF}\";\nconst DOT_ATOM = new RegExp(`^[${ATEXT}]+(?:\\\\.[${ATEXT}]+)*$`, 'u');\n/** RFC 5322 `quoted-string` as a whole local part: qtext, quoted-pairs, spaces. */\nconst QUOTED_LOCAL = /^\"(?:[\\x20\\x21\\x23-\\x5B\\x5D-\\x7E\\u0080-\\u{10FFFF}]|\\\\[\\x20-\\x7E])*\"$/u;\n/** An RFC 5321 label, with RFC 6531 U-labels: letters and digits of any\n script, hyphens inside. */\nconst LABEL = /^(?!-)[\\p{L}\\p{N}\\p{M}-]{1,63}(?<!-)$/u;\n/** Characters a display name must be quoted to carry: RFC 5322's specials. */\nconst SPECIALS = /[()<>[\\]:;@\\\\,.\"]/;\n\nconst octets = (text: string): number => new TextEncoder().encode(text).length;\n\n// ── addr-spec ────────────────────────────────────────────────────────────\n\n/** Whether a bare address — no name, no brackets — is well-formed by the\n rule above. */\nexport function isEmailAddress(address: string): boolean {\n const at = address.lastIndexOf('@');\n if (at < 1 || at === address.length - 1) return false;\n const local = address.slice(0, at);\n const domain = address.slice(at + 1);\n if (octets(local) > 64 || octets(address) > 254) return false;\n if (!DOT_ATOM.test(local) && !QUOTED_LOCAL.test(local)) return false;\n return isDomain(domain);\n}\n\nfunction isDomain(domain: string): boolean {\n if (domain.startsWith('[')) return isAddressLiteral(domain);\n const labels = domain.split('.');\n if (labels.length < 2 || !labels.every((label) => LABEL.test(label))) return false;\n return !/^\\d+$/.test(labels[labels.length - 1]);\n}\n\nfunction isAddressLiteral(literal: string): boolean {\n const inner = /^\\[(.*)\\]$/.exec(literal)?.[1];\n if (inner === undefined) return false;\n if (/^IPv6:/i.test(inner)) return isIPv6(inner.slice(5));\n return isIPv4(inner);\n}\n\nfunction isIPv4(text: string): boolean {\n const parts = text.split('.');\n return parts.length === 4 && parts.every((part) => /^(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)$/.test(part));\n}\n\nfunction isIPv6(text: string): boolean {\n // The platform's own parser is the reference; the shape check keeps a\n // host name or a port from sneaking through it.\n if (!/^[0-9A-Fa-f:.]+$/.test(text) || !text.includes(':')) return false;\n try {\n return new URL(`http://[${text}]/`).hostname.length > 2;\n } catch {\n return false;\n }\n}\n\n// ── the mailbox scanner ──────────────────────────────────────────────────\n\n/** Where the scan stands at one character: inside a quoted string, a\n comment (nested), angle brackets, or a `{{…}}` placeholder. */\ninterface ScanState {\n quoted: boolean;\n comment: number;\n angled: boolean;\n escaped: boolean;\n /** Inside `{{ … }}`. */\n braced: boolean;\n /** The character before was a `{` (outside) or a `}` (inside) — half\n of a doubled brace. */\n brace: boolean;\n}\n\nconst at_top = (state: ScanState) => !state.quoted && !state.comment && !state.angled && !state.braced;\n\n/** Steps the scan past one character; true when the character is structure\n (a quote, a bracket, part of a comment or an escape), not content at the\n top level. */\nfunction step(state: ScanState, char: string): boolean {\n const half = state.brace;\n state.brace = false;\n if (state.braced) {\n if (char === '}' && half) state.braced = false;\n else state.brace = char === '}';\n return true;\n }\n if (char === '{' && !state.quoted && !state.comment) {\n if (half) state.braced = true;\n else state.brace = true;\n return half;\n }\n if (state.escaped) {\n state.escaped = false;\n return true;\n }\n if ((state.quoted || state.comment) && char === '\\\\') {\n state.escaped = true;\n return true;\n }\n if (state.quoted) {\n if (char === '\"') state.quoted = false;\n return true;\n }\n if (state.comment) {\n if (char === '(') state.comment++;\n else if (char === ')') state.comment--;\n return true;\n }\n if (char === '\"' && !state.angled) {\n state.quoted = true;\n return true;\n }\n if (char === '(' && !state.angled) {\n state.comment = 1;\n return true;\n }\n if (char === '<') state.angled = true;\n else if (char === '>') state.angled = false;\n else return state.angled;\n return true;\n}\n\nconst fresh = (): ScanState => ({\n quoted: false,\n comment: 0,\n angled: false,\n escaped: false,\n braced: false,\n brace: false,\n});\n\n/** Whether a separator typed at `caret` would separate — false inside a\n quoted name, a comment or angle brackets, where a comma is part of the\n mailbox: `\"Lovelace, Ada\"`. */\nexport function separatesAt(raw: string, caret: number): boolean {\n const state = fresh();\n for (const char of raw.slice(0, caret)) step(state, char);\n return at_top(state) && !state.escaped;\n}\n\n// ── parse ────────────────────────────────────────────────────────────────\n\n/**\n * Splits a header-form mailbox into name and address: quoted strings\n * unquoted and unescaped, encoded words decoded, comments skipped — or, after\n * a bare address, taken as its name — and an obsolete route dropped.\n * Anything that is no mailbox comes back as its trimmed text, as the address,\n * for the rule to refuse.\n */\nexport function parseMailbox(raw: string): Mailbox {\n const text = raw.trim();\n const words: string[] = [];\n const comments: string[] = [];\n let address: string | null = null;\n let bare = '';\n let i = 0;\n while (i < text.length) {\n const char = text[i];\n if (char === '\"') {\n const end = closing(text, i, '\"');\n if (end < 0) return { address: text };\n words.push(unescape(text.slice(i + 1, end)));\n i = end + 1;\n } else if (char === '(') {\n const end = closingComment(text, i);\n if (end < 0) return { address: text };\n comments.push(decodeEncodedWords(unescape(text.slice(i + 1, end)).trim()));\n i = end + 1;\n } else if (char === '<') {\n const end = text.indexOf('>', i);\n if (end < 0 || address !== null) return { address: text };\n address = text.slice(i + 1, end).trim().replace(/^@[^:]*:/, '');\n i = end + 1;\n } else if (/\\s/.test(char)) {\n i++;\n } else {\n let end = i;\n while (end < text.length && !/[\\s\"(<]/.test(text[end])) end++;\n const atom = text.slice(i, end);\n if (address === null) words.push(atom);\n else return { address: text }; // text after the angle-addr\n bare += atom;\n i = end;\n }\n }\n if (address !== null) {\n const name = phrase(words) || comments.join(' ');\n return name ? { name, address } : { address };\n }\n // No angle brackets: an addr-spec, as one run of text.\n if (words.length !== 1 || !bare) return { address: text };\n const name = comments.join(' ');\n return name ? { name, address: bare } : { address: bare };\n}\n\n/** A phrase's words as a display name: one space between words, encoded\n words decoded — and two encoded words only whitespace separates joined\n with none (RFC 2047 §6.2). */\nfunction phrase(words: readonly string[]): string {\n return decodeEncodedWords(words.join(' ')).trim();\n}\n\nfunction closing(text: string, from: number, quote: string): number {\n for (let i = from + 1; i < text.length; i++) {\n if (text[i] === '\\\\') i++;\n else if (text[i] === quote) return i;\n }\n return -1;\n}\n\nfunction closingComment(text: string, from: number): number {\n let depth = 0;\n for (let i = from; i < text.length; i++) {\n if (text[i] === '\\\\') i++;\n else if (text[i] === '(') depth++;\n else if (text[i] === ')' && --depth === 0) return i;\n }\n return -1;\n}\n\nconst unescape = (text: string) => text.replace(/\\\\([\\s\\S])/g, '$1');\n\n// ── format ───────────────────────────────────────────────────────────────\n\n/** The header form of a mailbox — the name quoted (and escaped) when it has\n to be. UTF-8 stays as it is (RFC 6532); a mailer encodes for the wire. */\nexport function formatMailbox(mailbox: Mailbox): string {\n const name = mailbox.name?.trim();\n if (!name) return mailbox.address;\n const quoted = SPECIALS.test(name) || /\\s{2}/.test(name) ? `\"${name.replace(/[\"\\\\]/g, '\\\\$&')}\"` : name;\n return `${quoted} <${mailbox.address}>`;\n}\n\n// ── split ────────────────────────────────────────────────────────────────\n\n/** What separates the mailboxes of a typed run. */\nconst SEPARATORS = ',;\\n\\r';\n\n/**\n * Splits a typed, pasted or header run into mailboxes — see the leniencies\n * above. A well-formed mailbox comes back in its canonical header form\n * (`<a@x.io>` → `a@x.io`, `Dr. Ada <a@x.io>` → `\"Dr. Ada\" <a@x.io>`, a\n * comment or an encoded word turned into the name); anything else comes back\n * as typed, for the user to fix. Empty tokens are dropped; duplicates are\n * the caller's.\n */\nexport function splitAddresses(raw: string): string[] {\n const tokens: string[] = [];\n const state = fresh();\n let current = '';\n for (let i = 0; i < raw.length; i++) {\n const char = raw[i];\n if (step(state, char)) {\n current += char;\n continue;\n }\n if (SEPARATORS.includes(char)) {\n tokens.push(current);\n current = '';\n continue;\n }\n // A group's colon: what came before it is the group's name, not a\n // mailbox — dropped; its members follow, up to the `;`.\n if (char === ':' && !/[@<]/.test(current) && /^(?:[\\s,;]|$)/.test(raw.slice(i + 1, i + 2))) {\n current = '';\n continue;\n }\n current += char;\n }\n tokens.push(current);\n return tokens\n .map((token) => token.trim())\n .filter(Boolean)\n .flatMap((token) => (/[<\"(]/.test(token) ? [token] : bareRun(token)))\n .map(canonical);\n}\n\n/** A bare run's mailboxes: each run of words names the address after it. */\nfunction bareRun(token: string): string[] {\n const mailboxes: string[] = [];\n let name: string[] = [];\n for (const word of token.split(/\\s+/)) {\n if (!isEmailAddress(word)) {\n name.push(word);\n continue;\n }\n mailboxes.push(formatMailbox({ name: name.join(' ') || undefined, address: word }));\n name = [];\n }\n if (name.length) mailboxes.push(name.join(' '));\n return mailboxes;\n}\n\n/** A well-formed mailbox in its canonical header form; anything else as it is. */\nfunction canonical(token: string): string {\n const mailbox = parseMailbox(token);\n return isEmailAddress(mailbox.address) ? formatMailbox(mailbox) : token;\n}\n\n// ── identity ─────────────────────────────────────────────────────────────\n\n/**\n * What makes two entries one recipient. The domain is case-insensitive and\n * compared in its ASCII form (`müller.de` is `xn--mller-kva.de`); the local\n * part is lowercased too — RFC 5321 lets a server tell `Ada` from `ada`, no\n * real one does, and one recipient twice is the mistake that matters. Both\n * NFC-normalized, so a composed and a decomposed `ü` are one.\n */\nexport function addressIdentity(address: string): string {\n const at = address.lastIndexOf('@');\n if (at < 0) return address.trim().normalize('NFC').toLowerCase();\n const local = address.slice(0, at).normalize('NFC').toLowerCase();\n return `${local}@${asciiDomain(address.slice(at + 1).normalize('NFC').toLowerCase())}`;\n}\n\nfunction asciiDomain(domain: string): string {\n if (domain.startsWith('[') || /^[\\x00-\\x7F]*$/.test(domain)) return domain;\n try {\n return new URL(`http://${domain}/`).hostname;\n } catch {\n return domain;\n }\n}\n\n// ── RFC 2047 ─────────────────────────────────────────────────────────────\n\nconst ENCODED_WORD = /=\\?([^?\\s]+)\\?([BbQq])\\?([^?\\s]*)\\?=/g;\n\n/** Decodes the RFC 2047 encoded words in a header text; one that cannot be\n decoded stays as it is. Two encoded words only whitespace separates are\n joined with none. */\nexport function decodeEncodedWords(text: string): string {\n return text\n .replace(/(=\\?[^?\\s]+\\?[BbQq]\\?[^?\\s]*\\?=)\\s+(?==\\?[^?\\s]+\\?[BbQq]\\?[^?\\s]*\\?=)/g, '$1')\n .replace(ENCODED_WORD, (word, charset: string, encoding: string, data: string) => {\n try {\n const bytes =\n encoding.toUpperCase() === 'B'\n ? Uint8Array.from(atob(data), (c) => c.charCodeAt(0))\n : quotedPrintable(data);\n // RFC 2231 lets a charset carry a language: `UTF-8*de`.\n return new TextDecoder(charset.split('*')[0], { fatal: true }).decode(bytes);\n } catch {\n return word;\n }\n });\n}\n\nfunction quotedPrintable(data: string): Uint8Array {\n const bytes: number[] = [];\n for (let i = 0; i < data.length; i++) {\n const char = data[i];\n if (char === '_') bytes.push(0x20);\n else if (char === '=' && /^[0-9A-Fa-f]{2}$/.test(data.slice(i + 1, i + 3))) {\n bytes.push(parseInt(data.slice(i + 1, i + 3), 16));\n i += 2;\n } else bytes.push(...new TextEncoder().encode(char));\n }\n return Uint8Array.from(bytes);\n}\n\n// ── placeholders ─────────────────────────────────────────────────────────\n\n/** Whether an address holds a `{{…}}` placeholder — one a template fills\n in later, so neither valid nor invalid yet. */\nexport function isAddressPlaceholder(address: string): boolean {\n return /\\{\\{[^{}]*\\}\\}/.test(address);\n}\n\n/** Our rule, as the contract every part of the library asks through. */\nexport const defaultAddressRules: AddressRules = {\n split: splitAddresses,\n parse: parseMailbox,\n format: formatMailbox,\n isValid: isEmailAddress,\n isPlaceholder: isAddressPlaceholder,\n identity: addressIdentity,\n};\n","import { InjectionToken, Provider } from '@angular/core';\nimport { defaultAddressRules } from './rules';\n\n/**\n * A mailbox the way a header spells it: an address, and the display name a\n * client shows in front of it — `Ada Lovelace <ada@example.com>`,\n * `\"Lovelace, Ada\" <ada@example.com>`, or the bare `ada@example.com`.\n *\n * The chips hold that header form as one string, so a host's `string[]`\n * carries names without a second type and goes into a `To:` header as it\n * is.\n */\nexport interface Mailbox {\n /** The display name, if the mailbox has one; quotes already removed. */\n readonly name?: string;\n readonly address: string;\n}\n\n/**\n * The address rule — what an address is, how a mailbox is read and written,\n * how typed text splits into mailboxes. The library's own is `rules.ts`\n * ({@link defaultAddressRules}); a host with a rule of its own — a stricter\n * validator, an established parser, the rule its backend already enforces —\n * provides it with {@link provideAddressRules}, whole or in part, and every\n * address piece asks it instead: the input, the chip, the `addressList`\n * form rule, and (passed as an option) `replyEnvelope` and\n * `toInboundMessage`.\n */\nexport interface AddressRules {\n /** A typed, pasted or header run as mailboxes in header form. */\n split(raw: string): string[];\n /** A header-form mailbox as its name and address. */\n parse(raw: string): Mailbox;\n /** A mailbox in header form, the name quoted where it has to be. */\n format(mailbox: Mailbox): string;\n /** Whether a bare address — no name, no brackets — is well-formed. */\n isValid(address: string): boolean;\n /** Whether an address is a placeholder a template fills in later\n (`{{contact.email}}`) — neither valid nor invalid until then. A host\n that sends what is typed, unresolved, turns it off with\n `provideAddressRules({ isPlaceholder: () => false })`. */\n isPlaceholder(address: string): boolean;\n /** What makes two addresses one recipient: equal identities are. */\n identity(address: string): string;\n}\n\n/** The address rule in force — ours unless a host provides its own. */\nexport const ADDRESS_RULES = new InjectionToken<AddressRules>('ADDRESS_RULES', {\n providedIn: 'root',\n factory: () => defaultAddressRules,\n});\n\n/**\n * Puts a host's address rule in force: the parts given replace ours, the\n * rest stay ours. Keeping an existing validator is one line:\n *\n * ```ts\n * providers: [provideAddressRules({ isValid: isValidEmail })]\n * ```\n */\nexport function provideAddressRules(rules: Partial<AddressRules>): Provider {\n return { provide: ADDRESS_RULES, useValue: { ...defaultAddressRules, ...rules } };\n}\n\n/** Whether a header-form string is a mailbox whose address is well-formed. */\nexport function isMailbox(raw: string, rules: AddressRules = defaultAddressRules): boolean {\n return rules.isValid(rules.parse(raw).address);\n}\n\n/** Whether a header-form entry's address is a placeholder — pending: a\n template fills it in later, so it is no mistake to flag. */\nexport function isPending(raw: string, rules: AddressRules = defaultAddressRules): boolean {\n return rules.isPlaceholder(rules.parse(raw).address);\n}\n\n/** What makes two header-form entries one recipient — the name in front of\n the address makes no second one. For a list that holds each recipient\n once. */\nexport function addressKey(raw: string, rules: AddressRules = defaultAddressRules): string {\n return rules.identity(rules.parse(raw).address);\n}\n\n/**\n * A list of header-form entries as one header value — `\"Müller, Hans\"\n * <h@x.de>, b@c.de` — for a backend or a template that keeps To, Cc and Bcc\n * as text. The way back is the rule's own `split`: `rules.split(joined)` is\n * the list again, entry for entry. A well-formed mailbox is written in its\n * canonical form (a name with a comma quoted); anything else — a typo, a\n * `{{placeholder}}` — goes in as it is, for the backend to resolve or refuse.\n *\n * ```ts\n * to: joinAddresses(form.to().value()), // out to the backend\n * form.to().value.set(splitAddresses(template.to)) // back in\n * ```\n */\nexport function joinAddresses(list: readonly string[], rules: AddressRules = defaultAddressRules): string {\n return list\n .map((entry) => entry.trim())\n .filter(Boolean)\n .map((entry) => (isMailbox(entry, rules) ? rules.format(rules.parse(entry)) : entry))\n .join(', ');\n}\n","/*\n * Public API Surface of @h-k-dev/angular-email-editor/address\n */\n\nexport * from './address';\nexport * from './rules';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { input, inject, ElementRef, linkedSignal, Directive } from '@angular/core';
|
|
3
|
+
|
|
4
|
+
const NOWHERE = { left: 0, top: 0, width: 0, height: 0 };
|
|
5
|
+
/**
|
|
6
|
+
* A real element kept over a box the editor reports — a selection, a caret,
|
|
7
|
+
* a block. The editor knows rectangles; everything that floats wants an
|
|
8
|
+
* *element* to hold on to: the CDK's `cdkOverlayOrigin`, Material's
|
|
9
|
+
* `matMenuTriggerFor`, Aria's menu trigger, CSS anchor positioning,
|
|
10
|
+
* Floating UI. This is that element, so each of them works as it is:
|
|
11
|
+
*
|
|
12
|
+
* <span [emailAnchor]="state().boundingBox" cdkOverlayOrigin #origin="cdkOverlayOrigin"></span>
|
|
13
|
+
* <ng-template cdkConnectedOverlay [cdkConnectedOverlayOrigin]="origin" …>
|
|
14
|
+
*
|
|
15
|
+
* Behaviour only: it places its element — fixed, at the box, out of the
|
|
16
|
+
* pointer's way — and renders nothing. While the box is `null` it stays
|
|
17
|
+
* where it last was, so what is anchored to it can close (and animate out)
|
|
18
|
+
* in place rather than jump to the corner.
|
|
19
|
+
*
|
|
20
|
+
* The box is in viewport coordinates, so the element is `position: fixed`;
|
|
21
|
+
* it must not sit inside a transformed ancestor, which would become its
|
|
22
|
+
* frame. It does not follow a scroll by itself: it is as current as the box
|
|
23
|
+
* it is given.
|
|
24
|
+
*/
|
|
25
|
+
class Anchor {
|
|
26
|
+
/** The box to stand over; `null` while there is none. */
|
|
27
|
+
rect = input(null, { ...(ngDevMode ? { debugName: "rect" } : /* istanbul ignore next */ {}), alias: 'emailAnchor' });
|
|
28
|
+
/** The element itself — for what takes an element rather than a template
|
|
29
|
+
reference (`#anchor="emailAnchor"`, then `anchor.element`). */
|
|
30
|
+
element = inject(ElementRef).nativeElement;
|
|
31
|
+
/** The box being stood over: the last one given, held through `null`. */
|
|
32
|
+
box = linkedSignal({ ...(ngDevMode ? { debugName: "box" } : /* istanbul ignore next */ {}), source: this.rect,
|
|
33
|
+
computation: (rect, previous) => rect
|
|
34
|
+
? { left: rect.left, top: rect.top, width: rect.width ?? 0, height: rect.height ?? 0 }
|
|
35
|
+
: (previous?.value ?? NOWHERE) });
|
|
36
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: Anchor, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
37
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.6", type: Anchor, isStandalone: true, selector: "[emailAnchor]", inputs: { rect: { classPropertyName: "rect", publicName: "emailAnchor", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.position": "\"fixed\"", "style.pointer-events": "\"none\"", "style.left.px": "box().left", "style.top.px": "box().top", "style.width.px": "box().width", "style.height.px": "box().height" } }, exportAs: ["emailAnchor"], ngImport: i0 });
|
|
38
|
+
}
|
|
39
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: Anchor, decorators: [{
|
|
40
|
+
type: Directive,
|
|
41
|
+
args: [{
|
|
42
|
+
selector: '[emailAnchor]',
|
|
43
|
+
exportAs: 'emailAnchor',
|
|
44
|
+
host: {
|
|
45
|
+
'[style.position]': '"fixed"',
|
|
46
|
+
'[style.pointer-events]': '"none"',
|
|
47
|
+
'[style.left.px]': 'box().left',
|
|
48
|
+
'[style.top.px]': 'box().top',
|
|
49
|
+
'[style.width.px]': 'box().width',
|
|
50
|
+
'[style.height.px]': 'box().height',
|
|
51
|
+
},
|
|
52
|
+
}]
|
|
53
|
+
}], propDecorators: { rect: [{ type: i0.Input, args: [{ isSignal: true, alias: "emailAnchor", required: false }] }] } });
|
|
54
|
+
|
|
55
|
+
/*
|
|
56
|
+
* Public API Surface of @h-k-dev/angular-email-editor/anchor
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Generated bundle index. Do not edit.
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
export { Anchor };
|
|
64
|
+
//# sourceMappingURL=h-k-dev-angular-email-editor-anchor.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"h-k-dev-angular-email-editor-anchor.mjs","sources":["../../../projects/angular-email-editor/anchor/anchor.ts","../../../projects/angular-email-editor/anchor/public-api.ts","../../../projects/angular-email-editor/anchor/h-k-dev-angular-email-editor-anchor.ts"],"sourcesContent":["import { Directive, ElementRef, inject, input, linkedSignal } from '@angular/core';\n\n/** A box in viewport coordinates — a `DOMRect`, a menu state's\n `boundingBox`, a caret's `coordsAtPos`. A point when it has no size. */\nexport interface AnchorRect {\n left: number;\n top: number;\n width?: number;\n height?: number;\n}\n\nconst NOWHERE: Required<AnchorRect> = { left: 0, top: 0, width: 0, height: 0 };\n\n/**\n * A real element kept over a box the editor reports — a selection, a caret,\n * a block. The editor knows rectangles; everything that floats wants an\n * *element* to hold on to: the CDK's `cdkOverlayOrigin`, Material's\n * `matMenuTriggerFor`, Aria's menu trigger, CSS anchor positioning,\n * Floating UI. This is that element, so each of them works as it is:\n *\n * <span [emailAnchor]=\"state().boundingBox\" cdkOverlayOrigin #origin=\"cdkOverlayOrigin\"></span>\n * <ng-template cdkConnectedOverlay [cdkConnectedOverlayOrigin]=\"origin\" …>\n *\n * Behaviour only: it places its element — fixed, at the box, out of the\n * pointer's way — and renders nothing. While the box is `null` it stays\n * where it last was, so what is anchored to it can close (and animate out)\n * in place rather than jump to the corner.\n *\n * The box is in viewport coordinates, so the element is `position: fixed`;\n * it must not sit inside a transformed ancestor, which would become its\n * frame. It does not follow a scroll by itself: it is as current as the box\n * it is given.\n */\n@Directive({\n selector: '[emailAnchor]',\n exportAs: 'emailAnchor',\n host: {\n '[style.position]': '\"fixed\"',\n '[style.pointer-events]': '\"none\"',\n '[style.left.px]': 'box().left',\n '[style.top.px]': 'box().top',\n '[style.width.px]': 'box().width',\n '[style.height.px]': 'box().height',\n },\n})\nexport class Anchor {\n /** The box to stand over; `null` while there is none. */\n readonly rect = input<AnchorRect | null | undefined>(null, { alias: 'emailAnchor' });\n\n /** The element itself — for what takes an element rather than a template\n reference (`#anchor=\"emailAnchor\"`, then `anchor.element`). */\n readonly element = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n\n /** The box being stood over: the last one given, held through `null`. */\n protected readonly box = linkedSignal<AnchorRect | null | undefined, Required<AnchorRect>>({\n source: this.rect,\n computation: (rect, previous) =>\n rect\n ? { left: rect.left, top: rect.top, width: rect.width ?? 0, height: rect.height ?? 0 }\n : (previous?.value ?? NOWHERE),\n });\n}\n","/*\n * Public API Surface of @h-k-dev/angular-email-editor/anchor\n */\n\nexport * from './anchor';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;AAWA,MAAM,OAAO,GAAyB,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;AAE9E;;;;;;;;;;;;;;;;;;;AAmBG;MAaU,MAAM,CAAA;;IAER,IAAI,GAAG,KAAK,CAAgC,IAAI,4EAAI,KAAK,EAAE,aAAa,EAAA,CAAG;AAEpF;AACkE;AACzD,IAAA,OAAO,GAAG,MAAM,CAA0B,UAAU,CAAC,CAAC,aAAa;;AAGzD,IAAA,GAAG,GAAG,YAAY,CAAA,EAAA,IAAA,SAAA,GAAA,EAAA,SAAA,EAAA,KAAA,EAAA,8BAAA,EAAA,CAAA,EACnC,MAAM,EAAE,IAAI,CAAC,IAAI;QACjB,WAAW,EAAE,CAAC,IAAI,EAAE,QAAQ,KAC1B;AACE,cAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC;eACjF,QAAQ,EAAE,KAAK,IAAI,OAAO,CAAC,EAAA,CAClC;uGAfS,MAAM,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAN,MAAM,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,eAAA,EAAA,MAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,gBAAA,EAAA,WAAA,EAAA,sBAAA,EAAA,UAAA,EAAA,eAAA,EAAA,YAAA,EAAA,cAAA,EAAA,WAAA,EAAA,gBAAA,EAAA,aAAA,EAAA,iBAAA,EAAA,cAAA,EAAA,EAAA,EAAA,QAAA,EAAA,CAAA,aAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;2FAAN,MAAM,EAAA,UAAA,EAAA,CAAA;kBAZlB,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,eAAe;AACzB,oBAAA,QAAQ,EAAE,aAAa;AACvB,oBAAA,IAAI,EAAE;AACJ,wBAAA,kBAAkB,EAAE,SAAS;AAC7B,wBAAA,wBAAwB,EAAE,QAAQ;AAClC,wBAAA,iBAAiB,EAAE,YAAY;AAC/B,wBAAA,gBAAgB,EAAE,WAAW;AAC7B,wBAAA,kBAAkB,EAAE,aAAa;AACjC,wBAAA,mBAAmB,EAAE,cAAc;AACpC,qBAAA;AACF,iBAAA;;;AC5CD;;AAEG;;ACFH;;AAEG;;;;"}
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import { isPlatformBrowser, NgTemplateOutlet } from '@angular/common';
|
|
2
|
+
import * as i0 from '@angular/core';
|
|
3
|
+
import { inject, PLATFORM_ID, debounced, computed, TemplateRef, Directive, InjectionToken, LOCALE_ID, DestroyRef, input, booleanAttribute, output, contentChild, signal, Component } from '@angular/core';
|
|
4
|
+
|
|
5
|
+
/** The stages during which the transfer is not done — a waiting one
|
|
6
|
+
included, since the message is no more sendable for it. */
|
|
7
|
+
function attachmentInFlight(status) {
|
|
8
|
+
return (status === 'preprocessing' ||
|
|
9
|
+
status === 'queued' ||
|
|
10
|
+
status === 'uploading' ||
|
|
11
|
+
status === 'postprocessing');
|
|
12
|
+
}
|
|
13
|
+
/** MIME → kind. Ordered: the first match wins, so the specific document
|
|
14
|
+
types are tested before the `text/*` catch-all, and the Office types —
|
|
15
|
+
which all spell out "document" — before the plain document pattern. */
|
|
16
|
+
const KINDS = [
|
|
17
|
+
[/^image\//, 'image'],
|
|
18
|
+
[/^video\//, 'video'],
|
|
19
|
+
[/^audio\//, 'audio'],
|
|
20
|
+
[/pdf$/, 'pdf'],
|
|
21
|
+
[/(zip|compressed|tar|rar|7z)/, 'archive'],
|
|
22
|
+
[/(spreadsheet|excel|csv)/, 'spreadsheet'],
|
|
23
|
+
[/(presentation|powerpoint)/, 'presentation'],
|
|
24
|
+
[/(word|document|rtf|opendocument\.text)/, 'document'],
|
|
25
|
+
[/^message\/|rfc822/, 'message'],
|
|
26
|
+
[/^text\//, 'document'],
|
|
27
|
+
];
|
|
28
|
+
/** The kind of file a MIME type stands for; `file` when the type is unknown
|
|
29
|
+
or matches nothing above. Case-insensitive — a type off the wire may be
|
|
30
|
+
shouted. */
|
|
31
|
+
function attachmentKind(type) {
|
|
32
|
+
if (!type)
|
|
33
|
+
return 'file';
|
|
34
|
+
const mime = type.toLowerCase();
|
|
35
|
+
for (const [pattern, kind] of KINDS)
|
|
36
|
+
if (pattern.test(mime))
|
|
37
|
+
return kind;
|
|
38
|
+
return 'file';
|
|
39
|
+
}
|
|
40
|
+
const SIZE_UNITS = ['B', 'KB', 'MB', 'GB'];
|
|
41
|
+
/**
|
|
42
|
+
* A byte count the way a mail client prints it: binary steps, one decimal
|
|
43
|
+
* below ten ("2.5 MB", "15 MB"), none for bytes. The number follows the
|
|
44
|
+
* locale ("2,5 MB" in German); the unit labels are the ones every mail
|
|
45
|
+
* client uses, not CLDR's ("kB", "byte").
|
|
46
|
+
*/
|
|
47
|
+
function formatAttachmentSize(bytes, locale = 'en-US') {
|
|
48
|
+
let value = Math.max(0, bytes);
|
|
49
|
+
let unit = 0;
|
|
50
|
+
while (value >= 1024 && unit < SIZE_UNITS.length - 1) {
|
|
51
|
+
value /= 1024;
|
|
52
|
+
unit++;
|
|
53
|
+
}
|
|
54
|
+
const digits = unit > 0 && value < 10 ? 1 : 0;
|
|
55
|
+
const number = new Intl.NumberFormat(locale, {
|
|
56
|
+
maximumFractionDigits: digits,
|
|
57
|
+
useGrouping: false,
|
|
58
|
+
}).format(value);
|
|
59
|
+
return `${number} ${SIZE_UNITS[unit]}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The chip's animation state — the part of the motion that CSS alone cannot
|
|
64
|
+
* carry. The stylesheet owns the pictures (the sweep, the fade, the exit,
|
|
65
|
+
* the fill); this file owns the timing they key off, so the two stay in
|
|
66
|
+
* one component and no host has to know a band is moving.
|
|
67
|
+
*/
|
|
68
|
+
/** How long the scanning band takes to fade once it is told to go, in ms.
|
|
69
|
+
Matches the stylesheet: its exit animation runs this long, and the host
|
|
70
|
+
attribute that keeps the band on the page holds for the same stretch —
|
|
71
|
+
a tab that never paints must still let go of it. */
|
|
72
|
+
const SCAN_EXIT = 300;
|
|
73
|
+
/**
|
|
74
|
+
* The scanning band's state: `'true'` while `active()`, then `'leaving'`
|
|
75
|
+
* for `exit` ms after it stops, then nothing. The band's CSS keys off both
|
|
76
|
+
* — the sweep runs while the state is set at all, and `leaving` stacks the
|
|
77
|
+
* exit animation on top — so a number that arrives mid-sweep sees the band
|
|
78
|
+
* fade out on its way rather than vanish wherever it was.
|
|
79
|
+
*
|
|
80
|
+
* The hold is a `debounced` view of `active` that follows it at once on the
|
|
81
|
+
* way up and `exit` ms late on the way down: while the wait runs, the
|
|
82
|
+
* debounced value is still the old `true`, and that is the leaving state.
|
|
83
|
+
* Activity that returns mid-exit supersedes the pending wait, so the band
|
|
84
|
+
* simply carries on. No effect or timer of our own — and no exit on the
|
|
85
|
+
* server, where nothing is painted.
|
|
86
|
+
*
|
|
87
|
+
* Call in an injection context.
|
|
88
|
+
*/
|
|
89
|
+
function scanningState(active, exit = SCAN_EXIT) {
|
|
90
|
+
const browser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
91
|
+
const held = debounced(active, (on) => on || !browser ? undefined : new Promise((resolve) => setTimeout(resolve, exit)));
|
|
92
|
+
return computed(() => (active() ? 'true' : held.value() ? 'leaving' : null));
|
|
93
|
+
}
|
|
94
|
+
/** The user has asked for less motion: no fake fill, no sweep. */
|
|
95
|
+
function prefersReducedMotion() {
|
|
96
|
+
return typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* The chip's icon slot: mark an `ng-template` with it (or use the `*`
|
|
101
|
+
* shorthand on the element itself) and the chip renders it in place of the
|
|
102
|
+
* default glyph, in the icon box. The box is `--email-attachment-chip-icon-size`
|
|
103
|
+
* square and sets `font-size` and `color` to match, so a `mat-icon` with
|
|
104
|
+
* `inline`, a font icon, or an SVG drawn at `1em` all fit without a rule.
|
|
105
|
+
*/
|
|
106
|
+
class AttachmentChipIcon {
|
|
107
|
+
template = inject(TemplateRef);
|
|
108
|
+
static ngTemplateContextGuard(_directive, context) {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: AttachmentChipIcon, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
112
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.6", type: AttachmentChipIcon, isStandalone: true, selector: "ng-template[emailAttachmentIcon]", ngImport: i0 });
|
|
113
|
+
}
|
|
114
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: AttachmentChipIcon, decorators: [{
|
|
115
|
+
type: Directive,
|
|
116
|
+
args: [{ selector: 'ng-template[emailAttachmentIcon]' }]
|
|
117
|
+
}] });
|
|
118
|
+
/**
|
|
119
|
+
* The chip's progress slot: the readout in the trailing square, rendered
|
|
120
|
+
* from the host's template instead of the default percentage. The bar along
|
|
121
|
+
* the chip's edge and the scanning sweep stay the chip's — this replaces
|
|
122
|
+
* only what the square shows.
|
|
123
|
+
*/
|
|
124
|
+
class AttachmentChipProgress {
|
|
125
|
+
template = inject(TemplateRef);
|
|
126
|
+
static ngTemplateContextGuard(_directive, context) {
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: AttachmentChipProgress, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
130
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.6", type: AttachmentChipProgress, isStandalone: true, selector: "ng-template[emailAttachmentProgress]", ngImport: i0 });
|
|
131
|
+
}
|
|
132
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: AttachmentChipProgress, decorators: [{
|
|
133
|
+
type: Directive,
|
|
134
|
+
args: [{ selector: 'ng-template[emailAttachmentProgress]' }]
|
|
135
|
+
}] });
|
|
136
|
+
|
|
137
|
+
/** A page with its corner folded — the base the document-like kinds draw on. */
|
|
138
|
+
const PAGE = 'M3.5 1.5h5.5l3.5 3.5v9.5h-9z M9 1.5v3.5h3.5';
|
|
139
|
+
/** One stroked path per kind, on a 16×16 grid, drawn in `currentColor`.
|
|
140
|
+
Our own glyphs rather than an icon font: nothing to load, nothing to
|
|
141
|
+
install, and they take the chip's colour like text does. */
|
|
142
|
+
const ICONS = {
|
|
143
|
+
file: PAGE,
|
|
144
|
+
document: `${PAGE} M5.5 8h5 M5.5 10h5 M5.5 12h3`,
|
|
145
|
+
pdf: `${PAGE} M5.5 7.5h5v2.5h-5z M5.5 12h5`,
|
|
146
|
+
spreadsheet: `${PAGE} M5.5 7.5h5v5h-5z M5.5 10h5 M8 7.5v5`,
|
|
147
|
+
presentation: `${PAGE} M6 12.5v-2 M8 12.5v-4.5 M10 12.5v-3`,
|
|
148
|
+
archive: `${PAGE} M7 3h1 M8 4.5h1 M7 6h1 M8 7.5h1 M7 10h2v2.5h-2z`,
|
|
149
|
+
image: 'M2.5 3.5h11v9h-11z M2.5 10.5l3-3 3 3 2-2 3 3 M9.5 6a1 1 0 1 0 2 0a1 1 0 1 0 -2 0',
|
|
150
|
+
video: 'M1.5 4h9v8h-9z M10.5 7l4-2.5v7l-4-2.5',
|
|
151
|
+
audio: 'M6 12V4l7-1.5v8 M3 12a1.5 1.5 0 1 0 3 0a1.5 1.5 0 1 0 -3 0 M10 10.5a1.5 1.5 0 1 0 3 0a1.5 1.5 0 1 0 -3 0',
|
|
152
|
+
message: 'M1.5 3.5h13v9h-13z M1.5 4l6.5 5 6.5-5',
|
|
153
|
+
};
|
|
154
|
+
const DEFAULT_OPTIONS = {
|
|
155
|
+
bytesPerSecond: 10 * 1024 * 1024,
|
|
156
|
+
minDuration: 400,
|
|
157
|
+
maxDuration: 3000,
|
|
158
|
+
fallbackDuration: 800,
|
|
159
|
+
linger: 600,
|
|
160
|
+
};
|
|
161
|
+
/** Overrides for simulated progress, app-wide or per subtree:
|
|
162
|
+
`{ provide: ATTACHMENT_CHIP_OPTIONS, useValue: { bytesPerSecond: 2e6 } }`.
|
|
163
|
+
Anything left out keeps its default. */
|
|
164
|
+
const ATTACHMENT_CHIP_OPTIONS = new InjectionToken('ATTACHMENT_CHIP_OPTIONS', { factory: () => ({}) });
|
|
165
|
+
/** How long a simulated transfer of `size` bytes runs, in ms: size over the
|
|
166
|
+
assumed speed, clamped to the floor and ceiling. The linger after it is
|
|
167
|
+
not part of this — it is the same for every size. */
|
|
168
|
+
function simulatedDuration(size, options = DEFAULT_OPTIONS) {
|
|
169
|
+
if (size == null)
|
|
170
|
+
return options.fallbackDuration;
|
|
171
|
+
const ms = (size / options.bytesPerSecond) * 1000;
|
|
172
|
+
return Math.min(options.maxDuration, Math.max(options.minDuration, ms));
|
|
173
|
+
}
|
|
174
|
+
/** What the progress announces itself as, per stage, unless the host says
|
|
175
|
+
otherwise — "Preparing report.pdf". */
|
|
176
|
+
const STATUS_LABELS = {
|
|
177
|
+
preprocessing: 'Preparing',
|
|
178
|
+
queued: 'Waiting to upload',
|
|
179
|
+
uploading: 'Uploading',
|
|
180
|
+
postprocessing: 'Processing',
|
|
181
|
+
complete: 'Uploaded',
|
|
182
|
+
};
|
|
183
|
+
/**
|
|
184
|
+
* One attachment, laid out like a list row with an avatar:
|
|
185
|
+
*
|
|
186
|
+
* [icon] | name | [progress / ×]
|
|
187
|
+
* | size |
|
|
188
|
+
* ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
|
|
189
|
+
*
|
|
190
|
+
* Progress is a bar along the bottom edge that fills with the number; with
|
|
191
|
+
* no number the whole chip is swept by a band of light instead, the way a
|
|
192
|
+
* scanner passes over a page — and when the number arrives mid-sweep the
|
|
193
|
+
* band fades out on its way rather than vanishing. The square at the end is
|
|
194
|
+
* shared: while work is in flight it holds the readout (the percentage,
|
|
195
|
+
* nothing while there is none), and the remove button paints over it on
|
|
196
|
+
* hover or focus, always on a touch screen. The chip owns all of that
|
|
197
|
+
* motion: the stylesheet draws it and `attachment-chip.animation.ts` times
|
|
198
|
+
* it; a host only says where the transfer is.
|
|
199
|
+
*
|
|
200
|
+
* **Stages are `status`.** A host with an upload pipeline binds the stage
|
|
201
|
+
* it is in — `preprocessing`, `queued`, `uploading`, `postprocessing`,
|
|
202
|
+
* `complete`, the vocabulary of Uppy and tus — and `progress` (0–1) while
|
|
203
|
+
* uploading. The stages where work runs without a number sweep, the one
|
|
204
|
+
* with a number fills, and `queued` keeps quiet: busy, no bar, no sweep,
|
|
205
|
+
* announced as waiting. The progress names itself after the stage
|
|
206
|
+
* ("Preparing report.pdf") unless `progressLabel` says otherwise. The stage
|
|
207
|
+
* is reflected as `data-status` on the host. Which stages a host has, and
|
|
208
|
+
* in what order, is the host's pipeline; the chip only draws the one it is
|
|
209
|
+
* given.
|
|
210
|
+
*
|
|
211
|
+
* **State is ARIA.** `aria-busy` is reflected while any stage is in flight,
|
|
212
|
+
* and is also an input for a host with no stages to tell: busy with a
|
|
213
|
+
* `progress` fills, busy with `null` sweeps. `aria-disabled` takes the
|
|
214
|
+
* remove button out of play. Both inputs go under their ARIA names, so a
|
|
215
|
+
* host writes `[aria-busy]="upload.pending"` exactly as it would on any
|
|
216
|
+
* element, and the chip reflects them back onto itself — they are the
|
|
217
|
+
* styling hooks too (`[email-attachment-chip][aria-busy='true']`).
|
|
218
|
+
*
|
|
219
|
+
* **`simulateProgress`** is for hosts that already have the bytes and only
|
|
220
|
+
* want the "attaching" moment: the chip runs a fill paced by `size` over
|
|
221
|
+
* `ATTACHMENT_CHIP_OPTIONS`, is busy while it runs and for a moment after
|
|
222
|
+
* the bar is full, and settles on its own.
|
|
223
|
+
* It plays once, when the chip is created; it is skipped for reduced motion
|
|
224
|
+
* and on the server; and a real `status` or `aria-busy` from the host always wins. It
|
|
225
|
+
* never emits and never gates anything — don't use it for work that is
|
|
226
|
+
* really happening but untracked; that is `aria-busy` with no `progress`.
|
|
227
|
+
* The fake announces no number to assistive tech, only that it is busy.
|
|
228
|
+
*
|
|
229
|
+
* **Parts are `data-slot`s.** Every part carries one — `icon`, `label`,
|
|
230
|
+
* `name`, `size`, `trailing`, `progress` (the readout, with
|
|
231
|
+
* `progress-value` inside), `progress-track`, `progress-indicator` (the
|
|
232
|
+
* bar), `remove` — and that is how it is styled from outside:
|
|
233
|
+
* `[email-attachment-chip] [data-slot='name'] { … }`. Theme it through the
|
|
234
|
+
* `--email-*` tokens; a structural override reaches a part with two
|
|
235
|
+
* attributes (`[email-attachment-chip] [data-slot='name'][data-slot]`).
|
|
236
|
+
*
|
|
237
|
+
* **Slots are templates.** `icon` and `progress` are boxes the chip sizes;
|
|
238
|
+
* a host fills either with its own template by marking an `ng-template`
|
|
239
|
+
* `emailAttachmentIcon` / `emailAttachmentProgress` (or the `*` shorthand on
|
|
240
|
+
* the element). The icon box is `--email-attachment-chip-icon-size` square
|
|
241
|
+
* and sets `font-size` and `color` to match, so a `mat-icon` with `inline`, a
|
|
242
|
+
* font icon or an SVG drawn at `1em` all fit with no rule of their own; the
|
|
243
|
+
* template gets the attachment and its `kind`. The progress template gets
|
|
244
|
+
* the attachment, `status`, `progress`, `percent` and `mode`, and replaces
|
|
245
|
+
* only the readout — the bar and the sweep stay the chip's.
|
|
246
|
+
*
|
|
247
|
+
* **It asks, it does not do.** The chip holds no list: `removed` is a
|
|
248
|
+
* request the host answers by dropping the item (and cancelling its upload).
|
|
249
|
+
* Picking, dropping, uploading and assembling the MIME are the host's.
|
|
250
|
+
*
|
|
251
|
+
* Opt-in enter/leave animations ship with it:
|
|
252
|
+
* `animate.enter="email-attachment-chip-enter"` and
|
|
253
|
+
* `animate.leave="email-attachment-chip-leave"` on the chip's element.
|
|
254
|
+
*
|
|
255
|
+
* Meant for an `<li>` inside `<ul email-attachment-chips>`. No Material, no
|
|
256
|
+
* icon font, no headless library.
|
|
257
|
+
*/
|
|
258
|
+
class AttachmentChip {
|
|
259
|
+
#locale = inject(LOCALE_ID);
|
|
260
|
+
#options = { ...DEFAULT_OPTIONS, ...inject(ATTACHMENT_CHIP_OPTIONS) };
|
|
261
|
+
#browser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
262
|
+
#destroyRef = inject(DestroyRef);
|
|
263
|
+
/** The file this chip stands for. A `File` satisfies it as it is. */
|
|
264
|
+
attachment = input.required(/* @ts-ignore */
|
|
265
|
+
...(ngDevMode ? [{ debugName: "attachment" }] : /* istanbul ignore next */ []));
|
|
266
|
+
/** Set false for a read-only message (a sent copy, a preview): the chip
|
|
267
|
+
stays, the remove button goes. */
|
|
268
|
+
removable = input(true, { ...(ngDevMode ? { debugName: "removable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
269
|
+
/** Prefix for the remove button's accessible name — "Remove report.pdf". */
|
|
270
|
+
removeLabel = input('Remove', /* @ts-ignore */
|
|
271
|
+
...(ngDevMode ? [{ debugName: "removeLabel" }] : /* istanbul ignore next */ []));
|
|
272
|
+
/** Which stage the transfer is in. `null` for a host with no stages to
|
|
273
|
+
tell — then `aria-busy` and `progress` alone describe the work. */
|
|
274
|
+
status = input(null, /* @ts-ignore */
|
|
275
|
+
...(ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []));
|
|
276
|
+
/** Work is in flight for this attachment (an upload, a download, a scan),
|
|
277
|
+
for a host that has no stages to tell. */
|
|
278
|
+
ariaBusy = input(false, { ...(ngDevMode ? { debugName: "ariaBusy" } : /* istanbul ignore next */ {}), alias: 'aria-busy', transform: booleanAttribute });
|
|
279
|
+
/** How far the work is, 0–1, while uploading. `null` when there is no
|
|
280
|
+
number. */
|
|
281
|
+
progress = input(null, /* @ts-ignore */
|
|
282
|
+
...(ngDevMode ? [{ debugName: "progress" }] : /* istanbul ignore next */ []));
|
|
283
|
+
/** The remove button is out of play — e.g. while the message sends. */
|
|
284
|
+
ariaDisabled = input(false, { ...(ngDevMode ? { debugName: "ariaDisabled" } : /* istanbul ignore next */ {}), alias: 'aria-disabled', transform: booleanAttribute });
|
|
285
|
+
/** Run a fake transfer once, paced by the attachment's size. */
|
|
286
|
+
simulateProgress = input(false, { ...(ngDevMode ? { debugName: "simulateProgress" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
287
|
+
/** Prefix for the progress's accessible name — "Uploading report.pdf".
|
|
288
|
+
Left out, it follows the stage: Preparing, Uploading, Processing. */
|
|
289
|
+
progressLabel = input(undefined, /* @ts-ignore */
|
|
290
|
+
...(ngDevMode ? [{ debugName: "progressLabel" }] : /* istanbul ignore next */ []));
|
|
291
|
+
/** The remove button was pressed. The host drops the attachment; the chip
|
|
292
|
+
goes when the host's list no longer has it. */
|
|
293
|
+
removed = output();
|
|
294
|
+
/** The host's own icon, if it handed one in. */
|
|
295
|
+
iconTemplate = contentChild(AttachmentChipIcon, /* @ts-ignore */
|
|
296
|
+
...(ngDevMode ? [{ debugName: "iconTemplate" }] : /* istanbul ignore next */ []));
|
|
297
|
+
/** The host's own progress readout, if it handed one in. */
|
|
298
|
+
progressTemplate = contentChild(AttachmentChipProgress, /* @ts-ignore */
|
|
299
|
+
...(ngDevMode ? [{ debugName: "progressTemplate" }] : /* istanbul ignore next */ []));
|
|
300
|
+
#simulating = signal(false, /* @ts-ignore */
|
|
301
|
+
...(ngDevMode ? [{ debugName: "#simulating" }] : /* istanbul ignore next */ []));
|
|
302
|
+
/** Busy for real (a stage in flight, or the host said so) or for show (a
|
|
303
|
+
simulation running). Public so the container can tell whether any of
|
|
304
|
+
its chips is. */
|
|
305
|
+
busy = computed(() => attachmentInFlight(this.status()) || this.ariaBusy() || this.#simulating(), /* @ts-ignore */
|
|
306
|
+
...(ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []));
|
|
307
|
+
/** Which progress to draw, if any: a sweep for a stage with no number, a
|
|
308
|
+
bar for one with, nothing yet for one that waits, and the host's real
|
|
309
|
+
state beats a simulation. Reflected on the host as `data-mode`. */
|
|
310
|
+
mode = computed(() => {
|
|
311
|
+
const status = this.status();
|
|
312
|
+
if (status === 'preprocessing' || status === 'postprocessing')
|
|
313
|
+
return 'indeterminate';
|
|
314
|
+
if (status === 'queued')
|
|
315
|
+
return 'queued';
|
|
316
|
+
if (status === 'uploading' || this.ariaBusy()) {
|
|
317
|
+
return this.progress() == null ? 'indeterminate' : 'determinate';
|
|
318
|
+
}
|
|
319
|
+
return this.#simulating() ? 'simulated' : null;
|
|
320
|
+
}, /* @ts-ignore */
|
|
321
|
+
...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
|
|
322
|
+
/** The scanning band's state on the host — `true` while there is no
|
|
323
|
+
number, `leaving` for a moment after one arrives. */
|
|
324
|
+
scanning = scanningState(computed(() => this.mode() === 'indeterminate'));
|
|
325
|
+
/** What the progress is called: the host's word, or the stage's. */
|
|
326
|
+
label = computed(() => this.progressLabel() ?? STATUS_LABELS[this.status() ?? 'uploading'], /* @ts-ignore */
|
|
327
|
+
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
328
|
+
percent = computed(() => Math.round(Math.min(1, Math.max(0, this.progress() ?? 0)) * 100), /* @ts-ignore */
|
|
329
|
+
...(ngDevMode ? [{ debugName: "percent" }] : /* istanbul ignore next */ []));
|
|
330
|
+
kind = computed(() => attachmentKind(this.attachment().type), /* @ts-ignore */
|
|
331
|
+
...(ngDevMode ? [{ debugName: "kind" }] : /* istanbul ignore next */ []));
|
|
332
|
+
icon = computed(() => ICONS[this.kind()], /* @ts-ignore */
|
|
333
|
+
...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
|
|
334
|
+
iconContext = computed(() => ({
|
|
335
|
+
$implicit: this.attachment(),
|
|
336
|
+
kind: this.kind(),
|
|
337
|
+
}), /* @ts-ignore */
|
|
338
|
+
...(ngDevMode ? [{ debugName: "iconContext" }] : /* istanbul ignore next */ []));
|
|
339
|
+
progressContext = computed(() => ({
|
|
340
|
+
$implicit: this.attachment(),
|
|
341
|
+
status: this.status(),
|
|
342
|
+
progress: this.progress(),
|
|
343
|
+
percent: this.percent(),
|
|
344
|
+
mode: this.mode() ?? 'indeterminate',
|
|
345
|
+
}), /* @ts-ignore */
|
|
346
|
+
...(ngDevMode ? [{ debugName: "progressContext" }] : /* istanbul ignore next */ []));
|
|
347
|
+
size = computed(() => {
|
|
348
|
+
const size = this.attachment().size;
|
|
349
|
+
return size == null ? '' : formatAttachmentSize(size, this.#locale);
|
|
350
|
+
}, /* @ts-ignore */
|
|
351
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
352
|
+
duration = computed(() => simulatedDuration(this.attachment().size, this.#options), /* @ts-ignore */
|
|
353
|
+
...(ngDevMode ? [{ debugName: "duration" }] : /* istanbul ignore next */ []));
|
|
354
|
+
ngOnInit() {
|
|
355
|
+
if (!this.simulateProgress() || !this.#browser || prefersReducedMotion())
|
|
356
|
+
return;
|
|
357
|
+
// A timer, not `animationend`: the bar's CSS animation is only the
|
|
358
|
+
// picture. A tab that never paints must still see the chip settle.
|
|
359
|
+
this.#simulating.set(true);
|
|
360
|
+
const timer = setTimeout(() => this.#simulating.set(false), this.duration() + this.#options.linger);
|
|
361
|
+
this.#destroyRef.onDestroy(() => clearTimeout(timer));
|
|
362
|
+
}
|
|
363
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: AttachmentChip, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
364
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: AttachmentChip, isStandalone: true, selector: "[email-attachment-chip]", inputs: { attachment: { classPropertyName: "attachment", publicName: "attachment", isSignal: true, isRequired: true, transformFunction: null }, removable: { classPropertyName: "removable", publicName: "removable", isSignal: true, isRequired: false, transformFunction: null }, removeLabel: { classPropertyName: "removeLabel", publicName: "removeLabel", isSignal: true, isRequired: false, transformFunction: null }, status: { classPropertyName: "status", publicName: "status", isSignal: true, isRequired: false, transformFunction: null }, ariaBusy: { classPropertyName: "ariaBusy", publicName: "aria-busy", isSignal: true, isRequired: false, transformFunction: null }, progress: { classPropertyName: "progress", publicName: "progress", isSignal: true, isRequired: false, transformFunction: null }, ariaDisabled: { classPropertyName: "ariaDisabled", publicName: "aria-disabled", isSignal: true, isRequired: false, transformFunction: null }, simulateProgress: { classPropertyName: "simulateProgress", publicName: "simulateProgress", isSignal: true, isRequired: false, transformFunction: null }, progressLabel: { classPropertyName: "progressLabel", publicName: "progressLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { removed: "removed" }, host: { properties: { "attr.data-kind": "kind()", "attr.data-status": "status()", "attr.data-mode": "mode()", "attr.data-scanning": "scanning()", "attr.aria-busy": "busy() || null", "attr.aria-disabled": "ariaDisabled() || null" } }, queries: [{ propertyName: "iconTemplate", first: true, predicate: AttachmentChipIcon, descendants: true, isSignal: true }, { propertyName: "progressTemplate", first: true, predicate: AttachmentChipProgress, descendants: true, isSignal: true }], ngImport: i0, template: "<!-- Every part carries data-slot: that is its styling hook. The icon box\n and the progress readout are also template slots: each has a default\n the chip draws, and a host replaces it by marking an ng-template with\n emailAttachmentIcon or emailAttachmentProgress \u2014 the chip renders it\n inside the box it already sizes, so the stylesheet never has to reach\n into the host's markup. -->\n<span data-slot=\"icon\">\n @if (iconTemplate(); as slot) {\n <ng-container *ngTemplateOutlet=\"slot.template; context: iconContext()\" />\n } @else {\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path [attr.d]=\"icon()\" />\n </svg>\n }\n</span>\n\n<span data-slot=\"label\">\n <!-- title, not a tooltip component: the name is truncated in CSS, and the\n native title is what recovers the whole of it. -->\n <span data-slot=\"name\" [title]=\"attachment().name\">{{ attachment().name }}</span>\n @if (size()) {\n <span data-slot=\"size\">{{ size() }}</span>\n }\n</span>\n\n<!-- One square at the end that the progress readout and the remove button\n share: while work is in flight the readout has it, and the \u00D7 paints over\n it on hover or focus (always on a touch screen). The bar under the chip\n is the progress itself; the readout is only its number \u2014 or the host's\n template. With no number the square stays empty and the chip itself is\n swept by a band of light (a host pseudo-element, keyed off data-mode). -->\n<span data-slot=\"trailing\">\n @if (mode(); as m) {\n <!-- A simulated run is announced as busy, never with a number: the\n fake is for the eye, not for assistive tech. -->\n <span\n data-slot=\"progress\"\n [attr.data-mode]=\"m\"\n role=\"progressbar\"\n [attr.aria-label]=\"label() + ' ' + attachment().name\"\n [attr.aria-valuenow]=\"m === 'determinate' ? percent() : null\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n >\n @if (progressTemplate(); as slot) {\n <ng-container *ngTemplateOutlet=\"slot.template; context: progressContext()\" />\n } @else if (m === 'determinate') {\n <span data-slot=\"progress-value\" aria-hidden=\"true\">{{ percent() }}%</span>\n }\n </span>\n <!-- A waiting transfer has no bar: nothing is moving yet. -->\n @if (m !== 'queued') {\n <span data-slot=\"progress-track\" [attr.data-mode]=\"m\" aria-hidden=\"true\">\n <span\n data-slot=\"progress-indicator\"\n [style.transform]=\"m === 'determinate' ? 'scaleX(' + percent() / 100 + ')' : null\"\n [style.animation-duration.ms]=\"m === 'simulated' ? duration() : null\"\n ></span>\n </span>\n }\n }\n\n @if (removable()) {\n <button\n type=\"button\"\n data-slot=\"remove\"\n [disabled]=\"ariaDisabled()\"\n [attr.aria-label]=\"removeLabel() + ' ' + attachment().name\"\n (click)=\"removed.emit()\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M4.5 4.5l7 7 M11.5 4.5l-7 7\" />\n </svg>\n </button>\n }\n</span>\n", styles: [":host{position:relative;display:inline-flex;align-items:center;gap:8px;max-width:var(--email-attachment-chip-max-width, 240px);min-width:0;min-height:40px;margin:0;padding:2px 4px 2px 8px;box-sizing:border-box;background-color:var(--email-attachment-chip-background, var(--mat-sys-surface-container, light-dark(#f1f3f4, #303134)));color:var(--email-attachment-chip-color, var(--mat-sys-on-surface, light-dark(#1f1f1f, #e3e3e3)));border:1px solid var(--email-attachment-chip-border-color, var(--mat-sys-outline-variant, light-dark(#dadce0, #444746)));border-radius:var(--email-attachment-chip-corner, var(--mat-sys-corner-small, 8px));font-family:var(--email-attachment-chip-font-family, var(--mat-sys-label-large-font, system-ui, sans-serif));font-size:var(--email-attachment-chip-font-size, var(--mat-sys-label-large-size, .875rem));font-weight:var(--email-attachment-chip-font-weight, var(--mat-sys-label-large-weight, 500));line-height:var(--email-attachment-chip-line-height, var(--mat-sys-label-large-line-height, 1.25rem));letter-spacing:var(--email-attachment-chip-letter-spacing, var(--mat-sys-label-large-tracking, .006rem));transition:opacity .15s cubic-bezier(.645,.045,.355,1)}:host([aria-disabled=true]){opacity:.6}:host([data-scanning]):after{content:\"\";position:absolute;inset:0;border-radius:inherit;pointer-events:none;background-image:linear-gradient(100deg,transparent 35%,var(--email-attachment-chip-scan-color, color-mix(in srgb, var(--email-attachment-chip-progress-color, var(--mat-sys-primary, light-dark(#0b57d0, #a8c7fa))) 14%, transparent)) 50%,transparent 65%);background-size:300% 100%;background-repeat:no-repeat;animation:email-attachment-chip-scan 2.2s linear infinite,email-attachment-chip-scan-fade 2.2s linear infinite}:host([data-scanning=leaving]):after{animation:email-attachment-chip-scan 2.2s linear infinite,email-attachment-chip-scan-fade 2.2s linear infinite,email-attachment-chip-scan-exit .3s ease-out forwards}:host :where([data-slot=icon]){flex:none;display:inline-flex;align-items:center;justify-content:center;width:var(--email-attachment-chip-icon-size, 20px);height:var(--email-attachment-chip-icon-size, 20px);font-size:var(--email-attachment-chip-icon-size, 20px);line-height:1;color:var(--email-attachment-chip-icon-color, var(--mat-sys-on-surface-variant, light-dark(#5f6368, #9aa0a6)))}:host :where([data-slot=icon]>svg){width:100%;height:100%;fill:none;stroke:currentColor;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round}:host :where([data-slot=label]){display:flex;flex-direction:column;flex:1 1 auto;min-width:0}:host :where([data-slot=name],[data-slot=size]){overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host :where([data-slot=size]){font-family:var(--email-attachment-chip-secondary-font-family, var(--mat-sys-body-small-font, system-ui, sans-serif));font-size:var(--email-attachment-chip-secondary-font-size, var(--mat-sys-body-small-size, .75rem));font-weight:var(--email-attachment-chip-secondary-font-weight, var(--mat-sys-body-small-weight, 400));line-height:var(--email-attachment-chip-secondary-line-height, var(--mat-sys-body-small-line-height, 1rem));letter-spacing:var(--email-attachment-chip-secondary-letter-spacing, var(--mat-sys-body-small-tracking, .025rem));color:var(--email-attachment-chip-secondary-color, var(--mat-sys-on-surface-variant, light-dark(#5f6368, #9aa0a6)))}:host :where([data-slot=trailing]){flex:none;display:grid;place-items:center;width:24px;height:24px}:host :where([data-slot=trailing]>*){grid-area:1/1}:host :where([data-slot=progress]){display:flex;align-items:center;justify-content:center;width:24px;height:24px;font-size:var(--email-attachment-chip-progress-size, 1rem);line-height:1;color:var(--email-attachment-chip-secondary-color, var(--mat-sys-on-surface-variant, light-dark(#5f6368, #9aa0a6)))}:host :where([data-slot=progress-value]){font-family:var(--email-attachment-chip-secondary-font-family, var(--mat-sys-body-small-font, system-ui, sans-serif));font-size:.625rem;font-weight:var(--email-attachment-chip-secondary-font-weight, var(--mat-sys-body-small-weight, 400));line-height:1;letter-spacing:-.01em;font-variant-numeric:tabular-nums}:host :where([data-slot=progress-track]){position:absolute;bottom:0;inset-inline:var(--email-attachment-chip-corner, var(--mat-sys-corner-small, 8px));height:2px;overflow:hidden;border-radius:1px;background-color:var(--email-attachment-chip-progress-track, var(--mat-sys-outline-variant, light-dark(#dadce0, #444746)))}:host :where([data-slot=progress-indicator]){display:block;width:100%;height:100%;border-radius:inherit;background-color:var(--email-attachment-chip-progress-color, var(--mat-sys-primary, light-dark(#0b57d0, #a8c7fa)));transform-origin:left;transition:transform .15s linear}:host :where([data-mode=indeterminate] [data-slot=progress-indicator]){transform:scaleX(0)}:host :where([data-mode=simulated] [data-slot=progress-indicator]){animation:email-attachment-chip-fill 1s cubic-bezier(.2,.6,.35,1) forwards}:host :where([data-slot=remove]){display:flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;border:none;border-radius:50%;cursor:pointer;background:none;color:var(--email-attachment-chip-icon-color, var(--mat-sys-on-surface-variant, light-dark(#5f6368, #9aa0a6)));transition:opacity .12s ease,background-color .12s ease}:host :where([data-slot=remove] svg){width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:1.75;stroke-linecap:round}:host([aria-busy=true]) :where([data-slot=remove]){opacity:0}:host([aria-busy=true]:is(:hover,:focus-within)) :where([data-slot=remove]){opacity:1;background-color:var(--email-attachment-chip-background, var(--mat-sys-surface-container, light-dark(#f1f3f4, #303134)))}@media(hover:none){:host([aria-busy=true]) :where([data-slot=remove]){opacity:1;background-color:var(--email-attachment-chip-background, var(--mat-sys-surface-container, light-dark(#f1f3f4, #303134)))}}:host :where([data-slot=remove]:hover:not(:disabled),[data-slot=remove]:focus-visible){background-color:var(--email-attachment-chip-hover-background, var(--mat-sys-surface-container-highest, light-dark(#e3e3e3, #444746)));color:var(--email-attachment-chip-color, var(--mat-sys-on-surface, light-dark(#1f1f1f, #e3e3e3)))}:host :where([data-slot=remove]:focus-visible){outline:2px solid var(--email-attachment-chip-focus-ring, var(--mat-sys-primary, light-dark(#0b57d0, #a8c7fa)));outline-offset:1px}:host :where([data-slot=remove]:disabled){cursor:default}:host(.email-attachment-chip-enter){animation:email-attachment-chip-enter .16s ease-out}:host(.email-attachment-chip-leave){animation:email-attachment-chip-leave .12s ease-in forwards}@media(prefers-reduced-motion:reduce){:host(.email-attachment-chip-enter),:host(.email-attachment-chip-leave){animation:none}:host([data-scanning]):after{animation:none;background-image:none;background-color:var(--email-attachment-chip-scan-color, color-mix(in srgb, var(--email-attachment-chip-progress-color, var(--mat-sys-primary, light-dark(#0b57d0, #a8c7fa))) 8%, transparent))}}@keyframes email-attachment-chip-scan{0%{background-position:92% 0;animation-timing-function:cubic-bezier(.35,0,.85,1)}55%{background-position:10% 0;animation-timing-function:cubic-bezier(.15,0,.9,.9)}75%{background-position:30% 0;animation-timing-function:cubic-bezier(.1,.1,.3,1)}to{background-position:55% 0}}@keyframes email-attachment-chip-scan-fade{0%,55%{opacity:1}75%{opacity:.85;animation-timing-function:cubic-bezier(.5,0,.75,.5)}96%,to{opacity:0}}@keyframes email-attachment-chip-scan-exit{to{opacity:0}}@keyframes email-attachment-chip-fill{0%{transform:scaleX(0)}to{transform:scaleX(1)}}@keyframes email-attachment-chip-enter{0%{opacity:0;transform:scale(.92)}}@keyframes email-attachment-chip-leave{to{opacity:0;transform:scale(.92)}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
|
|
365
|
+
}
|
|
366
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: AttachmentChip, decorators: [{
|
|
367
|
+
type: Component,
|
|
368
|
+
args: [{ selector: '[email-attachment-chip]', imports: [NgTemplateOutlet], host: {
|
|
369
|
+
'[attr.data-kind]': 'kind()',
|
|
370
|
+
'[attr.data-status]': 'status()',
|
|
371
|
+
'[attr.data-mode]': 'mode()',
|
|
372
|
+
'[attr.data-scanning]': 'scanning()',
|
|
373
|
+
'[attr.aria-busy]': 'busy() || null',
|
|
374
|
+
'[attr.aria-disabled]': 'ariaDisabled() || null',
|
|
375
|
+
}, template: "<!-- Every part carries data-slot: that is its styling hook. The icon box\n and the progress readout are also template slots: each has a default\n the chip draws, and a host replaces it by marking an ng-template with\n emailAttachmentIcon or emailAttachmentProgress \u2014 the chip renders it\n inside the box it already sizes, so the stylesheet never has to reach\n into the host's markup. -->\n<span data-slot=\"icon\">\n @if (iconTemplate(); as slot) {\n <ng-container *ngTemplateOutlet=\"slot.template; context: iconContext()\" />\n } @else {\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path [attr.d]=\"icon()\" />\n </svg>\n }\n</span>\n\n<span data-slot=\"label\">\n <!-- title, not a tooltip component: the name is truncated in CSS, and the\n native title is what recovers the whole of it. -->\n <span data-slot=\"name\" [title]=\"attachment().name\">{{ attachment().name }}</span>\n @if (size()) {\n <span data-slot=\"size\">{{ size() }}</span>\n }\n</span>\n\n<!-- One square at the end that the progress readout and the remove button\n share: while work is in flight the readout has it, and the \u00D7 paints over\n it on hover or focus (always on a touch screen). The bar under the chip\n is the progress itself; the readout is only its number \u2014 or the host's\n template. With no number the square stays empty and the chip itself is\n swept by a band of light (a host pseudo-element, keyed off data-mode). -->\n<span data-slot=\"trailing\">\n @if (mode(); as m) {\n <!-- A simulated run is announced as busy, never with a number: the\n fake is for the eye, not for assistive tech. -->\n <span\n data-slot=\"progress\"\n [attr.data-mode]=\"m\"\n role=\"progressbar\"\n [attr.aria-label]=\"label() + ' ' + attachment().name\"\n [attr.aria-valuenow]=\"m === 'determinate' ? percent() : null\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n >\n @if (progressTemplate(); as slot) {\n <ng-container *ngTemplateOutlet=\"slot.template; context: progressContext()\" />\n } @else if (m === 'determinate') {\n <span data-slot=\"progress-value\" aria-hidden=\"true\">{{ percent() }}%</span>\n }\n </span>\n <!-- A waiting transfer has no bar: nothing is moving yet. -->\n @if (m !== 'queued') {\n <span data-slot=\"progress-track\" [attr.data-mode]=\"m\" aria-hidden=\"true\">\n <span\n data-slot=\"progress-indicator\"\n [style.transform]=\"m === 'determinate' ? 'scaleX(' + percent() / 100 + ')' : null\"\n [style.animation-duration.ms]=\"m === 'simulated' ? duration() : null\"\n ></span>\n </span>\n }\n }\n\n @if (removable()) {\n <button\n type=\"button\"\n data-slot=\"remove\"\n [disabled]=\"ariaDisabled()\"\n [attr.aria-label]=\"removeLabel() + ' ' + attachment().name\"\n (click)=\"removed.emit()\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M4.5 4.5l7 7 M11.5 4.5l-7 7\" />\n </svg>\n </button>\n }\n</span>\n", styles: [":host{position:relative;display:inline-flex;align-items:center;gap:8px;max-width:var(--email-attachment-chip-max-width, 240px);min-width:0;min-height:40px;margin:0;padding:2px 4px 2px 8px;box-sizing:border-box;background-color:var(--email-attachment-chip-background, var(--mat-sys-surface-container, light-dark(#f1f3f4, #303134)));color:var(--email-attachment-chip-color, var(--mat-sys-on-surface, light-dark(#1f1f1f, #e3e3e3)));border:1px solid var(--email-attachment-chip-border-color, var(--mat-sys-outline-variant, light-dark(#dadce0, #444746)));border-radius:var(--email-attachment-chip-corner, var(--mat-sys-corner-small, 8px));font-family:var(--email-attachment-chip-font-family, var(--mat-sys-label-large-font, system-ui, sans-serif));font-size:var(--email-attachment-chip-font-size, var(--mat-sys-label-large-size, .875rem));font-weight:var(--email-attachment-chip-font-weight, var(--mat-sys-label-large-weight, 500));line-height:var(--email-attachment-chip-line-height, var(--mat-sys-label-large-line-height, 1.25rem));letter-spacing:var(--email-attachment-chip-letter-spacing, var(--mat-sys-label-large-tracking, .006rem));transition:opacity .15s cubic-bezier(.645,.045,.355,1)}:host([aria-disabled=true]){opacity:.6}:host([data-scanning]):after{content:\"\";position:absolute;inset:0;border-radius:inherit;pointer-events:none;background-image:linear-gradient(100deg,transparent 35%,var(--email-attachment-chip-scan-color, color-mix(in srgb, var(--email-attachment-chip-progress-color, var(--mat-sys-primary, light-dark(#0b57d0, #a8c7fa))) 14%, transparent)) 50%,transparent 65%);background-size:300% 100%;background-repeat:no-repeat;animation:email-attachment-chip-scan 2.2s linear infinite,email-attachment-chip-scan-fade 2.2s linear infinite}:host([data-scanning=leaving]):after{animation:email-attachment-chip-scan 2.2s linear infinite,email-attachment-chip-scan-fade 2.2s linear infinite,email-attachment-chip-scan-exit .3s ease-out forwards}:host :where([data-slot=icon]){flex:none;display:inline-flex;align-items:center;justify-content:center;width:var(--email-attachment-chip-icon-size, 20px);height:var(--email-attachment-chip-icon-size, 20px);font-size:var(--email-attachment-chip-icon-size, 20px);line-height:1;color:var(--email-attachment-chip-icon-color, var(--mat-sys-on-surface-variant, light-dark(#5f6368, #9aa0a6)))}:host :where([data-slot=icon]>svg){width:100%;height:100%;fill:none;stroke:currentColor;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round}:host :where([data-slot=label]){display:flex;flex-direction:column;flex:1 1 auto;min-width:0}:host :where([data-slot=name],[data-slot=size]){overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host :where([data-slot=size]){font-family:var(--email-attachment-chip-secondary-font-family, var(--mat-sys-body-small-font, system-ui, sans-serif));font-size:var(--email-attachment-chip-secondary-font-size, var(--mat-sys-body-small-size, .75rem));font-weight:var(--email-attachment-chip-secondary-font-weight, var(--mat-sys-body-small-weight, 400));line-height:var(--email-attachment-chip-secondary-line-height, var(--mat-sys-body-small-line-height, 1rem));letter-spacing:var(--email-attachment-chip-secondary-letter-spacing, var(--mat-sys-body-small-tracking, .025rem));color:var(--email-attachment-chip-secondary-color, var(--mat-sys-on-surface-variant, light-dark(#5f6368, #9aa0a6)))}:host :where([data-slot=trailing]){flex:none;display:grid;place-items:center;width:24px;height:24px}:host :where([data-slot=trailing]>*){grid-area:1/1}:host :where([data-slot=progress]){display:flex;align-items:center;justify-content:center;width:24px;height:24px;font-size:var(--email-attachment-chip-progress-size, 1rem);line-height:1;color:var(--email-attachment-chip-secondary-color, var(--mat-sys-on-surface-variant, light-dark(#5f6368, #9aa0a6)))}:host :where([data-slot=progress-value]){font-family:var(--email-attachment-chip-secondary-font-family, var(--mat-sys-body-small-font, system-ui, sans-serif));font-size:.625rem;font-weight:var(--email-attachment-chip-secondary-font-weight, var(--mat-sys-body-small-weight, 400));line-height:1;letter-spacing:-.01em;font-variant-numeric:tabular-nums}:host :where([data-slot=progress-track]){position:absolute;bottom:0;inset-inline:var(--email-attachment-chip-corner, var(--mat-sys-corner-small, 8px));height:2px;overflow:hidden;border-radius:1px;background-color:var(--email-attachment-chip-progress-track, var(--mat-sys-outline-variant, light-dark(#dadce0, #444746)))}:host :where([data-slot=progress-indicator]){display:block;width:100%;height:100%;border-radius:inherit;background-color:var(--email-attachment-chip-progress-color, var(--mat-sys-primary, light-dark(#0b57d0, #a8c7fa)));transform-origin:left;transition:transform .15s linear}:host :where([data-mode=indeterminate] [data-slot=progress-indicator]){transform:scaleX(0)}:host :where([data-mode=simulated] [data-slot=progress-indicator]){animation:email-attachment-chip-fill 1s cubic-bezier(.2,.6,.35,1) forwards}:host :where([data-slot=remove]){display:flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;border:none;border-radius:50%;cursor:pointer;background:none;color:var(--email-attachment-chip-icon-color, var(--mat-sys-on-surface-variant, light-dark(#5f6368, #9aa0a6)));transition:opacity .12s ease,background-color .12s ease}:host :where([data-slot=remove] svg){width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:1.75;stroke-linecap:round}:host([aria-busy=true]) :where([data-slot=remove]){opacity:0}:host([aria-busy=true]:is(:hover,:focus-within)) :where([data-slot=remove]){opacity:1;background-color:var(--email-attachment-chip-background, var(--mat-sys-surface-container, light-dark(#f1f3f4, #303134)))}@media(hover:none){:host([aria-busy=true]) :where([data-slot=remove]){opacity:1;background-color:var(--email-attachment-chip-background, var(--mat-sys-surface-container, light-dark(#f1f3f4, #303134)))}}:host :where([data-slot=remove]:hover:not(:disabled),[data-slot=remove]:focus-visible){background-color:var(--email-attachment-chip-hover-background, var(--mat-sys-surface-container-highest, light-dark(#e3e3e3, #444746)));color:var(--email-attachment-chip-color, var(--mat-sys-on-surface, light-dark(#1f1f1f, #e3e3e3)))}:host :where([data-slot=remove]:focus-visible){outline:2px solid var(--email-attachment-chip-focus-ring, var(--mat-sys-primary, light-dark(#0b57d0, #a8c7fa)));outline-offset:1px}:host :where([data-slot=remove]:disabled){cursor:default}:host(.email-attachment-chip-enter){animation:email-attachment-chip-enter .16s ease-out}:host(.email-attachment-chip-leave){animation:email-attachment-chip-leave .12s ease-in forwards}@media(prefers-reduced-motion:reduce){:host(.email-attachment-chip-enter),:host(.email-attachment-chip-leave){animation:none}:host([data-scanning]):after{animation:none;background-image:none;background-color:var(--email-attachment-chip-scan-color, color-mix(in srgb, var(--email-attachment-chip-progress-color, var(--mat-sys-primary, light-dark(#0b57d0, #a8c7fa))) 8%, transparent))}}@keyframes email-attachment-chip-scan{0%{background-position:92% 0;animation-timing-function:cubic-bezier(.35,0,.85,1)}55%{background-position:10% 0;animation-timing-function:cubic-bezier(.15,0,.9,.9)}75%{background-position:30% 0;animation-timing-function:cubic-bezier(.1,.1,.3,1)}to{background-position:55% 0}}@keyframes email-attachment-chip-scan-fade{0%,55%{opacity:1}75%{opacity:.85;animation-timing-function:cubic-bezier(.5,0,.75,.5)}96%,to{opacity:0}}@keyframes email-attachment-chip-scan-exit{to{opacity:0}}@keyframes email-attachment-chip-fill{0%{transform:scaleX(0)}to{transform:scaleX(1)}}@keyframes email-attachment-chip-enter{0%{opacity:0;transform:scale(.92)}}@keyframes email-attachment-chip-leave{to{opacity:0;transform:scale(.92)}}\n"] }]
|
|
376
|
+
}], propDecorators: { attachment: [{ type: i0.Input, args: [{ isSignal: true, alias: "attachment", required: true }] }], removable: [{ type: i0.Input, args: [{ isSignal: true, alias: "removable", required: false }] }], removeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "removeLabel", required: false }] }], status: [{ type: i0.Input, args: [{ isSignal: true, alias: "status", required: false }] }], ariaBusy: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-busy", required: false }] }], progress: [{ type: i0.Input, args: [{ isSignal: true, alias: "progress", required: false }] }], ariaDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-disabled", required: false }] }], simulateProgress: [{ type: i0.Input, args: [{ isSignal: true, alias: "simulateProgress", required: false }] }], progressLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "progressLabel", required: false }] }], removed: [{ type: i0.Output, args: ["removed"] }], iconTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => AttachmentChipIcon), { isSignal: true }] }], progressTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => AttachmentChipProgress), { isSignal: true }] }] } });
|
|
377
|
+
|
|
378
|
+
/*
|
|
379
|
+
* Public API Surface of @h-k-dev/angular-email-editor/attachment-chip
|
|
380
|
+
*/
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Generated bundle index. Do not edit.
|
|
384
|
+
*/
|
|
385
|
+
|
|
386
|
+
export { ATTACHMENT_CHIP_OPTIONS, AttachmentChip, AttachmentChipIcon, AttachmentChipProgress, attachmentInFlight, attachmentKind, formatAttachmentSize, simulatedDuration };
|
|
387
|
+
//# sourceMappingURL=h-k-dev-angular-email-editor-attachment-chip.mjs.map
|