@authon/svelte 0.2.0 → 0.2.1
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.
- package/dist/index.cjs +113 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +55 -2
- package/dist/index.d.ts +55 -2
- package/dist/index.js +111 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -22,7 +22,8 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
createAuthonStore: () => createAuthonStore,
|
|
24
24
|
getAuthon: () => getAuthon,
|
|
25
|
-
initAuthon: () => initAuthon
|
|
25
|
+
initAuthon: () => initAuthon,
|
|
26
|
+
renderSocialButtons: () => renderSocialButtons
|
|
26
27
|
});
|
|
27
28
|
module.exports = __toCommonJS(index_exports);
|
|
28
29
|
|
|
@@ -80,10 +81,120 @@ function getAuthon() {
|
|
|
80
81
|
}
|
|
81
82
|
return store;
|
|
82
83
|
}
|
|
84
|
+
|
|
85
|
+
// src/SocialButtons.ts
|
|
86
|
+
var import_shared = require("@authon/shared");
|
|
87
|
+
var import_js2 = require("@authon/js");
|
|
88
|
+
function renderSocialButtons(options) {
|
|
89
|
+
const {
|
|
90
|
+
client,
|
|
91
|
+
container,
|
|
92
|
+
onSuccess,
|
|
93
|
+
onError,
|
|
94
|
+
compact = false,
|
|
95
|
+
gap,
|
|
96
|
+
labels,
|
|
97
|
+
iconSize,
|
|
98
|
+
borderRadius = 10,
|
|
99
|
+
height = 48,
|
|
100
|
+
size = 48
|
|
101
|
+
} = options;
|
|
102
|
+
const resolvedGap = gap ?? (compact ? 12 : 10);
|
|
103
|
+
const resolvedIconSize = iconSize ?? (compact ? 24 : 20);
|
|
104
|
+
let loadingProvider = null;
|
|
105
|
+
let buttons = [];
|
|
106
|
+
const handleClick = async (provider, btn) => {
|
|
107
|
+
if (loadingProvider) return;
|
|
108
|
+
loadingProvider = provider;
|
|
109
|
+
btn.innerHTML = '<span style="display:inline-block;width:16px;height:16px;border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:authon-spin 0.6s linear infinite"></span>';
|
|
110
|
+
buttons.forEach((b) => b.disabled = true);
|
|
111
|
+
try {
|
|
112
|
+
await client.signInWithOAuth(provider);
|
|
113
|
+
onSuccess?.();
|
|
114
|
+
} catch (e) {
|
|
115
|
+
const error = e instanceof Error ? e : new Error(String(e));
|
|
116
|
+
onError?.(error);
|
|
117
|
+
} finally {
|
|
118
|
+
loadingProvider = null;
|
|
119
|
+
renderButtons(providers);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
let providers = [];
|
|
123
|
+
function renderButtons(providerList) {
|
|
124
|
+
container.innerHTML = "";
|
|
125
|
+
buttons = [];
|
|
126
|
+
if (!document.getElementById("authon-spin-style")) {
|
|
127
|
+
const style = document.createElement("style");
|
|
128
|
+
style.id = "authon-spin-style";
|
|
129
|
+
style.textContent = "@keyframes authon-spin{to{transform:rotate(360deg)}}";
|
|
130
|
+
document.head.appendChild(style);
|
|
131
|
+
}
|
|
132
|
+
const wrapper = document.createElement("div");
|
|
133
|
+
wrapper.style.display = "flex";
|
|
134
|
+
wrapper.style.gap = `${resolvedGap}px`;
|
|
135
|
+
if (compact) {
|
|
136
|
+
wrapper.style.flexDirection = "row";
|
|
137
|
+
wrapper.style.flexWrap = "wrap";
|
|
138
|
+
wrapper.style.justifyContent = "center";
|
|
139
|
+
} else {
|
|
140
|
+
wrapper.style.flexDirection = "column";
|
|
141
|
+
}
|
|
142
|
+
for (const provider of providerList) {
|
|
143
|
+
const colors = import_shared.PROVIDER_COLORS[provider] || { bg: "#333", text: "#fff" };
|
|
144
|
+
const displayName = import_shared.PROVIDER_DISPLAY_NAMES[provider] || provider;
|
|
145
|
+
const config = (0, import_js2.getProviderButtonConfig)(provider);
|
|
146
|
+
const iconSvg = config.iconSvg.replace(/width="\d+"/, `width="${resolvedIconSize}"`).replace(/height="\d+"/, `height="${resolvedIconSize}"`);
|
|
147
|
+
const needsBorder = colors.bg.toLowerCase() === "#ffffff";
|
|
148
|
+
const btn = document.createElement("button");
|
|
149
|
+
btn.setAttribute("aria-label", `Sign in with ${displayName}`);
|
|
150
|
+
btn.style.display = "flex";
|
|
151
|
+
btn.style.alignItems = "center";
|
|
152
|
+
btn.style.justifyContent = "center";
|
|
153
|
+
btn.style.border = needsBorder ? "1px solid #dadce0" : "none";
|
|
154
|
+
btn.style.cursor = "pointer";
|
|
155
|
+
btn.style.backgroundColor = colors.bg;
|
|
156
|
+
btn.style.color = colors.text;
|
|
157
|
+
btn.style.borderRadius = `${borderRadius}px`;
|
|
158
|
+
btn.style.transition = "opacity 0.15s";
|
|
159
|
+
btn.style.fontFamily = "inherit";
|
|
160
|
+
if (compact) {
|
|
161
|
+
btn.style.width = `${size}px`;
|
|
162
|
+
btn.style.height = `${size}px`;
|
|
163
|
+
btn.style.padding = "0";
|
|
164
|
+
btn.innerHTML = `<span style="display:flex;align-items:center">${iconSvg}</span>`;
|
|
165
|
+
} else {
|
|
166
|
+
btn.style.width = "100%";
|
|
167
|
+
btn.style.height = `${height}px`;
|
|
168
|
+
btn.style.gap = "10px";
|
|
169
|
+
btn.style.paddingLeft = "16px";
|
|
170
|
+
btn.style.paddingRight = "16px";
|
|
171
|
+
const buttonLabel = labels?.[provider] ?? `Continue with ${displayName}`;
|
|
172
|
+
btn.innerHTML = `<span style="display:flex;align-items:center;flex-shrink:0">${iconSvg}</span><span style="font-size:15px;font-weight:600;white-space:nowrap">${buttonLabel}</span>`;
|
|
173
|
+
}
|
|
174
|
+
btn.addEventListener("click", () => handleClick(provider, btn));
|
|
175
|
+
btn.addEventListener("mouseenter", () => btn.style.opacity = "0.85");
|
|
176
|
+
btn.addEventListener("mouseleave", () => btn.style.opacity = "1");
|
|
177
|
+
buttons.push(btn);
|
|
178
|
+
wrapper.appendChild(btn);
|
|
179
|
+
}
|
|
180
|
+
container.appendChild(wrapper);
|
|
181
|
+
}
|
|
182
|
+
client.getProviders().then((p) => {
|
|
183
|
+
providers = p;
|
|
184
|
+
if (providers.length > 0) {
|
|
185
|
+
renderButtons(providers);
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
return () => {
|
|
189
|
+
container.innerHTML = "";
|
|
190
|
+
buttons = [];
|
|
191
|
+
};
|
|
192
|
+
}
|
|
83
193
|
// Annotate the CommonJS export names for ESM import in node:
|
|
84
194
|
0 && (module.exports = {
|
|
85
195
|
createAuthonStore,
|
|
86
196
|
getAuthon,
|
|
87
|
-
initAuthon
|
|
197
|
+
initAuthon,
|
|
198
|
+
renderSocialButtons
|
|
88
199
|
});
|
|
89
200
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/store.ts","../src/context.ts"],"sourcesContent":["export { createAuthonStore } from './store';\nexport type { AuthonStore } from './store';\nexport { initAuthon, getAuthon } from './context';\n","import { writable, derived, type Readable } from 'svelte/store';\nimport { Authon } from '@authon/js';\nimport type { AuthonConfig } from '@authon/js';\nimport type { AuthonUser } from '@authon/shared';\n\nexport interface AuthonStore {\n user: Readable<AuthonUser | null>;\n isSignedIn: Readable<boolean>;\n isLoading: Readable<boolean>;\n signOut: () => Promise<void>;\n openSignIn: () => Promise<void>;\n openSignUp: () => Promise<void>;\n getToken: () => string | null;\n destroy: () => void;\n client: Authon;\n}\n\n/**\n * Creates an Authon store with reactive Svelte stores.\n *\n * Usage:\n * ```ts\n * import { createAuthonStore } from '@authon/svelte'\n *\n * const authon = createAuthonStore('pk_live_...')\n *\n * // In your component:\n * $: user = $authon.user\n * $: isSignedIn = $authon.isSignedIn\n * ```\n */\nexport function createAuthonStore(\n publishableKey: string,\n config?: Omit<AuthonConfig, 'mode'>,\n): AuthonStore {\n const client = new Authon(publishableKey, config);\n const userStore = writable<AuthonUser | null>(null);\n const isLoadingStore = writable(true);\n\n const isSignedIn = derived(userStore, ($user) => $user !== null);\n\n client.on('signedIn', (user) => {\n userStore.set(user as AuthonUser);\n isLoadingStore.set(false);\n });\n\n client.on('signedOut', () => {\n userStore.set(null);\n });\n\n client.on('error', () => {\n isLoadingStore.set(false);\n });\n\n const existingUser = client.getUser();\n if (existingUser) {\n userStore.set(existingUser);\n }\n isLoadingStore.set(false);\n\n return {\n user: { subscribe: userStore.subscribe },\n isSignedIn,\n isLoading: { subscribe: isLoadingStore.subscribe },\n signOut: async () => {\n await client.signOut();\n userStore.set(null);\n },\n openSignIn: () => client.openSignIn(),\n openSignUp: () => client.openSignUp(),\n getToken: () => client.getToken(),\n destroy: () => client.destroy(),\n client,\n };\n}\n","import { setContext, getContext } from 'svelte';\nimport type { AuthonConfig } from '@authon/js';\nimport { createAuthonStore, type AuthonStore } from './store';\n\nconst AUTHON_CONTEXT_KEY = Symbol('authon');\n\n/**\n * Initialize Authon in a Svelte component tree.\n * Call this in your root layout or top-level component.\n *\n * Usage in +layout.svelte:\n * ```svelte\n * <script>\n * import { initAuthon } from '@authon/svelte'\n * const authon = initAuthon('pk_live_...')\n * </script>\n * ```\n */\nexport function initAuthon(\n publishableKey: string,\n config?: Omit<AuthonConfig, 'mode'>,\n): AuthonStore {\n const store = createAuthonStore(publishableKey, config);\n setContext(AUTHON_CONTEXT_KEY, store);\n return store;\n}\n\n/**\n * Get the Authon store from Svelte context.\n * Must be called within a component tree where `initAuthon` was called.\n *\n * Usage:\n * ```svelte\n * <script>\n * import { getAuthon } from '@authon/svelte'\n * const { user, isSignedIn, signOut } = getAuthon()\n * </script>\n * ```\n */\nexport function getAuthon(): AuthonStore {\n const store = getContext<AuthonStore | undefined>(AUTHON_CONTEXT_KEY);\n if (!store) {\n throw new Error('getAuthon() must be called within a component tree where initAuthon() was called.');\n }\n return store;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAAiD;AACjD,gBAAuB;AA8BhB,SAAS,kBACd,gBACA,QACa;AACb,QAAM,SAAS,IAAI,iBAAO,gBAAgB,MAAM;AAChD,QAAM,gBAAY,uBAA4B,IAAI;AAClD,QAAM,qBAAiB,uBAAS,IAAI;AAEpC,QAAM,iBAAa,sBAAQ,WAAW,CAAC,UAAU,UAAU,IAAI;AAE/D,SAAO,GAAG,YAAY,CAAC,SAAS;AAC9B,cAAU,IAAI,IAAkB;AAChC,mBAAe,IAAI,KAAK;AAAA,EAC1B,CAAC;AAED,SAAO,GAAG,aAAa,MAAM;AAC3B,cAAU,IAAI,IAAI;AAAA,EACpB,CAAC;AAED,SAAO,GAAG,SAAS,MAAM;AACvB,mBAAe,IAAI,KAAK;AAAA,EAC1B,CAAC;AAED,QAAM,eAAe,OAAO,QAAQ;AACpC,MAAI,cAAc;AAChB,cAAU,IAAI,YAAY;AAAA,EAC5B;AACA,iBAAe,IAAI,KAAK;AAExB,SAAO;AAAA,IACL,MAAM,EAAE,WAAW,UAAU,UAAU;AAAA,IACvC;AAAA,IACA,WAAW,EAAE,WAAW,eAAe,UAAU;AAAA,IACjD,SAAS,YAAY;AACnB,YAAM,OAAO,QAAQ;AACrB,gBAAU,IAAI,IAAI;AAAA,IACpB;AAAA,IACA,YAAY,MAAM,OAAO,WAAW;AAAA,IACpC,YAAY,MAAM,OAAO,WAAW;AAAA,IACpC,UAAU,MAAM,OAAO,SAAS;AAAA,IAChC,SAAS,MAAM,OAAO,QAAQ;AAAA,IAC9B;AAAA,EACF;AACF;;;AC1EA,oBAAuC;AAIvC,IAAM,qBAAqB,uBAAO,QAAQ;AAcnC,SAAS,WACd,gBACA,QACa;AACb,QAAM,QAAQ,kBAAkB,gBAAgB,MAAM;AACtD,gCAAW,oBAAoB,KAAK;AACpC,SAAO;AACT;AAcO,SAAS,YAAyB;AACvC,QAAM,YAAQ,0BAAoC,kBAAkB;AACpE,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,mFAAmF;AAAA,EACrG;AACA,SAAO;AACT;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/store.ts","../src/context.ts","../src/SocialButtons.ts"],"sourcesContent":["export { createAuthonStore } from './store';\nexport type { AuthonStore } from './store';\nexport { initAuthon, getAuthon } from './context';\nexport { renderSocialButtons } from './SocialButtons';\nexport type { SocialButtonsOptions } from './SocialButtons';\n","import { writable, derived, type Readable } from 'svelte/store';\nimport { Authon } from '@authon/js';\nimport type { AuthonConfig } from '@authon/js';\nimport type { AuthonUser } from '@authon/shared';\n\nexport interface AuthonStore {\n user: Readable<AuthonUser | null>;\n isSignedIn: Readable<boolean>;\n isLoading: Readable<boolean>;\n signOut: () => Promise<void>;\n openSignIn: () => Promise<void>;\n openSignUp: () => Promise<void>;\n getToken: () => string | null;\n destroy: () => void;\n client: Authon;\n}\n\n/**\n * Creates an Authon store with reactive Svelte stores.\n *\n * Usage:\n * ```ts\n * import { createAuthonStore } from '@authon/svelte'\n *\n * const authon = createAuthonStore('pk_live_...')\n *\n * // In your component:\n * $: user = $authon.user\n * $: isSignedIn = $authon.isSignedIn\n * ```\n */\nexport function createAuthonStore(\n publishableKey: string,\n config?: Omit<AuthonConfig, 'mode'>,\n): AuthonStore {\n const client = new Authon(publishableKey, config);\n const userStore = writable<AuthonUser | null>(null);\n const isLoadingStore = writable(true);\n\n const isSignedIn = derived(userStore, ($user) => $user !== null);\n\n client.on('signedIn', (user) => {\n userStore.set(user as AuthonUser);\n isLoadingStore.set(false);\n });\n\n client.on('signedOut', () => {\n userStore.set(null);\n });\n\n client.on('error', () => {\n isLoadingStore.set(false);\n });\n\n const existingUser = client.getUser();\n if (existingUser) {\n userStore.set(existingUser);\n }\n isLoadingStore.set(false);\n\n return {\n user: { subscribe: userStore.subscribe },\n isSignedIn,\n isLoading: { subscribe: isLoadingStore.subscribe },\n signOut: async () => {\n await client.signOut();\n userStore.set(null);\n },\n openSignIn: () => client.openSignIn(),\n openSignUp: () => client.openSignUp(),\n getToken: () => client.getToken(),\n destroy: () => client.destroy(),\n client,\n };\n}\n","import { setContext, getContext } from 'svelte';\nimport type { AuthonConfig } from '@authon/js';\nimport { createAuthonStore, type AuthonStore } from './store';\n\nconst AUTHON_CONTEXT_KEY = Symbol('authon');\n\n/**\n * Initialize Authon in a Svelte component tree.\n * Call this in your root layout or top-level component.\n *\n * Usage in +layout.svelte:\n * ```svelte\n * <script>\n * import { initAuthon } from '@authon/svelte'\n * const authon = initAuthon('pk_live_...')\n * </script>\n * ```\n */\nexport function initAuthon(\n publishableKey: string,\n config?: Omit<AuthonConfig, 'mode'>,\n): AuthonStore {\n const store = createAuthonStore(publishableKey, config);\n setContext(AUTHON_CONTEXT_KEY, store);\n return store;\n}\n\n/**\n * Get the Authon store from Svelte context.\n * Must be called within a component tree where `initAuthon` was called.\n *\n * Usage:\n * ```svelte\n * <script>\n * import { getAuthon } from '@authon/svelte'\n * const { user, isSignedIn, signOut } = getAuthon()\n * </script>\n * ```\n */\nexport function getAuthon(): AuthonStore {\n const store = getContext<AuthonStore | undefined>(AUTHON_CONTEXT_KEY);\n if (!store) {\n throw new Error('getAuthon() must be called within a component tree where initAuthon() was called.');\n }\n return store;\n}\n","import { PROVIDER_COLORS, PROVIDER_DISPLAY_NAMES, type OAuthProviderType } from '@authon/shared';\nimport { getProviderButtonConfig, type Authon } from '@authon/js';\n\nexport interface SocialButtonsOptions {\n /** Authon client instance */\n client: Authon;\n /** Target container element */\n container: HTMLElement;\n /** Called after successful OAuth sign-in */\n onSuccess?: () => void;\n /** Called on OAuth error */\n onError?: (error: Error) => void;\n /** Compact mode — icon-only square buttons in a row (default: false) */\n compact?: boolean;\n /** Gap between buttons in px (default: 10, compact default: 12) */\n gap?: number;\n /** Custom labels per provider */\n labels?: Partial<Record<OAuthProviderType, string>>;\n /** Icon size (default: 20, compact default: 24) */\n iconSize?: number;\n /** Border radius in px (default: 10) */\n borderRadius?: number;\n /** Button height in px (default: 48) */\n height?: number;\n /** Button size for compact mode in px (default: 48) */\n size?: number;\n}\n\n/**\n * Render social login buttons into a container element.\n *\n * Usage in +page.svelte:\n * ```svelte\n * <script>\n * import { getAuthon } from '@authon/svelte'\n * import { renderSocialButtons } from '@authon/svelte'\n * import { onMount } from 'svelte'\n *\n * const { client } = getAuthon()\n * let container: HTMLElement\n *\n * onMount(() => {\n * const cleanup = renderSocialButtons({\n * client,\n * container,\n * compact: true,\n * onError: (err) => console.error(err),\n * })\n * return cleanup\n * })\n * </script>\n *\n * <div bind:this={container}></div>\n * ```\n */\nexport function renderSocialButtons(options: SocialButtonsOptions): () => void {\n const {\n client,\n container,\n onSuccess,\n onError,\n compact = false,\n gap,\n labels,\n iconSize,\n borderRadius = 10,\n height = 48,\n size = 48,\n } = options;\n\n const resolvedGap = gap ?? (compact ? 12 : 10);\n const resolvedIconSize = iconSize ?? (compact ? 24 : 20);\n let loadingProvider: string | null = null;\n let buttons: HTMLButtonElement[] = [];\n\n const handleClick = async (provider: OAuthProviderType, btn: HTMLButtonElement) => {\n if (loadingProvider) return;\n loadingProvider = provider;\n btn.innerHTML = '<span style=\"display:inline-block;width:16px;height:16px;border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:authon-spin 0.6s linear infinite\"></span>';\n buttons.forEach((b) => (b.disabled = true));\n\n try {\n await client.signInWithOAuth(provider);\n onSuccess?.();\n } catch (e: any) {\n const error = e instanceof Error ? e : new Error(String(e));\n onError?.(error);\n } finally {\n loadingProvider = null;\n renderButtons(providers);\n }\n };\n\n let providers: OAuthProviderType[] = [];\n\n function renderButtons(providerList: OAuthProviderType[]) {\n container.innerHTML = '';\n buttons = [];\n\n // Inject keyframe if not present\n if (!document.getElementById('authon-spin-style')) {\n const style = document.createElement('style');\n style.id = 'authon-spin-style';\n style.textContent = '@keyframes authon-spin{to{transform:rotate(360deg)}}';\n document.head.appendChild(style);\n }\n\n const wrapper = document.createElement('div');\n wrapper.style.display = 'flex';\n wrapper.style.gap = `${resolvedGap}px`;\n\n if (compact) {\n wrapper.style.flexDirection = 'row';\n wrapper.style.flexWrap = 'wrap';\n wrapper.style.justifyContent = 'center';\n } else {\n wrapper.style.flexDirection = 'column';\n }\n\n for (const provider of providerList) {\n const colors = PROVIDER_COLORS[provider] || { bg: '#333', text: '#fff' };\n const displayName = PROVIDER_DISPLAY_NAMES[provider] || provider;\n const config = getProviderButtonConfig(provider);\n const iconSvg = config.iconSvg\n .replace(/width=\"\\d+\"/, `width=\"${resolvedIconSize}\"`)\n .replace(/height=\"\\d+\"/, `height=\"${resolvedIconSize}\"`);\n const needsBorder = colors.bg.toLowerCase() === '#ffffff';\n\n const btn = document.createElement('button');\n btn.setAttribute('aria-label', `Sign in with ${displayName}`);\n btn.style.display = 'flex';\n btn.style.alignItems = 'center';\n btn.style.justifyContent = 'center';\n btn.style.border = needsBorder ? '1px solid #dadce0' : 'none';\n btn.style.cursor = 'pointer';\n btn.style.backgroundColor = colors.bg;\n btn.style.color = colors.text;\n btn.style.borderRadius = `${borderRadius}px`;\n btn.style.transition = 'opacity 0.15s';\n btn.style.fontFamily = 'inherit';\n\n if (compact) {\n btn.style.width = `${size}px`;\n btn.style.height = `${size}px`;\n btn.style.padding = '0';\n btn.innerHTML = `<span style=\"display:flex;align-items:center\">${iconSvg}</span>`;\n } else {\n btn.style.width = '100%';\n btn.style.height = `${height}px`;\n btn.style.gap = '10px';\n btn.style.paddingLeft = '16px';\n btn.style.paddingRight = '16px';\n const buttonLabel = labels?.[provider] ?? `Continue with ${displayName}`;\n btn.innerHTML = `<span style=\"display:flex;align-items:center;flex-shrink:0\">${iconSvg}</span><span style=\"font-size:15px;font-weight:600;white-space:nowrap\">${buttonLabel}</span>`;\n }\n\n btn.addEventListener('click', () => handleClick(provider, btn));\n btn.addEventListener('mouseenter', () => (btn.style.opacity = '0.85'));\n btn.addEventListener('mouseleave', () => (btn.style.opacity = '1'));\n\n buttons.push(btn);\n wrapper.appendChild(btn);\n }\n\n container.appendChild(wrapper);\n }\n\n // Init\n client.getProviders().then((p: OAuthProviderType[]) => {\n providers = p;\n if (providers.length > 0) {\n renderButtons(providers);\n }\n });\n\n // Cleanup\n return () => {\n container.innerHTML = '';\n buttons = [];\n };\n}\n"],"mappings":"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","names":["import_js"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Readable } from 'svelte/store';
|
|
2
2
|
import { Authon, AuthonConfig } from '@authon/js';
|
|
3
|
-
import { AuthonUser } from '@authon/shared';
|
|
3
|
+
import { AuthonUser, OAuthProviderType } from '@authon/shared';
|
|
4
4
|
|
|
5
5
|
interface AuthonStore {
|
|
6
6
|
user: Readable<AuthonUser | null>;
|
|
@@ -56,4 +56,57 @@ declare function initAuthon(publishableKey: string, config?: Omit<AuthonConfig,
|
|
|
56
56
|
*/
|
|
57
57
|
declare function getAuthon(): AuthonStore;
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
interface SocialButtonsOptions {
|
|
60
|
+
/** Authon client instance */
|
|
61
|
+
client: Authon;
|
|
62
|
+
/** Target container element */
|
|
63
|
+
container: HTMLElement;
|
|
64
|
+
/** Called after successful OAuth sign-in */
|
|
65
|
+
onSuccess?: () => void;
|
|
66
|
+
/** Called on OAuth error */
|
|
67
|
+
onError?: (error: Error) => void;
|
|
68
|
+
/** Compact mode — icon-only square buttons in a row (default: false) */
|
|
69
|
+
compact?: boolean;
|
|
70
|
+
/** Gap between buttons in px (default: 10, compact default: 12) */
|
|
71
|
+
gap?: number;
|
|
72
|
+
/** Custom labels per provider */
|
|
73
|
+
labels?: Partial<Record<OAuthProviderType, string>>;
|
|
74
|
+
/** Icon size (default: 20, compact default: 24) */
|
|
75
|
+
iconSize?: number;
|
|
76
|
+
/** Border radius in px (default: 10) */
|
|
77
|
+
borderRadius?: number;
|
|
78
|
+
/** Button height in px (default: 48) */
|
|
79
|
+
height?: number;
|
|
80
|
+
/** Button size for compact mode in px (default: 48) */
|
|
81
|
+
size?: number;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Render social login buttons into a container element.
|
|
85
|
+
*
|
|
86
|
+
* Usage in +page.svelte:
|
|
87
|
+
* ```svelte
|
|
88
|
+
* <script>
|
|
89
|
+
* import { getAuthon } from '@authon/svelte'
|
|
90
|
+
* import { renderSocialButtons } from '@authon/svelte'
|
|
91
|
+
* import { onMount } from 'svelte'
|
|
92
|
+
*
|
|
93
|
+
* const { client } = getAuthon()
|
|
94
|
+
* let container: HTMLElement
|
|
95
|
+
*
|
|
96
|
+
* onMount(() => {
|
|
97
|
+
* const cleanup = renderSocialButtons({
|
|
98
|
+
* client,
|
|
99
|
+
* container,
|
|
100
|
+
* compact: true,
|
|
101
|
+
* onError: (err) => console.error(err),
|
|
102
|
+
* })
|
|
103
|
+
* return cleanup
|
|
104
|
+
* })
|
|
105
|
+
* </script>
|
|
106
|
+
*
|
|
107
|
+
* <div bind:this={container}></div>
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
declare function renderSocialButtons(options: SocialButtonsOptions): () => void;
|
|
111
|
+
|
|
112
|
+
export { type AuthonStore, type SocialButtonsOptions, createAuthonStore, getAuthon, initAuthon, renderSocialButtons };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Readable } from 'svelte/store';
|
|
2
2
|
import { Authon, AuthonConfig } from '@authon/js';
|
|
3
|
-
import { AuthonUser } from '@authon/shared';
|
|
3
|
+
import { AuthonUser, OAuthProviderType } from '@authon/shared';
|
|
4
4
|
|
|
5
5
|
interface AuthonStore {
|
|
6
6
|
user: Readable<AuthonUser | null>;
|
|
@@ -56,4 +56,57 @@ declare function initAuthon(publishableKey: string, config?: Omit<AuthonConfig,
|
|
|
56
56
|
*/
|
|
57
57
|
declare function getAuthon(): AuthonStore;
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
interface SocialButtonsOptions {
|
|
60
|
+
/** Authon client instance */
|
|
61
|
+
client: Authon;
|
|
62
|
+
/** Target container element */
|
|
63
|
+
container: HTMLElement;
|
|
64
|
+
/** Called after successful OAuth sign-in */
|
|
65
|
+
onSuccess?: () => void;
|
|
66
|
+
/** Called on OAuth error */
|
|
67
|
+
onError?: (error: Error) => void;
|
|
68
|
+
/** Compact mode — icon-only square buttons in a row (default: false) */
|
|
69
|
+
compact?: boolean;
|
|
70
|
+
/** Gap between buttons in px (default: 10, compact default: 12) */
|
|
71
|
+
gap?: number;
|
|
72
|
+
/** Custom labels per provider */
|
|
73
|
+
labels?: Partial<Record<OAuthProviderType, string>>;
|
|
74
|
+
/** Icon size (default: 20, compact default: 24) */
|
|
75
|
+
iconSize?: number;
|
|
76
|
+
/** Border radius in px (default: 10) */
|
|
77
|
+
borderRadius?: number;
|
|
78
|
+
/** Button height in px (default: 48) */
|
|
79
|
+
height?: number;
|
|
80
|
+
/** Button size for compact mode in px (default: 48) */
|
|
81
|
+
size?: number;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Render social login buttons into a container element.
|
|
85
|
+
*
|
|
86
|
+
* Usage in +page.svelte:
|
|
87
|
+
* ```svelte
|
|
88
|
+
* <script>
|
|
89
|
+
* import { getAuthon } from '@authon/svelte'
|
|
90
|
+
* import { renderSocialButtons } from '@authon/svelte'
|
|
91
|
+
* import { onMount } from 'svelte'
|
|
92
|
+
*
|
|
93
|
+
* const { client } = getAuthon()
|
|
94
|
+
* let container: HTMLElement
|
|
95
|
+
*
|
|
96
|
+
* onMount(() => {
|
|
97
|
+
* const cleanup = renderSocialButtons({
|
|
98
|
+
* client,
|
|
99
|
+
* container,
|
|
100
|
+
* compact: true,
|
|
101
|
+
* onError: (err) => console.error(err),
|
|
102
|
+
* })
|
|
103
|
+
* return cleanup
|
|
104
|
+
* })
|
|
105
|
+
* </script>
|
|
106
|
+
*
|
|
107
|
+
* <div bind:this={container}></div>
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
declare function renderSocialButtons(options: SocialButtonsOptions): () => void;
|
|
111
|
+
|
|
112
|
+
export { type AuthonStore, type SocialButtonsOptions, createAuthonStore, getAuthon, initAuthon, renderSocialButtons };
|
package/dist/index.js
CHANGED
|
@@ -52,9 +52,119 @@ function getAuthon() {
|
|
|
52
52
|
}
|
|
53
53
|
return store;
|
|
54
54
|
}
|
|
55
|
+
|
|
56
|
+
// src/SocialButtons.ts
|
|
57
|
+
import { PROVIDER_COLORS, PROVIDER_DISPLAY_NAMES } from "@authon/shared";
|
|
58
|
+
import { getProviderButtonConfig } from "@authon/js";
|
|
59
|
+
function renderSocialButtons(options) {
|
|
60
|
+
const {
|
|
61
|
+
client,
|
|
62
|
+
container,
|
|
63
|
+
onSuccess,
|
|
64
|
+
onError,
|
|
65
|
+
compact = false,
|
|
66
|
+
gap,
|
|
67
|
+
labels,
|
|
68
|
+
iconSize,
|
|
69
|
+
borderRadius = 10,
|
|
70
|
+
height = 48,
|
|
71
|
+
size = 48
|
|
72
|
+
} = options;
|
|
73
|
+
const resolvedGap = gap ?? (compact ? 12 : 10);
|
|
74
|
+
const resolvedIconSize = iconSize ?? (compact ? 24 : 20);
|
|
75
|
+
let loadingProvider = null;
|
|
76
|
+
let buttons = [];
|
|
77
|
+
const handleClick = async (provider, btn) => {
|
|
78
|
+
if (loadingProvider) return;
|
|
79
|
+
loadingProvider = provider;
|
|
80
|
+
btn.innerHTML = '<span style="display:inline-block;width:16px;height:16px;border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:authon-spin 0.6s linear infinite"></span>';
|
|
81
|
+
buttons.forEach((b) => b.disabled = true);
|
|
82
|
+
try {
|
|
83
|
+
await client.signInWithOAuth(provider);
|
|
84
|
+
onSuccess?.();
|
|
85
|
+
} catch (e) {
|
|
86
|
+
const error = e instanceof Error ? e : new Error(String(e));
|
|
87
|
+
onError?.(error);
|
|
88
|
+
} finally {
|
|
89
|
+
loadingProvider = null;
|
|
90
|
+
renderButtons(providers);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
let providers = [];
|
|
94
|
+
function renderButtons(providerList) {
|
|
95
|
+
container.innerHTML = "";
|
|
96
|
+
buttons = [];
|
|
97
|
+
if (!document.getElementById("authon-spin-style")) {
|
|
98
|
+
const style = document.createElement("style");
|
|
99
|
+
style.id = "authon-spin-style";
|
|
100
|
+
style.textContent = "@keyframes authon-spin{to{transform:rotate(360deg)}}";
|
|
101
|
+
document.head.appendChild(style);
|
|
102
|
+
}
|
|
103
|
+
const wrapper = document.createElement("div");
|
|
104
|
+
wrapper.style.display = "flex";
|
|
105
|
+
wrapper.style.gap = `${resolvedGap}px`;
|
|
106
|
+
if (compact) {
|
|
107
|
+
wrapper.style.flexDirection = "row";
|
|
108
|
+
wrapper.style.flexWrap = "wrap";
|
|
109
|
+
wrapper.style.justifyContent = "center";
|
|
110
|
+
} else {
|
|
111
|
+
wrapper.style.flexDirection = "column";
|
|
112
|
+
}
|
|
113
|
+
for (const provider of providerList) {
|
|
114
|
+
const colors = PROVIDER_COLORS[provider] || { bg: "#333", text: "#fff" };
|
|
115
|
+
const displayName = PROVIDER_DISPLAY_NAMES[provider] || provider;
|
|
116
|
+
const config = getProviderButtonConfig(provider);
|
|
117
|
+
const iconSvg = config.iconSvg.replace(/width="\d+"/, `width="${resolvedIconSize}"`).replace(/height="\d+"/, `height="${resolvedIconSize}"`);
|
|
118
|
+
const needsBorder = colors.bg.toLowerCase() === "#ffffff";
|
|
119
|
+
const btn = document.createElement("button");
|
|
120
|
+
btn.setAttribute("aria-label", `Sign in with ${displayName}`);
|
|
121
|
+
btn.style.display = "flex";
|
|
122
|
+
btn.style.alignItems = "center";
|
|
123
|
+
btn.style.justifyContent = "center";
|
|
124
|
+
btn.style.border = needsBorder ? "1px solid #dadce0" : "none";
|
|
125
|
+
btn.style.cursor = "pointer";
|
|
126
|
+
btn.style.backgroundColor = colors.bg;
|
|
127
|
+
btn.style.color = colors.text;
|
|
128
|
+
btn.style.borderRadius = `${borderRadius}px`;
|
|
129
|
+
btn.style.transition = "opacity 0.15s";
|
|
130
|
+
btn.style.fontFamily = "inherit";
|
|
131
|
+
if (compact) {
|
|
132
|
+
btn.style.width = `${size}px`;
|
|
133
|
+
btn.style.height = `${size}px`;
|
|
134
|
+
btn.style.padding = "0";
|
|
135
|
+
btn.innerHTML = `<span style="display:flex;align-items:center">${iconSvg}</span>`;
|
|
136
|
+
} else {
|
|
137
|
+
btn.style.width = "100%";
|
|
138
|
+
btn.style.height = `${height}px`;
|
|
139
|
+
btn.style.gap = "10px";
|
|
140
|
+
btn.style.paddingLeft = "16px";
|
|
141
|
+
btn.style.paddingRight = "16px";
|
|
142
|
+
const buttonLabel = labels?.[provider] ?? `Continue with ${displayName}`;
|
|
143
|
+
btn.innerHTML = `<span style="display:flex;align-items:center;flex-shrink:0">${iconSvg}</span><span style="font-size:15px;font-weight:600;white-space:nowrap">${buttonLabel}</span>`;
|
|
144
|
+
}
|
|
145
|
+
btn.addEventListener("click", () => handleClick(provider, btn));
|
|
146
|
+
btn.addEventListener("mouseenter", () => btn.style.opacity = "0.85");
|
|
147
|
+
btn.addEventListener("mouseleave", () => btn.style.opacity = "1");
|
|
148
|
+
buttons.push(btn);
|
|
149
|
+
wrapper.appendChild(btn);
|
|
150
|
+
}
|
|
151
|
+
container.appendChild(wrapper);
|
|
152
|
+
}
|
|
153
|
+
client.getProviders().then((p) => {
|
|
154
|
+
providers = p;
|
|
155
|
+
if (providers.length > 0) {
|
|
156
|
+
renderButtons(providers);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
return () => {
|
|
160
|
+
container.innerHTML = "";
|
|
161
|
+
buttons = [];
|
|
162
|
+
};
|
|
163
|
+
}
|
|
55
164
|
export {
|
|
56
165
|
createAuthonStore,
|
|
57
166
|
getAuthon,
|
|
58
|
-
initAuthon
|
|
167
|
+
initAuthon,
|
|
168
|
+
renderSocialButtons
|
|
59
169
|
};
|
|
60
170
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/store.ts","../src/context.ts"],"sourcesContent":["import { writable, derived, type Readable } from 'svelte/store';\nimport { Authon } from '@authon/js';\nimport type { AuthonConfig } from '@authon/js';\nimport type { AuthonUser } from '@authon/shared';\n\nexport interface AuthonStore {\n user: Readable<AuthonUser | null>;\n isSignedIn: Readable<boolean>;\n isLoading: Readable<boolean>;\n signOut: () => Promise<void>;\n openSignIn: () => Promise<void>;\n openSignUp: () => Promise<void>;\n getToken: () => string | null;\n destroy: () => void;\n client: Authon;\n}\n\n/**\n * Creates an Authon store with reactive Svelte stores.\n *\n * Usage:\n * ```ts\n * import { createAuthonStore } from '@authon/svelte'\n *\n * const authon = createAuthonStore('pk_live_...')\n *\n * // In your component:\n * $: user = $authon.user\n * $: isSignedIn = $authon.isSignedIn\n * ```\n */\nexport function createAuthonStore(\n publishableKey: string,\n config?: Omit<AuthonConfig, 'mode'>,\n): AuthonStore {\n const client = new Authon(publishableKey, config);\n const userStore = writable<AuthonUser | null>(null);\n const isLoadingStore = writable(true);\n\n const isSignedIn = derived(userStore, ($user) => $user !== null);\n\n client.on('signedIn', (user) => {\n userStore.set(user as AuthonUser);\n isLoadingStore.set(false);\n });\n\n client.on('signedOut', () => {\n userStore.set(null);\n });\n\n client.on('error', () => {\n isLoadingStore.set(false);\n });\n\n const existingUser = client.getUser();\n if (existingUser) {\n userStore.set(existingUser);\n }\n isLoadingStore.set(false);\n\n return {\n user: { subscribe: userStore.subscribe },\n isSignedIn,\n isLoading: { subscribe: isLoadingStore.subscribe },\n signOut: async () => {\n await client.signOut();\n userStore.set(null);\n },\n openSignIn: () => client.openSignIn(),\n openSignUp: () => client.openSignUp(),\n getToken: () => client.getToken(),\n destroy: () => client.destroy(),\n client,\n };\n}\n","import { setContext, getContext } from 'svelte';\nimport type { AuthonConfig } from '@authon/js';\nimport { createAuthonStore, type AuthonStore } from './store';\n\nconst AUTHON_CONTEXT_KEY = Symbol('authon');\n\n/**\n * Initialize Authon in a Svelte component tree.\n * Call this in your root layout or top-level component.\n *\n * Usage in +layout.svelte:\n * ```svelte\n * <script>\n * import { initAuthon } from '@authon/svelte'\n * const authon = initAuthon('pk_live_...')\n * </script>\n * ```\n */\nexport function initAuthon(\n publishableKey: string,\n config?: Omit<AuthonConfig, 'mode'>,\n): AuthonStore {\n const store = createAuthonStore(publishableKey, config);\n setContext(AUTHON_CONTEXT_KEY, store);\n return store;\n}\n\n/**\n * Get the Authon store from Svelte context.\n * Must be called within a component tree where `initAuthon` was called.\n *\n * Usage:\n * ```svelte\n * <script>\n * import { getAuthon } from '@authon/svelte'\n * const { user, isSignedIn, signOut } = getAuthon()\n * </script>\n * ```\n */\nexport function getAuthon(): AuthonStore {\n const store = getContext<AuthonStore | undefined>(AUTHON_CONTEXT_KEY);\n if (!store) {\n throw new Error('getAuthon() must be called within a component tree where initAuthon() was called.');\n }\n return store;\n}\n"],"mappings":";AAAA,SAAS,UAAU,eAA8B;AACjD,SAAS,cAAc;AA8BhB,SAAS,kBACd,gBACA,QACa;AACb,QAAM,SAAS,IAAI,OAAO,gBAAgB,MAAM;AAChD,QAAM,YAAY,SAA4B,IAAI;AAClD,QAAM,iBAAiB,SAAS,IAAI;AAEpC,QAAM,aAAa,QAAQ,WAAW,CAAC,UAAU,UAAU,IAAI;AAE/D,SAAO,GAAG,YAAY,CAAC,SAAS;AAC9B,cAAU,IAAI,IAAkB;AAChC,mBAAe,IAAI,KAAK;AAAA,EAC1B,CAAC;AAED,SAAO,GAAG,aAAa,MAAM;AAC3B,cAAU,IAAI,IAAI;AAAA,EACpB,CAAC;AAED,SAAO,GAAG,SAAS,MAAM;AACvB,mBAAe,IAAI,KAAK;AAAA,EAC1B,CAAC;AAED,QAAM,eAAe,OAAO,QAAQ;AACpC,MAAI,cAAc;AAChB,cAAU,IAAI,YAAY;AAAA,EAC5B;AACA,iBAAe,IAAI,KAAK;AAExB,SAAO;AAAA,IACL,MAAM,EAAE,WAAW,UAAU,UAAU;AAAA,IACvC;AAAA,IACA,WAAW,EAAE,WAAW,eAAe,UAAU;AAAA,IACjD,SAAS,YAAY;AACnB,YAAM,OAAO,QAAQ;AACrB,gBAAU,IAAI,IAAI;AAAA,IACpB;AAAA,IACA,YAAY,MAAM,OAAO,WAAW;AAAA,IACpC,YAAY,MAAM,OAAO,WAAW;AAAA,IACpC,UAAU,MAAM,OAAO,SAAS;AAAA,IAChC,SAAS,MAAM,OAAO,QAAQ;AAAA,IAC9B;AAAA,EACF;AACF;;;AC1EA,SAAS,YAAY,kBAAkB;AAIvC,IAAM,qBAAqB,uBAAO,QAAQ;AAcnC,SAAS,WACd,gBACA,QACa;AACb,QAAM,QAAQ,kBAAkB,gBAAgB,MAAM;AACtD,aAAW,oBAAoB,KAAK;AACpC,SAAO;AACT;AAcO,SAAS,YAAyB;AACvC,QAAM,QAAQ,WAAoC,kBAAkB;AACpE,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,mFAAmF;AAAA,EACrG;AACA,SAAO;AACT;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/store.ts","../src/context.ts","../src/SocialButtons.ts"],"sourcesContent":["import { writable, derived, type Readable } from 'svelte/store';\nimport { Authon } from '@authon/js';\nimport type { AuthonConfig } from '@authon/js';\nimport type { AuthonUser } from '@authon/shared';\n\nexport interface AuthonStore {\n user: Readable<AuthonUser | null>;\n isSignedIn: Readable<boolean>;\n isLoading: Readable<boolean>;\n signOut: () => Promise<void>;\n openSignIn: () => Promise<void>;\n openSignUp: () => Promise<void>;\n getToken: () => string | null;\n destroy: () => void;\n client: Authon;\n}\n\n/**\n * Creates an Authon store with reactive Svelte stores.\n *\n * Usage:\n * ```ts\n * import { createAuthonStore } from '@authon/svelte'\n *\n * const authon = createAuthonStore('pk_live_...')\n *\n * // In your component:\n * $: user = $authon.user\n * $: isSignedIn = $authon.isSignedIn\n * ```\n */\nexport function createAuthonStore(\n publishableKey: string,\n config?: Omit<AuthonConfig, 'mode'>,\n): AuthonStore {\n const client = new Authon(publishableKey, config);\n const userStore = writable<AuthonUser | null>(null);\n const isLoadingStore = writable(true);\n\n const isSignedIn = derived(userStore, ($user) => $user !== null);\n\n client.on('signedIn', (user) => {\n userStore.set(user as AuthonUser);\n isLoadingStore.set(false);\n });\n\n client.on('signedOut', () => {\n userStore.set(null);\n });\n\n client.on('error', () => {\n isLoadingStore.set(false);\n });\n\n const existingUser = client.getUser();\n if (existingUser) {\n userStore.set(existingUser);\n }\n isLoadingStore.set(false);\n\n return {\n user: { subscribe: userStore.subscribe },\n isSignedIn,\n isLoading: { subscribe: isLoadingStore.subscribe },\n signOut: async () => {\n await client.signOut();\n userStore.set(null);\n },\n openSignIn: () => client.openSignIn(),\n openSignUp: () => client.openSignUp(),\n getToken: () => client.getToken(),\n destroy: () => client.destroy(),\n client,\n };\n}\n","import { setContext, getContext } from 'svelte';\nimport type { AuthonConfig } from '@authon/js';\nimport { createAuthonStore, type AuthonStore } from './store';\n\nconst AUTHON_CONTEXT_KEY = Symbol('authon');\n\n/**\n * Initialize Authon in a Svelte component tree.\n * Call this in your root layout or top-level component.\n *\n * Usage in +layout.svelte:\n * ```svelte\n * <script>\n * import { initAuthon } from '@authon/svelte'\n * const authon = initAuthon('pk_live_...')\n * </script>\n * ```\n */\nexport function initAuthon(\n publishableKey: string,\n config?: Omit<AuthonConfig, 'mode'>,\n): AuthonStore {\n const store = createAuthonStore(publishableKey, config);\n setContext(AUTHON_CONTEXT_KEY, store);\n return store;\n}\n\n/**\n * Get the Authon store from Svelte context.\n * Must be called within a component tree where `initAuthon` was called.\n *\n * Usage:\n * ```svelte\n * <script>\n * import { getAuthon } from '@authon/svelte'\n * const { user, isSignedIn, signOut } = getAuthon()\n * </script>\n * ```\n */\nexport function getAuthon(): AuthonStore {\n const store = getContext<AuthonStore | undefined>(AUTHON_CONTEXT_KEY);\n if (!store) {\n throw new Error('getAuthon() must be called within a component tree where initAuthon() was called.');\n }\n return store;\n}\n","import { PROVIDER_COLORS, PROVIDER_DISPLAY_NAMES, type OAuthProviderType } from '@authon/shared';\nimport { getProviderButtonConfig, type Authon } from '@authon/js';\n\nexport interface SocialButtonsOptions {\n /** Authon client instance */\n client: Authon;\n /** Target container element */\n container: HTMLElement;\n /** Called after successful OAuth sign-in */\n onSuccess?: () => void;\n /** Called on OAuth error */\n onError?: (error: Error) => void;\n /** Compact mode — icon-only square buttons in a row (default: false) */\n compact?: boolean;\n /** Gap between buttons in px (default: 10, compact default: 12) */\n gap?: number;\n /** Custom labels per provider */\n labels?: Partial<Record<OAuthProviderType, string>>;\n /** Icon size (default: 20, compact default: 24) */\n iconSize?: number;\n /** Border radius in px (default: 10) */\n borderRadius?: number;\n /** Button height in px (default: 48) */\n height?: number;\n /** Button size for compact mode in px (default: 48) */\n size?: number;\n}\n\n/**\n * Render social login buttons into a container element.\n *\n * Usage in +page.svelte:\n * ```svelte\n * <script>\n * import { getAuthon } from '@authon/svelte'\n * import { renderSocialButtons } from '@authon/svelte'\n * import { onMount } from 'svelte'\n *\n * const { client } = getAuthon()\n * let container: HTMLElement\n *\n * onMount(() => {\n * const cleanup = renderSocialButtons({\n * client,\n * container,\n * compact: true,\n * onError: (err) => console.error(err),\n * })\n * return cleanup\n * })\n * </script>\n *\n * <div bind:this={container}></div>\n * ```\n */\nexport function renderSocialButtons(options: SocialButtonsOptions): () => void {\n const {\n client,\n container,\n onSuccess,\n onError,\n compact = false,\n gap,\n labels,\n iconSize,\n borderRadius = 10,\n height = 48,\n size = 48,\n } = options;\n\n const resolvedGap = gap ?? (compact ? 12 : 10);\n const resolvedIconSize = iconSize ?? (compact ? 24 : 20);\n let loadingProvider: string | null = null;\n let buttons: HTMLButtonElement[] = [];\n\n const handleClick = async (provider: OAuthProviderType, btn: HTMLButtonElement) => {\n if (loadingProvider) return;\n loadingProvider = provider;\n btn.innerHTML = '<span style=\"display:inline-block;width:16px;height:16px;border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:authon-spin 0.6s linear infinite\"></span>';\n buttons.forEach((b) => (b.disabled = true));\n\n try {\n await client.signInWithOAuth(provider);\n onSuccess?.();\n } catch (e: any) {\n const error = e instanceof Error ? e : new Error(String(e));\n onError?.(error);\n } finally {\n loadingProvider = null;\n renderButtons(providers);\n }\n };\n\n let providers: OAuthProviderType[] = [];\n\n function renderButtons(providerList: OAuthProviderType[]) {\n container.innerHTML = '';\n buttons = [];\n\n // Inject keyframe if not present\n if (!document.getElementById('authon-spin-style')) {\n const style = document.createElement('style');\n style.id = 'authon-spin-style';\n style.textContent = '@keyframes authon-spin{to{transform:rotate(360deg)}}';\n document.head.appendChild(style);\n }\n\n const wrapper = document.createElement('div');\n wrapper.style.display = 'flex';\n wrapper.style.gap = `${resolvedGap}px`;\n\n if (compact) {\n wrapper.style.flexDirection = 'row';\n wrapper.style.flexWrap = 'wrap';\n wrapper.style.justifyContent = 'center';\n } else {\n wrapper.style.flexDirection = 'column';\n }\n\n for (const provider of providerList) {\n const colors = PROVIDER_COLORS[provider] || { bg: '#333', text: '#fff' };\n const displayName = PROVIDER_DISPLAY_NAMES[provider] || provider;\n const config = getProviderButtonConfig(provider);\n const iconSvg = config.iconSvg\n .replace(/width=\"\\d+\"/, `width=\"${resolvedIconSize}\"`)\n .replace(/height=\"\\d+\"/, `height=\"${resolvedIconSize}\"`);\n const needsBorder = colors.bg.toLowerCase() === '#ffffff';\n\n const btn = document.createElement('button');\n btn.setAttribute('aria-label', `Sign in with ${displayName}`);\n btn.style.display = 'flex';\n btn.style.alignItems = 'center';\n btn.style.justifyContent = 'center';\n btn.style.border = needsBorder ? '1px solid #dadce0' : 'none';\n btn.style.cursor = 'pointer';\n btn.style.backgroundColor = colors.bg;\n btn.style.color = colors.text;\n btn.style.borderRadius = `${borderRadius}px`;\n btn.style.transition = 'opacity 0.15s';\n btn.style.fontFamily = 'inherit';\n\n if (compact) {\n btn.style.width = `${size}px`;\n btn.style.height = `${size}px`;\n btn.style.padding = '0';\n btn.innerHTML = `<span style=\"display:flex;align-items:center\">${iconSvg}</span>`;\n } else {\n btn.style.width = '100%';\n btn.style.height = `${height}px`;\n btn.style.gap = '10px';\n btn.style.paddingLeft = '16px';\n btn.style.paddingRight = '16px';\n const buttonLabel = labels?.[provider] ?? `Continue with ${displayName}`;\n btn.innerHTML = `<span style=\"display:flex;align-items:center;flex-shrink:0\">${iconSvg}</span><span style=\"font-size:15px;font-weight:600;white-space:nowrap\">${buttonLabel}</span>`;\n }\n\n btn.addEventListener('click', () => handleClick(provider, btn));\n btn.addEventListener('mouseenter', () => (btn.style.opacity = '0.85'));\n btn.addEventListener('mouseleave', () => (btn.style.opacity = '1'));\n\n buttons.push(btn);\n wrapper.appendChild(btn);\n }\n\n container.appendChild(wrapper);\n }\n\n // Init\n client.getProviders().then((p: OAuthProviderType[]) => {\n providers = p;\n if (providers.length > 0) {\n renderButtons(providers);\n }\n });\n\n // Cleanup\n return () => {\n container.innerHTML = '';\n buttons = [];\n };\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@authon/svelte",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Authon Svelte SDK — stores and components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"store"
|
|
38
38
|
],
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@authon/js": "
|
|
41
|
-
"@authon/shared": "
|
|
40
|
+
"@authon/js": "^0.2.1",
|
|
41
|
+
"@authon/shared": "^0.2.0"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"svelte": "^4.0.0 || ^5.0.0"
|