fix and update

This commit is contained in:
lendry
2026-06-29 16:23:40 +03:00
parent aebce54bd7
commit 10253fc76b
8 changed files with 797 additions and 106 deletions

View 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>`;
}