fix and update
This commit is contained in:
186
apps/docs/lib/one-tap-builder.ts
Normal file
186
apps/docs/lib/one-tap-builder.ts
Normal file
@@ -0,0 +1,186 @@
|
||||
import type { OneTapUrls } from '@/lib/one-tap-examples';
|
||||
|
||||
export type ButtonSize = 's' | 'm' | 'l' | 'xl';
|
||||
export type ButtonTheme = 'light' | 'dark';
|
||||
export type ButtonView = 'main' | 'icon';
|
||||
export type ButtonIcon = 'id' | 'none';
|
||||
|
||||
export interface ButtonBuilderOptions {
|
||||
clientId: string;
|
||||
providerName: string;
|
||||
redirectUri: string;
|
||||
size: ButtonSize;
|
||||
theme: ButtonTheme;
|
||||
view: ButtonView;
|
||||
radius: number;
|
||||
icon: ButtonIcon;
|
||||
// Пустая строка = использовать цвет темы по умолчанию.
|
||||
bg: string;
|
||||
bgHover: string;
|
||||
border: string;
|
||||
borderHover: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface SizePreset {
|
||||
height: number;
|
||||
font: number;
|
||||
padX: number;
|
||||
gap: number;
|
||||
badge: number;
|
||||
radius: number;
|
||||
}
|
||||
|
||||
// Должно совпадать с SIZE_PRESETS в apps/frontend/public/sso-widget.js.
|
||||
export const SIZE_PRESETS: Record<ButtonSize, SizePreset> = {
|
||||
s: { height: 32, font: 13, padX: 12, gap: 8, badge: 20, radius: 16 },
|
||||
m: { height: 40, font: 14, padX: 16, gap: 10, badge: 24, radius: 20 },
|
||||
l: { height: 44, font: 15, padX: 18, gap: 10, badge: 28, radius: 22 },
|
||||
xl: { height: 52, font: 16, padX: 22, gap: 12, badge: 32, radius: 26 }
|
||||
};
|
||||
|
||||
export interface ButtonPalette {
|
||||
bg: string;
|
||||
bgHover: string;
|
||||
border: string;
|
||||
borderHover: string;
|
||||
text: string;
|
||||
badgeBg: string;
|
||||
badgeColor: string;
|
||||
}
|
||||
|
||||
// Должно совпадать с themePalette в apps/frontend/public/sso-widget.js.
|
||||
export function themePalette(theme: ButtonTheme): ButtonPalette {
|
||||
if (theme === 'dark') {
|
||||
return {
|
||||
bg: '#1f2430',
|
||||
bgHover: '#2a3040',
|
||||
border: 'transparent',
|
||||
borderHover: 'transparent',
|
||||
text: '#ffffff',
|
||||
badgeBg: '#ffffff',
|
||||
badgeColor: '#1f2430'
|
||||
};
|
||||
}
|
||||
return {
|
||||
bg: '#ffffff',
|
||||
bgHover: '#f6f8fb',
|
||||
border: '#e4e8ef',
|
||||
borderHover: '#d4dae6',
|
||||
text: '#1f2430',
|
||||
badgeBg: '#111111',
|
||||
badgeColor: '#ffffff'
|
||||
};
|
||||
}
|
||||
|
||||
export const SIZE_OPTIONS: Array<{ value: ButtonSize; label: string }> = [
|
||||
{ value: 's', label: 'S — 32px' },
|
||||
{ value: 'm', label: 'M — 40px' },
|
||||
{ value: 'l', label: 'L — 44px' },
|
||||
{ value: 'xl', label: 'XL — 52px' }
|
||||
];
|
||||
|
||||
export const THEME_OPTIONS: Array<{ value: ButtonTheme; label: string }> = [
|
||||
{ value: 'light', label: 'Светлая' },
|
||||
{ value: 'dark', label: 'Тёмная' }
|
||||
];
|
||||
|
||||
export const VIEW_OPTIONS: Array<{ value: ButtonView; label: string }> = [
|
||||
{ value: 'main', label: 'Кнопка с текстом' },
|
||||
{ value: 'icon', label: 'Только иконка' }
|
||||
];
|
||||
|
||||
export const ICON_OPTIONS: Array<{ value: ButtonIcon; label: string }> = [
|
||||
{ value: 'id', label: 'Значок ID' },
|
||||
{ value: 'none', label: 'Без значка' }
|
||||
];
|
||||
|
||||
export const DEFAULT_BUILDER_OPTIONS: ButtonBuilderOptions = {
|
||||
clientId: 'YOUR_CLIENT_ID',
|
||||
providerName: 'MVK ID',
|
||||
redirectUri: 'https://app.example.com/auth/callback',
|
||||
size: 'xl',
|
||||
theme: 'light',
|
||||
view: 'main',
|
||||
radius: 26,
|
||||
icon: 'id',
|
||||
bg: '',
|
||||
bgHover: '',
|
||||
border: '',
|
||||
borderHover: '',
|
||||
text: ''
|
||||
};
|
||||
|
||||
export interface ResolvedButtonStyle {
|
||||
preset: SizePreset;
|
||||
palette: ButtonPalette;
|
||||
bg: string;
|
||||
bgHover: string;
|
||||
border: string;
|
||||
borderHover: string;
|
||||
text: string;
|
||||
radius: number;
|
||||
iconOnly: boolean;
|
||||
showBadge: boolean;
|
||||
}
|
||||
|
||||
export function resolveButtonStyle(options: ButtonBuilderOptions): ResolvedButtonStyle {
|
||||
const preset = SIZE_PRESETS[options.size] ?? SIZE_PRESETS.xl;
|
||||
const palette = themePalette(options.theme === 'dark' ? 'dark' : 'light');
|
||||
const radius = Number.isFinite(options.radius) ? options.radius : preset.radius;
|
||||
return {
|
||||
preset,
|
||||
palette,
|
||||
bg: options.bg || palette.bg,
|
||||
bgHover: options.bgHover || palette.bgHover,
|
||||
border: options.border || palette.border,
|
||||
borderHover: options.borderHover || palette.borderHover,
|
||||
text: options.text || palette.text,
|
||||
radius,
|
||||
iconOnly: options.view === 'icon',
|
||||
showBadge: options.icon !== 'none'
|
||||
};
|
||||
}
|
||||
|
||||
export function buildButtonSnippet(options: ButtonBuilderOptions, urls: OneTapUrls): string {
|
||||
const providerName = options.providerName || urls.projectName;
|
||||
const lines: string[] = [
|
||||
` src="${urls.widgetUrl}"`,
|
||||
` data-client-id="${options.clientId || 'YOUR_CLIENT_ID'}"`,
|
||||
` data-idp-url="${urls.apiBase}"`,
|
||||
` data-idp-frontend-url="${urls.frontendBase}"`,
|
||||
` data-provider-name="${providerName}"`,
|
||||
` data-redirect-uri="${options.redirectUri}"`,
|
||||
` data-button-container="mvkid-button"`,
|
||||
` data-button-size="${options.size}"`,
|
||||
` data-button-theme="${options.theme}"`,
|
||||
` data-button-view="${options.view}"`,
|
||||
` data-button-radius="${options.radius}"`,
|
||||
` data-button-icon="${options.icon}"`
|
||||
];
|
||||
|
||||
if (options.bg) lines.push(` data-button-bg="${options.bg}"`);
|
||||
if (options.bgHover) lines.push(` data-button-bg-hover="${options.bgHover}"`);
|
||||
if (options.border) lines.push(` data-button-border="${options.border}"`);
|
||||
if (options.borderHover) lines.push(` data-button-border-hover="${options.borderHover}"`);
|
||||
if (options.text) lines.push(` data-button-text="${options.text}"`);
|
||||
|
||||
lines.push(' data-on-success="onMvkIdLogin"');
|
||||
|
||||
return `<!-- 1) Контейнер, в котором появится кнопка -->
|
||||
<div id="mvkid-button"></div>
|
||||
|
||||
<!-- 2) Подключение виджета ${providerName} -->
|
||||
<script
|
||||
${lines.join('\n')}
|
||||
></script>
|
||||
|
||||
<script>
|
||||
function onMvkIdLogin(payload) {
|
||||
// payload.token — id_token (FedCM) или токен из popup
|
||||
// payload.method — 'fedcm' | 'popup'
|
||||
console.log('${providerName}: вход через', payload.method, payload.token);
|
||||
// Отправьте токен на ваш backend для проверки и создания сессии
|
||||
}
|
||||
</script>`;
|
||||
}
|
||||
Reference in New Issue
Block a user