mirror of
https://github.com/logto-io/logto.git
synced 2025-03-24 22:41:28 -05:00
feat: custom css (#3155)
This commit is contained in:
parent
c93d744041
commit
1ef5519e75
7 changed files with 74 additions and 43 deletions
|
@ -91,4 +91,5 @@ export const mockSignInExperience: SignInExperience = {
|
|||
},
|
||||
socialSignInConnectorTargets: ['github', 'facebook', 'wechat'],
|
||||
signInMode: SignInMode.SignInAndRegister,
|
||||
customCss: null,
|
||||
};
|
||||
|
|
|
@ -35,7 +35,7 @@ describe('sign-in-experience query', () => {
|
|||
it('findDefaultSignInExperience', async () => {
|
||||
/* eslint-disable sql/no-unsafe-query */
|
||||
const expectSql = `
|
||||
select "tenant_id", "id", "color", "branding", "language_info", "terms_of_use_url", "sign_in", "sign_up", "social_sign_in_connector_targets", "sign_in_mode"
|
||||
select "tenant_id", "id", "color", "branding", "language_info", "terms_of_use_url", "sign_in", "sign_up", "social_sign_in_connector_targets", "sign_in_mode", "custom_css"
|
||||
from "sign_in_experiences"
|
||||
where "id"=$1
|
||||
`;
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import { sql } from 'slonik';
|
||||
|
||||
import type { AlterationScript } from '../lib/types/alteration.js';
|
||||
|
||||
const alteration: AlterationScript = {
|
||||
up: async (pool) => {
|
||||
await pool.query(sql`
|
||||
alter table sign_in_experiences
|
||||
add column if not exists custom_css text;
|
||||
`);
|
||||
},
|
||||
down: async (pool) => {
|
||||
await pool.query(sql`
|
||||
alter table sign_in_experiences
|
||||
drop column custom_css;
|
||||
`);
|
||||
},
|
||||
};
|
||||
|
||||
export default alteration;
|
|
@ -1,55 +1,55 @@
|
|||
import { generateDarkColor } from '@logto/core-kit';
|
||||
|
||||
import type { CreateSignInExperience } from '../db-entries/index.js';
|
||||
import type { SignInExperience } from '../db-entries/index.js';
|
||||
import { SignInMode } from '../db-entries/index.js';
|
||||
import { BrandingStyle, SignInIdentifier } from '../foundations/index.js';
|
||||
import { defaultTenantId } from './tenant.js';
|
||||
|
||||
const defaultPrimaryColor = '#6139F6';
|
||||
|
||||
export const createDefaultSignInExperience = (
|
||||
forTenantId: string
|
||||
): Readonly<CreateSignInExperience> => ({
|
||||
tenantId: forTenantId,
|
||||
id: 'default',
|
||||
color: {
|
||||
primaryColor: defaultPrimaryColor,
|
||||
isDarkModeEnabled: false,
|
||||
darkPrimaryColor: generateDarkColor(defaultPrimaryColor),
|
||||
},
|
||||
branding: {
|
||||
style: BrandingStyle.Logo,
|
||||
logoUrl: 'https://logto.io/logo.svg',
|
||||
darkLogoUrl: 'https://logto.io/logo-dark.svg',
|
||||
},
|
||||
languageInfo: {
|
||||
autoDetect: true,
|
||||
fallbackLanguage: 'en',
|
||||
},
|
||||
termsOfUseUrl: null,
|
||||
signUp: {
|
||||
identifiers: [SignInIdentifier.Username],
|
||||
password: true,
|
||||
verify: false,
|
||||
},
|
||||
signIn: {
|
||||
methods: [
|
||||
{
|
||||
identifier: SignInIdentifier.Username,
|
||||
password: true,
|
||||
verificationCode: false,
|
||||
isPasswordPrimary: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
socialSignInConnectorTargets: [],
|
||||
signInMode: SignInMode.SignInAndRegister,
|
||||
});
|
||||
export const createDefaultSignInExperience = (forTenantId: string): Readonly<SignInExperience> =>
|
||||
Object.freeze({
|
||||
tenantId: forTenantId,
|
||||
id: 'default',
|
||||
color: {
|
||||
primaryColor: defaultPrimaryColor,
|
||||
isDarkModeEnabled: false,
|
||||
darkPrimaryColor: generateDarkColor(defaultPrimaryColor),
|
||||
},
|
||||
branding: {
|
||||
style: BrandingStyle.Logo,
|
||||
logoUrl: 'https://logto.io/logo.svg',
|
||||
darkLogoUrl: 'https://logto.io/logo-dark.svg',
|
||||
},
|
||||
languageInfo: {
|
||||
autoDetect: true,
|
||||
fallbackLanguage: 'en' as const,
|
||||
},
|
||||
termsOfUseUrl: null,
|
||||
signUp: {
|
||||
identifiers: [SignInIdentifier.Username],
|
||||
password: true,
|
||||
verify: false,
|
||||
},
|
||||
signIn: {
|
||||
methods: [
|
||||
{
|
||||
identifier: SignInIdentifier.Username,
|
||||
password: true,
|
||||
verificationCode: false,
|
||||
isPasswordPrimary: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
socialSignInConnectorTargets: [],
|
||||
signInMode: SignInMode.SignInAndRegister,
|
||||
customCss: null,
|
||||
});
|
||||
|
||||
/** @deprecated Use `createDefaultSignInExperience()` instead. */
|
||||
export const defaultSignInExperience = createDefaultSignInExperience(defaultTenantId);
|
||||
|
||||
export const adminConsoleSignInExperience: CreateSignInExperience = {
|
||||
export const adminConsoleSignInExperience: Readonly<SignInExperience> = Object.freeze({
|
||||
...defaultSignInExperience,
|
||||
color: {
|
||||
...defaultSignInExperience.color,
|
||||
|
@ -61,4 +61,4 @@ export const adminConsoleSignInExperience: CreateSignInExperience = {
|
|||
darkLogoUrl: 'https://logto.io/logo-dark.svg',
|
||||
slogan: 'admin_console.welcome.title', // TODO: @simeng should we programmatically support an i18n key for slogan?
|
||||
},
|
||||
};
|
||||
});
|
||||
|
|
|
@ -12,5 +12,6 @@ create table sign_in_experiences (
|
|||
sign_up jsonb /* @use SignUp */ not null,
|
||||
social_sign_in_connector_targets jsonb /* @use ConnectorTargets */ not null default '[]'::jsonb,
|
||||
sign_in_mode sign_in_mode not null default 'SignInAndRegister',
|
||||
custom_css text,
|
||||
primary key (tenant_id, id)
|
||||
);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { SignInMode } from '@logto/schemas';
|
||||
import { useEffect } from 'react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { Route, Routes, BrowserRouter, Navigate } from 'react-router-dom';
|
||||
|
||||
import AppBoundary from './containers/AppBoundary';
|
||||
|
@ -29,8 +29,13 @@ import './scss/normalized.scss';
|
|||
const App = () => {
|
||||
const { context, Provider } = usePageContext();
|
||||
const { experienceSettings, setLoading, setExperienceSettings } = context;
|
||||
const customCssRef = useRef(document.createElement('style'));
|
||||
const [isPreview] = usePreview(context);
|
||||
|
||||
useEffect(() => {
|
||||
document.head.append(customCssRef.current);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (isPreview) {
|
||||
return;
|
||||
|
@ -38,6 +43,8 @@ const App = () => {
|
|||
|
||||
(async () => {
|
||||
const settings = await getSignInExperienceSettings();
|
||||
// eslint-disable-next-line @silverhand/fp/no-mutation
|
||||
customCssRef.current.textContent = settings.customCss;
|
||||
|
||||
// Note: i18n must be initialized ahead of page render
|
||||
await initI18n(settings.languageInfo);
|
||||
|
|
|
@ -209,6 +209,7 @@ export const mockSignInExperience: SignInExperience = {
|
|||
},
|
||||
socialSignInConnectorTargets: ['BE8QXN0VsrOH7xdWFDJZ9', 'lcXT4o2GSjbV9kg2shZC7'],
|
||||
signInMode: SignInMode.SignInAndRegister,
|
||||
customCss: null,
|
||||
};
|
||||
|
||||
export const mockSignInExperienceSettings: SignInExperienceResponse = {
|
||||
|
@ -230,6 +231,7 @@ export const mockSignInExperienceSettings: SignInExperienceResponse = {
|
|||
email: true,
|
||||
phone: true,
|
||||
},
|
||||
customCss: null,
|
||||
};
|
||||
|
||||
const usernameSettings = {
|
||||
|
|
Loading…
Add table
Reference in a new issue