Skip to content

Commit 83f1456

Browse files
wobsorianoLekoArts
andauthored
chore(vue): Convert render function based UI components to Vue single-file components (#5022)
Co-authored-by: Lennart <[email protected]>
1 parent 280ece0 commit 83f1456

31 files changed

+612
-611
lines changed

.changeset/eighty-beds-behave.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@clerk/vue": patch
3+
---
4+
5+
Improve runtime prop checking of all Vue Clerk components
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import type { PropType } from 'vue';
2+
import { defineComponent, h, onScopeDispose, ref, toRef, watchEffect } from 'vue';
3+
4+
import type { CustomPortalsRendererProps } from '../types';
5+
import { ClerkLoaded } from './controlComponents';
6+
7+
type AnyObject = Record<string, any>;
8+
9+
export const CustomPortalsRenderer = defineComponent((props: CustomPortalsRendererProps) => {
10+
return () => [...(props?.customPagesPortals ?? []), ...(props?.customMenuItemsPortals ?? [])];
11+
});
12+
13+
/**
14+
* Used to orchestrate mounting of Clerk components in a host Vue application.
15+
* Components are rendered into a specific DOM node using mount/unmount methods provided by the Clerk class.
16+
*/
17+
export const ClerkHostRenderer = defineComponent({
18+
props: {
19+
mount: {
20+
type: Function as PropType<(node: HTMLDivElement, props: AnyObject) => void>,
21+
required: false,
22+
},
23+
unmount: {
24+
type: Function as PropType<(node: HTMLDivElement) => void>,
25+
required: false,
26+
},
27+
open: {
28+
type: Function as PropType<(props: AnyObject) => void>,
29+
required: false,
30+
},
31+
close: {
32+
type: Function as PropType<() => void>,
33+
required: false,
34+
},
35+
updateProps: {
36+
type: Function as PropType<(props: { node: HTMLDivElement; props: AnyObject | undefined }) => void>,
37+
required: false,
38+
},
39+
props: {
40+
type: Object,
41+
required: false,
42+
default: () => ({}),
43+
},
44+
},
45+
setup(props) {
46+
const portalRef = ref<HTMLDivElement | null>(null);
47+
const isPortalMounted = ref(false);
48+
// Make the props reactive so the watcher can react to changes
49+
const componentProps = toRef(props, 'props');
50+
51+
watchEffect(() => {
52+
if (!portalRef.value) {
53+
return;
54+
}
55+
56+
if (isPortalMounted.value) {
57+
props.updateProps?.({ node: portalRef.value, props: componentProps.value });
58+
} else {
59+
if (props.mount) {
60+
props.mount(portalRef.value, componentProps.value);
61+
}
62+
if (props.open) {
63+
props.open(componentProps.value);
64+
}
65+
isPortalMounted.value = true;
66+
}
67+
});
68+
69+
onScopeDispose(() => {
70+
if (isPortalMounted.value && portalRef.value) {
71+
if (props.unmount) {
72+
props.unmount(portalRef.value);
73+
}
74+
if (props.close) {
75+
props.close();
76+
}
77+
}
78+
});
79+
80+
return () => h(ClerkLoaded, () => h('div', { ref: portalRef }));
81+
},
82+
});

packages/vue/src/components/SignInButton.ts

Lines changed: 0 additions & 62 deletions
This file was deleted.

packages/vue/src/components/SignInWithMetamaskButton.ts

Lines changed: 0 additions & 32 deletions
This file was deleted.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<script setup lang="ts">
2+
import { useAttrs, useSlots } from 'vue';
3+
import type { RedirectUrlProp } from '@clerk/types';
4+
import { useClerk } from '../composables/useClerk';
5+
import { assertSingleChild, normalizeWithDefaultValue } from '../utils';
6+
7+
type Props = RedirectUrlProp & {
8+
mode?: 'modal' | 'redirect';
9+
};
10+
11+
const props = defineProps<Props>();
12+
13+
const clerk = useClerk();
14+
const slots = useSlots();
15+
const attrs = useAttrs();
16+
17+
function getChildComponent() {
18+
const children = normalizeWithDefaultValue(slots.default?.({}), 'Sign in with Metamask');
19+
return assertSingleChild(children, 'SignInWithMetamaskButton');
20+
}
21+
22+
function clickHandler() {
23+
void clerk.value?.authenticateWithMetamask({ redirectUrl: props.redirectUrl || undefined });
24+
}
25+
</script>
26+
27+
<template>
28+
<component
29+
:is="getChildComponent"
30+
v-bind="attrs"
31+
@click="clickHandler"
32+
>
33+
<slot />
34+
</component>
35+
</template>

packages/vue/src/components/SignOutButton.ts

Lines changed: 0 additions & 37 deletions
This file was deleted.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<script setup lang="ts">
2+
import { useAttrs, useSlots } from 'vue';
3+
import type { SignOutOptions } from '@clerk/types';
4+
import { useClerk } from '../composables/useClerk';
5+
import { assertSingleChild, normalizeWithDefaultValue } from '../utils';
6+
7+
interface SignOutButtonProps {
8+
signOutOptions?: SignOutOptions;
9+
sessionId?: string;
10+
redirectUrl?: string;
11+
}
12+
13+
const props = defineProps<SignOutButtonProps>();
14+
15+
const clerk = useClerk();
16+
const slots = useSlots();
17+
const attrs = useAttrs();
18+
19+
function getChildComponent() {
20+
const children = normalizeWithDefaultValue(slots.default?.({}), 'Sign out');
21+
return assertSingleChild(children, 'SignOutButton');
22+
}
23+
24+
function clickHandler() {
25+
const signOutOptions: SignOutOptions = {
26+
redirectUrl: props.signOutOptions?.redirectUrl ?? props.redirectUrl,
27+
sessionId: props.signOutOptions?.sessionId ?? props.sessionId,
28+
};
29+
void clerk.value?.signOut(signOutOptions);
30+
}
31+
</script>
32+
33+
<template>
34+
<component
35+
:is="getChildComponent"
36+
v-bind="attrs"
37+
@click="clickHandler"
38+
>
39+
<slot />
40+
</component>
41+
</template>

packages/vue/src/components/SignUpButton.ts

Lines changed: 0 additions & 57 deletions
This file was deleted.

0 commit comments

Comments
 (0)