@maizzle/framework 6.0.0-rc.7 → 6.0.0-rc.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Body.vue +105 -36
- package/dist/components/Button.vue +4 -1
- package/dist/components/CodeBlock.vue +1 -1
- package/dist/components/CodeInline.vue +6 -1
- package/dist/components/Column.vue +30 -5
- package/dist/components/Container.vue +10 -2
- package/dist/components/Divider.vue +28 -0
- package/dist/components/Head.vue +22 -0
- package/dist/components/Heading.vue +28 -0
- package/dist/components/Html.vue +98 -47
- package/dist/components/Layout.vue +93 -0
- package/dist/components/Link.vue +26 -0
- package/dist/components/Markdown.vue +15 -2
- package/dist/components/Outlook.vue +36 -0
- package/dist/components/Overlap.vue +25 -5
- package/dist/components/Preheader.vue +1 -1
- package/dist/components/Row.vue +16 -5
- package/dist/components/Section.vue +83 -0
- package/dist/components/Text.vue +29 -0
- package/dist/components/Vml.vue +165 -13
- package/dist/render/createRenderer.d.mts.map +1 -1
- package/dist/render/createRenderer.mjs +13 -1
- package/dist/render/createRenderer.mjs.map +1 -1
- package/dist/serve.mjs +1 -1
- package/dist/serve.mjs.map +1 -1
- package/dist/server/compatibility.mjs +15 -1
- package/dist/server/compatibility.mjs.map +1 -1
- package/dist/server/email.mjs +2 -1
- package/dist/server/email.mjs.map +1 -1
- package/dist/server/linter.d.mts +1 -2
- package/dist/server/linter.d.mts.map +1 -1
- package/dist/server/linter.mjs +60 -71
- package/dist/server/linter.mjs.map +1 -1
- package/dist/server/ui/App.vue +9 -9
- package/dist/server/ui/pages/Preview.vue +215 -150
- package/dist/transformers/inlineCSS.d.mts +1 -14
- package/dist/transformers/inlineCSS.d.mts.map +1 -1
- package/dist/transformers/inlineCSS.mjs +16 -34
- package/dist/transformers/inlineCSS.mjs.map +1 -1
- package/dist/types/config.d.mts +11 -27
- package/dist/types/config.d.mts.map +1 -1
- package/package.json +1 -1
package/dist/components/Body.vue
CHANGED
|
@@ -1,42 +1,111 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { createStaticVNode } from 'vue'
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { createStaticVNode, inject, useAttrs, useSlots } from 'vue'
|
|
3
3
|
import type { PropType } from 'vue'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
5
|
+
defineOptions({ inheritAttrs: false })
|
|
6
|
+
|
|
7
|
+
const attrs = useAttrs()
|
|
8
|
+
const slots = useSlots()
|
|
9
|
+
|
|
10
|
+
const props = defineProps({
|
|
11
|
+
/**
|
|
12
|
+
* Language code for the `xml:lang` attribute on `<body>`.
|
|
13
|
+
*
|
|
14
|
+
* Inherited from the parent `Html` component's `lang` prop by default.
|
|
15
|
+
*
|
|
16
|
+
* @example 'fr'
|
|
17
|
+
*/
|
|
18
|
+
xmlLang: {
|
|
19
|
+
type: String as PropType<
|
|
20
|
+
| 'af' | 'ar' | 'az'
|
|
21
|
+
| 'be' | 'bg' | 'bs'
|
|
22
|
+
| 'ca' | 'cs' | 'cy'
|
|
23
|
+
| 'da' | 'de' | 'dv'
|
|
24
|
+
| 'el' | 'en' | 'es' | 'et' | 'eu'
|
|
25
|
+
| 'fa' | 'fi' | 'fo' | 'fr'
|
|
26
|
+
| 'gl' | 'gu'
|
|
27
|
+
| 'he' | 'hi' | 'hr' | 'hu' | 'hy'
|
|
28
|
+
| 'id' | 'is' | 'it'
|
|
29
|
+
| 'ja'
|
|
30
|
+
| 'ka' | 'kk' | 'kn' | 'ko' | 'ky'
|
|
31
|
+
| 'lt' | 'lv'
|
|
32
|
+
| 'mk' | 'mn' | 'mr' | 'ms' | 'mt'
|
|
33
|
+
| 'nb' | 'nl' | 'nn' | 'no'
|
|
34
|
+
| 'pa' | 'pl' | 'pt'
|
|
35
|
+
| 'ro' | 'ru'
|
|
36
|
+
| 'sa' | 'se' | 'sk' | 'sl' | 'sq' | 'sr' | 'sv' | 'sw'
|
|
37
|
+
| 'ta' | 'te' | 'th' | 'tr' | 'tt'
|
|
38
|
+
| 'uk' | 'ur' | 'uz'
|
|
39
|
+
| 'vi'
|
|
40
|
+
| 'zh'
|
|
41
|
+
| (string & {})
|
|
42
|
+
>,
|
|
43
|
+
default: undefined
|
|
44
|
+
},
|
|
45
|
+
/**
|
|
46
|
+
* Text direction of the body.
|
|
47
|
+
*
|
|
48
|
+
* - `ltr` — left to right (default)
|
|
49
|
+
* - `rtl` — right to left
|
|
50
|
+
*
|
|
51
|
+
* @default 'ltr'
|
|
52
|
+
*/
|
|
53
|
+
dir: {
|
|
54
|
+
type: String as PropType<'ltr' | 'rtl'>,
|
|
55
|
+
default: 'ltr'
|
|
17
56
|
},
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Accessible label for the email article wrapper.
|
|
59
|
+
*
|
|
60
|
+
* Used as the `aria-label` on the inner `<div role="article">`.
|
|
61
|
+
* Helps screen readers identify the email content.
|
|
62
|
+
*
|
|
63
|
+
* @example 'Order confirmation'
|
|
64
|
+
*/
|
|
65
|
+
ariaLabel: {
|
|
66
|
+
type: String,
|
|
67
|
+
default: undefined
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
const htmlLang = inject<string>('htmlLang', 'en')
|
|
72
|
+
|
|
73
|
+
const render = () => {
|
|
74
|
+
const extraAttrs = Object.entries(attrs)
|
|
75
|
+
.map(([key, value]) => value === true ? key : `${key}="${value}"`)
|
|
76
|
+
.join(' ')
|
|
77
|
+
|
|
78
|
+
const lang = props.xmlLang ?? htmlLang
|
|
79
|
+
|
|
80
|
+
const parts = [
|
|
81
|
+
`xml:lang="${lang}"`,
|
|
82
|
+
`dir="${props.dir}"`,
|
|
83
|
+
'style="margin: 0; padding: 0; width: 100%; word-break: break-word;"',
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
if (extraAttrs) {
|
|
87
|
+
parts.push(extraAttrs)
|
|
40
88
|
}
|
|
89
|
+
|
|
90
|
+
const articleParts = [
|
|
91
|
+
'role="article"',
|
|
92
|
+
'aria-roledescription="email"',
|
|
93
|
+
props.ariaLabel ? `aria-label="${props.ariaLabel}"` : '',
|
|
94
|
+
`lang="${lang}"`,
|
|
95
|
+
`dir="${props.dir}"`,
|
|
96
|
+
'style="font-size: medium; font-size: max(16px, 1rem)"',
|
|
97
|
+
].filter(Boolean).join(' ')
|
|
98
|
+
|
|
99
|
+
return [
|
|
100
|
+
createStaticVNode(`<body ${parts.join(' ')}>`, 1),
|
|
101
|
+
createStaticVNode(`<div ${articleParts}>`, 1),
|
|
102
|
+
slots.default?.(),
|
|
103
|
+
createStaticVNode('</div>', 1),
|
|
104
|
+
createStaticVNode('</body>', 1),
|
|
105
|
+
]
|
|
41
106
|
}
|
|
42
107
|
</script>
|
|
108
|
+
|
|
109
|
+
<template>
|
|
110
|
+
<render />
|
|
111
|
+
</template>
|
|
@@ -4,7 +4,12 @@ import { createStaticVNode } from 'vue'
|
|
|
4
4
|
export default {
|
|
5
5
|
inheritAttrs: false,
|
|
6
6
|
props: {
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* The inline code text to render.
|
|
9
|
+
*
|
|
10
|
+
* If not provided, the slot content is used instead.
|
|
11
|
+
* The text is HTML-escaped automatically.
|
|
12
|
+
*/
|
|
8
13
|
code: {
|
|
9
14
|
type: String,
|
|
10
15
|
default: ''
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { computed, createStaticVNode, inject, useAttrs } from 'vue'
|
|
2
|
+
import { computed, createStaticVNode, inject, provide, useAttrs } from 'vue'
|
|
3
3
|
import type { ComputedRef } from 'vue'
|
|
4
4
|
import { normalizeToPixels } from './utils.ts'
|
|
5
5
|
|
|
@@ -8,10 +8,26 @@ defineOptions({ inheritAttrs: false })
|
|
|
8
8
|
const attrs = useAttrs()
|
|
9
9
|
|
|
10
10
|
const props = defineProps({
|
|
11
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Override the auto-computed column width.
|
|
13
|
+
*
|
|
14
|
+
* By default, the width is calculated from the parent `Row`
|
|
15
|
+
* by dividing its width by the column count.
|
|
16
|
+
*/
|
|
12
17
|
width: {
|
|
13
18
|
type: [String, Number],
|
|
14
19
|
default: null
|
|
20
|
+
},
|
|
21
|
+
/**
|
|
22
|
+
* Inline CSS applied only to the MSO `<td>` element.
|
|
23
|
+
*
|
|
24
|
+
* Use for Outlook-specific styling that shouldn't affect other clients.
|
|
25
|
+
*
|
|
26
|
+
* @example 'padding: 10px'
|
|
27
|
+
*/
|
|
28
|
+
msoStyle: {
|
|
29
|
+
type: String,
|
|
30
|
+
default: undefined
|
|
15
31
|
}
|
|
16
32
|
})
|
|
17
33
|
|
|
@@ -26,10 +42,10 @@ const minWidth = computed(() => {
|
|
|
26
42
|
// Fallback: divide container width by 2 if available
|
|
27
43
|
if (containerWidth?.value) {
|
|
28
44
|
const val = containerWidth.value
|
|
29
|
-
if (typeof val === 'number') return `${val / 2}px`
|
|
45
|
+
if (typeof val === 'number') return `${parseFloat((val / 2).toFixed(2))}px`
|
|
30
46
|
const num = Number.parseFloat(val)
|
|
31
47
|
const unit = val.replace(String(num), '') || 'px'
|
|
32
|
-
return `${num / 2}${unit}`
|
|
48
|
+
return `${parseFloat((num / 2).toFixed(2))}${unit}`
|
|
33
49
|
}
|
|
34
50
|
|
|
35
51
|
return '18.75em'
|
|
@@ -37,12 +53,21 @@ const minWidth = computed(() => {
|
|
|
37
53
|
|
|
38
54
|
const msoWidth = computed(() => injectedMsoWidth?.value ?? '50%')
|
|
39
55
|
|
|
56
|
+
// Provide column width as containerWidth for nested Rows
|
|
57
|
+
provide('containerWidth', minWidth)
|
|
58
|
+
|
|
40
59
|
const styles = computed(() => {
|
|
41
60
|
return `display: inline-block; min-width: ${minWidth.value}; font-size: 16px; vertical-align: top;`
|
|
42
61
|
})
|
|
43
62
|
|
|
63
|
+
const tdStyle = computed(() => {
|
|
64
|
+
const parts = ['vertical-align: top']
|
|
65
|
+
if (props.msoStyle) parts.push(props.msoStyle)
|
|
66
|
+
return parts.join('; ')
|
|
67
|
+
})
|
|
68
|
+
|
|
44
69
|
const MsoBefore = () => createStaticVNode(
|
|
45
|
-
`<!--[if mso]><td width="${msoWidth.value}" style="
|
|
70
|
+
`<!--[if mso]><td width="${msoWidth.value}" style="${tdStyle.value}"><![endif]-->`,
|
|
46
71
|
1
|
|
47
72
|
)
|
|
48
73
|
|
|
@@ -7,7 +7,15 @@ defineOptions({ inheritAttrs: false })
|
|
|
7
7
|
const attrs = useAttrs()
|
|
8
8
|
|
|
9
9
|
const props = defineProps({
|
|
10
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* Max width of the container.
|
|
12
|
+
*
|
|
13
|
+
* Applied as `max-width` on the div and as `width` on the MSO table.
|
|
14
|
+
* Also provided to child `Row` and `Column` components for
|
|
15
|
+
* automatic column width calculation.
|
|
16
|
+
*
|
|
17
|
+
* @default '37.5em'
|
|
18
|
+
*/
|
|
11
19
|
width: {
|
|
12
20
|
type: [String, Number],
|
|
13
21
|
default: '37.5em'
|
|
@@ -21,7 +29,7 @@ const styles = computed(() => {
|
|
|
21
29
|
})
|
|
22
30
|
|
|
23
31
|
const MsoBefore = () => createStaticVNode(
|
|
24
|
-
`<!--[if mso]><table role="none" cellpadding="0" cellspacing="0" style="width
|
|
32
|
+
`<!--[if mso]><table role="none" cellpadding="0" cellspacing="0" style="width: ${normalizeToPixels(props.width)}" align="center"><tr><td><![endif]-->`,
|
|
25
33
|
1
|
|
26
34
|
)
|
|
27
35
|
|
|
@@ -5,34 +5,62 @@ import { normalizeToPixels } from './utils.ts'
|
|
|
5
5
|
const attrs = useAttrs()
|
|
6
6
|
|
|
7
7
|
const props = defineProps({
|
|
8
|
+
/**
|
|
9
|
+
* Height (thickness) of the divider line.
|
|
10
|
+
*
|
|
11
|
+
* @default '1px'
|
|
12
|
+
*/
|
|
8
13
|
height: {
|
|
9
14
|
type: [String, Number],
|
|
10
15
|
default: '1px'
|
|
11
16
|
},
|
|
17
|
+
/**
|
|
18
|
+
* Background color of the divider line.
|
|
19
|
+
*
|
|
20
|
+
* Defaults to `#cbd5e1` when no Tailwind `bg-*` class is used.
|
|
21
|
+
*
|
|
22
|
+
* @example '#e2e8f0'
|
|
23
|
+
*/
|
|
12
24
|
color: {
|
|
13
25
|
type: String,
|
|
14
26
|
default: null
|
|
15
27
|
},
|
|
28
|
+
/**
|
|
29
|
+
* Vertical spacing (margin) above and below the divider.
|
|
30
|
+
*
|
|
31
|
+
* Overridden by `top` and `bottom` if set.
|
|
32
|
+
*
|
|
33
|
+
* @default '24px'
|
|
34
|
+
*/
|
|
16
35
|
spaceY: {
|
|
17
36
|
type: [String, Number],
|
|
18
37
|
default: '24px'
|
|
19
38
|
},
|
|
39
|
+
/**
|
|
40
|
+
* Horizontal spacing (margin) on both sides of the divider.
|
|
41
|
+
*
|
|
42
|
+
* Overridden by `left` and `right` if set.
|
|
43
|
+
*/
|
|
20
44
|
spaceX: {
|
|
21
45
|
type: [String, Number],
|
|
22
46
|
default: null
|
|
23
47
|
},
|
|
48
|
+
/** Margin above the divider. Overrides `spaceY` for the top side. */
|
|
24
49
|
top: {
|
|
25
50
|
type: [String, Number],
|
|
26
51
|
default: null
|
|
27
52
|
},
|
|
53
|
+
/** Margin below the divider. Overrides `spaceY` for the bottom side. */
|
|
28
54
|
bottom: {
|
|
29
55
|
type: [String, Number],
|
|
30
56
|
default: null
|
|
31
57
|
},
|
|
58
|
+
/** Margin to the left of the divider. Overrides `spaceX` for the left side. */
|
|
32
59
|
left: {
|
|
33
60
|
type: [String, Number],
|
|
34
61
|
default: null
|
|
35
62
|
},
|
|
63
|
+
/** Margin to the right of the divider. Overrides `spaceX` for the right side. */
|
|
36
64
|
right: {
|
|
37
65
|
type: [String, Number],
|
|
38
66
|
default: null
|
package/dist/components/Head.vue
CHANGED
|
@@ -1,4 +1,26 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { createStaticVNode } from 'vue'
|
|
3
|
+
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
/**
|
|
6
|
+
* Render an empty `<head>` before the main head element.
|
|
7
|
+
*
|
|
8
|
+
* This is a workaround for Yahoo! Mail on Android, which
|
|
9
|
+
* strips styles from the first `<head>` element.
|
|
10
|
+
*
|
|
11
|
+
* @default false
|
|
12
|
+
*/
|
|
13
|
+
double: {
|
|
14
|
+
type: [Boolean, String],
|
|
15
|
+
default: false
|
|
16
|
+
}
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const EmptyHead = () => createStaticVNode('<head></head>', 1)
|
|
20
|
+
</script>
|
|
21
|
+
|
|
1
22
|
<template>
|
|
23
|
+
<EmptyHead v-if="props.double === true || props.double === 'true'" />
|
|
2
24
|
<head>
|
|
3
25
|
<meta charset="utf-8">
|
|
4
26
|
<meta name="x-apple-disable-message-reformatting">
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, useAttrs } from 'vue'
|
|
3
|
+
import { twMerge } from 'tailwind-merge'
|
|
4
|
+
|
|
5
|
+
defineOptions({ inheritAttrs: false })
|
|
6
|
+
|
|
7
|
+
const props = defineProps({
|
|
8
|
+
/**
|
|
9
|
+
* The heading level (1-6), corresponding to `<h1>` through `<h6>`.
|
|
10
|
+
* @default 1
|
|
11
|
+
*/
|
|
12
|
+
level: {
|
|
13
|
+
type: [String, Number],
|
|
14
|
+
default: 1,
|
|
15
|
+
validator: (v: string | number) => [1, 2, 3, 4, 5, 6].includes(Number(v)),
|
|
16
|
+
},
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const attrs = useAttrs()
|
|
20
|
+
const tag = computed(() => `h${props.level}`)
|
|
21
|
+
const mergedClass = computed(() => twMerge('m-0', attrs.class as string))
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<template>
|
|
25
|
+
<component :is="tag" v-bind="$attrs" :class="mergedClass">
|
|
26
|
+
<slot />
|
|
27
|
+
</component>
|
|
28
|
+
</template>
|
package/dist/components/Html.vue
CHANGED
|
@@ -1,53 +1,104 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { createStaticVNode } from 'vue'
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { createStaticVNode, provide, useAttrs, useSlots } from 'vue'
|
|
3
3
|
import type { PropType } from 'vue'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
5
|
+
defineOptions({ inheritAttrs: false })
|
|
6
|
+
|
|
7
|
+
const attrs = useAttrs()
|
|
8
|
+
const slots = useSlots()
|
|
9
|
+
|
|
10
|
+
const props = defineProps({
|
|
11
|
+
/**
|
|
12
|
+
* Language code for the `lang` attribute on `<html>`.
|
|
13
|
+
*
|
|
14
|
+
* Also provided to the child `Body` component for `xml:lang`.
|
|
15
|
+
*
|
|
16
|
+
* @default 'en'
|
|
17
|
+
*/
|
|
18
|
+
lang: {
|
|
19
|
+
type: String as PropType<
|
|
20
|
+
| 'af' | 'ar' | 'az'
|
|
21
|
+
| 'be' | 'bg' | 'bs'
|
|
22
|
+
| 'ca' | 'cs' | 'cy'
|
|
23
|
+
| 'da' | 'de' | 'dv'
|
|
24
|
+
| 'el' | 'en' | 'es' | 'et' | 'eu'
|
|
25
|
+
| 'fa' | 'fi' | 'fo' | 'fr'
|
|
26
|
+
| 'gl' | 'gu'
|
|
27
|
+
| 'he' | 'hi' | 'hr' | 'hu' | 'hy'
|
|
28
|
+
| 'id' | 'is' | 'it'
|
|
29
|
+
| 'ja'
|
|
30
|
+
| 'ka' | 'kk' | 'kn' | 'ko' | 'ky'
|
|
31
|
+
| 'lt' | 'lv'
|
|
32
|
+
| 'mk' | 'mn' | 'mr' | 'ms' | 'mt'
|
|
33
|
+
| 'nb' | 'nl' | 'nn' | 'no'
|
|
34
|
+
| 'pa' | 'pl' | 'pt'
|
|
35
|
+
| 'ro' | 'ru'
|
|
36
|
+
| 'sa' | 'se' | 'sk' | 'sl' | 'sq' | 'sr' | 'sv' | 'sw'
|
|
37
|
+
| 'ta' | 'te' | 'th' | 'tr' | 'tt'
|
|
38
|
+
| 'uk' | 'ur' | 'uz'
|
|
39
|
+
| 'vi'
|
|
40
|
+
| 'zh'
|
|
41
|
+
| (string & {})
|
|
42
|
+
>,
|
|
43
|
+
default: 'en'
|
|
44
|
+
},
|
|
45
|
+
/**
|
|
46
|
+
* Text direction of the document.
|
|
47
|
+
*
|
|
48
|
+
* - `ltr` — left to right (default)
|
|
49
|
+
* - `rtl` — right to left
|
|
50
|
+
*
|
|
51
|
+
* @default 'ltr'
|
|
52
|
+
*/
|
|
53
|
+
dir: {
|
|
54
|
+
type: String as PropType<'ltr' | 'rtl'>,
|
|
55
|
+
default: 'ltr'
|
|
21
56
|
},
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Whether to include VML and Office XML namespace declarations.
|
|
59
|
+
*
|
|
60
|
+
* Required for Outlook VML support (background images, etc.).
|
|
61
|
+
* Set to `false` to omit the `xmlns:v` and `xmlns:o` attributes.
|
|
62
|
+
*
|
|
63
|
+
* @default true
|
|
64
|
+
*/
|
|
65
|
+
xmlns: {
|
|
66
|
+
type: [Boolean, String],
|
|
67
|
+
default: true
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
provide('htmlLang', props.lang)
|
|
72
|
+
|
|
73
|
+
const render = () => {
|
|
74
|
+
const extraAttrs = Object.entries(attrs)
|
|
75
|
+
.map(([key, value]) => value === true ? key : `${key}="${value}"`)
|
|
76
|
+
.join(' ')
|
|
77
|
+
|
|
78
|
+
const parts = [
|
|
79
|
+
`lang="${props.lang}"`,
|
|
80
|
+
`dir="${props.dir}"`,
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
if (props.xmlns !== false && props.xmlns !== 'false') {
|
|
84
|
+
parts.push(
|
|
85
|
+
'xmlns:v="urn:schemas-microsoft-com:vml"',
|
|
86
|
+
'xmlns:o="urn:schemas-microsoft-com:office:office"',
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (extraAttrs) {
|
|
91
|
+
parts.push(extraAttrs)
|
|
51
92
|
}
|
|
93
|
+
|
|
94
|
+
return [
|
|
95
|
+
createStaticVNode(`<html ${parts.join(' ')}>`, 1),
|
|
96
|
+
slots.default?.(),
|
|
97
|
+
createStaticVNode('</html>', 1),
|
|
98
|
+
]
|
|
52
99
|
}
|
|
53
100
|
</script>
|
|
101
|
+
|
|
102
|
+
<template>
|
|
103
|
+
<render />
|
|
104
|
+
</template>
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { PropType } from 'vue'
|
|
3
|
+
|
|
4
|
+
defineOptions({ inheritAttrs: false })
|
|
5
|
+
|
|
6
|
+
defineProps({
|
|
7
|
+
/**
|
|
8
|
+
* Classes to add to the `<body>` tag.
|
|
9
|
+
*/
|
|
10
|
+
bodyClass: {
|
|
11
|
+
type: String,
|
|
12
|
+
default: ''
|
|
13
|
+
},
|
|
14
|
+
/**
|
|
15
|
+
* Language code for the `lang` and `xml:lang` attributes.
|
|
16
|
+
*
|
|
17
|
+
* @default 'en'
|
|
18
|
+
*/
|
|
19
|
+
lang: {
|
|
20
|
+
type: String,
|
|
21
|
+
default: 'en'
|
|
22
|
+
},
|
|
23
|
+
/**
|
|
24
|
+
* Text direction.
|
|
25
|
+
*
|
|
26
|
+
* @default 'ltr'
|
|
27
|
+
*/
|
|
28
|
+
dir: {
|
|
29
|
+
type: String as PropType<'ltr' | 'rtl'>,
|
|
30
|
+
default: 'ltr'
|
|
31
|
+
},
|
|
32
|
+
/**
|
|
33
|
+
* Accessible label for the email article wrapper.
|
|
34
|
+
*
|
|
35
|
+
* Used as the `aria-label` on the inner `<div role="article">`.
|
|
36
|
+
*
|
|
37
|
+
* @example 'Order confirmation'
|
|
38
|
+
*/
|
|
39
|
+
ariaLabel: {
|
|
40
|
+
type: String,
|
|
41
|
+
default: undefined
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<template>
|
|
47
|
+
<html :lang="lang" :dir="dir" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
|
48
|
+
<head>
|
|
49
|
+
<meta charset="utf-8">
|
|
50
|
+
<meta name="x-apple-disable-message-reformatting">
|
|
51
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
52
|
+
<meta name="format-detection" content="telephone=no, date=no, address=no, email=no, url=no">
|
|
53
|
+
<meta name="color-scheme" content="light dark">
|
|
54
|
+
<meta name="supported-color-schemes" content="light dark">
|
|
55
|
+
<!--[if mso]>
|
|
56
|
+
<noscript>
|
|
57
|
+
<xml>
|
|
58
|
+
<o:OfficeDocumentSettings xmlns:o="urn:schemas-microsoft-com:office:office">
|
|
59
|
+
<o:PixelsPerInch>96</o:PixelsPerInch>
|
|
60
|
+
</o:OfficeDocumentSettings>
|
|
61
|
+
</xml>
|
|
62
|
+
</noscript>
|
|
63
|
+
<style>
|
|
64
|
+
td,th,div,p,a,h1,h2,h3,h4,h5,h6 {font-family: "Segoe UI", sans-serif; mso-line-height-rule: exactly;}
|
|
65
|
+
.mso-break-all {word-break: break-all;}
|
|
66
|
+
</style>
|
|
67
|
+
<![endif]-->
|
|
68
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
69
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous">
|
|
70
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap" rel="stylesheet" media="screen">
|
|
71
|
+
<style>
|
|
72
|
+
@import "@maizzle/tailwindcss";
|
|
73
|
+
|
|
74
|
+
img {
|
|
75
|
+
@apply max-w-full align-middle;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
</style>
|
|
79
|
+
</head>
|
|
80
|
+
<body :xml:lang="lang" :class="['m-0 p-0 size-full [word-break:break-word]', bodyClass]">
|
|
81
|
+
<div
|
|
82
|
+
role="article"
|
|
83
|
+
aria-roledescription="email"
|
|
84
|
+
:aria-label="ariaLabel"
|
|
85
|
+
:lang="lang"
|
|
86
|
+
:dir="dir"
|
|
87
|
+
style="font-size: medium;"
|
|
88
|
+
:class="['[font-size:max(16px,1rem)]', $attrs.class]">
|
|
89
|
+
<slot />
|
|
90
|
+
</div>
|
|
91
|
+
</body>
|
|
92
|
+
</html>
|
|
93
|
+
</template>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, useAttrs } from 'vue'
|
|
3
|
+
import { twMerge } from 'tailwind-merge'
|
|
4
|
+
|
|
5
|
+
defineOptions({ inheritAttrs: false })
|
|
6
|
+
|
|
7
|
+
defineProps({
|
|
8
|
+
/**
|
|
9
|
+
* The URL the link points to.
|
|
10
|
+
*/
|
|
11
|
+
href: {
|
|
12
|
+
type: String,
|
|
13
|
+
required: true,
|
|
14
|
+
validator: (v: string) => v.trim().length > 0,
|
|
15
|
+
},
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const attrs = useAttrs()
|
|
19
|
+
const mergedClass = computed(() => twMerge('no-underline', attrs.class as string))
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<template>
|
|
23
|
+
<a :href="href" v-bind="$attrs" :class="mergedClass">
|
|
24
|
+
<slot />
|
|
25
|
+
</a>
|
|
26
|
+
</template>
|