├── .vscode └── extensions.json ├── packages ├── icons │ ├── index.ts │ ├── IconZoom.vue │ ├── IconGitlab.vue │ ├── IconTwitch.vue │ ├── IconFacebook.vue │ ├── IconLinkedin.vue │ ├── IconFigma.vue │ ├── IconTwitter.vue │ ├── IconGithub.vue │ ├── IconWorkos.vue │ ├── IconBitbucket.vue │ ├── IconGoogle.vue │ ├── IconApple.vue │ ├── IconSpotify.vue │ ├── Icons.vue │ ├── IconSlack.vue │ ├── IconDiscord.vue │ ├── IconKakao.vue │ ├── IconNotion.vue │ ├── IconKeycloak.vue │ └── IconAzure.vue ├── utils │ └── index.ts ├── component.ts ├── ui │ ├── index.ts │ ├── Divider.vue │ ├── Label.vue │ ├── Anchor.vue │ ├── Message.vue │ ├── BrandButton.vue │ ├── Container.vue │ ├── Loader.vue │ ├── Input.vue │ └── Button.vue ├── index.ts ├── auth │ ├── SocialAuthContainer.vue │ ├── AuthCard.vue │ ├── UserContextProvider.ts │ ├── AnonymousAuth.vue │ ├── UpdatePassword.vue │ ├── ForgottenPassword.vue │ ├── MagicLink.vue │ ├── SocialAuth.vue │ ├── VerifyOtp.vue │ ├── Auth.vue │ └── EmailAuth.vue └── types.ts ├── public ├── og.png ├── preview.mp4 ├── preview.png ├── extension_icon.png ├── nuxtbase-icon.svg ├── nuxtbase-dark.svg └── nuxtbase-light.svg ├── src ├── components │ ├── ui │ │ ├── input │ │ │ ├── index.ts │ │ │ └── Input.vue │ │ ├── label │ │ │ ├── index.ts │ │ │ └── Label.vue │ │ ├── anchor │ │ │ ├── index.ts │ │ │ └── Anchor.vue │ │ ├── separator │ │ │ ├── index.ts │ │ │ └── Separator.vue │ │ ├── tooltip │ │ │ ├── index.ts │ │ │ ├── TooltipTrigger.vue │ │ │ ├── TooltipProvider.vue │ │ │ ├── Tooltip.vue │ │ │ └── TooltipContent.vue │ │ └── button │ │ │ ├── Button.vue │ │ │ └── index.ts │ ├── IconMenu.vue │ ├── IconPalette.vue │ ├── ToggleButton.vue │ ├── Footer.vue │ └── Hero.vue ├── locales │ ├── en-US.yml │ └── zh-CN.yml ├── composables │ ├── useDarkmode.ts │ ├── useCopyCode.ts │ ├── useSEOHeader.ts │ └── useLanguage.ts ├── lib │ └── index.ts ├── vite-env.d.ts ├── main.ts ├── plugins │ ├── i18n.ts │ └── highlight.ts ├── global.css └── App.vue ├── .github └── FUNDING.yml ├── .env.example ├── .prettierrc ├── tsconfig.node.json ├── .gitignore ├── index.html ├── components.json ├── volar.d.ts ├── tsconfig.json ├── LICENSE ├── components.d.ts ├── vite.config.ts ├── package.json ├── tailwind.config.ts ├── CHANGELOG.md └── README.md /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /packages/icons/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Icons } from './Icons.vue' 2 | -------------------------------------------------------------------------------- /public/og.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supa-kit/auth-ui-vue/HEAD/public/og.png -------------------------------------------------------------------------------- /src/components/ui/input/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Input } from './Input.vue' 2 | -------------------------------------------------------------------------------- /src/components/ui/label/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Label } from './Label.vue' 2 | -------------------------------------------------------------------------------- /src/components/ui/anchor/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Anchor } from './Anchor.vue' 2 | -------------------------------------------------------------------------------- /public/preview.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supa-kit/auth-ui-vue/HEAD/public/preview.mp4 -------------------------------------------------------------------------------- /public/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supa-kit/auth-ui-vue/HEAD/public/preview.png -------------------------------------------------------------------------------- /src/components/ui/separator/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Separator } from './Separator.vue' 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [xiaoluoboding] 4 | -------------------------------------------------------------------------------- /src/locales/en-US.yml: -------------------------------------------------------------------------------- 1 | hello: 'Hello, {name}!' 2 | language: Language 3 | menu: 4 | home: Home 5 | -------------------------------------------------------------------------------- /src/locales/zh-CN.yml: -------------------------------------------------------------------------------- 1 | hello: 'Hello, {name}!' 2 | language: Language 3 | menu: 4 | home: Home 5 | -------------------------------------------------------------------------------- /public/extension_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supa-kit/auth-ui-vue/HEAD/public/extension_icon.png -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | VITE_SUPABASE_URL=https://xxx.supabase.co 2 | VITE_SUPABASE_ANON_KEY=xxx 3 | VITE_SITE_URL=https://www.example.com -------------------------------------------------------------------------------- /src/composables/useDarkmode.ts: -------------------------------------------------------------------------------- 1 | import { useDark, useToggle } from '@vueuse/core' 2 | 3 | export const isDark = useDark() 4 | export const toggleDarkmode = useToggle(isDark) 5 | -------------------------------------------------------------------------------- /src/lib/index.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from 'clsx' 2 | import { twMerge } from 'tailwind-merge' 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "useTabs": false, 4 | "singleQuote": true, 5 | "semi": false, 6 | "trailingComma": "none", 7 | "printWidth": 80, 8 | "htmlWhitespaceSensitivity": "strict" 9 | } -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.vue' { 4 | import type { DefineComponent } from 'vue' 5 | const component: DefineComponent<{}, {}, any> 6 | export default component 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /src/components/ui/tooltip/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Tooltip } from './Tooltip.vue' 2 | export { default as TooltipContent } from './TooltipContent.vue' 3 | export { default as TooltipProvider } from './TooltipProvider.vue' 4 | export { default as TooltipTrigger } from './TooltipTrigger.vue' 5 | -------------------------------------------------------------------------------- /packages/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { InjectionKey, inject } from 'vue' 2 | 3 | export function injectStrict(key: InjectionKey, fallback?: T) { 4 | const resolved = inject(key, fallback) 5 | if (!resolved) { 6 | throw new Error(`Could not resolve ${key.description}`) 7 | } 8 | return resolved 9 | } 10 | -------------------------------------------------------------------------------- /src/components/ui/tooltip/TooltipTrigger.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /src/components/ui/tooltip/TooltipProvider.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /packages/component.ts: -------------------------------------------------------------------------------- 1 | export { default as Auth } from './auth/Auth.vue' 2 | export { default as ForgottenPassword } from './auth/ForgottenPassword.vue' 3 | export { default as UpdatePassword } from './auth/UpdatePassword.vue' 4 | export { default as MagicLink } from './auth/MagicLink.vue' 5 | export { 6 | default as UserContextProvider, 7 | useSupabaseUser 8 | } from './auth/UserContextProvider' 9 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import { createHead } from '@vueuse/head' 3 | import App from './App.vue' 4 | import './global.css' 5 | import highlight from '~/plugins/highlight' 6 | import i18n from '~/plugins/i18n' 7 | 8 | const app = createApp(App) 9 | const head = createHead() 10 | 11 | app.use(head) 12 | app.use(highlight) 13 | app.use(i18n) 14 | app.mount('#app') 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | lib 13 | !src/lib 14 | dist-ssr 15 | *.local 16 | 17 | 18 | # Editor directories and files 19 | .vscode/* 20 | !.vscode/extensions.json 21 | .idea 22 | .DS_Store 23 | *.suo 24 | *.ntvs* 25 | *.njsproj 26 | *.sln 27 | *.sw? 28 | .env -------------------------------------------------------------------------------- /src/components/IconMenu.vue: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Auth UI Vue 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/icons/IconZoom.vue: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /src/components/ui/tooltip/Tooltip.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 15 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://shadcn-vue.com/schema.json", 3 | "style": "new-york", 4 | "typescript": true, 5 | "tsConfigPath": "./tsconfig.json", 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "~/global.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "framework": "vite", 14 | "aliases": { 15 | "components": "~/components", 16 | "utils": "~/lib" 17 | } 18 | } -------------------------------------------------------------------------------- /packages/ui/index.ts: -------------------------------------------------------------------------------- 1 | export { Anchor } from '../../src/components/ui/anchor' 2 | export { default as Container } from './Container.vue' 3 | export { Button } from '../../src/components/ui/button' 4 | export { Separator } from '../../src/components/ui/separator' 5 | export { Input } from '../../src/components/ui/input' 6 | export { Label } from '../../src/components/ui/label' 7 | export { default as Message } from './Message.vue' 8 | export { default as BrandButton } from './BrandButton.vue' 9 | -------------------------------------------------------------------------------- /src/composables/useCopyCode.ts: -------------------------------------------------------------------------------- 1 | import { useClipboard } from '@vueuse/core' 2 | import { Ref } from 'vue' 3 | 4 | type CopyCodeParams = { 5 | code: string 6 | checkIconRef: Ref 7 | } 8 | 9 | export const useCopyCode = async ({ code, checkIconRef }: CopyCodeParams) => { 10 | const { copy } = useClipboard({ 11 | source: code 12 | }) 13 | 14 | checkIconRef.value = false 15 | await copy(code) 16 | checkIconRef.value = true 17 | 18 | setTimeout(() => { 19 | checkIconRef.value = false 20 | }, 1500) 21 | } 22 | -------------------------------------------------------------------------------- /src/components/IconPalette.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /packages/icons/IconGitlab.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /volar.d.ts: -------------------------------------------------------------------------------- 1 | // Auto generated component declarations 2 | import '@vue/runtime-core' 3 | 4 | declare module '@vue/runtime-core' { 5 | export interface GlobalComponents { 6 | Auth: typeof import('@supa-kit/auth-ui-vue')['Auth'] 7 | ForgottenPassword: typeof import('@supa-kit/auth-ui-vue')['ForgottenPassword'] 8 | UpdatePassword: typeof import('@supa-kit/auth-ui-vue')['UpdatePassword'] 9 | MagicLink: typeof import('@supa-kit/auth-ui-vue')['MagicLink'] 10 | UserContextProvider: typeof import('@supa-kit/auth-ui-vue')['UserContextProvider'] 11 | } 12 | } 13 | 14 | export {} 15 | -------------------------------------------------------------------------------- /packages/icons/IconTwitch.vue: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /packages/icons/IconFacebook.vue: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /src/components/ui/anchor/Anchor.vue: -------------------------------------------------------------------------------- 1 | 12 | 21 | -------------------------------------------------------------------------------- /src/components/ui/label/Label.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 28 | -------------------------------------------------------------------------------- /packages/index.ts: -------------------------------------------------------------------------------- 1 | import type { Plugin } from 'vue' 2 | import { 3 | Auth, 4 | ForgottenPassword, 5 | UpdatePassword, 6 | MagicLink, 7 | UserContextProvider, 8 | useSupabaseUser 9 | } from './component' 10 | import { injectStrict } from './utils' 11 | import type { UserContextProviderInjection } from './types' 12 | import { UserContextProviderKey } from './types' 13 | 14 | export { 15 | Auth, 16 | ForgottenPassword, 17 | UpdatePassword, 18 | MagicLink, 19 | injectStrict, 20 | UserContextProvider, 21 | useSupabaseUser, 22 | UserContextProviderInjection, 23 | UserContextProviderKey 24 | } 25 | 26 | const plugin: Plugin = { 27 | install(app) { 28 | app.component('Auth', Auth) 29 | } 30 | } 31 | 32 | export default plugin 33 | -------------------------------------------------------------------------------- /packages/icons/IconLinkedin.vue: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /packages/icons/IconFigma.vue: -------------------------------------------------------------------------------- 1 | 30 | -------------------------------------------------------------------------------- /packages/auth/SocialAuthContainer.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 33 | -------------------------------------------------------------------------------- /packages/icons/IconTwitter.vue: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "moduleResolution": "Node", 7 | "strict": true, 8 | "jsx": "preserve", 9 | "sourceMap": true, 10 | "resolveJsonModule": true, 11 | "isolatedModules": true, 12 | "esModuleInterop": true, 13 | "lib": ["ESNext", "DOM"], 14 | "skipLibCheck": true, 15 | "baseUrl": "./", 16 | "paths": { 17 | "@/*": ["packages/*"], 18 | "~/*": ["./src/*"] 19 | }, 20 | "types": ["unplugin-icons/types/vue3"] 21 | }, 22 | "include": [ 23 | "src/**/*.ts", 24 | "src/**/*.d.ts", 25 | "src/**/*.tsx", 26 | "src/**/*.vue", 27 | "packages/**/*.ts", 28 | "packages/**/*.d.ts", 29 | "packages/**/*.tsx", 30 | "packages/**/*.vue" 31 | ], 32 | "references": [{ "path": "./tsconfig.node.json" }], 33 | "baseUrl": ".", 34 | "paths": { 35 | "@/*": ["./src/*"] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/components/ui/button/Button.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 34 | -------------------------------------------------------------------------------- /packages/ui/Divider.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 43 | -------------------------------------------------------------------------------- /packages/auth/AuthCard.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 39 | -------------------------------------------------------------------------------- /packages/icons/IconGithub.vue: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /packages/ui/Label.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 43 | -------------------------------------------------------------------------------- /src/composables/useSEOHeader.ts: -------------------------------------------------------------------------------- 1 | import { useHead, useSeoMeta } from '@vueuse/head' 2 | 3 | const useSEOHeader = () => { 4 | useHead({ 5 | title: 'Auth UI Vue', 6 | titleTemplate: (title) => `${title} | Pre-built auth ui for Vue 3 | Nuxt 3`, 7 | meta: [ 8 | { 9 | name: 'author', 10 | content: '@xiaoluoboding' 11 | }, 12 | { 13 | name: 'description', 14 | content: 'Pre-built auth ui for Vue 3 | Nuxt 3' 15 | } 16 | ] 17 | }) 18 | useSeoMeta({ 19 | title: 'Auth UI Vue', 20 | description: 'Pre-built auth ui for Vue 3 | Nuxt 3', 21 | ogDescription: 'Pre-built auth ui for Vue 3 | Nuxt 3', 22 | ogTitle: 'Auth UI Vue', 23 | ogImage: 'https://auth-ui-vue.netlify.app/og.png', 24 | ogImageHeight: '882', 25 | ogImageWidth: '1686', 26 | twitterCard: 'summary_large_image', 27 | twitterImage: 'https://auth-ui-vue.netlify.app/og.png', 28 | twitterTitle: 'Auth UI Vue', 29 | twitterDescription: 'Pre-built auth ui for Vue 3 | Nuxt 3' 30 | }) 31 | } 32 | export { useSEOHeader } 33 | -------------------------------------------------------------------------------- /packages/icons/IconWorkos.vue: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /src/plugins/i18n.ts: -------------------------------------------------------------------------------- 1 | import type { App } from 'vue' 2 | import type { Locale } from 'vue-i18n' 3 | import { createI18n } from 'vue-i18n' 4 | import { useLocalStorage } from '@vueuse/core' 5 | 6 | const storage = useLocalStorage('otg-options', {}) as any 7 | const locale = storage.value?.apperance?.language 8 | 9 | // import i18n resources 10 | const localesMap = Object.fromEntries( 11 | Object.entries(import.meta.glob('../locales/*.yml')).map( 12 | ([path, loadLocale]) => { 13 | return [path.match(/([\w-]*)\.yml$/)?.[1], loadLocale] 14 | } 15 | ) 16 | ) 17 | 18 | export const availableLocales = Object.keys(localesMap) 19 | 20 | const i18n = createI18n({ 21 | legacy: false, 22 | locale, 23 | globalInjection: true, 24 | messages: {} 25 | }) 26 | 27 | export async function loadLanguageAsync(lang: string): Promise { 28 | const messages = await localesMap[lang]() 29 | i18n.global.setLocaleMessage(lang, messages.default) 30 | return lang 31 | } 32 | 33 | const install = (app: App) => { 34 | app.use(i18n) 35 | loadLanguageAsync('en-US') 36 | } 37 | 38 | export default install 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 supa-kit 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/icons/IconBitbucket.vue: -------------------------------------------------------------------------------- 1 | 36 | -------------------------------------------------------------------------------- /packages/ui/Anchor.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 48 | -------------------------------------------------------------------------------- /src/components/ui/separator/Separator.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 43 | -------------------------------------------------------------------------------- /packages/icons/IconGoogle.vue: -------------------------------------------------------------------------------- 1 | 26 | -------------------------------------------------------------------------------- /packages/icons/IconApple.vue: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /src/components/ui/tooltip/TooltipContent.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 32 | -------------------------------------------------------------------------------- /packages/ui/Message.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 56 | -------------------------------------------------------------------------------- /packages/icons/IconSpotify.vue: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /src/components/ui/input/Input.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 44 | -------------------------------------------------------------------------------- /packages/ui/BrandButton.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 56 | -------------------------------------------------------------------------------- /src/components/ToggleButton.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /packages/ui/Container.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 63 | -------------------------------------------------------------------------------- /packages/icons/Icons.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 57 | -------------------------------------------------------------------------------- /packages/icons/IconSlack.vue: -------------------------------------------------------------------------------- 1 | 42 | -------------------------------------------------------------------------------- /packages/icons/IconDiscord.vue: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /packages/ui/Loader.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 62 | -------------------------------------------------------------------------------- /public/nuxtbase-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/components/ui/button/index.ts: -------------------------------------------------------------------------------- 1 | import { cva, type VariantProps } from 'class-variance-authority' 2 | 3 | export { default as Button } from './Button.vue' 4 | 5 | export const buttonVariants = cva( 6 | 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0', 7 | { 8 | variants: { 9 | variant: { 10 | default: 11 | 'bg-primary text-primary-foreground shadow hover:bg-primary/90', 12 | destructive: 13 | 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90', 14 | outline: 15 | 'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground', 16 | secondary: 17 | 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80', 18 | ghost: 'hover:bg-accent hover:text-accent-foreground', 19 | link: 'text-primary underline-offset-4 hover:underline', 20 | brand: 'shadow' 21 | }, 22 | size: { 23 | default: 'h-9 px-4 py-2', 24 | xs: 'h-7 rounded px-2', 25 | sm: 'h-8 rounded-md px-3 text-xs', 26 | lg: 'h-10 rounded-md px-8', 27 | icon: 'h-9 w-9' 28 | }, 29 | shape: { 30 | square: 'rounded-none', 31 | rounded: 'rounded-md', 32 | pill: 'rounded-full' 33 | } 34 | }, 35 | defaultVariants: { 36 | variant: 'default', 37 | size: 'default', 38 | shape: 'rounded' 39 | } 40 | } 41 | ) 42 | 43 | export type ButtonVariants = VariantProps 44 | -------------------------------------------------------------------------------- /src/global.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | :root { 7 | --background: 0 0% 100%; 8 | --foreground: 0 0% 3.9%; 9 | 10 | --muted: 0 0% 96.1%; 11 | --muted-foreground: 0 0% 45.1%; 12 | 13 | --popover: 0 0% 100%; 14 | --popover-foreground: 0 0% 3.9%; 15 | 16 | --card: 0 0% 100%; 17 | --card-foreground: 0 0% 3.9%; 18 | 19 | --border: 0 0% 89.8%; 20 | --input: 0 0% 89.8%; 21 | 22 | --primary: 0 0% 9%; 23 | --primary-foreground: 0 0% 98%; 24 | 25 | --secondary: 0 0% 96.1%; 26 | --secondary-foreground: 0 0% 9%; 27 | 28 | --accent: 0 0% 96.1%; 29 | --accent-foreground: 0 0% 9%; 30 | 31 | --destructive: 0 84.2% 60.2%; 32 | --destructive-foreground: 0 0% 98%; 33 | 34 | --brand: 150 40% 52%; 35 | 36 | --ring: 0 0% 3.9%; 37 | 38 | --radius: 0.5rem; 39 | } 40 | 41 | .dark { 42 | --background: 0 0% 3.9%; 43 | --foreground: 0 0% 98%; 44 | 45 | --muted: 0 0% 14.9%; 46 | --muted-foreground: 0 0% 63.9%; 47 | 48 | --popover: 0 0% 3.9%; 49 | --popover-foreground: 0 0% 98%; 50 | 51 | --card: 0 0% 3.9%; 52 | --card-foreground: 0 0% 98%; 53 | 54 | --border: 0 0% 14.9%; 55 | --input: 0 0% 14.9%; 56 | 57 | --primary: 0 0% 98%; 58 | --primary-foreground: 0 0% 9%; 59 | 60 | --secondary: 0 0% 14.9%; 61 | --secondary-foreground: 0 0% 98%; 62 | 63 | --accent: 0 0% 14.9%; 64 | --accent-foreground: 0 0% 98%; 65 | 66 | --destructive: 0 62.8% 30.6%; 67 | --destructive-foreground: 0 0% 98%; 68 | 69 | --ring: 0 0% 83.1%; 70 | } 71 | } 72 | 73 | @layer base { 74 | * { 75 | @apply border-border; 76 | } 77 | body { 78 | @apply bg-background text-foreground; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /packages/icons/IconKakao.vue: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /src/components/Footer.vue: -------------------------------------------------------------------------------- 1 | 59 | -------------------------------------------------------------------------------- /packages/icons/IconNotion.vue: -------------------------------------------------------------------------------- 1 | 30 | -------------------------------------------------------------------------------- /components.d.ts: -------------------------------------------------------------------------------- 1 | // generated by unplugin-vue-components 2 | // We suggest you to commit this file into source control 3 | // Read more: https://github.com/vuejs/core/pull/3399 4 | import '@vue/runtime-core' 5 | 6 | export {} 7 | 8 | declare module '@vue/runtime-core' { 9 | export interface GlobalComponents { 10 | Anchor: typeof import('./src/components/ui/anchor/Anchor.vue')['default'] 11 | Button: typeof import('./src/components/ui/button/Button.vue')['default'] 12 | 'Carbon:cafe': typeof import('~icons/carbon/cafe')['default'] 13 | 'Carbon:logoTwitter': typeof import('~icons/carbon/logo-twitter')['default'] 14 | Footer: typeof import('./src/components/Footer.vue')['default'] 15 | Hero: typeof import('./src/components/Hero.vue')['default'] 16 | IconMenu: typeof import('./src/components/IconMenu.vue')['default'] 17 | IconPalette: typeof import('./src/components/IconPalette.vue')['default'] 18 | Input: typeof import('./src/components/ui/input/Input.vue')['default'] 19 | Label: typeof import('./src/components/ui/label/Label.vue')['default'] 20 | 'Logos:nuxtIcon': typeof import('~icons/logos/nuxt-icon')['default'] 21 | 'Logos:vue': typeof import('~icons/logos/vue')['default'] 22 | 'Mdi:heart': typeof import('~icons/mdi/heart')['default'] 23 | 'Ri:githubFill': typeof import('~icons/ri/github-fill')['default'] 24 | 'Ri:moonLine': typeof import('~icons/ri/moon-line')['default'] 25 | 'Ri:sunLine': typeof import('~icons/ri/sun-line')['default'] 26 | 'Ri:translate2': typeof import('~icons/ri/translate2')['default'] 27 | Separator: typeof import('./src/components/ui/separator/Separator.vue')['default'] 28 | ToggleButton: typeof import('./src/components/ToggleButton.vue')['default'] 29 | Tooltip: typeof import('./src/components/ui/tooltip/Tooltip.vue')['default'] 30 | TooltipContent: typeof import('./src/components/ui/tooltip/TooltipContent.vue')['default'] 31 | TooltipProvider: typeof import('./src/components/ui/tooltip/TooltipProvider.vue')['default'] 32 | TooltipTrigger: typeof import('./src/components/ui/tooltip/TooltipTrigger.vue')['default'] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/ui/Input.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 82 | -------------------------------------------------------------------------------- /packages/icons/IconKeycloak.vue: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, UserConfig } from 'vite' 2 | import { resolve } from 'path' 3 | import Vue from '@vitejs/plugin-vue' 4 | import autoprefixer from 'autoprefixer' 5 | import tailwind from 'tailwindcss' 6 | 7 | import dts from 'vite-plugin-dts' 8 | import Icons from 'unplugin-icons/vite' 9 | import IconsResolver from 'unplugin-icons/resolver' 10 | import Components from 'unplugin-vue-components/vite' 11 | import { libInjectCss } from 'vite-plugin-lib-inject-css' 12 | import VueDevTools from 'vite-plugin-vue-devtools' 13 | import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite' 14 | 15 | // https://vitejs.dev/config/ 16 | export default defineConfig(({ command, mode }) => { 17 | let userConfig: UserConfig = {} 18 | 19 | // Development mode CSS configuration 20 | const cssConfig = 21 | mode !== 'lib' 22 | ? { 23 | css: { 24 | postcss: { 25 | plugins: [tailwind(), autoprefixer()] 26 | } 27 | } 28 | } 29 | : {} 30 | 31 | const commonPlugins = [ 32 | Vue(), 33 | VueDevTools(), 34 | Components({ 35 | resolvers: [ 36 | IconsResolver({ 37 | prefix: '' 38 | }) 39 | ] 40 | }), 41 | Icons(), 42 | VueI18nPlugin({ 43 | include: [resolve(__dirname, 'src/locales/**')] 44 | }) 45 | ] 46 | 47 | if (mode === 'lib') { 48 | userConfig.build = { 49 | lib: { 50 | entry: resolve(__dirname, 'packages/index.ts'), 51 | name: 'AuthUIVue', 52 | fileName: 'auth-ui-vue' 53 | }, 54 | outDir: 'lib', 55 | emptyOutDir: true, 56 | sourcemap: false, 57 | rollupOptions: { 58 | external: ['vue'], 59 | output: { 60 | globals: { 61 | vue: 'Vue' 62 | } 63 | } 64 | } 65 | } 66 | userConfig.plugins = [ 67 | ...commonPlugins, 68 | dts({ 69 | include: './packages' 70 | }) 71 | // libInjectCss() 72 | ] 73 | } 74 | 75 | return { 76 | ...cssConfig, 77 | resolve: { 78 | alias: { 79 | '@': resolve(__dirname, '/packages'), 80 | '~': resolve(__dirname, '/src') 81 | } 82 | }, 83 | plugins: [...commonPlugins], 84 | ...userConfig 85 | } 86 | }) 87 | -------------------------------------------------------------------------------- /packages/ui/Button.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 86 | -------------------------------------------------------------------------------- /packages/auth/UserContextProvider.ts: -------------------------------------------------------------------------------- 1 | import { Session, SupabaseClient, User } from '@supabase/supabase-js' 2 | import { PropType, defineComponent, h, onMounted, provide, ref } from 'vue' 3 | 4 | import { UserContextProviderKey } from '../types' 5 | 6 | export default defineComponent({ 7 | name: 'UserContextProvider', 8 | props: { 9 | supabaseClient: { 10 | type: Object as PropType, 11 | required: true 12 | } 13 | }, 14 | setup({ supabaseClient }, { slots }) { 15 | const session = ref(null) 16 | const user = ref(null) 17 | 18 | onMounted(async () => { 19 | const { data } = await supabaseClient.auth.getSession() 20 | session.value = data.session 21 | user.value = data.session?.user! ?? null 22 | 23 | const { data: authListener } = supabaseClient.auth.onAuthStateChange( 24 | async (event, newSession) => { 25 | session.value = newSession 26 | user.value = newSession?.user! ?? null 27 | } 28 | ) 29 | 30 | return () => { 31 | // console.log('authListener') 32 | authListener?.subscription.unsubscribe() 33 | } 34 | }) 35 | 36 | provide(UserContextProviderKey, { 37 | session, 38 | user 39 | }) 40 | 41 | return () => h('div', null, slots?.default?.()) 42 | } 43 | }) 44 | 45 | export const useSupabaseUser = (supabaseClient: SupabaseClient) => { 46 | const supabaseUser = ref(null) 47 | 48 | const setSupbaseUser = (session: Session) => { 49 | if (session) { 50 | if (JSON.stringify(supabaseUser.value) !== JSON.stringify(session.user)) { 51 | supabaseUser.value = session.user 52 | } 53 | } else { 54 | supabaseUser.value = null 55 | } 56 | } 57 | 58 | // Asyncronous refresh session and ensure user is still logged in 59 | supabaseClient?.auth.getSession().then(({ data: { session } }) => { 60 | session && setSupbaseUser(session) 61 | }) 62 | 63 | onMounted(async () => { 64 | const { data: authListener } = supabaseClient.auth.onAuthStateChange( 65 | async (event, newSession) => { 66 | newSession && setSupbaseUser(newSession) 67 | } 68 | ) 69 | 70 | return () => { 71 | authListener?.subscription.unsubscribe() 72 | } 73 | }) 74 | 75 | return { 76 | supabaseUser 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /packages/auth/AnonymousAuth.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 89 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@supa-kit/auth-ui-vue", 3 | "version": "0.4.3", 4 | "type": "module", 5 | "author": "xiaoluoboding ", 6 | "scripts": { 7 | "dev": "vite --port 3003", 8 | "build:docs": "vite build --mode docs", 9 | "build:lib": "vue-tsc --noEmit && vite build --mode lib && npm run build:clean", 10 | "build:clean": "cd lib && rm -rf *.mp4 *.png *.svg && cd ..", 11 | "preview": "vite preview", 12 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/supa-kit/auth-ui-vue.git" 17 | }, 18 | "homepage": "https://github.com/supa-kit/auth-ui-vue", 19 | "files": [ 20 | "lib", 21 | "volar.d.ts" 22 | ], 23 | "main": "./lib/auth-ui-vue.umd.cjs", 24 | "module": "./lib/auth-ui-vue.js", 25 | "exports": { 26 | ".": { 27 | "types": "./lib/packages/index.d.ts", 28 | "import": "./lib/auth-ui-vue.js", 29 | "require": "./lib/auth-ui-vue.umd.cjs" 30 | } 31 | }, 32 | "license": "MIT", 33 | "keywords": [ 34 | "Supabase", 35 | "Nuxt", 36 | "Vue", 37 | "Auth" 38 | ], 39 | "sideEffects": [ 40 | "**/*.css" 41 | ], 42 | "types": "./lib/index.d.ts", 43 | "publishConfig": { 44 | "access": "public" 45 | }, 46 | "peerDependencies": { 47 | "@supabase/auth-ui-shared": "^0.1.8", 48 | "@supabase/supabase-js": "^2.42.7" 49 | }, 50 | "devDependencies": { 51 | "@iconify/json": "^2.2.263", 52 | "@intlify/unplugin-vue-i18n": "^0.12.3", 53 | "@stitches/core": "^1.2.8", 54 | "@supabase/auth-ui-shared": "^0.1.8", 55 | "@supabase/supabase-js": "^2.45.6", 56 | "@types/lodash.clonedeep": "^4.5.9", 57 | "@types/node": "^18.19.59", 58 | "@vitejs/plugin-vue": "^4.6.2", 59 | "@vueuse/core": "^10.11.1", 60 | "@vueuse/head": "^2.0.0", 61 | "autoprefixer": "^10.4.20", 62 | "highlight.js": "^11.10.0", 63 | "lodash.clonedeep": "^4.5.0", 64 | "tailwindcss": "^3.4.14", 65 | "typescript": "^4.9.5", 66 | "unplugin-icons": "^0.14.15", 67 | "unplugin-vue-components": "^0.22.12", 68 | "vite": "^4.5.5", 69 | "vite-plugin-dts": "^3.9.1", 70 | "vite-plugin-lib-inject-css": "^1.3.0", 71 | "vite-plugin-vue-devtools": "1.0.0-rc.0", 72 | "vue": "^3.5.12", 73 | "vue-i18n": "^9.14.1", 74 | "vue-tsc": "^1.8.27" 75 | }, 76 | "dependencies": { 77 | "@radix-icons/vue": "^1.0.0", 78 | "class-variance-authority": "^0.7.0", 79 | "clsx": "^2.1.1", 80 | "colord": "^2.9.3", 81 | "radix-vue": "^1.9.7", 82 | "tailwind-merge": "^2.5.4", 83 | "tailwindcss-animate": "^1.0.7" 84 | } 85 | } -------------------------------------------------------------------------------- /packages/icons/IconAzure.vue: -------------------------------------------------------------------------------- 1 | 65 | -------------------------------------------------------------------------------- /packages/auth/UpdatePassword.vue: -------------------------------------------------------------------------------- 1 | 45 | 46 | 101 | -------------------------------------------------------------------------------- /tailwind.config.ts: -------------------------------------------------------------------------------- 1 | const animate = require('tailwindcss-animate') 2 | 3 | /** @type {import('tailwindcss').Config} */ 4 | module.exports = { 5 | darkMode: ['class'], 6 | safelist: ['dark'], 7 | prefix: '', 8 | 9 | content: [ 10 | './pages/**/*.{ts,tsx,vue}', 11 | './components/**/*.{ts,tsx,vue}', 12 | './app/**/*.{ts,tsx,vue}', 13 | './src/**/*.{ts,tsx,vue}' 14 | ], 15 | 16 | theme: { 17 | container: { 18 | center: true, 19 | padding: '2rem', 20 | screens: { 21 | '2xl': '1400px' 22 | } 23 | }, 24 | extend: { 25 | colors: { 26 | border: 'hsl(var(--border))', 27 | input: 'hsl(var(--input))', 28 | ring: 'hsl(var(--ring))', 29 | background: 'hsl(var(--background))', 30 | foreground: 'hsl(var(--foreground))', 31 | primary: { 32 | DEFAULT: 'hsl(var(--primary))', 33 | foreground: 'hsl(var(--primary-foreground))' 34 | }, 35 | secondary: { 36 | DEFAULT: 'hsl(var(--secondary))', 37 | foreground: 'hsl(var(--secondary-foreground))' 38 | }, 39 | destructive: { 40 | DEFAULT: 'hsl(var(--destructive))', 41 | foreground: 'hsl(var(--destructive-foreground))' 42 | }, 43 | muted: { 44 | DEFAULT: 'hsl(var(--muted))', 45 | foreground: 'hsl(var(--muted-foreground))' 46 | }, 47 | accent: { 48 | DEFAULT: 'hsl(var(--accent))', 49 | foreground: 'hsl(var(--accent-foreground))' 50 | }, 51 | popover: { 52 | DEFAULT: 'hsl(var(--popover))', 53 | foreground: 'hsl(var(--popover-foreground))' 54 | }, 55 | card: { 56 | DEFAULT: 'hsl(var(--card))', 57 | foreground: 'hsl(var(--card-foreground))' 58 | } 59 | }, 60 | borderRadius: { 61 | xl: 'calc(var(--radius) + 4px)', 62 | lg: 'var(--radius)', 63 | md: 'calc(var(--radius) - 2px)', 64 | sm: 'calc(var(--radius) - 4px)' 65 | }, 66 | keyframes: { 67 | 'accordion-down': { 68 | from: { height: 0 }, 69 | to: { height: 'var(--radix-accordion-content-height)' } 70 | }, 71 | 'accordion-up': { 72 | from: { height: 'var(--radix-accordion-content-height)' }, 73 | to: { height: 0 } 74 | }, 75 | 'collapsible-down': { 76 | from: { height: 0 }, 77 | to: { height: 'var(--radix-collapsible-content-height)' } 78 | }, 79 | 'collapsible-up': { 80 | from: { height: 'var(--radix-collapsible-content-height)' }, 81 | to: { height: 0 } 82 | } 83 | }, 84 | animation: { 85 | 'accordion-down': 'accordion-down 0.2s ease-out', 86 | 'accordion-up': 'accordion-up 0.2s ease-out', 87 | 'collapsible-down': 'collapsible-down 0.2s ease-in-out', 88 | 'collapsible-up': 'collapsible-up 0.2s ease-in-out' 89 | } 90 | } 91 | }, 92 | plugins: [animate] 93 | } 94 | -------------------------------------------------------------------------------- /src/plugins/highlight.ts: -------------------------------------------------------------------------------- 1 | import { ref, h, computed, defineComponent, Plugin, watch } from 'vue' 2 | import hljs from 'highlight.js/lib/core' 3 | import javascript from 'highlight.js/lib/languages/javascript' 4 | import xml from 'highlight.js/lib/languages/xml' 5 | import 'highlight.js/styles/github.css' 6 | 7 | hljs.registerLanguage('javascript', javascript) 8 | hljs.registerLanguage('xml', xml) 9 | 10 | function escapeHtml(value: string): string { 11 | return value 12 | .replace(/&/g, '&') 13 | .replace(//g, '>') 15 | .replace(/"/g, '"') 16 | .replace(/'/g, ''') 17 | } 18 | 19 | const component = defineComponent({ 20 | props: { 21 | className: { 22 | type: String, 23 | default: '' 24 | }, 25 | code: { 26 | type: String, 27 | required: true 28 | }, 29 | language: { 30 | type: String, 31 | default: '' 32 | }, 33 | autodetect: { 34 | type: Boolean, 35 | default: true 36 | }, 37 | ignoreIllegals: { 38 | type: Boolean, 39 | default: true 40 | } 41 | }, 42 | setup(props) { 43 | const language = ref(props.language) 44 | watch( 45 | () => props.language, 46 | (newLanguage) => { 47 | language.value = newLanguage 48 | } 49 | ) 50 | 51 | const autodetect = computed(() => props.autodetect || !language.value) 52 | const cannotDetectLanguage = computed( 53 | () => !autodetect.value && !hljs.getLanguage(language.value) 54 | ) 55 | 56 | const className = computed((): string => { 57 | if (cannotDetectLanguage.value) { 58 | return '' 59 | } else { 60 | return `hljs ${language.value} ${props.className}` 61 | } 62 | }) 63 | 64 | const highlightedCode = computed((): string => { 65 | // No idea what language to use, return raw code 66 | if (cannotDetectLanguage.value) { 67 | console.warn( 68 | `The language "${language.value}" you specified could not be found.` 69 | ) 70 | return escapeHtml(props.code) 71 | } 72 | 73 | if (autodetect.value) { 74 | const result = hljs.highlightAuto(props.code) 75 | language.value = result.language ?? '' 76 | return result.value 77 | } else { 78 | const result = hljs.highlight(props.code, { 79 | language: language.value, 80 | ignoreIllegals: props.ignoreIllegals 81 | }) 82 | return result.value 83 | } 84 | }) 85 | 86 | return { 87 | className, 88 | highlightedCode 89 | } 90 | }, 91 | render() { 92 | return h('pre', {}, [ 93 | h('code', { 94 | class: this.className, 95 | innerHTML: this.highlightedCode, 96 | tabindex: '0' 97 | }) 98 | ]) 99 | } 100 | }) 101 | 102 | const plugin: Plugin & { component: typeof component } = { 103 | install(app) { 104 | app.component('Highlight', component) 105 | }, 106 | component 107 | } 108 | 109 | export default plugin 110 | -------------------------------------------------------------------------------- /packages/types.ts: -------------------------------------------------------------------------------- 1 | import type { CSSProperties, InjectionKey, Ref } from 'vue' 2 | import colors from 'tailwindcss/colors' 3 | import type { 4 | BaseAppearance, 5 | I18nVariables, 6 | OtpType, 7 | ProviderScopes, 8 | RedirectTo, 9 | SocialLayout, 10 | ViewType 11 | } from '@supabase/auth-ui-shared' 12 | import type { 13 | Provider, 14 | Session, 15 | SignInAnonymouslyCredentials, 16 | SupabaseClient, 17 | User 18 | } from '@supabase/supabase-js' 19 | 20 | export interface Appearance extends BaseAppearance { 21 | brand: keyof typeof colors 22 | size?: 'default' | 'sm' | 'md' | 'lg' 23 | shape?: 'square' | 'rounded' | 'pill' 24 | style?: { 25 | anchor?: CSSProperties 26 | button?: CSSProperties 27 | container?: CSSProperties 28 | divider?: CSSProperties 29 | input?: CSSProperties 30 | label?: CSSProperties 31 | loader?: CSSProperties 32 | message?: CSSProperties 33 | } 34 | } 35 | 36 | declare type ViewAnonymousSignIn = 'anonymous_sign_in' 37 | export type AuthViewType = ViewType | ViewAnonymousSignIn 38 | 39 | export interface AuthI18nVariables extends I18nVariables { 40 | anonymous_sign_in?: { 41 | button_label?: string 42 | loading_button_label?: string 43 | } 44 | } 45 | 46 | interface BaseAuth { 47 | supabaseClient: SupabaseClient 48 | socialLayout?: SocialLayout 49 | providers?: Provider[] 50 | providerScopes?: Partial 51 | queryParams?: { 52 | [key: string]: string 53 | } 54 | redirectTo?: RedirectTo 55 | onlyThirdPartyProviders?: boolean 56 | magicLink?: boolean 57 | showLinks?: boolean 58 | otpType?: OtpType 59 | additionalData?: { 60 | [key: string]: any 61 | } 62 | /** 63 | * This will toggle on the dark variation of the theme 64 | */ 65 | dark?: boolean 66 | theme?: 'default' | string 67 | } 68 | 69 | export interface AuthProps extends BaseAuth { 70 | appearance?: Appearance 71 | // add anonymous-signins view 72 | view?: AuthViewType 73 | /** 74 | * Override the labels and button text 75 | */ 76 | localization?: { 77 | variables?: AuthI18nVariables 78 | } 79 | anonymouslyCredentials?: SignInAnonymouslyCredentials 80 | } 81 | 82 | export type AuthProvider = 83 | | 'apple' 84 | | 'azure' 85 | | 'bitbucket' 86 | | 'discord' 87 | | 'facebook' 88 | | 'figma' 89 | | 'github' 90 | | 'gitlab' 91 | | 'google' 92 | | 'kakao' 93 | | 'keycloak' 94 | | 'linkedin' 95 | | 'linkedin_oidc' 96 | | 'notion' 97 | | 'slack' 98 | | 'spotify' 99 | | 'twitch' 100 | | 'twitter' 101 | | 'workos' 102 | | 'zoom' 103 | 104 | export interface AuthViewInjection { 105 | authView: Ref 106 | setAuthView: (view: AuthViewType) => void 107 | } 108 | 109 | export interface UserContextProviderInjection { 110 | session: Ref 111 | user: Ref 112 | } 113 | 114 | export const AuthViewKey: InjectionKey = Symbol('AuthView') 115 | export const UserContextProviderKey: InjectionKey = 116 | Symbol('UserContextProvider') 117 | -------------------------------------------------------------------------------- /src/composables/useLanguage.ts: -------------------------------------------------------------------------------- 1 | import { en as enLocale } from '@supabase/auth-ui-shared' 2 | import { useI18n } from 'vue-i18n' 3 | import cloneDeep from 'lodash.clonedeep' 4 | 5 | import { loadLanguageAsync, availableLocales } from '~/plugins/i18n' 6 | 7 | const sign_up = { 8 | email_label: '邮箱地址', 9 | password_label: '密码', 10 | email_input_placeholder: '请输入邮箱地址', 11 | password_input_placeholder: '请输入密码', 12 | button_label: '注册', 13 | loading_button_label: '注册中...', 14 | social_provider_text: '通过 {{provider}} 继续', 15 | link_text: '还没有账号? 注册', 16 | confirmation_text: '请查看您的电子邮件以获取确认链接' 17 | } 18 | const sign_in = { 19 | email_label: '邮箱地址', 20 | password_label: '密码', 21 | email_input_placeholder: '请输入邮箱地址', 22 | password_input_placeholder: '请输入密码', 23 | button_label: '登录', 24 | loading_button_label: '登录中...', 25 | social_provider_text: '通过 {{provider}} 继续', 26 | link_text: '已经账号?登录' 27 | } 28 | const magic_link = { 29 | email_input_label: '邮箱地址', 30 | email_input_placeholder: '请输入邮箱地址', 31 | button_label: '登录', 32 | loading_button_label: '登录中...', 33 | link_text: '发送一封魔法链接邮件', 34 | confirmation_text: '请检查您的电子邮件以获取确认链接' 35 | } 36 | const forgotten_password = { 37 | email_label: '邮箱地址', 38 | password_label: '密码', 39 | email_input_placeholder: '请输入邮箱地址', 40 | button_label: '发送重置密码指令', 41 | loading_button_label: '发送重置密码指令中 ...', 42 | link_text: '忘记密码?', 43 | confirmation_text: '请检查您的电子邮件以获取密码重置链接' 44 | } 45 | const update_password = { 46 | password_label: '新密码', 47 | password_input_placeholder: '请输入新密码', 48 | button_label: '更新密码', 49 | loading_button_label: '更新密码中 ...', 50 | confirmation_text: '密码已更新' 51 | } 52 | const verify_otp = { 53 | email_input_label: '邮箱地址', 54 | email_input_placeholder: '请输入邮箱地址', 55 | phone_input_label: '电话号码', 56 | phone_input_placeholder: '请输入电话号码', 57 | token_input_label: '密钥', 58 | token_input_placeholder: '请输入一次性密钥', 59 | button_label: '验证密钥', 60 | loading_button_label: '登录中...' 61 | } 62 | const anonymous_sign_in = { 63 | button_label: '以访客身份登录', 64 | loading_button_label: '登录中...' 65 | } 66 | const zh = { 67 | sign_up: sign_up, 68 | sign_in: sign_in, 69 | magic_link: magic_link, 70 | forgotten_password: forgotten_password, 71 | update_password: update_password, 72 | verify_otp: verify_otp, 73 | anonymous_sign_in: anonymous_sign_in 74 | } 75 | 76 | const anonymous_sign_in_en = { 77 | button_label: 'Login as Guest', 78 | loading_button_label: 'Signing in ...' 79 | } 80 | 81 | const en = { 82 | ...enLocale, 83 | anonymous_sign_in: anonymous_sign_in_en 84 | } 85 | 86 | const useLanguage = () => { 87 | const { locale } = useI18n() 88 | 89 | const toggleLocales = () => { 90 | const locales = availableLocales 91 | const newLocale = 92 | locales[(locales.indexOf(locale.value) + 1) % locales.length] 93 | loadLanguageAsync(newLocale) 94 | locale.value = newLocale 95 | } 96 | return { 97 | en: cloneDeep(en), 98 | zh: cloneDeep(zh), 99 | availableLocales, 100 | toggleLocales 101 | } 102 | } 103 | 104 | export { useLanguage } 105 | -------------------------------------------------------------------------------- /packages/auth/ForgottenPassword.vue: -------------------------------------------------------------------------------- 1 | 56 | 57 | 117 | -------------------------------------------------------------------------------- /packages/auth/MagicLink.vue: -------------------------------------------------------------------------------- 1 | 56 | 57 | 136 | -------------------------------------------------------------------------------- /packages/auth/SocialAuth.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 142 | -------------------------------------------------------------------------------- /packages/auth/VerifyOtp.vue: -------------------------------------------------------------------------------- 1 | 86 | 87 | 168 | -------------------------------------------------------------------------------- /packages/auth/Auth.vue: -------------------------------------------------------------------------------- 1 | 73 | 74 | 178 | -------------------------------------------------------------------------------- /src/components/Hero.vue: -------------------------------------------------------------------------------- 1 | 52 | 53 | 80 | 81 | 194 | ~/composables/useLanguage 195 | -------------------------------------------------------------------------------- /public/nuxtbase-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /public/nuxtbase-light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [0.4.3](https://github.com/supa-kit/auth-ui-vue/compare/v0.4.2...v0.4.3) (2025-02-03) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * fixed build ([2e18ca6](https://github.com/supa-kit/auth-ui-vue/commit/2e18ca68ee2b0026088b75e512f2d787703a5283)) 7 | * fixed the build ([511059e](https://github.com/supa-kit/auth-ui-vue/commit/511059e270ff57554940dde3c5516b4caa4e679a)) 8 | 9 | 10 | 11 | ## [0.4.2](https://github.com/supa-kit/auth-ui-vue/compare/v0.4.1...v0.4.2) (2025-02-03) 12 | 13 | 14 | ### Features 15 | 16 | * fixed the brand button ([df48196](https://github.com/supa-kit/auth-ui-vue/commit/df481964a68f98b6a0e4309c1e9762afd5241a71)) 17 | 18 | 19 | 20 | ## [0.4.1](https://github.com/supa-kit/auth-ui-vue/compare/v0.4.0...v0.4.1) (2025-02-03) 21 | 22 | 23 | 24 | # [0.4.0](https://github.com/supa-kit/auth-ui-vue/compare/v0.3.8...v0.4.0) (2025-02-02) 25 | 26 | 27 | ### Bug Fixes 28 | 29 | * fixed the build ([e3ae97e](https://github.com/supa-kit/auth-ui-vue/commit/e3ae97ea5b318566185dfb38d751d647ea68e429)) 30 | 31 | 32 | ### Features 33 | 34 | * add shadcn vue support for auth ui ([ec58c33](https://github.com/supa-kit/auth-ui-vue/commit/ec58c331c542293cdc5427dcfba6046b9e8f57ea)) 35 | * add shape variant ([8088f5e](https://github.com/supa-kit/auth-ui-vue/commit/8088f5e0136dd8eb241feac227184641a21ce9f2)) 36 | * add tooltip ([1bf6e15](https://github.com/supa-kit/auth-ui-vue/commit/1bf6e15048f7823d4dedd1e23c0b7344fec526c9)) 37 | * change color with the brand props ([317221c](https://github.com/supa-kit/auth-ui-vue/commit/317221cb61401ba13116abe3c5545a6232d60e6f)) 38 | * change the background style ([c376824](https://github.com/supa-kit/auth-ui-vue/commit/c376824eb9ccc9aa931d40361f4ad2d2ef586fc4)) 39 | * change the magic link layout ([c11b9ff](https://github.com/supa-kit/auth-ui-vue/commit/c11b9ff6fff8420e8b6908d841db5c2bf620ee9c)) 40 | * change the shape type ([584519a](https://github.com/supa-kit/auth-ui-vue/commit/584519a3b3e56381b4f68943e2a258be53fcdc5b)) 41 | * improve the toggle button style ([6bfd81e](https://github.com/supa-kit/auth-ui-vue/commit/6bfd81ec4fb77fed1250f1b65a9f02b535fedea2)) 42 | * improve the ui ([fa2944f](https://github.com/supa-kit/auth-ui-vue/commit/fa2944f915b59dffcab03bc070326543251fb185)) 43 | 44 | 45 | 46 | ## [0.3.8](https://github.com/supa-kit/auth-ui-vue/compare/v0.3.7...v0.3.8) (2024-10-23) 47 | 48 | 49 | 50 | ## [0.3.7](https://github.com/supa-kit/auth-ui-vue/compare/v0.3.6...v0.3.7) (2024-05-02) 51 | 52 | 53 | ### Features 54 | 55 | * add anonymous sign-ins ([6bbe538](https://github.com/supa-kit/auth-ui-vue/commit/6bbe538012761072e02733e9e192b1cb0b330e38)) 56 | 57 | 58 | 59 | ## [0.3.6](https://github.com/supa-kit/auth-ui-vue/compare/v0.3.5...v0.3.6) (2023-12-26) 60 | 61 | 62 | ### Bug Fixes 63 | 64 | * fixed [#10](https://github.com/supa-kit/auth-ui-vue/issues/10) ([8be7e03](https://github.com/supa-kit/auth-ui-vue/commit/8be7e03f0f68414fc4ed3df6d6a1e0ce12f4f0b3)) 65 | * fixed the build ([9ecd55c](https://github.com/supa-kit/auth-ui-vue/commit/9ecd55caf04e86870ba47596ba020b56bc67c7ec)) 66 | 67 | 68 | 69 | ## [0.3.5](https://github.com/supa-kit/auth-ui-vue/compare/v0.3.4...v0.3.5) (2023-12-04) 70 | 71 | 72 | ### Features 73 | 74 | * make the auth view reactivity ([d8b4d84](https://github.com/supa-kit/auth-ui-vue/commit/d8b4d8476a0e34db00546e3ddd2f38469b1ba367)) 75 | 76 | 77 | 78 | ## [0.3.4](https://github.com/supa-kit/auth-ui-vue/compare/v0.3.3...v0.3.4) (2023-11-27) 79 | 80 | 81 | ### Features 82 | 83 | * add providers ([8057b0f](https://github.com/supa-kit/auth-ui-vue/commit/8057b0ffaff3c9aafa7a38dedd289a50aeb94ced)) 84 | 85 | 86 | 87 | ## [0.3.3](https://github.com/supa-kit/auth-ui-vue/compare/v0.3.2...v0.3.3) (2023-08-16) 88 | 89 | 90 | ### Bug Fixes 91 | 92 | * fixed the sign in error message bug ([124b221](https://github.com/supa-kit/auth-ui-vue/commit/124b221ea9a1bd7e693c275b38349da7d1b87ceb)) 93 | 94 | 95 | ### Features 96 | 97 | * receive a new user state every time an auth event happens ([a297c62](https://github.com/supa-kit/auth-ui-vue/commit/a297c620f4120ae99052212178cda6e9cc8098d7)) 98 | 99 | 100 | 101 | ## [0.3.2](https://github.com/supa-kit/auth-ui-vue/compare/v0.3.1...v0.3.2) (2023-08-15) 102 | 103 | 104 | ### Bug Fixes 105 | 106 | * fixed the type ([36a4356](https://github.com/supa-kit/auth-ui-vue/commit/36a43562f52a41578c6883fc73fd63fd446fb27f)) 107 | 108 | 109 | 110 | ## [0.3.1](https://github.com/supa-kit/auth-ui-vue/compare/v0.3.0...v0.3.1) (2023-08-15) 111 | 112 | 113 | ### Features 114 | 115 | * export more components and helpers ([20cade3](https://github.com/supa-kit/auth-ui-vue/commit/20cade3c101bb62ceafa1c8c2442188d3d374e02)) 116 | 117 | 118 | 119 | # [0.3.0](https://github.com/supa-kit/auth-ui-vue/compare/v0.2.0...v0.3.0) (2023-08-13) 120 | 121 | 122 | ### Bug Fixes 123 | 124 | * fix loading locales ([2233a33](https://github.com/supa-kit/auth-ui-vue/commit/2233a336bb9ba9cb9cf8cb318c9c9b127941ee38)) 125 | * fixed the types bug ([0c0a3b3](https://github.com/supa-kit/auth-ui-vue/commit/0c0a3b368f571a6e99ad007c8ae08c23c5fd1132)) 126 | 127 | 128 | ### Features 129 | 130 | * add i18n support ([0d8c5b8](https://github.com/supa-kit/auth-ui-vue/commit/0d8c5b8587743e9855d5e45dc4939305352a5def)) 131 | 132 | 133 | 134 | # [0.2.0](https://github.com/supa-kit/auth-ui-vue/compare/v0.1.0...v0.2.0) (2023-08-13) 135 | 136 | 137 | ### Bug Fixes 138 | 139 | * fixed the type error ([d126850](https://github.com/supa-kit/auth-ui-vue/commit/d12685060f3d52b727be482b13284d8e1b6eb659)) 140 | 141 | 142 | ### Features 143 | 144 | * add the darkmode support ([6c46385](https://github.com/supa-kit/auth-ui-vue/commit/6c46385d5e2a5fb9b2274e4b6b7f1692e30dfefd)) 145 | * add vue dev tools ([8efeace](https://github.com/supa-kit/auth-ui-vue/commit/8efeace23d3f6e20401e1149e88d494b1c2813f6)) 146 | 147 | 148 | 149 | # [0.1.0](https://github.com/supa-kit/auth-ui-vue/compare/v0.0.2...v0.1.0) (2023-08-09) 150 | 151 | 152 | ### Features 153 | 154 | * add user context provider & use users hooks ([d164ec1](https://github.com/supa-kit/auth-ui-vue/commit/d164ec12385d5085012a82ea4facd5fd97e4f689)) 155 | 156 | 157 | 158 | ## [0.0.2](https://github.com/supa-kit/auth-ui-vue/compare/v0.0.1...v0.0.2) (2023-08-08) 159 | 160 | 161 | ### Bug Fixes 162 | 163 | * fixed the console ([971f5df](https://github.com/supa-kit/auth-ui-vue/commit/971f5df946e5d7ddd62d63eba9a1e2c497a59db8)) 164 | 165 | 166 | 167 | ## [0.0.1](https://github.com/supa-kit/auth-ui-vue/compare/11eadb71b9eb2256a5cec7c215746536ee1a0f71...v0.0.1) (2023-08-08) 168 | 169 | 170 | ### Features 171 | 172 | * improve the code ([11eadb7](https://github.com/supa-kit/auth-ui-vue/commit/11eadb71b9eb2256a5cec7c215746536ee1a0f71)) 173 | 174 | 175 | 176 | -------------------------------------------------------------------------------- /packages/auth/EmailAuth.vue: -------------------------------------------------------------------------------- 1 | 105 | 106 | 205 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 220 | 221 | 331 | 332 | 373 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Auth UI Vue 2 | 3 | [![NPM][npmBadge]][npmUrl] 4 | [![Minzip Package][bundlePhobiaBadge]][bundlePhobiaUrl] 5 | [![NPM Download][npmDtBadge]][npmDtUrl] 6 | 7 | [npmBadge]: https://img.shields.io/npm/v/@supa-kit/auth-ui-vue.svg?maxAge=2592000 8 | [npmUrl]: https://www.npmjs.com/package/@supa-kit/auth-ui-vue 9 | [npmDtBadge]: https://img.shields.io/npm/dt/@supa-kit/auth-ui-vue.svg 10 | [npmDtUrl]: https://www.npmjs.com/package/@supa-kit/auth-ui-vue 11 | [bundlePhobiaBadge]: https://img.shields.io/bundlephobia/minzip/@supa-kit/auth-ui-vue 12 | [bundlePhobiaUrl]: https://bundlephobia.com/package/@supa-kit/auth-ui-vue@latest 13 | 14 | > Pre-built auth widgets to get started in minutes. 15 | 16 | ## Preview 17 | 18 | https://user-images.githubusercontent.com/6118824/260960744-03a20835-76bc-4541-87ac-2e23277b7200.mp4 19 | 20 | ## Introduction 21 | 22 | `auth-ui-vue` is a pre-built, customizable `Vue` component for authenticating users base on `supabase` 23 | 24 | Customizable authentication UI component with custom themes and extensible styles to match your brand and aesthetic. 25 | 26 | ## Table of Contents 27 | 28 | - [Auth UI Vue](#auth-ui-vue) 29 | - [Preview](#preview) 30 | - [Introduction](#introduction) 31 | - [Table of Contents](#table-of-contents) 32 | - [Usage](#usage) 33 | - [For Vue 3](#for-vue-3) 34 | - [For Nuxt 3](#for-nuxt-3) 35 | - [Set up Auth UI](#set-up-auth-ui) 36 | - [Social Providers](#social-providers) 37 | - [Options](#options) 38 | - [Supported Views](#supported-views) 39 | - [Anonymous Sign-ins](#anonymous-sign-ins) 40 | - [Customization](#customization) 41 | - [Predefined themes](#predefined-themes) 42 | - [Switch theme variations](#switch-theme-variations) 43 | - [Override themes](#override-themes) 44 | - [Create your own theme](#create-your-own-theme) 45 | - [Custom CSS classes](#custom-css-classes) 46 | - [Custom inline CSS](#custom-inline-css) 47 | - [Custom lables (i18n)](#custom-lables-i18n) 48 | - [Auth Helpers](#auth-helpers) 49 | - [UserContextProvider](#usercontextprovider) 50 | - [useSupabaseUser](#usesupabaseuser) 51 | - [Inspiration](#inspiration) 52 | - [License](#license) 53 | 54 | ## Usage 55 | 56 | ### For Vue 3 57 | 58 | To start using the library in Vue 3, install these in your project: 59 | 60 | ```bash 61 | pnpm install @supabase/supabase-js @supabase/auth-ui-shared @supa-kit/auth-ui-vue -D 62 | or 63 | yarn add @supabase/supabase-js @supabase/auth-ui-shared @supa-kit/auth-ui-vue -D 64 | ``` 65 | 66 | ```html 67 | 68 | 79 | 80 | 92 | ``` 93 | 94 | ### For Nuxt 3 95 | 96 | To begin, install the [Supabase module for Nuxt](https://github.com/nuxt-modules/supabase). The `auth-ui-vue` integration is seamless with this module. 97 | 98 | Or, You can choose to use it in the same way as in Vue 3. 99 | 100 | Here's a example work with `@nuxtjs/supabase`: 101 | 102 | First install these in your project: 103 | 104 | ```bash 105 | pnpm install @nuxtjs/supabase @supabase/auth-ui-shared @supa-kit/auth-ui-vue -D 106 | or 107 | yarn add @nuxtjs/supabase @supabase/auth-ui-shared @supa-kit/auth-ui-vue -D 108 | ``` 109 | 110 | Add `@nuxtjs/supabase` to the modules section of `nuxt.config.ts`: 111 | 112 | ```ts 113 | // nuxt.config.ts 114 | export default defineNuxtConfig({ 115 | modules: ['@nuxtjs/supabase'] 116 | }) 117 | ``` 118 | 119 | Add `SUPABASE_URL` and `SUPABASE_KEY` to the .env: 120 | 121 | ```bash 122 | SUPABASE_URL="" 123 | SUPABASE_KEY="" 124 | ``` 125 | 126 | Now, you can access the [supabase client](https://supabase.com/docs/reference/javascript/initializing) everywhere inside your vue components. 127 | 128 | ```html 129 | 130 | 140 | 141 | 148 | ``` 149 | 150 | ## Set up Auth UI 151 | 152 | ### Social Providers 153 | 154 | The Auth component also supports login with [official social providers](https://supabase.com/docs/guides/auth#providers). 155 | 156 | ```html 157 | 158 | 169 | 170 | 182 | ``` 183 | 184 | ### Options 185 | 186 | Options are available via `queryParams`: 187 | 188 | ```html 189 |