nuxt-generation-emails 1.1.0 → 1.2.0

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.
@@ -46,15 +46,11 @@ const props = withDefaults(defineProps<{
46
46
 
47
47
  /**
48
48
  * useNgeTemplate auto-loads the sibling .mjml file, compiles it with
49
- * Handlebars, and returns a reactive ComputedRef<string> of rendered HTML.
49
+ * Handlebars, and sets the render function \u2014 no <template> block needed.
50
50
  * MJML components from components/ are registered automatically.
51
51
  */
52
- const html = useNgeTemplate('${templatePath}', props)
52
+ useNgeTemplate('${templatePath}', props)
53
53
  ${scriptClose}
54
-
55
- <template>
56
- <div v-html="html" />
57
- </template>
58
54
  `;
59
55
  }
60
56
 
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=4.0.0"
6
6
  },
7
- "version": "1.1.0",
7
+ "version": "1.2.0",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -789,8 +789,8 @@ export function registerMjmlComponents(): void {
789
789
  addTemplate({
790
790
  filename: "nge/use-template.ts",
791
791
  write: true,
792
- getContents: () => `import { ref, watch, toRef } from 'vue'
793
- import type { Ref } from 'vue'
792
+ getContents: () => `import { h, ref, watch, toRef, getCurrentInstance } from 'vue'
793
+ import type { Ref, VNode } from 'vue'
794
794
  import Handlebars from 'handlebars'
795
795
  import { registerMjmlComponents } from './register-components'
796
796
 
@@ -823,26 +823,37 @@ let _componentsRegistered = false
823
823
  /**
824
824
  * Load and render an MJML email template by name.
825
825
  * Registers MJML components (Handlebars partials) automatically on first call.
826
+ * Sets the current component's render function \u2014 no <template> block needed.
826
827
  *
827
828
  * @param name - Template name relative to the emails directory (e.g. 'example', 'v1/test')
828
829
  * @param props - Reactive props object from defineProps
829
- * @returns Ref<string> containing the rendered HTML \u2014 use with v-html in your template
830
830
  */
831
- export function useNgeTemplate(name: string, props: Record<string, unknown>): Ref<string> {
831
+ export function useNgeTemplate(name: string, props: Record<string, unknown>): void {
832
832
  if (!_componentsRegistered) {
833
833
  registerMjmlComponents()
834
834
  _componentsRegistered = true
835
835
  }
836
836
 
837
+ const instance = getCurrentInstance()
838
+ const renderedHtml = ref('')
839
+
837
840
  const source = templateMap[name]
838
841
  if (!source) {
839
842
  const available = Object.keys(templateMap).join(', ')
840
843
  console.error(\`[nuxt-generation-emails] Template "\${name}" not found. Available: \${available}\`)
841
- return ref(\`<pre style="color:red;">Template "\${name}" not found</pre>\`) as Ref<string>
844
+ renderedHtml.value = \`<pre style="color:red;">Template "\${name}" not found</pre>\`
845
+ if (instance) {
846
+ instance.render = (): VNode => h('div', { innerHTML: renderedHtml.value })
847
+ }
848
+ return
849
+ }
850
+
851
+ // Set the render function on the component instance so Vue renders the HTML directly.
852
+ if (instance) {
853
+ instance.render = (): VNode => h('div', { innerHTML: renderedHtml.value })
842
854
  }
843
855
 
844
856
  const compiled = Handlebars.compile(source)
845
- const renderedHtml = ref('')
846
857
 
847
858
  // mjml-browser v5 returns a Promise \u2014 use watch to resolve async results reactively.
848
859
  // Tracks both the mjml2html loader ref AND all accessed props (via the Handlebars compile spread).
@@ -862,8 +873,6 @@ export function useNgeTemplate(name: string, props: Record<string, unknown>): Re
862
873
  },
863
874
  { immediate: true, deep: true },
864
875
  )
865
-
866
- return renderedHtml
867
876
  }
868
877
  `
869
878
  });
@@ -157,5 +157,5 @@ onUnmounted(() => {
157
157
  </template>
158
158
 
159
159
  <style scoped>
160
- .nge-template-selector{position:relative}.nge-template-selector__trigger{align-items:center;backdrop-filter:blur(10px);background:hsla(0,0%,100%,.15);border:1px solid hsla(0,0%,100%,.25);border-radius:8px;color:#fff;cursor:pointer;display:flex;font-family:DM Sans,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;font-size:13px;font-weight:500;gap:8px;justify-content:space-between;letter-spacing:-.01em;min-width:200px;padding:8px 16px;transition:all .2s cubic-bezier(.4,0,.2,1)}.nge-template-selector__trigger:hover{background:hsla(0,0%,100%,.25)}.nge-template-selector__label{flex:1;overflow:hidden;text-align:left;text-overflow:ellipsis;white-space:nowrap}.nge-template-selector__icon{flex-shrink:0;transition:transform .2s cubic-bezier(.4,0,.2,1)}.nge-template-selector__icon--open{transform:rotate(180deg)}.nge-template-selector__dropdown{animation:slideDown .2s cubic-bezier(.4,0,.2,1);background:#fff;border:1px solid #e5e7eb;border-radius:8px;box-shadow:0 10px 25px rgba(0,0,0,.1),0 4px 10px rgba(0,0,0,.05);left:0;max-height:300px;overflow-y:auto;position:absolute;right:0;top:calc(100% + 8px);z-index:1000}@keyframes slideDown{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}.nge-template-selector__item{border-bottom:1px solid #f3f4f6;color:#374151;cursor:pointer;font-family:DM Sans,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;font-size:13px;font-weight:450;letter-spacing:-.01em;padding:10px 16px;transition:all .15s ease}.nge-template-selector__item:last-child{border-bottom:none}.nge-template-selector__item:hover{background:#f9fafb;color:#00dc82}.nge-template-selector__item--active{background:#f0fdf4;color:#00dc82;font-weight:500}.nge-template-selector__item--active:before{color:#00dc82;content:"✓";margin-right:8px}.nge-template-selector__directory{border-bottom:1px solid #f3f4f6}.nge-template-selector__directory:last-child{border-bottom:none}.nge-template-selector__dir-header{align-items:center;background:#fafafa;color:#111827;cursor:pointer;display:flex;font-family:DM Sans,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;font-size:13px;font-weight:600;gap:8px;letter-spacing:-.01em;padding:10px 16px;transition:all .15s ease}.nge-template-selector__dir-header:hover{background:#f3f4f6}.nge-template-selector__dir-icon{color:#6b7280;flex-shrink:0;transition:transform .2s cubic-bezier(.4,0,.2,1)}.nge-template-selector__dir-icon--open{transform:rotate(90deg)}.nge-template-selector__dir-content{background:#fff}.nge-template-selector__dir-content .nge-template-selector__item{border-bottom:1px solid #f9fafb;padding-left:36px}.nge-template-selector__dir-content .nge-template-selector__item:last-child{border-bottom:none;color:#00dc82}
160
+ .nge-template-selector{position:relative}.nge-template-selector__trigger{align-items:center;backdrop-filter:blur(10px);background:hsla(0,0%,100%,.15);border:1px solid hsla(0,0%,100%,.25);border-radius:8px;color:#fff;cursor:pointer;display:flex;font-family:DM Sans,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;font-size:13px;font-weight:500;gap:8px;justify-content:space-between;letter-spacing:-.01em;min-width:200px;padding:8px 16px;transition:all .2s cubic-bezier(.4,0,.2,1)}.nge-template-selector__trigger:hover{background:hsla(0,0%,100%,.25)}.nge-template-selector__label{flex:1;overflow:hidden;text-align:left;text-overflow:ellipsis;white-space:nowrap}.nge-template-selector__icon{flex-shrink:0;transition:transform .2s cubic-bezier(.4,0,.2,1)}.nge-template-selector__icon--open{transform:rotate(180deg)}.nge-template-selector__dropdown{animation:slideDown .2s cubic-bezier(.4,0,.2,1);background:#fff;border:1px solid #e5e7eb;border-radius:8px;box-shadow:0 10px 25px rgba(0,0,0,.1),0 4px 10px rgba(0,0,0,.05);left:0;max-height:300px;min-width:100%;overflow-y:auto;position:absolute;top:calc(100% + 8px);width:-moz-max-content;width:max-content;z-index:1000}@keyframes slideDown{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}.nge-template-selector__item{border-bottom:1px solid #f3f4f6;color:#374151;cursor:pointer;font-family:DM Sans,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;font-size:13px;font-weight:450;letter-spacing:-.01em;padding:10px 16px;transition:all .15s ease}.nge-template-selector__item:last-child{border-bottom:none}.nge-template-selector__item:hover{background:#f9fafb;color:#00dc82}.nge-template-selector__item--active{background:#f0fdf4;color:#00dc82;font-weight:500}.nge-template-selector__item--active:before{color:#00dc82;content:"✓";margin-right:8px}.nge-template-selector__directory{border-bottom:1px solid #f3f4f6}.nge-template-selector__directory:last-child{border-bottom:none}.nge-template-selector__dir-header{align-items:center;background:#fafafa;color:#111827;cursor:pointer;display:flex;font-family:DM Sans,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;font-size:13px;font-weight:600;gap:8px;letter-spacing:-.01em;padding:10px 16px;transition:all .15s ease}.nge-template-selector__dir-header:hover{background:#f3f4f6}.nge-template-selector__dir-icon{color:#6b7280;flex-shrink:0;transition:transform .2s cubic-bezier(.4,0,.2,1)}.nge-template-selector__dir-icon--open{transform:rotate(90deg)}.nge-template-selector__dir-content{background:#fff}.nge-template-selector__dir-content .nge-template-selector__item{border-bottom:1px solid #f9fafb;padding-left:36px}.nge-template-selector__dir-content .nge-template-selector__item:last-child{border-bottom:none;color:#00dc82}
161
161
  </style>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-generation-emails",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "A Nuxt module for authoring, previewing, and sending transactional email templates with MJML and Handlebars.",
5
5
  "author": "nullcarry@icloud.com",
6
6
  "repository": {