@maizzle/framework 6.0.0-rc.5 → 6.0.0-rc.6
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 +42 -0
- package/dist/components/Column.vue +61 -0
- package/dist/components/Container.vue +40 -0
- package/dist/components/Head.vue +8 -0
- package/dist/components/Html.vue +53 -0
- package/dist/components/Image.vue +70 -0
- package/dist/components/Overlap.vue +60 -0
- package/dist/components/Row.vue +80 -0
- package/dist/components/Spacer.vue +50 -7
- package/package.json +1 -1
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { createStaticVNode } from 'vue'
|
|
3
|
+
import type { PropType } from 'vue'
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
name: 'Body',
|
|
7
|
+
inheritAttrs: false,
|
|
8
|
+
props: {
|
|
9
|
+
xmlLang: {
|
|
10
|
+
type: String,
|
|
11
|
+
default: 'en'
|
|
12
|
+
},
|
|
13
|
+
dir: {
|
|
14
|
+
type: String as PropType<'ltr' | 'rtl'>,
|
|
15
|
+
default: 'ltr'
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
setup(props, { slots, attrs }) {
|
|
19
|
+
return () => {
|
|
20
|
+
const extraAttrs = Object.entries(attrs)
|
|
21
|
+
.map(([key, value]) => value === true ? key : `${key}="${value}"`)
|
|
22
|
+
.join(' ')
|
|
23
|
+
|
|
24
|
+
const parts = [
|
|
25
|
+
`xml:lang="${props.xmlLang}"`,
|
|
26
|
+
`dir="${props.dir}"`,
|
|
27
|
+
'style="margin: 0; padding: 0; width: 100%; word-break: break-word;"',
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
if (extraAttrs) {
|
|
31
|
+
parts.push(extraAttrs)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return [
|
|
35
|
+
createStaticVNode(`<body ${parts.join(' ')}>`, 1),
|
|
36
|
+
slots.default?.(),
|
|
37
|
+
createStaticVNode('</body>', 1),
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
</script>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, createStaticVNode, inject, useAttrs } from 'vue'
|
|
3
|
+
import type { ComputedRef } from 'vue'
|
|
4
|
+
import { normalizeToPixels } from './utils.ts'
|
|
5
|
+
|
|
6
|
+
defineOptions({ inheritAttrs: false })
|
|
7
|
+
|
|
8
|
+
const attrs = useAttrs()
|
|
9
|
+
|
|
10
|
+
const props = defineProps({
|
|
11
|
+
/** Override the auto-computed min-width. */
|
|
12
|
+
width: {
|
|
13
|
+
type: [String, Number],
|
|
14
|
+
default: null
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const injectedMinWidth = inject<ComputedRef<string> | null>('columnMinWidth', null)
|
|
19
|
+
const containerWidth = inject<ComputedRef<string | number> | null>('containerWidth', null)
|
|
20
|
+
const injectedMsoWidth = inject<ComputedRef<string> | null>('columnMsoWidth', null)
|
|
21
|
+
|
|
22
|
+
const minWidth = computed(() => {
|
|
23
|
+
if (props.width) return normalizeToPixels(props.width)
|
|
24
|
+
if (injectedMinWidth?.value) return injectedMinWidth.value
|
|
25
|
+
|
|
26
|
+
// Fallback: divide container width by 2 if available
|
|
27
|
+
if (containerWidth?.value) {
|
|
28
|
+
const val = containerWidth.value
|
|
29
|
+
if (typeof val === 'number') return `${val / 2}px`
|
|
30
|
+
const num = Number.parseFloat(val)
|
|
31
|
+
const unit = val.replace(String(num), '') || 'px'
|
|
32
|
+
return `${num / 2}${unit}`
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return '18.75em'
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const msoWidth = computed(() => injectedMsoWidth?.value ?? '50%')
|
|
39
|
+
|
|
40
|
+
const styles = computed(() => {
|
|
41
|
+
return `display: inline-block; min-width: ${minWidth.value}; font-size: 16px; vertical-align: top;`
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
const MsoBefore = () => createStaticVNode(
|
|
45
|
+
`<!--[if mso]><td width="${msoWidth.value}" style="vertical-align:top"><![endif]-->`,
|
|
46
|
+
1
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
const MsoAfter = () => createStaticVNode(
|
|
50
|
+
'<!--[if mso]></td><![endif]-->',
|
|
51
|
+
1
|
|
52
|
+
)
|
|
53
|
+
</script>
|
|
54
|
+
|
|
55
|
+
<template>
|
|
56
|
+
<MsoBefore />
|
|
57
|
+
<div v-bind="attrs" :style="styles">
|
|
58
|
+
<slot />
|
|
59
|
+
</div>
|
|
60
|
+
<MsoAfter />
|
|
61
|
+
</template>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, provide, createStaticVNode, useAttrs } from 'vue'
|
|
3
|
+
import { normalizeToPixels } from './utils.ts'
|
|
4
|
+
|
|
5
|
+
defineOptions({ inheritAttrs: false })
|
|
6
|
+
|
|
7
|
+
const attrs = useAttrs()
|
|
8
|
+
|
|
9
|
+
const props = defineProps({
|
|
10
|
+
/** Max width of the container. */
|
|
11
|
+
width: {
|
|
12
|
+
type: [String, Number],
|
|
13
|
+
default: '37.5em'
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
provide('containerWidth', computed(() => props.width))
|
|
18
|
+
|
|
19
|
+
const styles = computed(() => {
|
|
20
|
+
return `max-width: ${normalizeToPixels(props.width)}; margin: 0 auto;`
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
const MsoBefore = () => createStaticVNode(
|
|
24
|
+
`<!--[if mso]><table role="none" cellpadding="0" cellspacing="0" style="width:${normalizeToPixels(props.width)}" align="center"><tr><td><![endif]-->`,
|
|
25
|
+
1
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
const MsoAfter = () => createStaticVNode(
|
|
29
|
+
'<!--[if mso]></td></tr></table><![endif]-->',
|
|
30
|
+
1
|
|
31
|
+
)
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<template>
|
|
35
|
+
<MsoBefore />
|
|
36
|
+
<div v-bind="attrs" :style="styles">
|
|
37
|
+
<slot />
|
|
38
|
+
</div>
|
|
39
|
+
<MsoAfter />
|
|
40
|
+
</template>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { createStaticVNode } from 'vue'
|
|
3
|
+
import type { PropType } from 'vue'
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
name: 'Html',
|
|
7
|
+
inheritAttrs: false,
|
|
8
|
+
props: {
|
|
9
|
+
lang: {
|
|
10
|
+
type: String,
|
|
11
|
+
default: 'en'
|
|
12
|
+
},
|
|
13
|
+
dir: {
|
|
14
|
+
type: String as PropType<'ltr' | 'rtl'>,
|
|
15
|
+
default: 'ltr'
|
|
16
|
+
},
|
|
17
|
+
xmlns: {
|
|
18
|
+
type: String,
|
|
19
|
+
default: null
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
setup(props, { slots, attrs }) {
|
|
23
|
+
return () => {
|
|
24
|
+
const extraAttrs = Object.entries(attrs)
|
|
25
|
+
.map(([key, value]) => value === true ? key : `${key}="${value}"`)
|
|
26
|
+
.join(' ')
|
|
27
|
+
|
|
28
|
+
const parts = [
|
|
29
|
+
`lang="${props.lang}"`,
|
|
30
|
+
`dir="${props.dir}"`,
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
if (props.xmlns) {
|
|
34
|
+
parts.push(
|
|
35
|
+
`xmlns="${props.xmlns}"`,
|
|
36
|
+
'xmlns:v="urn:schemas-microsoft-com:vml"',
|
|
37
|
+
'xmlns:o="urn:schemas-microsoft-com:office:office"',
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (extraAttrs) {
|
|
42
|
+
parts.push(extraAttrs)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return [
|
|
46
|
+
createStaticVNode(`<html ${parts.join(' ')}>`, 1),
|
|
47
|
+
slots.default?.(),
|
|
48
|
+
createStaticVNode('</html>', 1),
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
</script>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, useAttrs } from 'vue'
|
|
3
|
+
|
|
4
|
+
defineOptions({ inheritAttrs: false })
|
|
5
|
+
|
|
6
|
+
const attrs = useAttrs()
|
|
7
|
+
|
|
8
|
+
const props = defineProps({
|
|
9
|
+
/** The image source URL. When reducedMotionSrc is used, this becomes the static fallback. */
|
|
10
|
+
src: {
|
|
11
|
+
type: String,
|
|
12
|
+
required: true
|
|
13
|
+
},
|
|
14
|
+
/** Alt text for the image. */
|
|
15
|
+
alt: {
|
|
16
|
+
type: String,
|
|
17
|
+
default: ''
|
|
18
|
+
},
|
|
19
|
+
/** Image source for dark mode. */
|
|
20
|
+
darkSrc: {
|
|
21
|
+
type: String,
|
|
22
|
+
default: null
|
|
23
|
+
},
|
|
24
|
+
/** The width of the image, rendered without units. */
|
|
25
|
+
width: {
|
|
26
|
+
type: [String, Number],
|
|
27
|
+
required: true
|
|
28
|
+
},
|
|
29
|
+
/** Animated image source, shown when user has no reduced motion preference. */
|
|
30
|
+
reducedMotionSrc: {
|
|
31
|
+
type: String,
|
|
32
|
+
default: null
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
function mimeFromExtension(src: string): string {
|
|
37
|
+
const ext = src.split('.').pop()?.toLowerCase() ?? ''
|
|
38
|
+
|
|
39
|
+
const types: Record<string, string> = {
|
|
40
|
+
apng: 'image/apng',
|
|
41
|
+
avif: 'image/avif',
|
|
42
|
+
gif: 'image/gif',
|
|
43
|
+
jpg: 'image/jpeg',
|
|
44
|
+
jpeg: 'image/jpeg',
|
|
45
|
+
jfif: 'image/jpeg',
|
|
46
|
+
png: 'image/png',
|
|
47
|
+
svg: 'image/svg+xml',
|
|
48
|
+
webp: 'image/webp',
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return types[ext] ?? ''
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const reducedMotionType = computed(() => mimeFromExtension(props.reducedMotionSrc ?? ''))
|
|
55
|
+
|
|
56
|
+
const imgWidth = computed(() => Number.parseInt(String(props.width), 10))
|
|
57
|
+
|
|
58
|
+
const usePicture = computed(() => props.darkSrc || props.reducedMotionSrc)
|
|
59
|
+
|
|
60
|
+
const imgStyle = 'max-width: 100%; vertical-align: middle;'
|
|
61
|
+
</script>
|
|
62
|
+
|
|
63
|
+
<template>
|
|
64
|
+
<picture v-if="usePicture">
|
|
65
|
+
<source v-if="darkSrc" :srcset="darkSrc" media="(prefers-color-scheme: dark)">
|
|
66
|
+
<source v-if="reducedMotionSrc" :srcset="reducedMotionSrc" :type="reducedMotionType || undefined" media="(prefers-reduced-motion: no-preference)">
|
|
67
|
+
<img v-bind="attrs" :src="src" :alt="alt" :width="imgWidth" :style="imgStyle">
|
|
68
|
+
</picture>
|
|
69
|
+
<img v-else v-bind="attrs" :src="src" :alt="alt" :width="imgWidth" :style="imgStyle">
|
|
70
|
+
</template>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, useAttrs, createStaticVNode } from 'vue'
|
|
3
|
+
import { normalizeToPixels } from './utils.ts'
|
|
4
|
+
|
|
5
|
+
defineOptions({ inheritAttrs: false })
|
|
6
|
+
|
|
7
|
+
const attrs = useAttrs()
|
|
8
|
+
|
|
9
|
+
const props = defineProps({
|
|
10
|
+
/** Max height of the overlapped (background) content. */
|
|
11
|
+
height: {
|
|
12
|
+
type: [String, Number],
|
|
13
|
+
required: true
|
|
14
|
+
},
|
|
15
|
+
/** Width of the overlay table and VML rect. */
|
|
16
|
+
width: {
|
|
17
|
+
type: [String, Number],
|
|
18
|
+
required: true
|
|
19
|
+
},
|
|
20
|
+
/** Height of the VML rect for Outlook. Defaults to height. */
|
|
21
|
+
msoHeight: {
|
|
22
|
+
type: [String, Number],
|
|
23
|
+
default: null
|
|
24
|
+
},
|
|
25
|
+
/** VML textbox inset value for Outlook positioning. */
|
|
26
|
+
msoInset: {
|
|
27
|
+
type: String,
|
|
28
|
+
default: '0,-60px,0,0'
|
|
29
|
+
},
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const backgroundStyles = computed(() => {
|
|
33
|
+
return `max-height: ${normalizeToPixels(props.height)}; margin: 0 auto; text-align: center;`
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const vmlOpen = computed(() => {
|
|
37
|
+
const w = normalizeToPixels(props.width)
|
|
38
|
+
const h = normalizeToPixels(props.msoHeight ?? props.height)
|
|
39
|
+
|
|
40
|
+
return `<!--[if mso]><v:rect xmlns:v="urn:schemas-microsoft-com:vml" stroked="f" filled="f" style="width:${w};height:${h};"><v:textbox inset="${props.msoInset}"><![endif]-->`
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const VmlBefore = () => createStaticVNode(vmlOpen.value, 1)
|
|
44
|
+
const VmlAfter = () => createStaticVNode('<!--[if mso]></v:textbox></v:rect><![endif]-->', 1)
|
|
45
|
+
</script>
|
|
46
|
+
|
|
47
|
+
<template>
|
|
48
|
+
<div v-bind="attrs" :style="backgroundStyles">
|
|
49
|
+
<slot />
|
|
50
|
+
</div>
|
|
51
|
+
<table style="max-height: 0; position: relative; opacity: 0.999;">
|
|
52
|
+
<tr>
|
|
53
|
+
<td :style="`width: ${normalizeToPixels(props.width)}; max-width: 100%; vertical-align: top;`">
|
|
54
|
+
<VmlBefore />
|
|
55
|
+
<slot name="overlay" />
|
|
56
|
+
<VmlAfter />
|
|
57
|
+
</td>
|
|
58
|
+
</tr>
|
|
59
|
+
</table>
|
|
60
|
+
</template>
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { Comment, computed, createStaticVNode, inject, provide, useAttrs, useSlots, Fragment } from 'vue'
|
|
3
|
+
import type { ComputedRef, VNode } from 'vue'
|
|
4
|
+
|
|
5
|
+
defineOptions({ inheritAttrs: false })
|
|
6
|
+
|
|
7
|
+
const attrs = useAttrs()
|
|
8
|
+
|
|
9
|
+
const props = defineProps({
|
|
10
|
+
/** Override the inherited container width. */
|
|
11
|
+
width: {
|
|
12
|
+
type: [String, Number],
|
|
13
|
+
default: null
|
|
14
|
+
},
|
|
15
|
+
/** Override the auto-detected column count. */
|
|
16
|
+
cols: {
|
|
17
|
+
type: Number,
|
|
18
|
+
default: null
|
|
19
|
+
}
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const slots = useSlots()
|
|
23
|
+
|
|
24
|
+
function countChildren(vnodes: VNode[]): number {
|
|
25
|
+
let count = 0
|
|
26
|
+
|
|
27
|
+
for (const vnode of vnodes) {
|
|
28
|
+
if (vnode.type === Fragment && Array.isArray(vnode.children)) {
|
|
29
|
+
count += countChildren(vnode.children as VNode[])
|
|
30
|
+
} else if (vnode.type !== Comment && typeof vnode.type !== 'symbol') {
|
|
31
|
+
count++
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return count
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const columnCount = computed(() => {
|
|
39
|
+
if (props.cols) return props.cols
|
|
40
|
+
|
|
41
|
+
const children = slots.default?.() ?? []
|
|
42
|
+
return countChildren(children) || 1
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
const containerWidth = inject<ComputedRef<string | number> | null>('containerWidth', null)
|
|
46
|
+
|
|
47
|
+
const rowWidth = computed(() => props.width ?? containerWidth?.value ?? '37.5em')
|
|
48
|
+
|
|
49
|
+
function divideValue(value: string | number, divisor: number): string {
|
|
50
|
+
if (typeof value === 'number') {
|
|
51
|
+
return `${value / divisor}px`
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const num = Number.parseFloat(value)
|
|
55
|
+
const unit = value.replace(String(num), '') || 'px'
|
|
56
|
+
|
|
57
|
+
return `${num / divisor}${unit}`
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
provide('columnMinWidth', computed(() => divideValue(rowWidth.value, columnCount.value)))
|
|
61
|
+
provide('columnMsoWidth', computed(() => `${Math.round(100 / columnCount.value)}%`))
|
|
62
|
+
|
|
63
|
+
const MsoBefore = () => createStaticVNode(
|
|
64
|
+
'<!--[if mso]><table role="none" cellpadding="0" cellspacing="0" width="100%"><tr><![endif]-->',
|
|
65
|
+
1
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
const MsoAfter = () => createStaticVNode(
|
|
69
|
+
'<!--[if mso]></tr></table><![endif]-->',
|
|
70
|
+
1
|
|
71
|
+
)
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
<template>
|
|
75
|
+
<MsoBefore />
|
|
76
|
+
<div v-bind="attrs" style="width: 100%; font-size: 0;">
|
|
77
|
+
<slot />
|
|
78
|
+
</div>
|
|
79
|
+
<MsoAfter />
|
|
80
|
+
</template>
|
|
@@ -3,11 +3,21 @@ import { computed } from 'vue'
|
|
|
3
3
|
import { normalizeToPixels } from './utils.ts'
|
|
4
4
|
|
|
5
5
|
const props = defineProps({
|
|
6
|
-
/** The
|
|
7
|
-
|
|
6
|
+
/** The type of spacer. */
|
|
7
|
+
type: {
|
|
8
|
+
type: String as () => 'vertical' | 'horizontal',
|
|
9
|
+
default: 'vertical'
|
|
10
|
+
},
|
|
11
|
+
/** The height of the spacer (vertical). */
|
|
12
|
+
height: {
|
|
8
13
|
type: [String, Number],
|
|
9
14
|
default: null
|
|
10
15
|
},
|
|
16
|
+
/** The width of the spacer (horizontal). */
|
|
17
|
+
width: {
|
|
18
|
+
type: [String, Number],
|
|
19
|
+
default: 16
|
|
20
|
+
},
|
|
11
21
|
/** The alternative height to use in Outlook. */
|
|
12
22
|
msoHeight: {
|
|
13
23
|
type: [String, Number],
|
|
@@ -15,11 +25,16 @@ const props = defineProps({
|
|
|
15
25
|
}
|
|
16
26
|
})
|
|
17
27
|
|
|
18
|
-
|
|
28
|
+
function parsePixelValue(value: string | number): number {
|
|
29
|
+
if (typeof value === 'number') return value
|
|
30
|
+
return Number.parseFloat(value) || 0
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const verticalStyles = computed(() => {
|
|
19
34
|
const s = []
|
|
20
35
|
|
|
21
|
-
if (props.
|
|
22
|
-
s.push(`line-height: ${normalizeToPixels(props.
|
|
36
|
+
if (props.height) {
|
|
37
|
+
s.push(`line-height: ${normalizeToPixels(props.height)};`)
|
|
23
38
|
}
|
|
24
39
|
|
|
25
40
|
if (props.msoHeight) {
|
|
@@ -28,9 +43,37 @@ const styles = computed(() => {
|
|
|
28
43
|
|
|
29
44
|
return s.join('')
|
|
30
45
|
})
|
|
46
|
+
|
|
47
|
+
const horizontalStyles = computed(() => {
|
|
48
|
+
return `display:inline-block; width: ${normalizeToPixels(props.width)}; font-size: 16px;${msoFontWidth.value}`
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
const msoFontWidth = computed(() => {
|
|
52
|
+
const widthPx = parsePixelValue(props.width)
|
|
53
|
+
const emspBase = 16
|
|
54
|
+
const maxPercent = 500
|
|
55
|
+
const maxPerEmsp = emspBase * (maxPercent / 100)
|
|
56
|
+
const numEmsps = Math.ceil(widthPx / maxPerEmsp)
|
|
57
|
+
const percent = Math.round((widthPx / (numEmsps * emspBase)) * 100)
|
|
58
|
+
|
|
59
|
+
return ` mso-font-width:${percent}%;`
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
const emspCount = computed(() => {
|
|
63
|
+
const widthPx = parsePixelValue(props.width)
|
|
64
|
+
const maxPerEmsp = 16 * 5
|
|
65
|
+
return Math.ceil(widthPx / maxPerEmsp)
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
const emsps = computed(() => '\u2003'.repeat(emspCount.value))
|
|
31
69
|
</script>
|
|
32
70
|
|
|
33
71
|
<template>
|
|
34
|
-
<
|
|
35
|
-
|
|
72
|
+
<template v-if="type === 'horizontal'">
|
|
73
|
+
<i :style="horizontalStyles">{{ emsps }}</i>
|
|
74
|
+
</template>
|
|
75
|
+
<template v-else>
|
|
76
|
+
<div v-if="height" role="separator" :style="verticalStyles">‍</div>
|
|
77
|
+
<div v-else role="separator">‍</div>
|
|
78
|
+
</template>
|
|
36
79
|
</template>
|