@aglyn/plugins-email 1.0.0-beta.182 → 1.0.0-beta.184
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/package.json +17 -17
- package/src/lib/components/email-blocks.d.ts +20 -0
- package/src/lib/components/email-blocks.js +135 -2
- package/src/lib/components/email-blocks.js.map +1 -1
- package/src/lib/components/email-design-preview.d.ts +7 -0
- package/src/lib/components/email-design-preview.js +79 -3
- package/src/lib/components/email-design-preview.js.map +1 -1
- package/src/lib/server-email-drafts.d.ts +11 -4
- package/src/lib/server-email-drafts.js +23 -4
- package/src/lib/server-email-drafts.js.map +1 -1
- package/src/lib/server-list-gate.js +8 -4
- package/src/lib/server-list-gate.js.map +1 -1
- package/src/lib/server.js +269 -68
- package/src/lib/server.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/plugins-email",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.184",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://aglyn.com",
|
|
6
6
|
"repository": {
|
|
@@ -25,22 +25,22 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@aglyn/aglyn": "1.0.0-beta.
|
|
29
|
-
"@aglyn/shared-data-enums": "1.0.0-beta.
|
|
30
|
-
"@aglyn/shared-data-mdi": "1.0.0-beta.
|
|
31
|
-
"@aglyn/shared-data-types": "1.0.0-beta.
|
|
32
|
-
"@aglyn/shared-ui-email-campaigns": "1.0.0-beta.
|
|
33
|
-
"@aglyn/shared-ui-jsx": "1.0.0-beta.
|
|
34
|
-
"@aglyn/shared-ui-jsx-forms": "1.0.0-beta.
|
|
35
|
-
"@aglyn/shared-ui-next": "1.0.0-beta.
|
|
36
|
-
"@aglyn/shared-ui-snackstack": "1.0.0-beta.
|
|
37
|
-
"@aglyn/shared-ui-theme": "1.0.0-beta.
|
|
38
|
-
"@aglyn/shared-util-email": "1.0.0-beta.
|
|
39
|
-
"@aglyn/shared-util-http": "1.0.0-beta.
|
|
40
|
-
"@aglyn/shared-util-timestamp": "1.0.0-beta.
|
|
41
|
-
"@aglyn/shared-util-tools": "1.0.0-beta.
|
|
42
|
-
"@aglyn/tenant-data-admin": "1.0.0-beta.
|
|
43
|
-
"@aglyn/tenant-feature-instance": "1.0.0-beta.
|
|
28
|
+
"@aglyn/aglyn": "1.0.0-beta.184",
|
|
29
|
+
"@aglyn/shared-data-enums": "1.0.0-beta.184",
|
|
30
|
+
"@aglyn/shared-data-mdi": "1.0.0-beta.184",
|
|
31
|
+
"@aglyn/shared-data-types": "1.0.0-beta.184",
|
|
32
|
+
"@aglyn/shared-ui-email-campaigns": "1.0.0-beta.184",
|
|
33
|
+
"@aglyn/shared-ui-jsx": "1.0.0-beta.184",
|
|
34
|
+
"@aglyn/shared-ui-jsx-forms": "1.0.0-beta.184",
|
|
35
|
+
"@aglyn/shared-ui-next": "1.0.0-beta.184",
|
|
36
|
+
"@aglyn/shared-ui-snackstack": "1.0.0-beta.184",
|
|
37
|
+
"@aglyn/shared-ui-theme": "1.0.0-beta.184",
|
|
38
|
+
"@aglyn/shared-util-email": "1.0.0-beta.184",
|
|
39
|
+
"@aglyn/shared-util-http": "1.0.0-beta.184",
|
|
40
|
+
"@aglyn/shared-util-timestamp": "1.0.0-beta.184",
|
|
41
|
+
"@aglyn/shared-util-tools": "1.0.0-beta.184",
|
|
42
|
+
"@aglyn/tenant-data-admin": "1.0.0-beta.184",
|
|
43
|
+
"@aglyn/tenant-feature-instance": "1.0.0-beta.184",
|
|
44
44
|
"@swc/helpers": "0.5.23"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
@@ -107,5 +107,25 @@ export interface EmailHtmlProps extends EmailBlockStyleProps {
|
|
|
107
107
|
}
|
|
108
108
|
export declare const EmailHtml: import("react").ForwardRefExoticComponent<EmailHtmlProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
109
109
|
export declare const emailHtmlSchema: Aglyn.ComponentSchema<EmailHtmlProps>;
|
|
110
|
+
/**
|
|
111
|
+
* The two bands most emails a site sends begin and end with (AGL-3287).
|
|
112
|
+
*
|
|
113
|
+
* Each drops into any email like the blocks above, and the Components page
|
|
114
|
+
* starts a reusable Header or Footer email block from these same trees, by
|
|
115
|
+
* the ids core names in `REUSABLE_EMAIL_BLOCK_STARTER_PRESET_IDS` — one tree
|
|
116
|
+
* each, wherever it is started from.
|
|
117
|
+
*
|
|
118
|
+
* The words are placeholders to replace. No merge token names the company in
|
|
119
|
+
* both kinds of email — a campaign supplies `{{org.name}}`, and no token is
|
|
120
|
+
* supplied by every one of a site's own emails — while a token an email does
|
|
121
|
+
* not supply is blanked before sending, which would leave the header with no
|
|
122
|
+
* name at all.
|
|
123
|
+
*
|
|
124
|
+
* The footer carries no unsubscribe link: a campaign send appends its own,
|
|
125
|
+
* and a second one would do the same thing twice.
|
|
126
|
+
*/
|
|
127
|
+
export declare const EMAIL_HEADER_PRESET: Aglyn.PresetSchema;
|
|
128
|
+
/** See {@link EMAIL_HEADER_PRESET}. */
|
|
129
|
+
export declare const EMAIL_FOOTER_PRESET: Aglyn.PresetSchema;
|
|
110
130
|
export declare const emailPresets: Aglyn.PresetSchema[];
|
|
111
131
|
export {};
|
|
@@ -17,7 +17,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
17
17
|
* See the License for the specific language governing permissions and
|
|
18
18
|
* limitations under the License.
|
|
19
19
|
*/ import * as Aglyn from "@aglyn/aglyn";
|
|
20
|
-
import { mdiCardTextOutline, mdiEmailOutline, mdiFormatText, mdiImageOutline, mdiMinus, mdiPackageVariantClosed, mdiArrowExpandVertical, mdiGestureTapButton, mdiCodeTags } from "@aglyn/shared-data-mdi";
|
|
20
|
+
import { mdiCardTextOutline, mdiEmailOutline, mdiFormatText, mdiImageOutline, mdiMinus, mdiPackageVariantClosed, mdiPageLayoutFooter, mdiPageLayoutHeader, mdiArrowExpandVertical, mdiGestureTapButton, mdiCodeTags } from "@aglyn/shared-data-mdi";
|
|
21
21
|
import { sanitizeAuthorHtml } from "@aglyn/aglyn/app-utils/author-html";
|
|
22
22
|
import { mergeSxProps } from "@aglyn/shared-ui-theme";
|
|
23
23
|
import Box from "@mui/material/Box";
|
|
@@ -693,6 +693,137 @@ export const emailHtmlSchema = {
|
|
|
693
693
|
}
|
|
694
694
|
]
|
|
695
695
|
};
|
|
696
|
+
/**
|
|
697
|
+
* The two bands most emails a site sends begin and end with (AGL-3287).
|
|
698
|
+
*
|
|
699
|
+
* Each drops into any email like the blocks above, and the Components page
|
|
700
|
+
* starts a reusable Header or Footer email block from these same trees, by
|
|
701
|
+
* the ids core names in `REUSABLE_EMAIL_BLOCK_STARTER_PRESET_IDS` — one tree
|
|
702
|
+
* each, wherever it is started from.
|
|
703
|
+
*
|
|
704
|
+
* The words are placeholders to replace. No merge token names the company in
|
|
705
|
+
* both kinds of email — a campaign supplies `{{org.name}}`, and no token is
|
|
706
|
+
* supplied by every one of a site's own emails — while a token an email does
|
|
707
|
+
* not supply is blanked before sending, which would leave the header with no
|
|
708
|
+
* name at all.
|
|
709
|
+
*
|
|
710
|
+
* The footer carries no unsubscribe link: a campaign send appends its own,
|
|
711
|
+
* and a second one would do the same thing twice.
|
|
712
|
+
*/ export const EMAIL_HEADER_PRESET = {
|
|
713
|
+
$id: Aglyn.REUSABLE_EMAIL_BLOCK_STARTER_PRESET_IDS.header,
|
|
714
|
+
type: Aglyn.NodeType.PRESET,
|
|
715
|
+
displayName: 'Header',
|
|
716
|
+
description: 'Your logo above your company name, for the top of an email. Pick the ' + 'logo from your media library — until you do, the picture shows nothing ' + 'in a sent email.',
|
|
717
|
+
pluginId: BUNDLE_ID,
|
|
718
|
+
category: Aglyn.ComponentCategory.BLOCKS,
|
|
719
|
+
tags: [
|
|
720
|
+
'logo',
|
|
721
|
+
'masthead',
|
|
722
|
+
'top',
|
|
723
|
+
'brand'
|
|
724
|
+
],
|
|
725
|
+
icon: {
|
|
726
|
+
path: mdiPageLayoutHeader.path,
|
|
727
|
+
sx: {
|
|
728
|
+
color: '#f57c00'
|
|
729
|
+
}
|
|
730
|
+
},
|
|
731
|
+
data: {
|
|
732
|
+
$id: null,
|
|
733
|
+
componentId: SECTION_ID,
|
|
734
|
+
pluginId: BUNDLE_ID,
|
|
735
|
+
props: {
|
|
736
|
+
align: 'center',
|
|
737
|
+
padding: 24
|
|
738
|
+
},
|
|
739
|
+
nodes: [
|
|
740
|
+
{
|
|
741
|
+
$id: null,
|
|
742
|
+
componentId: IMAGE_ID,
|
|
743
|
+
pluginId: BUNDLE_ID,
|
|
744
|
+
props: {
|
|
745
|
+
alt: 'Your company logo',
|
|
746
|
+
width: 160,
|
|
747
|
+
align: 'center'
|
|
748
|
+
}
|
|
749
|
+
},
|
|
750
|
+
{
|
|
751
|
+
$id: null,
|
|
752
|
+
componentId: TEXT_ID,
|
|
753
|
+
pluginId: BUNDLE_ID,
|
|
754
|
+
props: {
|
|
755
|
+
children: 'Your company',
|
|
756
|
+
variant: 'subheading',
|
|
757
|
+
align: 'center'
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
]
|
|
761
|
+
}
|
|
762
|
+
};
|
|
763
|
+
/**
|
|
764
|
+
* The Footer's small print, a muted gray that still reads at 5:1 on white.
|
|
765
|
+
* A literal on purpose: it is written into the sent mail's own HTML, where no
|
|
766
|
+
* theme token resolves.
|
|
767
|
+
*/ const FOOTER_TEXT_COLOR = '#6b6b6b';
|
|
768
|
+
/** See {@link EMAIL_HEADER_PRESET}. */ export const EMAIL_FOOTER_PRESET = {
|
|
769
|
+
$id: Aglyn.REUSABLE_EMAIL_BLOCK_STARTER_PRESET_IDS.footer,
|
|
770
|
+
type: Aglyn.NodeType.PRESET,
|
|
771
|
+
displayName: 'Footer',
|
|
772
|
+
description: 'Your company name and address, and why the reader gets your emails, for ' + 'the bottom of an email. Campaign emails add the unsubscribe link for you.',
|
|
773
|
+
pluginId: BUNDLE_ID,
|
|
774
|
+
category: Aglyn.ComponentCategory.BLOCKS,
|
|
775
|
+
tags: [
|
|
776
|
+
'address',
|
|
777
|
+
'bottom',
|
|
778
|
+
'contact',
|
|
779
|
+
'legal'
|
|
780
|
+
],
|
|
781
|
+
icon: {
|
|
782
|
+
path: mdiPageLayoutFooter.path,
|
|
783
|
+
sx: {
|
|
784
|
+
color: '#f57c00'
|
|
785
|
+
}
|
|
786
|
+
},
|
|
787
|
+
data: {
|
|
788
|
+
$id: null,
|
|
789
|
+
componentId: SECTION_ID,
|
|
790
|
+
pluginId: BUNDLE_ID,
|
|
791
|
+
props: {
|
|
792
|
+
align: 'center',
|
|
793
|
+
padding: 24
|
|
794
|
+
},
|
|
795
|
+
nodes: [
|
|
796
|
+
{
|
|
797
|
+
$id: null,
|
|
798
|
+
componentId: DIVIDER_ID,
|
|
799
|
+
pluginId: BUNDLE_ID,
|
|
800
|
+
props: {}
|
|
801
|
+
},
|
|
802
|
+
{
|
|
803
|
+
$id: null,
|
|
804
|
+
componentId: TEXT_ID,
|
|
805
|
+
pluginId: BUNDLE_ID,
|
|
806
|
+
props: {
|
|
807
|
+
children: 'Your company · 123 Main St, City',
|
|
808
|
+
variant: 'caption',
|
|
809
|
+
align: 'center',
|
|
810
|
+
color: FOOTER_TEXT_COLOR
|
|
811
|
+
}
|
|
812
|
+
},
|
|
813
|
+
{
|
|
814
|
+
$id: null,
|
|
815
|
+
componentId: TEXT_ID,
|
|
816
|
+
pluginId: BUNDLE_ID,
|
|
817
|
+
props: {
|
|
818
|
+
children: "You're receiving this because you're a customer of Your company.",
|
|
819
|
+
variant: 'caption',
|
|
820
|
+
align: 'center',
|
|
821
|
+
color: FOOTER_TEXT_COLOR
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
]
|
|
825
|
+
}
|
|
826
|
+
};
|
|
696
827
|
export const emailPresets = [
|
|
697
828
|
{
|
|
698
829
|
$id: generatePresetId(SECTION_ID),
|
|
@@ -869,7 +1000,9 @@ export const emailPresets = [
|
|
|
869
1000
|
pluginId: BUNDLE_ID,
|
|
870
1001
|
props: {}
|
|
871
1002
|
}
|
|
872
|
-
}
|
|
1003
|
+
},
|
|
1004
|
+
EMAIL_HEADER_PRESET,
|
|
1005
|
+
EMAIL_FOOTER_PRESET
|
|
873
1006
|
];
|
|
874
1007
|
|
|
875
1008
|
//# sourceMappingURL=email-blocks.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/email/src/lib/components/email-blocks.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport {\n mdiCardTextOutline,\n mdiEmailOutline,\n mdiFormatText,\n mdiImageOutline,\n mdiMinus,\n mdiPackageVariantClosed,\n mdiArrowExpandVertical,\n mdiGestureTapButton,\n mdiCodeTags,\n} from '@aglyn/shared-data-mdi'\nimport { sanitizeAuthorHtml } from '@aglyn/aglyn/app-utils/author-html'\nimport { mergeSxProps } from '@aglyn/shared-ui-theme'\nimport Box from '@mui/material/Box'\nimport type { SxProps, Theme } from '@mui/material/styles'\nimport Typography from '@mui/material/Typography'\nimport { forwardRef } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n/**\n * Email-safe blocks (AGL-346): the besigner canvas renders these web\n * previews; the render pipeline (email-render.ts in app-utils) converts\n * the same nodes to inline-styled table HTML for email clients. Merge\n * tokens like {{contact.firstName}} substitute at send time.\n *\n * Component ids are persisted in screen documents; never rename.\n *\n * Every block pulls `sx` out of its props and composes it AFTER its own\n * defaults with MUI array composition (AGL-587): the renderer's Leaf\n * passes the node-level sx (styles-panel output) through props.sx, and a\n * literal `sx={{...}}` after `{...rest}` used to silently discard it.\n */\n\ninterface EmailBlockStyleProps {\n /** Node-level styles merged by the renderer; wins over block defaults. */\n sx?: SxProps<Theme>\n}\n\n// ---------------------------------------------------------------- section\nexport const SECTION_ID: Aglyn.ComponentId = 'emailSection'\n\nexport interface EmailSectionProps extends EmailBlockStyleProps {\n backgroundColor?: string\n /** Inner padding in px (default 24). */\n padding?: number\n align?: 'left' | 'center' | 'right'\n children?: JSX.Children\n}\n\nexport const EmailSection = forwardRef<HTMLDivElement, EmailSectionProps>(\n (props, ref) => {\n const { backgroundColor, padding, align, children, sx, ...rest } = props\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps(\n {\n maxWidth: 600,\n mx: 'auto',\n backgroundColor: backgroundColor || '#ffffff',\n p: `${padding ?? 24}px`,\n textAlign: align ?? 'left',\n },\n sx,\n )}\n >\n {children}\n </Box>\n )\n },\n)\nEmailSection.displayName = 'EmailSection'\n\nexport const emailSectionSchema: Aglyn.ComponentSchema<EmailSectionProps> = {\n $id: SECTION_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email section',\n description: 'An email-safe band with its own background color and padding.',\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiEmailOutline.path, sx: { color: '#f57c00' } },\n attributes: [\n {\n name: 'backgroundColor',\n label: 'Background color',\n description: 'Solid colors only — gradients are unreliable in email.',\n component: Aglyn.FieldComponentType.COLOR_PICKER,\n },\n {\n name: 'padding',\n label: 'Padding (px)',\n description: 'Inner spacing around the section content.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'align',\n label: 'Content alignment',\n description:\n 'How every block inside this section lines up. Each block can still ' +\n 'override it with its own alignment.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'left', label: 'Left' },\n { value: 'center', label: 'Center' },\n { value: 'right', label: 'Right' },\n ],\n },\n ],\n}\n\n// ------------------------------------------------------------------- text\nexport const TEXT_ID: Aglyn.ComponentId = 'emailText'\n\nexport interface EmailTextProps extends EmailBlockStyleProps {\n children?: string\n variant?: 'heading' | 'subheading' | 'body' | 'caption'\n color?: string\n align?: 'left' | 'center' | 'right'\n}\n\nconst TEXT_PRESETS: Record<\n NonNullable<EmailTextProps['variant']>,\n { fontSize: number; fontWeight: number; lineHeight: number }\n> = {\n heading: { fontSize: 28, fontWeight: 700, lineHeight: 1.25 },\n subheading: { fontSize: 20, fontWeight: 600, lineHeight: 1.3 },\n body: { fontSize: 15, fontWeight: 400, lineHeight: 1.55 },\n caption: { fontSize: 12, fontWeight: 400, lineHeight: 1.4 },\n}\n\nexport const EmailText = forwardRef<HTMLDivElement, EmailTextProps>(\n (props, ref) => {\n const { children, variant, color, align, sx, ...rest } = props\n const preset = TEXT_PRESETS[variant ?? 'body']\n return (\n <Typography\n ref={ref}\n component=\"div\"\n {...rest}\n sx={mergeSxProps(\n {\n fontSize: preset.fontSize,\n fontWeight: preset.fontWeight,\n lineHeight: preset.lineHeight,\n color: color || '#1a1a1a',\n textAlign: align ?? 'left',\n fontFamily: 'Helvetica, Arial, sans-serif',\n mb: 1,\n },\n sx,\n )}\n >\n {children ?? 'Email text'}\n </Typography>\n )\n },\n)\nEmailText.displayName = 'EmailText'\n\nexport const emailTextSchema: Aglyn.ComponentSchema<EmailTextProps> = {\n $id: TEXT_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email text',\n description: 'A paragraph of email copy in one of the preset styles.',\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiFormatText.path, sx: { color: '#f57c00' } },\n flags: { textEditable: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'children',\n label: 'Text',\n description:\n 'Supports merge tokens: {{contact.firstName}}, {{contact.email}}, ' +\n '{{org.name}}, {{unsubscribeUrl}}.',\n component: Aglyn.FieldComponentType.TEXTAREA,\n },\n {\n name: 'variant',\n label: 'Style',\n description:\n 'Size and weight of this text — Heading and Subheading also give it ' +\n 'the spacing above and below that a heading needs.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'heading', label: 'Heading' },\n { value: 'subheading', label: 'Subheading' },\n { value: 'body', label: 'Body' },\n { value: 'caption', label: 'Caption' },\n ],\n },\n {\n name: 'color',\n label: 'Color',\n description:\n 'Text color. Keep it dark on a light background: many clients ' +\n 'ignore dark mode overrides and show your color as-is.',\n component: Aglyn.FieldComponentType.COLOR_PICKER,\n },\n {\n name: 'align',\n label: 'Alignment',\n description: 'How this block of text lines up within the section.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'left', label: 'Left' },\n { value: 'center', label: 'Center' },\n { value: 'right', label: 'Right' },\n ],\n },\n ],\n}\n\n// --------------------------------------------------------------- richtext\nexport const RICHTEXT_ID: Aglyn.ComponentId = 'emailRichtext'\n\nexport interface EmailRichtextProps extends EmailBlockStyleProps {\n /** HTML fragment; sanitized with the custom-html policy (AGL-320). */\n html?: string\n}\n\nexport const EmailRichtext = forwardRef<HTMLDivElement, EmailRichtextProps>(\n (props, ref) => {\n const { html, sx, ...rest } = props\n if (!html?.trim()) {\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps(\n {\n p: 2,\n border: '1px dashed',\n borderColor: 'divider',\n color: 'text.secondary',\n fontSize: 13,\n },\n sx,\n )}\n >\n {'Rich text — add HTML in the attributes panel'}\n </Box>\n )\n }\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps(\n { fontFamily: 'Helvetica, Arial, sans-serif', fontSize: 15 },\n sx,\n )}\n // Sanitized on every render — same policy as the custom HTML block.\n // The `typeof window` guard this replaced existed only because that\n // policy was DOMPurify, which needs a DOM; it is now a pure function\n // (AGL-1901), so the body survives a server render instead of being\n // blanked — which for an email is the render that gets SENT.\n dangerouslySetInnerHTML={{ __html: sanitizeAuthorHtml(html) }}\n />\n )\n },\n)\nEmailRichtext.displayName = 'EmailRichtext'\n\nexport const emailRichtextSchema: Aglyn.ComponentSchema<EmailRichtextProps> = {\n $id: RICHTEXT_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email rich text',\n description: 'An HTML fragment in an email, sanitised on render.',\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiCardTextOutline.path, sx: { color: '#f57c00' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'html',\n label: 'HTML',\n description:\n 'Formatted content (p, b, i, a, ul/ol, h1-h4). Scripts and forms ' +\n 'are stripped; keep styling simple for email clients. Merge ' +\n 'tokens work here too.',\n component: Aglyn.FieldComponentType.TEXTAREA,\n },\n ],\n}\n\n// ------------------------------------------------------------------ image\nexport const IMAGE_ID: Aglyn.ComponentId = 'emailImage'\n\nexport interface EmailImageProps extends EmailBlockStyleProps {\n src?: string\n alt?: string\n /** Rendered width in px (max 600). */\n width?: number\n href?: string\n align?: 'left' | 'center' | 'right'\n}\n\nexport const EmailImage = forwardRef<HTMLDivElement, EmailImageProps>(\n (props, ref) => {\n const { src: storedSrc, alt, width, href, align, sx, ...rest } = props\n // A media-picker target, so it can hold a media reference (AGL-1215).\n // This is the CANVAS preview only — the sent HTML is built by\n // `email-render`, which needs an absolute URL and cannot resolve either\n // a reference or the relative CDN path it replaces.\n const src = Aglyn.resolveMediaSrc(storedSrc)\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps({ textAlign: align ?? 'center', my: 1 }, sx)}\n >\n {src ? (\n <Box\n component=\"img\"\n src={src}\n alt={alt ?? ''}\n sx={{ maxWidth: '100%', width: width ? `${width}px` : '100%' }}\n />\n ) : (\n <Box\n sx={{\n height: 120,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n border: '1px dashed',\n borderColor: 'divider',\n color: 'text.secondary',\n fontSize: 13,\n }}\n >\n {'Image — pick from the media library'}\n </Box>\n )}\n </Box>\n )\n },\n)\nEmailImage.displayName = 'EmailImage'\n\nexport const emailImageSchema: Aglyn.ComponentSchema<EmailImageProps> = {\n $id: IMAGE_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email image',\n description: 'An email-safe image, capped at 600px wide, optionally linked.',\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiImageOutline.path, sx: { color: '#f57c00' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'src',\n label: 'Source URL',\n description: 'Use an absolute URL — email clients cannot resolve relative paths.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'alt',\n label: 'Alt text',\n // AGL-1896: the same \"Browse media\" default the site Image element\n // gets. It matters MORE here — most clients block images by default,\n // so for most recipients the alt text IS the block.\n description:\n 'Shown while images are blocked — most clients block by default. ' +\n 'Filled in from the media library when you pick a file that has ' +\n 'alt text.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'width',\n label: 'Width (px)',\n description:\n 'How wide the image renders. Blank fills the section — which is ' +\n 'usually right, since a phone is narrower than any number you pick.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'href',\n label: 'Link URL',\n description:\n 'Where the image goes when it is clicked. Blank leaves it as a ' +\n 'plain picture.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'align',\n label: 'Alignment',\n description: 'Where the image sits when it is narrower than the section.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'left', label: 'Left' },\n { value: 'center', label: 'Center' },\n { value: 'right', label: 'Right' },\n ],\n },\n ],\n}\n\n// ----------------------------------------------------------------- button\nexport const BUTTON_ID: Aglyn.ComponentId = 'emailButton'\n\nexport interface EmailButtonProps extends EmailBlockStyleProps {\n children?: string\n href?: string\n backgroundColor?: string\n color?: string\n align?: 'left' | 'center' | 'right'\n}\n\nexport const EmailButton = forwardRef<HTMLDivElement, EmailButtonProps>(\n (props, ref) => {\n const { children, href, backgroundColor, color, align, sx, ...rest } =\n props\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps({ textAlign: align ?? 'center', my: 1.5 }, sx)}\n >\n <Box\n component=\"span\"\n sx={{\n display: 'inline-block',\n px: 3,\n py: 1.25,\n borderRadius: 1,\n backgroundColor: backgroundColor || '#1a73e8',\n color: color || '#ffffff',\n fontFamily: 'Helvetica, Arial, sans-serif',\n fontSize: 15,\n fontWeight: 600,\n }}\n >\n {children ?? 'Call to action'}\n </Box>\n </Box>\n )\n },\n)\nEmailButton.displayName = 'EmailButton'\n\nexport const emailButtonSchema: Aglyn.ComponentSchema<EmailButtonProps> = {\n $id: BUTTON_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email button',\n description: 'A call-to-action button that survives every mail client.',\n category: Aglyn.ComponentCategory.INPUT,\n icon: { path: mdiGestureTapButton.path, sx: { color: '#f57c00' } },\n flags: { textEditable: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'children',\n label: 'Label',\n description:\n 'The words on the button. Say what happens next — \"See the report\" ' +\n 'reads as a destination where \"Click here\" does not.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'href',\n label: 'Link URL',\n description: 'Absolute URL (https://…). Merge tokens allowed.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'backgroundColor',\n label: 'Button color',\n description:\n 'The fill behind the label. Check it against the label color — a ' +\n 'button nobody can read is a link nobody clicks.',\n component: Aglyn.FieldComponentType.COLOR_PICKER,\n },\n {\n name: 'color',\n label: 'Label color',\n description: 'The color of the words on the button.',\n component: Aglyn.FieldComponentType.COLOR_PICKER,\n },\n {\n name: 'align',\n label: 'Alignment',\n description: 'Where the button sits across the width of the section.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'left', label: 'Left' },\n { value: 'center', label: 'Center' },\n { value: 'right', label: 'Right' },\n ],\n },\n ],\n}\n\n// ---------------------------------------------------------------- divider\nexport const DIVIDER_ID: Aglyn.ComponentId = 'emailDivider'\n\nexport interface EmailDividerProps extends EmailBlockStyleProps {\n color?: string\n}\n\nexport const EmailDivider = forwardRef<HTMLHRElement, EmailDividerProps>(\n (props, ref) => {\n const { color, sx, ...rest } = props\n return (\n <Box\n ref={ref}\n component=\"hr\"\n {...rest}\n sx={mergeSxProps(\n {\n border: 0,\n // Split shorthand so a palette token path from the theme-\n // reference color picker (AGL-588) resolves through the sx\n // system instead of dying inside a template string.\n borderTopStyle: 'solid',\n borderTopWidth: 1,\n borderTopColor: color || '#e0e0e0',\n my: 2,\n },\n sx,\n )}\n />\n )\n },\n)\nEmailDivider.displayName = 'EmailDivider'\n\nexport const emailDividerSchema: Aglyn.ComponentSchema<EmailDividerProps> = {\n $id: DIVIDER_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email divider',\n description: 'A horizontal rule between email sections.',\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiMinus.path, sx: { color: '#f57c00' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'color',\n label: 'Line color',\n description:\n 'The color of the rule. A light grey separates sections without ' +\n 'drawing the eye to the line itself.',\n component: Aglyn.FieldComponentType.COLOR_PICKER,\n },\n ],\n}\n\n// ----------------------------------------------------------------- spacer\nexport const SPACER_ID: Aglyn.ComponentId = 'emailSpacer'\n\nexport interface EmailSpacerProps extends EmailBlockStyleProps {\n /** Height in px (default 24). */\n height?: number\n}\n\nexport const EmailSpacer = forwardRef<HTMLDivElement, EmailSpacerProps>(\n (props, ref) => {\n const { height, sx, ...rest } = props\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps({ height: height ?? 24 }, sx)}\n />\n )\n },\n)\nEmailSpacer.displayName = 'EmailSpacer'\n\nexport const emailSpacerSchema: Aglyn.ComponentSchema<EmailSpacerProps> = {\n $id: SPACER_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email spacer',\n description: 'Fixed vertical whitespace between email blocks.',\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiArrowExpandVertical.path, sx: { color: '#f57c00' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'height',\n label: 'Height (px)',\n description:\n 'How much empty vertical space this block adds. Email clients drop ' +\n 'CSS margins, so a spacer is how you get reliable breathing room.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n ],\n}\n\n// ---------------------------------------------------------------- product\nexport const PRODUCT_ID: Aglyn.ComponentId = 'emailProduct'\n\nexport interface EmailProductProps extends EmailBlockStyleProps {\n /** Product id — resolved server-side at render (rename-safe). */\n productId?: string\n buttonLabel?: string\n}\n\nexport const EmailProduct = forwardRef<HTMLDivElement, EmailProductProps>(\n (props, ref) => {\n const { productId, buttonLabel, sx, ...rest } = props\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps(\n {\n border: '1px solid',\n borderColor: 'divider',\n borderRadius: 1,\n p: 2,\n my: 1,\n textAlign: 'center',\n color: 'text.secondary',\n fontSize: 13,\n },\n sx,\n )}\n >\n {productId\n ? `Product card (${productId}) — name, price, image fill in at send`\n : 'Product card — pick a product in the attributes panel'}\n <Box sx={{ mt: 1, fontWeight: 600 }}>\n {buttonLabel ?? 'Shop now'}\n </Box>\n </Box>\n )\n },\n)\nEmailProduct.displayName = 'EmailProduct'\n\nexport const emailProductSchema: Aglyn.ComponentSchema<EmailProductProps> = {\n $id: PRODUCT_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email product',\n description: 'A product card resolved at send time, so it never goes stale.',\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiPackageVariantClosed.path, sx: { color: '#f57c00' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'productId',\n label: 'Product',\n description:\n 'Stored by id — the current name, price, and image render at send.',\n component: Aglyn.FieldComponentType.PRODUCT_SELECT,\n },\n {\n name: 'buttonLabel',\n label: 'Button label',\n description:\n 'The words on the card’s button. Blank uses the block’s default, ' +\n 'and the link always points at this product’s page.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n ],\n}\n\n// ------------------------------------------------------------ custom html\nexport const HTML_ID: Aglyn.ComponentId = 'emailHtml'\n\nexport interface EmailHtmlProps extends EmailBlockStyleProps {\n html?: string\n}\n\nexport const EmailHtml = forwardRef<HTMLDivElement, EmailHtmlProps>(\n (props, ref) => {\n const { html, sx, ...rest } = props\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps({ fontFamily: 'Helvetica, Arial, sans-serif' }, sx)}\n // No `typeof window` guard any more (AGL-1901): the sanitizer needs\n // no DOM, so a server-rendered email keeps its custom HTML.\n dangerouslySetInnerHTML={{\n __html: html ? sanitizeAuthorHtml(html) : '',\n }}\n />\n )\n },\n)\nEmailHtml.displayName = 'EmailHtml'\n\nexport const emailHtmlSchema: Aglyn.ComponentSchema<EmailHtmlProps> = {\n $id: HTML_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email custom HTML',\n description: 'Hand-written HTML for an email, used as-is.',\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiCodeTags.path, sx: { color: '#f57c00' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'html',\n label: 'HTML',\n description:\n 'Raw table markup for advanced layouts. Sanitized with the ' +\n 'custom-html policy; the render pipeline passes it through as-is ' +\n 'after sanitization.',\n component: Aglyn.FieldComponentType.TEXTAREA,\n },\n ],\n}\n\nexport const emailPresets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(SECTION_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email section',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiEmailOutline.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: SECTION_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId(TEXT_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email text',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiFormatText.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: TEXT_ID,\n pluginId: BUNDLE_ID,\n props: { children: 'Hello {{contact.firstName}},', variant: 'body' },\n },\n },\n {\n $id: generatePresetId(RICHTEXT_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email rich text',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiCardTextOutline.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: RICHTEXT_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId(IMAGE_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email image',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiImageOutline.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: IMAGE_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId(BUTTON_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email button',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.INPUT,\n icon: { path: mdiGestureTapButton.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: BUTTON_ID,\n pluginId: BUNDLE_ID,\n props: { children: 'Shop now' },\n },\n },\n {\n $id: generatePresetId(DIVIDER_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email divider',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiMinus.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: DIVIDER_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId(SPACER_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email spacer',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiArrowExpandVertical.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: SPACER_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId(PRODUCT_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email product',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiPackageVariantClosed.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: PRODUCT_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId(HTML_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email custom HTML',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiCodeTags.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: HTML_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n]\n"],"names":["Aglyn","mdiCardTextOutline","mdiEmailOutline","mdiFormatText","mdiImageOutline","mdiMinus","mdiPackageVariantClosed","mdiArrowExpandVertical","mdiGestureTapButton","mdiCodeTags","sanitizeAuthorHtml","mergeSxProps","Box","Typography","forwardRef","BUNDLE_ID","generatePresetId","SECTION_ID","EmailSection","props","ref","backgroundColor","padding","align","children","sx","rest","maxWidth","mx","p","textAlign","displayName","emailSectionSchema","$id","pluginId","description","category","ComponentCategory","LAYOUT","icon","path","color","attributes","name","label","component","FieldComponentType","COLOR_PICKER","TEXT_FIELD","type","SELECT","options","value","TEXT_ID","TEXT_PRESETS","heading","fontSize","fontWeight","lineHeight","subheading","body","caption","EmailText","variant","preset","fontFamily","mb","emailTextSchema","DATA_DISPLAY","flags","textEditable","FEATURE_FLAG","ENABLED","TEXTAREA","RICHTEXT_ID","EmailRichtext","html","trim","border","borderColor","dangerouslySetInnerHTML","__html","emailRichtextSchema","selfClosing","IMAGE_ID","EmailImage","src","storedSrc","alt","width","href","resolveMediaSrc","my","height","display","alignItems","justifyContent","emailImageSchema","BUTTON_ID","EmailButton","px","py","borderRadius","emailButtonSchema","INPUT","DIVIDER_ID","EmailDivider","borderTopStyle","borderTopWidth","borderTopColor","emailDividerSchema","SPACER_ID","EmailSpacer","emailSpacerSchema","PRODUCT_ID","EmailProduct","productId","buttonLabel","mt","emailProductSchema","PRODUCT_SELECT","HTML_ID","EmailHtml","emailHtmlSchema","emailPresets","NodeType","PRESET","data","componentId"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/email/src/lib/components/email-blocks.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport {\n mdiCardTextOutline,\n mdiEmailOutline,\n mdiFormatText,\n mdiImageOutline,\n mdiMinus,\n mdiPackageVariantClosed,\n mdiPageLayoutFooter,\n mdiPageLayoutHeader,\n mdiArrowExpandVertical,\n mdiGestureTapButton,\n mdiCodeTags,\n} from '@aglyn/shared-data-mdi'\nimport { sanitizeAuthorHtml } from '@aglyn/aglyn/app-utils/author-html'\nimport { mergeSxProps } from '@aglyn/shared-ui-theme'\nimport Box from '@mui/material/Box'\nimport type { SxProps, Theme } from '@mui/material/styles'\nimport Typography from '@mui/material/Typography'\nimport { forwardRef } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n/**\n * Email-safe blocks (AGL-346): the besigner canvas renders these web\n * previews; the render pipeline (email-render.ts in app-utils) converts\n * the same nodes to inline-styled table HTML for email clients. Merge\n * tokens like {{contact.firstName}} substitute at send time.\n *\n * Component ids are persisted in screen documents; never rename.\n *\n * Every block pulls `sx` out of its props and composes it AFTER its own\n * defaults with MUI array composition (AGL-587): the renderer's Leaf\n * passes the node-level sx (styles-panel output) through props.sx, and a\n * literal `sx={{...}}` after `{...rest}` used to silently discard it.\n */\n\ninterface EmailBlockStyleProps {\n /** Node-level styles merged by the renderer; wins over block defaults. */\n sx?: SxProps<Theme>\n}\n\n// ---------------------------------------------------------------- section\nexport const SECTION_ID: Aglyn.ComponentId = 'emailSection'\n\nexport interface EmailSectionProps extends EmailBlockStyleProps {\n backgroundColor?: string\n /** Inner padding in px (default 24). */\n padding?: number\n align?: 'left' | 'center' | 'right'\n children?: JSX.Children\n}\n\nexport const EmailSection = forwardRef<HTMLDivElement, EmailSectionProps>(\n (props, ref) => {\n const { backgroundColor, padding, align, children, sx, ...rest } = props\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps(\n {\n maxWidth: 600,\n mx: 'auto',\n backgroundColor: backgroundColor || '#ffffff',\n p: `${padding ?? 24}px`,\n textAlign: align ?? 'left',\n },\n sx,\n )}\n >\n {children}\n </Box>\n )\n },\n)\nEmailSection.displayName = 'EmailSection'\n\nexport const emailSectionSchema: Aglyn.ComponentSchema<EmailSectionProps> = {\n $id: SECTION_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email section',\n description: 'An email-safe band with its own background color and padding.',\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiEmailOutline.path, sx: { color: '#f57c00' } },\n attributes: [\n {\n name: 'backgroundColor',\n label: 'Background color',\n description: 'Solid colors only — gradients are unreliable in email.',\n component: Aglyn.FieldComponentType.COLOR_PICKER,\n },\n {\n name: 'padding',\n label: 'Padding (px)',\n description: 'Inner spacing around the section content.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'align',\n label: 'Content alignment',\n description:\n 'How every block inside this section lines up. Each block can still ' +\n 'override it with its own alignment.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'left', label: 'Left' },\n { value: 'center', label: 'Center' },\n { value: 'right', label: 'Right' },\n ],\n },\n ],\n}\n\n// ------------------------------------------------------------------- text\nexport const TEXT_ID: Aglyn.ComponentId = 'emailText'\n\nexport interface EmailTextProps extends EmailBlockStyleProps {\n children?: string\n variant?: 'heading' | 'subheading' | 'body' | 'caption'\n color?: string\n align?: 'left' | 'center' | 'right'\n}\n\nconst TEXT_PRESETS: Record<\n NonNullable<EmailTextProps['variant']>,\n { fontSize: number; fontWeight: number; lineHeight: number }\n> = {\n heading: { fontSize: 28, fontWeight: 700, lineHeight: 1.25 },\n subheading: { fontSize: 20, fontWeight: 600, lineHeight: 1.3 },\n body: { fontSize: 15, fontWeight: 400, lineHeight: 1.55 },\n caption: { fontSize: 12, fontWeight: 400, lineHeight: 1.4 },\n}\n\nexport const EmailText = forwardRef<HTMLDivElement, EmailTextProps>(\n (props, ref) => {\n const { children, variant, color, align, sx, ...rest } = props\n const preset = TEXT_PRESETS[variant ?? 'body']\n return (\n <Typography\n ref={ref}\n component=\"div\"\n {...rest}\n sx={mergeSxProps(\n {\n fontSize: preset.fontSize,\n fontWeight: preset.fontWeight,\n lineHeight: preset.lineHeight,\n color: color || '#1a1a1a',\n textAlign: align ?? 'left',\n fontFamily: 'Helvetica, Arial, sans-serif',\n mb: 1,\n },\n sx,\n )}\n >\n {children ?? 'Email text'}\n </Typography>\n )\n },\n)\nEmailText.displayName = 'EmailText'\n\nexport const emailTextSchema: Aglyn.ComponentSchema<EmailTextProps> = {\n $id: TEXT_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email text',\n description: 'A paragraph of email copy in one of the preset styles.',\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiFormatText.path, sx: { color: '#f57c00' } },\n flags: { textEditable: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'children',\n label: 'Text',\n description:\n 'Supports merge tokens: {{contact.firstName}}, {{contact.email}}, ' +\n '{{org.name}}, {{unsubscribeUrl}}.',\n component: Aglyn.FieldComponentType.TEXTAREA,\n },\n {\n name: 'variant',\n label: 'Style',\n description:\n 'Size and weight of this text — Heading and Subheading also give it ' +\n 'the spacing above and below that a heading needs.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'heading', label: 'Heading' },\n { value: 'subheading', label: 'Subheading' },\n { value: 'body', label: 'Body' },\n { value: 'caption', label: 'Caption' },\n ],\n },\n {\n name: 'color',\n label: 'Color',\n description:\n 'Text color. Keep it dark on a light background: many clients ' +\n 'ignore dark mode overrides and show your color as-is.',\n component: Aglyn.FieldComponentType.COLOR_PICKER,\n },\n {\n name: 'align',\n label: 'Alignment',\n description: 'How this block of text lines up within the section.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'left', label: 'Left' },\n { value: 'center', label: 'Center' },\n { value: 'right', label: 'Right' },\n ],\n },\n ],\n}\n\n// --------------------------------------------------------------- richtext\nexport const RICHTEXT_ID: Aglyn.ComponentId = 'emailRichtext'\n\nexport interface EmailRichtextProps extends EmailBlockStyleProps {\n /** HTML fragment; sanitized with the custom-html policy (AGL-320). */\n html?: string\n}\n\nexport const EmailRichtext = forwardRef<HTMLDivElement, EmailRichtextProps>(\n (props, ref) => {\n const { html, sx, ...rest } = props\n if (!html?.trim()) {\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps(\n {\n p: 2,\n border: '1px dashed',\n borderColor: 'divider',\n color: 'text.secondary',\n fontSize: 13,\n },\n sx,\n )}\n >\n {'Rich text — add HTML in the attributes panel'}\n </Box>\n )\n }\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps(\n { fontFamily: 'Helvetica, Arial, sans-serif', fontSize: 15 },\n sx,\n )}\n // Sanitized on every render — same policy as the custom HTML block.\n // The `typeof window` guard this replaced existed only because that\n // policy was DOMPurify, which needs a DOM; it is now a pure function\n // (AGL-1901), so the body survives a server render instead of being\n // blanked — which for an email is the render that gets SENT.\n dangerouslySetInnerHTML={{ __html: sanitizeAuthorHtml(html) }}\n />\n )\n },\n)\nEmailRichtext.displayName = 'EmailRichtext'\n\nexport const emailRichtextSchema: Aglyn.ComponentSchema<EmailRichtextProps> = {\n $id: RICHTEXT_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email rich text',\n description: 'An HTML fragment in an email, sanitised on render.',\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiCardTextOutline.path, sx: { color: '#f57c00' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'html',\n label: 'HTML',\n description:\n 'Formatted content (p, b, i, a, ul/ol, h1-h4). Scripts and forms ' +\n 'are stripped; keep styling simple for email clients. Merge ' +\n 'tokens work here too.',\n component: Aglyn.FieldComponentType.TEXTAREA,\n },\n ],\n}\n\n// ------------------------------------------------------------------ image\nexport const IMAGE_ID: Aglyn.ComponentId = 'emailImage'\n\nexport interface EmailImageProps extends EmailBlockStyleProps {\n src?: string\n alt?: string\n /** Rendered width in px (max 600). */\n width?: number\n href?: string\n align?: 'left' | 'center' | 'right'\n}\n\nexport const EmailImage = forwardRef<HTMLDivElement, EmailImageProps>(\n (props, ref) => {\n const { src: storedSrc, alt, width, href, align, sx, ...rest } = props\n // A media-picker target, so it can hold a media reference (AGL-1215).\n // This is the CANVAS preview only — the sent HTML is built by\n // `email-render`, which needs an absolute URL and cannot resolve either\n // a reference or the relative CDN path it replaces.\n const src = Aglyn.resolveMediaSrc(storedSrc)\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps({ textAlign: align ?? 'center', my: 1 }, sx)}\n >\n {src ? (\n <Box\n component=\"img\"\n src={src}\n alt={alt ?? ''}\n sx={{ maxWidth: '100%', width: width ? `${width}px` : '100%' }}\n />\n ) : (\n <Box\n sx={{\n height: 120,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n border: '1px dashed',\n borderColor: 'divider',\n color: 'text.secondary',\n fontSize: 13,\n }}\n >\n {'Image — pick from the media library'}\n </Box>\n )}\n </Box>\n )\n },\n)\nEmailImage.displayName = 'EmailImage'\n\nexport const emailImageSchema: Aglyn.ComponentSchema<EmailImageProps> = {\n $id: IMAGE_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email image',\n description: 'An email-safe image, capped at 600px wide, optionally linked.',\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiImageOutline.path, sx: { color: '#f57c00' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'src',\n label: 'Source URL',\n description: 'Use an absolute URL — email clients cannot resolve relative paths.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'alt',\n label: 'Alt text',\n // AGL-1896: the same \"Browse media\" default the site Image element\n // gets. It matters MORE here — most clients block images by default,\n // so for most recipients the alt text IS the block.\n description:\n 'Shown while images are blocked — most clients block by default. ' +\n 'Filled in from the media library when you pick a file that has ' +\n 'alt text.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'width',\n label: 'Width (px)',\n description:\n 'How wide the image renders. Blank fills the section — which is ' +\n 'usually right, since a phone is narrower than any number you pick.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'href',\n label: 'Link URL',\n description:\n 'Where the image goes when it is clicked. Blank leaves it as a ' +\n 'plain picture.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'align',\n label: 'Alignment',\n description: 'Where the image sits when it is narrower than the section.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'left', label: 'Left' },\n { value: 'center', label: 'Center' },\n { value: 'right', label: 'Right' },\n ],\n },\n ],\n}\n\n// ----------------------------------------------------------------- button\nexport const BUTTON_ID: Aglyn.ComponentId = 'emailButton'\n\nexport interface EmailButtonProps extends EmailBlockStyleProps {\n children?: string\n href?: string\n backgroundColor?: string\n color?: string\n align?: 'left' | 'center' | 'right'\n}\n\nexport const EmailButton = forwardRef<HTMLDivElement, EmailButtonProps>(\n (props, ref) => {\n const { children, href, backgroundColor, color, align, sx, ...rest } =\n props\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps({ textAlign: align ?? 'center', my: 1.5 }, sx)}\n >\n <Box\n component=\"span\"\n sx={{\n display: 'inline-block',\n px: 3,\n py: 1.25,\n borderRadius: 1,\n backgroundColor: backgroundColor || '#1a73e8',\n color: color || '#ffffff',\n fontFamily: 'Helvetica, Arial, sans-serif',\n fontSize: 15,\n fontWeight: 600,\n }}\n >\n {children ?? 'Call to action'}\n </Box>\n </Box>\n )\n },\n)\nEmailButton.displayName = 'EmailButton'\n\nexport const emailButtonSchema: Aglyn.ComponentSchema<EmailButtonProps> = {\n $id: BUTTON_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email button',\n description: 'A call-to-action button that survives every mail client.',\n category: Aglyn.ComponentCategory.INPUT,\n icon: { path: mdiGestureTapButton.path, sx: { color: '#f57c00' } },\n flags: { textEditable: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'children',\n label: 'Label',\n description:\n 'The words on the button. Say what happens next — \"See the report\" ' +\n 'reads as a destination where \"Click here\" does not.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'href',\n label: 'Link URL',\n description: 'Absolute URL (https://…). Merge tokens allowed.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'backgroundColor',\n label: 'Button color',\n description:\n 'The fill behind the label. Check it against the label color — a ' +\n 'button nobody can read is a link nobody clicks.',\n component: Aglyn.FieldComponentType.COLOR_PICKER,\n },\n {\n name: 'color',\n label: 'Label color',\n description: 'The color of the words on the button.',\n component: Aglyn.FieldComponentType.COLOR_PICKER,\n },\n {\n name: 'align',\n label: 'Alignment',\n description: 'Where the button sits across the width of the section.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'left', label: 'Left' },\n { value: 'center', label: 'Center' },\n { value: 'right', label: 'Right' },\n ],\n },\n ],\n}\n\n// ---------------------------------------------------------------- divider\nexport const DIVIDER_ID: Aglyn.ComponentId = 'emailDivider'\n\nexport interface EmailDividerProps extends EmailBlockStyleProps {\n color?: string\n}\n\nexport const EmailDivider = forwardRef<HTMLHRElement, EmailDividerProps>(\n (props, ref) => {\n const { color, sx, ...rest } = props\n return (\n <Box\n ref={ref}\n component=\"hr\"\n {...rest}\n sx={mergeSxProps(\n {\n border: 0,\n // Split shorthand so a palette token path from the theme-\n // reference color picker (AGL-588) resolves through the sx\n // system instead of dying inside a template string.\n borderTopStyle: 'solid',\n borderTopWidth: 1,\n borderTopColor: color || '#e0e0e0',\n my: 2,\n },\n sx,\n )}\n />\n )\n },\n)\nEmailDivider.displayName = 'EmailDivider'\n\nexport const emailDividerSchema: Aglyn.ComponentSchema<EmailDividerProps> = {\n $id: DIVIDER_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email divider',\n description: 'A horizontal rule between email sections.',\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiMinus.path, sx: { color: '#f57c00' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'color',\n label: 'Line color',\n description:\n 'The color of the rule. A light grey separates sections without ' +\n 'drawing the eye to the line itself.',\n component: Aglyn.FieldComponentType.COLOR_PICKER,\n },\n ],\n}\n\n// ----------------------------------------------------------------- spacer\nexport const SPACER_ID: Aglyn.ComponentId = 'emailSpacer'\n\nexport interface EmailSpacerProps extends EmailBlockStyleProps {\n /** Height in px (default 24). */\n height?: number\n}\n\nexport const EmailSpacer = forwardRef<HTMLDivElement, EmailSpacerProps>(\n (props, ref) => {\n const { height, sx, ...rest } = props\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps({ height: height ?? 24 }, sx)}\n />\n )\n },\n)\nEmailSpacer.displayName = 'EmailSpacer'\n\nexport const emailSpacerSchema: Aglyn.ComponentSchema<EmailSpacerProps> = {\n $id: SPACER_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email spacer',\n description: 'Fixed vertical whitespace between email blocks.',\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiArrowExpandVertical.path, sx: { color: '#f57c00' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'height',\n label: 'Height (px)',\n description:\n 'How much empty vertical space this block adds. Email clients drop ' +\n 'CSS margins, so a spacer is how you get reliable breathing room.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n ],\n}\n\n// ---------------------------------------------------------------- product\nexport const PRODUCT_ID: Aglyn.ComponentId = 'emailProduct'\n\nexport interface EmailProductProps extends EmailBlockStyleProps {\n /** Product id — resolved server-side at render (rename-safe). */\n productId?: string\n buttonLabel?: string\n}\n\nexport const EmailProduct = forwardRef<HTMLDivElement, EmailProductProps>(\n (props, ref) => {\n const { productId, buttonLabel, sx, ...rest } = props\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps(\n {\n border: '1px solid',\n borderColor: 'divider',\n borderRadius: 1,\n p: 2,\n my: 1,\n textAlign: 'center',\n color: 'text.secondary',\n fontSize: 13,\n },\n sx,\n )}\n >\n {productId\n ? `Product card (${productId}) — name, price, image fill in at send`\n : 'Product card — pick a product in the attributes panel'}\n <Box sx={{ mt: 1, fontWeight: 600 }}>\n {buttonLabel ?? 'Shop now'}\n </Box>\n </Box>\n )\n },\n)\nEmailProduct.displayName = 'EmailProduct'\n\nexport const emailProductSchema: Aglyn.ComponentSchema<EmailProductProps> = {\n $id: PRODUCT_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email product',\n description: 'A product card resolved at send time, so it never goes stale.',\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiPackageVariantClosed.path, sx: { color: '#f57c00' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'productId',\n label: 'Product',\n description:\n 'Stored by id — the current name, price, and image render at send.',\n component: Aglyn.FieldComponentType.PRODUCT_SELECT,\n },\n {\n name: 'buttonLabel',\n label: 'Button label',\n description:\n 'The words on the card’s button. Blank uses the block’s default, ' +\n 'and the link always points at this product’s page.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n ],\n}\n\n// ------------------------------------------------------------ custom html\nexport const HTML_ID: Aglyn.ComponentId = 'emailHtml'\n\nexport interface EmailHtmlProps extends EmailBlockStyleProps {\n html?: string\n}\n\nexport const EmailHtml = forwardRef<HTMLDivElement, EmailHtmlProps>(\n (props, ref) => {\n const { html, sx, ...rest } = props\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps({ fontFamily: 'Helvetica, Arial, sans-serif' }, sx)}\n // No `typeof window` guard any more (AGL-1901): the sanitizer needs\n // no DOM, so a server-rendered email keeps its custom HTML.\n dangerouslySetInnerHTML={{\n __html: html ? sanitizeAuthorHtml(html) : '',\n }}\n />\n )\n },\n)\nEmailHtml.displayName = 'EmailHtml'\n\nexport const emailHtmlSchema: Aglyn.ComponentSchema<EmailHtmlProps> = {\n $id: HTML_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email custom HTML',\n description: 'Hand-written HTML for an email, used as-is.',\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiCodeTags.path, sx: { color: '#f57c00' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'html',\n label: 'HTML',\n description:\n 'Raw table markup for advanced layouts. Sanitized with the ' +\n 'custom-html policy; the render pipeline passes it through as-is ' +\n 'after sanitization.',\n component: Aglyn.FieldComponentType.TEXTAREA,\n },\n ],\n}\n\n/**\n * The two bands most emails a site sends begin and end with (AGL-3287).\n *\n * Each drops into any email like the blocks above, and the Components page\n * starts a reusable Header or Footer email block from these same trees, by\n * the ids core names in `REUSABLE_EMAIL_BLOCK_STARTER_PRESET_IDS` — one tree\n * each, wherever it is started from.\n *\n * The words are placeholders to replace. No merge token names the company in\n * both kinds of email — a campaign supplies `{{org.name}}`, and no token is\n * supplied by every one of a site's own emails — while a token an email does\n * not supply is blanked before sending, which would leave the header with no\n * name at all.\n *\n * The footer carries no unsubscribe link: a campaign send appends its own,\n * and a second one would do the same thing twice.\n */\nexport const EMAIL_HEADER_PRESET: Aglyn.PresetSchema = {\n $id: Aglyn.REUSABLE_EMAIL_BLOCK_STARTER_PRESET_IDS.header,\n type: Aglyn.NodeType.PRESET,\n displayName: 'Header',\n description:\n 'Your logo above your company name, for the top of an email. Pick the ' +\n 'logo from your media library — until you do, the picture shows nothing ' +\n 'in a sent email.',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.BLOCKS,\n tags: ['logo', 'masthead', 'top', 'brand'],\n icon: { path: mdiPageLayoutHeader.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: SECTION_ID,\n pluginId: BUNDLE_ID,\n props: { align: 'center', padding: 24 },\n nodes: [\n {\n $id: null,\n componentId: IMAGE_ID,\n pluginId: BUNDLE_ID,\n props: { alt: 'Your company logo', width: 160, align: 'center' },\n },\n {\n $id: null,\n componentId: TEXT_ID,\n pluginId: BUNDLE_ID,\n props: {\n children: 'Your company',\n variant: 'subheading',\n align: 'center',\n },\n },\n ],\n },\n}\n\n/**\n * The Footer's small print, a muted gray that still reads at 5:1 on white.\n * A literal on purpose: it is written into the sent mail's own HTML, where no\n * theme token resolves.\n */\nconst FOOTER_TEXT_COLOR = '#6b6b6b'\n\n/** See {@link EMAIL_HEADER_PRESET}. */\nexport const EMAIL_FOOTER_PRESET: Aglyn.PresetSchema = {\n $id: Aglyn.REUSABLE_EMAIL_BLOCK_STARTER_PRESET_IDS.footer,\n type: Aglyn.NodeType.PRESET,\n displayName: 'Footer',\n description:\n 'Your company name and address, and why the reader gets your emails, for ' +\n 'the bottom of an email. Campaign emails add the unsubscribe link for you.',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.BLOCKS,\n tags: ['address', 'bottom', 'contact', 'legal'],\n icon: { path: mdiPageLayoutFooter.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: SECTION_ID,\n pluginId: BUNDLE_ID,\n props: { align: 'center', padding: 24 },\n nodes: [\n {\n $id: null,\n componentId: DIVIDER_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n {\n $id: null,\n componentId: TEXT_ID,\n pluginId: BUNDLE_ID,\n props: {\n children: 'Your company · 123 Main St, City',\n variant: 'caption',\n align: 'center',\n color: FOOTER_TEXT_COLOR,\n },\n },\n {\n $id: null,\n componentId: TEXT_ID,\n pluginId: BUNDLE_ID,\n props: {\n children:\n \"You're receiving this because you're a customer of Your company.\",\n variant: 'caption',\n align: 'center',\n color: FOOTER_TEXT_COLOR,\n },\n },\n ],\n },\n}\n\nexport const emailPresets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(SECTION_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email section',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiEmailOutline.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: SECTION_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId(TEXT_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email text',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiFormatText.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: TEXT_ID,\n pluginId: BUNDLE_ID,\n props: { children: 'Hello {{contact.firstName}},', variant: 'body' },\n },\n },\n {\n $id: generatePresetId(RICHTEXT_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email rich text',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiCardTextOutline.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: RICHTEXT_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId(IMAGE_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email image',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiImageOutline.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: IMAGE_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId(BUTTON_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email button',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.INPUT,\n icon: { path: mdiGestureTapButton.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: BUTTON_ID,\n pluginId: BUNDLE_ID,\n props: { children: 'Shop now' },\n },\n },\n {\n $id: generatePresetId(DIVIDER_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email divider',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiMinus.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: DIVIDER_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId(SPACER_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email spacer',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiArrowExpandVertical.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: SPACER_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId(PRODUCT_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email product',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiPackageVariantClosed.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: PRODUCT_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId(HTML_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email custom HTML',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiCodeTags.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: HTML_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n EMAIL_HEADER_PRESET,\n EMAIL_FOOTER_PRESET,\n]\n"],"names":["Aglyn","mdiCardTextOutline","mdiEmailOutline","mdiFormatText","mdiImageOutline","mdiMinus","mdiPackageVariantClosed","mdiPageLayoutFooter","mdiPageLayoutHeader","mdiArrowExpandVertical","mdiGestureTapButton","mdiCodeTags","sanitizeAuthorHtml","mergeSxProps","Box","Typography","forwardRef","BUNDLE_ID","generatePresetId","SECTION_ID","EmailSection","props","ref","backgroundColor","padding","align","children","sx","rest","maxWidth","mx","p","textAlign","displayName","emailSectionSchema","$id","pluginId","description","category","ComponentCategory","LAYOUT","icon","path","color","attributes","name","label","component","FieldComponentType","COLOR_PICKER","TEXT_FIELD","type","SELECT","options","value","TEXT_ID","TEXT_PRESETS","heading","fontSize","fontWeight","lineHeight","subheading","body","caption","EmailText","variant","preset","fontFamily","mb","emailTextSchema","DATA_DISPLAY","flags","textEditable","FEATURE_FLAG","ENABLED","TEXTAREA","RICHTEXT_ID","EmailRichtext","html","trim","border","borderColor","dangerouslySetInnerHTML","__html","emailRichtextSchema","selfClosing","IMAGE_ID","EmailImage","src","storedSrc","alt","width","href","resolveMediaSrc","my","height","display","alignItems","justifyContent","emailImageSchema","BUTTON_ID","EmailButton","px","py","borderRadius","emailButtonSchema","INPUT","DIVIDER_ID","EmailDivider","borderTopStyle","borderTopWidth","borderTopColor","emailDividerSchema","SPACER_ID","EmailSpacer","emailSpacerSchema","PRODUCT_ID","EmailProduct","productId","buttonLabel","mt","emailProductSchema","PRODUCT_SELECT","HTML_ID","EmailHtml","emailHtmlSchema","EMAIL_HEADER_PRESET","REUSABLE_EMAIL_BLOCK_STARTER_PRESET_IDS","header","NodeType","PRESET","BLOCKS","tags","data","componentId","nodes","FOOTER_TEXT_COLOR","EMAIL_FOOTER_PRESET","footer","emailPresets"],"mappings":"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"}
|
|
@@ -43,6 +43,13 @@ export interface EmailDesignPreviewProps {
|
|
|
43
43
|
* doing so would put a catalog read on every preview for a picture that can
|
|
44
44
|
* change between now and the send anyway.
|
|
45
45
|
*
|
|
46
|
+
* The site's reusable blocks are NOT one of the differences (AGL-3287). A
|
|
47
|
+
* shared header or footer placed in the design is grafted from the site's
|
|
48
|
+
* published components before rendering, by the same composition the send
|
|
49
|
+
* runs, so the preview shows the header the inbox will get. Only the
|
|
50
|
+
* components the design places are read, in parallel, and a design that
|
|
51
|
+
* places none costs no read.
|
|
52
|
+
*
|
|
46
53
|
* ## Sandboxed, because the HTML is tenant-authored
|
|
47
54
|
*
|
|
48
55
|
* The design is written by a site's own editors — or by a marketplace
|
|
@@ -18,8 +18,22 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
18
18
|
import { decodeStoredNodes } from "@aglyn/aglyn";
|
|
19
19
|
import { EMAIL_NODE_ROOT_ID, renderEmailHtml, renderTextEmailHtml } from "@aglyn/shared-util-email";
|
|
20
20
|
import { sanitizeAuthorHtml } from "@aglyn/aglyn/app-utils/author-html";
|
|
21
|
+
import { collectReferencedComponentIds } from "@aglyn/aglyn/app-utils/compose-reusable-components";
|
|
22
|
+
import { composeReferencedComponents } from "@aglyn/aglyn/app-utils/load-referenced-components";
|
|
23
|
+
import { useFirestore } from "@aglyn/tenant-feature-instance";
|
|
21
24
|
import { Box, Stack, Typography } from "@mui/material";
|
|
25
|
+
import { doc, getDoc } from "firebase/firestore";
|
|
22
26
|
import { useEffect, useMemo, useState } from "react";
|
|
27
|
+
/**
|
|
28
|
+
* One of the site's component documents through the CLIENT SDK, as the
|
|
29
|
+
* on-demand loader reads it. `snapshot.data()` hands a compressed definition
|
|
30
|
+
* back as a Firestore `Bytes`, which `readStoredComponentTree` decodes.
|
|
31
|
+
*/ function clientComponentReader(firestore, hostId) {
|
|
32
|
+
return async (componentId)=>{
|
|
33
|
+
const snapshot = await getDoc(doc(firestore, 'hosts', hostId, 'components', componentId));
|
|
34
|
+
return snapshot.exists() ? snapshot.data() : null;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
23
37
|
/**
|
|
24
38
|
* THE EMAIL AS AN INBOX RECEIVES IT, DRAWN SAFELY.
|
|
25
39
|
*
|
|
@@ -39,6 +53,13 @@ import { useEffect, useMemo, useState } from "react";
|
|
|
39
53
|
* doing so would put a catalog read on every preview for a picture that can
|
|
40
54
|
* change between now and the send anyway.
|
|
41
55
|
*
|
|
56
|
+
* The site's reusable blocks are NOT one of the differences (AGL-3287). A
|
|
57
|
+
* shared header or footer placed in the design is grafted from the site's
|
|
58
|
+
* published components before rendering, by the same composition the send
|
|
59
|
+
* runs, so the preview shows the header the inbox will get. Only the
|
|
60
|
+
* components the design places are read, in parallel, and a design that
|
|
61
|
+
* places none costs no read.
|
|
62
|
+
*
|
|
42
63
|
* ## Sandboxed, because the HTML is tenant-authored
|
|
43
64
|
*
|
|
44
65
|
* The design is written by a site's own editors — or by a marketplace
|
|
@@ -51,6 +72,7 @@ import { useEffect, useMemo, useState } from "react";
|
|
|
51
72
|
* from the console's own origin, where the sandbox attribute would be the
|
|
52
73
|
* only thing between tenant HTML and a live session.
|
|
53
74
|
*/ export function EmailDesignPreview(props) {
|
|
75
|
+
var _ref;
|
|
54
76
|
const { hostId, nodes: rawNodes, text, loading, subject, preheader, emptyMessage, note } = props;
|
|
55
77
|
/*
|
|
56
78
|
* The origin the preview's images are fetched from.
|
|
@@ -64,16 +86,65 @@ import { useEffect, useMemo, useState } from "react";
|
|
|
64
86
|
* the size of a whole email.
|
|
65
87
|
*/ const [origin, setOrigin] = useState('');
|
|
66
88
|
useEffect(()=>setOrigin(window.location.origin), []);
|
|
67
|
-
const
|
|
89
|
+
const firestore = useFirestore();
|
|
90
|
+
const stored = useMemo(()=>{
|
|
68
91
|
var _decodeStoredNodes;
|
|
69
92
|
return (_decodeStoredNodes = decodeStoredNodes(rawNodes)) != null ? _decodeStoredNodes : {};
|
|
70
93
|
}, [
|
|
71
94
|
rawNodes
|
|
72
95
|
]);
|
|
73
|
-
|
|
96
|
+
/*
|
|
97
|
+
* THE SITE'S REUSABLE BLOCKS, GRAFTED AS THE SEND GRAFTS THEM (AGL-3287).
|
|
98
|
+
*
|
|
99
|
+
* Tagged with the map it was composed FROM, so a stale answer — the design
|
|
100
|
+
* changed while its components were being read — is never drawn as the
|
|
101
|
+
* current one. Until the answer for this map arrives the frame shows the
|
|
102
|
+
* loading line rather than the email with its header missing, which would be
|
|
103
|
+
* a preview of a message nobody sends.
|
|
104
|
+
*
|
|
105
|
+
* A failed read draws the design without the blocks it could not load, and
|
|
106
|
+
* says so: an empty space presented as the mail is the one thing a preview
|
|
107
|
+
* must not do silently.
|
|
108
|
+
*/ const placesComponents = useMemo(()=>collectReferencedComponentIds(stored).size > 0, [
|
|
109
|
+
stored
|
|
110
|
+
]);
|
|
111
|
+
const [composition, setComposition] = useState(null);
|
|
112
|
+
/** The composition of THIS map for THIS site, once there is one. */ const composed = (composition == null ? void 0 : composition.source) === stored && composition.hostId === hostId ? composition : null;
|
|
113
|
+
useEffect(()=>{
|
|
114
|
+
// Composed already: a re-render asks for nothing more, so the reads are
|
|
115
|
+
// one per design shown rather than one per render.
|
|
116
|
+
if (!placesComponents || composed) return;
|
|
117
|
+
let active = true;
|
|
118
|
+
const settle = (nodes, failed)=>{
|
|
119
|
+
if (active) setComposition({
|
|
120
|
+
source: stored,
|
|
121
|
+
hostId,
|
|
122
|
+
nodes,
|
|
123
|
+
failed
|
|
124
|
+
});
|
|
125
|
+
};
|
|
126
|
+
composeReferencedComponents(stored, clientComponentReader(firestore, hostId)).then((nodes)=>settle(nodes, false)).catch((error)=>{
|
|
127
|
+
console.error(error);
|
|
128
|
+
settle(stored, true);
|
|
129
|
+
});
|
|
130
|
+
return ()=>{
|
|
131
|
+
active = false;
|
|
132
|
+
};
|
|
133
|
+
}, [
|
|
134
|
+
stored,
|
|
135
|
+
placesComponents,
|
|
136
|
+
composed,
|
|
137
|
+
firestore,
|
|
138
|
+
hostId
|
|
139
|
+
]);
|
|
140
|
+
const composing = placesComponents && !composed;
|
|
141
|
+
/** What is drawn: the design with its blocks grafted; `null` while grafting. */ const nodes = !placesComponents ? stored : (_ref = composed == null ? void 0 : composed.nodes) != null ? _ref : null;
|
|
142
|
+
const productBlocks = useMemo(()=>Object.values(nodes != null ? nodes : {}).filter((node)=>(node == null ? void 0 : node.componentId) === 'emailProduct').length, [
|
|
74
143
|
nodes
|
|
75
144
|
]);
|
|
76
145
|
const rendered = useMemo(()=>{
|
|
146
|
+
// Still reading the components this design places.
|
|
147
|
+
if (!nodes) return null;
|
|
77
148
|
if (!Object.keys(nodes).length) {
|
|
78
149
|
/*
|
|
79
150
|
* A plain-text message renders through the send path's own text
|
|
@@ -116,7 +187,7 @@ import { useEffect, useMemo, useState } from "react";
|
|
|
116
187
|
return /*#__PURE__*/ _jsx(Typography, {
|
|
117
188
|
variant: "body2",
|
|
118
189
|
color: "text.secondary",
|
|
119
|
-
children: loading ? 'Loading this email…' : emptyMessage
|
|
190
|
+
children: loading || composing ? 'Loading this email…' : emptyMessage
|
|
120
191
|
});
|
|
121
192
|
}
|
|
122
193
|
return /*#__PURE__*/ _jsxs(Stack, {
|
|
@@ -160,6 +231,11 @@ import { useEffect, useMemo, useState } from "react";
|
|
|
160
231
|
color: "text.secondary",
|
|
161
232
|
children: `${productBlocks} product ${productBlocks === 1 ? 'block is' : 'blocks are'} not drawn here — each resolves against the catalog when an email ` + 'sends, so what it shows depends on the product at that moment.'
|
|
162
233
|
}) : null,
|
|
234
|
+
(composed == null ? void 0 : composed.failed) ? /*#__PURE__*/ _jsx(Typography, {
|
|
235
|
+
variant: "caption",
|
|
236
|
+
color: "warning.main",
|
|
237
|
+
children: 'A reusable block in this email could not be loaded, so it is ' + 'not drawn here.'
|
|
238
|
+
}) : null,
|
|
163
239
|
note ? /*#__PURE__*/ _jsx(Typography, {
|
|
164
240
|
variant: "caption",
|
|
165
241
|
color: "text.secondary",
|