Skip to content
This repository was archived by the owner on Jun 22, 2024. It is now read-only.

Commit 4446dc9

Browse files
committed
feat: send props to renderer
1 parent 8d0aa3b commit 4446dc9

File tree

8 files changed

+123
-38
lines changed

8 files changed

+123
-38
lines changed

client/components/CodeContainer.vue

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ defineEmits(['setlang'])
66
77
const toast = useToast()
88
const { editorCode } = useTool()
9-
const { template, email } = useEmail()
9+
const { template, email, props, renderEmail } = useEmail()
1010
1111
function handleDownload(lang: 'html' | 'txt' | 'vue') {
1212
const content = template.value[lang]
@@ -58,6 +58,11 @@ const items = computed(() => {
5858
icon: 'i-ph-text-t-duotone',
5959
code: template.value.txt,
6060
},
61+
{
62+
key: 'props',
63+
label: 'Props',
64+
icon: 'i-ph-code-duotone',
65+
},
6166
]
6267
}
6368
else if (editorCode.value.id === 'html') {
@@ -102,7 +107,7 @@ const tab = ref(0)
102107
<UIcon :name="item.icon" class="w-7 h-7 flex-shrink-0" />
103108

104109
<span class="truncate">{{ item.label }}</span>
105-
<template v-if="selected">
110+
<template v-if="selected && item.code">
106111
<UTooltip text="Copy to clipboard">
107112
<UButton class="ml-6" icon="i-ph-copy-duotone" size="xs" square color="gray" variant="solid" @click="handleClipboard(item.key)" />
108113
</UTooltip>
@@ -116,7 +121,18 @@ const tab = ref(0)
116121
</template>
117122

118123
<template #item="{ item }">
119-
<div class="w-full h-full" v-html="highlight(item.code, item.key)" />
124+
<div v-if="item.code" class="w-full h-full" v-html="highlight(item.code, item.key)" />
125+
<div v-else-if="item.key === 'props'" class="w-full h-full">
126+
<UContainer class="py-5 flex flex-col gap-y-4">
127+
<template v-for="(prop, idx) in props" :key="idx">
128+
<UFormGroup v-if="prop.type === 'string'" :label="prop.label" :description="prop.description">
129+
<UInput v-model="prop.value" type="text" />
130+
</UFormGroup>
131+
132+
<UButton label="Update" @click="renderEmail()" />
133+
</template>
134+
</UContainer>
135+
</div>
120136
</template>
121137
</UTabs>
122138
</template>

client/composables/shiki.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import type { Highlighter } from 'shikiji'
2-
import { getHighlighter } from 'shikiji'
1+
import type { Highlighter } from 'shiki'
2+
import { getHighlighter } from 'shiki'
33
import { ref } from 'vue'
44

55
export const shiki = ref<Highlighter>()

client/composables/useEmail.ts

Lines changed: 68 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,39 @@
11
import pretty from 'pretty'
22
import type { Result } from '@vue-email/compiler'
3-
import type { Email } from '@/types/email'
3+
import { upperFirst } from 'scule'
4+
import type { Email, Template } from '@/types/email'
5+
6+
function removeQuotes(inputString: any) {
7+
// Check if the input is a string and has at least two characters
8+
if (typeof inputString === 'string' && inputString.length >= 2) {
9+
// Check if the string starts and ends with double quotes
10+
if (inputString[0] === '"' && inputString[inputString.length - 1] === '"') {
11+
// Remove the quotes and return the modified string
12+
return inputString.slice(1, -1)
13+
}
14+
else {
15+
// If the string doesn't have quotes at the start and end, return the original string
16+
return inputString
17+
}
18+
}
19+
else {
20+
// If the input is not a valid string, return an empty string or handle it accordingly
21+
return ''
22+
}
23+
}
424

525
export function useEmail() {
626
const emails = useState<Email[]>('emails')
727
const email = useState<Email>('email')
828
const sending = useState<boolean>('sending', () => false)
929
const refresh = useState<boolean>('refresh', () => false)
10-
const template = useState<{
11-
vue: string
12-
html: string
13-
txt: string
14-
}>('template')
30+
const template = useState<Template>('template')
31+
const props = useState<{
32+
label: string
33+
value: any
34+
type: string
35+
description?: string
36+
}[]>('props')
1537

1638
const { host } = useWindow()
1739

@@ -34,18 +56,20 @@ export function useEmail() {
3456
return null
3557

3658
const { data } = await useFetch<Result>(`/api/render/${email.value.filename}`, {
59+
method: 'POST',
3760
baseURL: host.value,
61+
body: {
62+
props: props.value,
63+
},
3864
})
3965

4066
if (data.value) {
41-
return {
67+
template.value = {
4268
vue: email.value.content,
4369
html: pretty(data.value.html),
4470
txt: data.value.text,
45-
}
71+
} as Template
4672
}
47-
48-
return null
4973
}
5074

5175
const getEmail = async (filename: string) => {
@@ -54,11 +78,39 @@ export function useEmail() {
5478

5579
if (found) {
5680
email.value = found
57-
58-
await renderEmail().then((value) => {
59-
if (value)
60-
template.value = value
61-
})
81+
try {
82+
if (found.props) {
83+
props.value = found.props.map((prop) => {
84+
const value = removeQuotes(prop.default) || ''
85+
const destructuredType = prop.type.split('|').map((type) => {
86+
if (type === 'string')
87+
return 'string'
88+
89+
if (type === 'number')
90+
return 'number'
91+
92+
if (type === 'boolean')
93+
return 'boolean'
94+
95+
if (type === 'object')
96+
return 'object'
97+
98+
return 'string'
99+
})
100+
101+
return {
102+
label: upperFirst(prop.name),
103+
type: destructuredType[0],
104+
value,
105+
}
106+
})
107+
}
108+
}
109+
catch (error) {
110+
console.error(error)
111+
}
112+
113+
await renderEmail()
62114
}
63115
}
64116
}
@@ -119,6 +171,7 @@ export function useEmail() {
119171
sending,
120172
refresh,
121173
template,
174+
props,
122175
getEmail,
123176
sendTestEmail,
124177
renderEmail,

client/emails/code-components.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const box = {
1515
padding: '0 48px',
1616
}
1717
18-
const code = `import { codeToThemedTokens } from 'shikiji'
18+
const code = `import { codeToThemedTokens } from 'shiki'
1919
2020
const tokens = await codeToThemedTokens('<div class="foo">bar</div>', {
2121
lang: 'html',

client/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
"nuxt": "^3.9.3",
2727
"pretty": "^2.0.0",
2828
"scule": "^1.2.0",
29-
"shikiji": "^0.9.19",
29+
"shiki": "^1.0.0-beta.3",
3030
"splitpanes": "^3.1.5",
3131
"vue-component-meta": "^1.8.27"
3232
}

client/pages/email/[file].vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ const route = useRoute()
44
const { getEmail, template } = useEmail()
55
const { horizontalSplit, previewMode } = useTool({
66
async onReload() {
7-
await getEmail(`${route.params.file}`)
7+
await getEmail(route.params.file as string)
88
},
99
})
1010
1111
onMounted(async () => {
12-
await getEmail(`${route.params.file}`)
12+
await getEmail(route.params.file as string)
1313
})
1414
1515
const showBoth = computed(() => previewMode.value.id === 'both')

client/server/api/render/[file].get.ts renamed to client/server/api/render/[file].post.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,20 @@ import { createError, defineEventHandler } from '#imports'
44
export default defineEventHandler(async (event: any) => {
55
try {
66
const file = event.context.params && event.context.params.file ? event.context.params.file : null
7+
const body = await readBody(event)
8+
9+
let props: any = null
10+
if (body && body.props) {
11+
props = body.props.reduce((acc: Record<string, any>, prop: any) => {
12+
acc[prop.label.toLowerCase()] = prop.value
13+
return acc
14+
}, {})
15+
}
716

817
// TODO: pass props to template
9-
const template = await useCompiler(file)
18+
const template = await useCompiler(file, {
19+
props,
20+
})
1021

1122
if (!template) {
1223
throw createError({

pnpm-lock.yaml

Lines changed: 18 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)