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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy