-
Notifications
You must be signed in to change notification settings - Fork 330
/
Copy pathparseAppearance.ts
127 lines (109 loc) · 4.08 KB
/
parseAppearance.ts
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import type { Appearance, DeepPartial, Elements, Layout, Theme } from '@clerk/types';
import { createInternalTheme, defaultInternalTheme } from '../foundations';
import { polishedAppearance } from '../polishedAppearance';
import type { InternalTheme } from '../styledSystem';
import { fastDeepMergeAndReplace } from '../utils';
import {
createColorScales,
createFonts,
createFontSizeScale,
createFontWeightScale,
createRadiiUnits,
createSpaceScale,
} from './parseVariables';
export type ParsedElements = Elements[];
export type ParsedInternalTheme = InternalTheme;
export type ParsedLayout = Required<Layout>;
type PublicAppearanceTopLevelKey = keyof Omit<Appearance, 'baseTheme' | 'elements' | 'layout' | 'variables'>;
export type AppearanceCascade = {
globalAppearance?: Appearance;
appearance?: Appearance;
appearanceKey: PublicAppearanceTopLevelKey | 'impersonationFab';
};
export type ParsedAppearance = {
parsedElements: ParsedElements;
parsedInternalTheme: ParsedInternalTheme;
parsedLayout: ParsedLayout;
};
const defaultLayout: ParsedLayout = {
logoPlacement: 'inside',
socialButtonsPlacement: 'top',
socialButtonsVariant: 'auto',
logoImageUrl: '',
logoLinkUrl: '',
showOptionalFields: true,
helpPageUrl: '',
privacyPageUrl: '',
termsPageUrl: '',
shimmer: true,
};
/**
* Parses the public appearance object.
* It splits the resulting styles into 2 objects: parsedElements, parsedInternalTheme
* parsedElements is used by the makeCustomizables HOC to handle per-element styling
* parsedInternalTheme is used by FlowCard/InternalThemeProvider for generic theming
* Both are injected by the AppearanceContext
*/
export const parseAppearance = (cascade: AppearanceCascade): ParsedAppearance => {
const { globalAppearance, appearance: componentAppearance, appearanceKey } = cascade;
const appearanceList: Appearance[] = [];
[globalAppearance, globalAppearance?.[appearanceKey as PublicAppearanceTopLevelKey], componentAppearance].forEach(a =>
expand(a, appearanceList),
);
const parsedInternalTheme = parseVariables(appearanceList);
const parsedLayout = parseLayout(appearanceList);
if (
!appearanceList.find(a => {
//@ts-expect-error not public api
return !!a.simpleStyles;
})
) {
appearanceList.unshift(polishedAppearance);
}
const parsedElements = parseElements(
appearanceList.map(appearance => {
if (!appearance.elements || typeof appearance.elements !== 'function') {
return appearance;
}
const res = { ...appearance };
res.elements = appearance.elements({ theme: parsedInternalTheme });
return res;
}),
);
return { parsedElements, parsedInternalTheme, parsedLayout };
};
const expand = (theme: Theme | undefined, cascade: any[]) => {
if (!theme) {
return;
}
(Array.isArray(theme.baseTheme) ? theme.baseTheme : [theme.baseTheme]).forEach(baseTheme =>
expand(baseTheme as Theme, cascade),
);
cascade.push(theme);
};
const parseElements = (appearances: Appearance[]) => {
return appearances.map(appearance => ({ ...appearance?.elements }));
};
const parseLayout = (appearanceList: Appearance[]) => {
return { ...defaultLayout, ...appearanceList.reduce((acc, appearance) => ({ ...acc, ...appearance.layout }), {}) };
};
const parseVariables = (appearances: Appearance[]) => {
const res = {} as DeepPartial<InternalTheme>;
fastDeepMergeAndReplace({ ...defaultInternalTheme }, res);
appearances.forEach(appearance => {
fastDeepMergeAndReplace(createInternalThemeFromVariables(appearance), res);
});
return res as ParsedInternalTheme;
};
const createInternalThemeFromVariables = (theme: Theme | undefined): DeepPartial<InternalTheme> => {
if (!theme) {
return {};
}
const colors = { ...createColorScales(theme) };
const radii = { ...createRadiiUnits(theme) };
const space = { ...createSpaceScale(theme) };
const fontSizes = { ...createFontSizeScale(theme) };
const fontWeights = { ...createFontWeightScale(theme) };
const fonts = { ...createFonts(theme) };
return createInternalTheme({ colors, radii, space, fontSizes, fontWeights, fonts } as any);
};