@udixio/theme 1.0.0-beta.3 → 1.0.0-beta.30
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/app.container.d.ts +5 -0
- package/dist/app.module.d.ts +2 -2
- package/dist/app.service.d.ts +9 -3
- package/dist/color/color.interface.d.ts +1 -2
- package/dist/color/color.module.d.ts +2 -2
- package/dist/color/entities/color.entity.d.ts +6 -1
- package/dist/color/entities/index.d.ts +1 -0
- package/dist/color/index.d.ts +5 -0
- package/dist/color/models/default-color.model.d.ts +2 -3
- package/dist/color/models/index.d.ts +1 -0
- package/dist/color/services/color-manager.service.d.ts +18 -0
- package/dist/color/services/color.service.d.ts +21 -0
- package/dist/color/services/index.d.ts +2 -0
- package/dist/config/config.interface.d.ts +13 -0
- package/dist/config/config.module.d.ts +2 -0
- package/dist/config/config.service.d.ts +12 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/index.d.ts +11 -0
- package/dist/main.d.ts +6 -1
- package/dist/material-color-utilities/index.d.ts +1 -0
- package/dist/plugin/index.d.ts +3 -0
- package/dist/plugin/plugin.module.d.ts +2 -0
- package/dist/plugin/plugin.service.d.ts +8 -0
- package/dist/plugin/pluginAbstract.d.ts +18 -0
- package/dist/plugins/font/font.plugin.d.ts +49 -0
- package/dist/plugins/font/index.d.ts +1 -0
- package/dist/plugins/tailwind/index.d.ts +3 -0
- package/dist/plugins/tailwind/main.d.ts +13 -0
- package/dist/plugins/tailwind/plugins-tailwind/font.d.ts +5 -0
- package/dist/plugins/tailwind/plugins-tailwind/index.d.ts +2 -0
- package/dist/plugins/tailwind/plugins-tailwind/state.d.ts +4 -0
- package/dist/plugins/tailwind/plugins-tailwind/themer.d.ts +10 -0
- package/dist/plugins/tailwind/tailwind.plugin.d.ts +18 -0
- package/dist/theme/entities/index.d.ts +2 -0
- package/dist/theme/entities/variant.entity.d.ts +2 -1
- package/dist/theme/index.d.ts +4 -0
- package/dist/theme/models/index.d.ts +1 -0
- package/dist/theme/services/index.d.ts +3 -0
- package/dist/theme/services/scheme.service.d.ts +7 -2
- package/dist/theme/services/theme.service.d.ts +16 -7
- package/dist/theme/services/variant.service.d.ts +8 -2
- package/dist/theme/theme.module.d.ts +2 -2
- package/dist/theme.cjs.development.js +1416 -906
- package/dist/theme.cjs.development.js.map +1 -1
- package/dist/theme.cjs.production.min.js +1 -1
- package/dist/theme.cjs.production.min.js.map +1 -1
- package/dist/theme.esm.js +1383 -907
- package/dist/theme.esm.js.map +1 -1
- package/package.json +21 -22
- package/src/app.container.ts +46 -0
- package/src/app.module.ts +5 -8
- package/src/app.service.spec.ts +1 -1
- package/src/app.service.ts +20 -8
- package/src/color/color.interface.ts +1 -3
- package/src/color/color.module.ts +8 -10
- package/src/color/entities/color.entity.ts +13 -1
- package/src/color/entities/index.ts +1 -0
- package/src/color/index.ts +5 -0
- package/src/color/models/default-color.model.ts +205 -202
- package/src/color/models/index.ts +1 -0
- package/src/color/{color-manager.service.ts → services/color-manager.service.ts} +23 -15
- package/src/color/{color.service.spec.ts → services/color.service.spec.ts} +1 -1
- package/src/color/services/color.service.ts +75 -0
- package/src/color/services/index.ts +2 -0
- package/src/config/config.interface.ts +14 -0
- package/src/config/config.module.ts +7 -0
- package/src/config/config.service.ts +101 -0
- package/src/config/index.ts +3 -0
- package/src/index.ts +11 -0
- package/src/main.ts +13 -7
- package/src/material-color-utilities/index.ts +1 -0
- package/src/plugin/index.ts +3 -0
- package/src/plugin/plugin.module.ts +7 -0
- package/src/plugin/plugin.service.ts +42 -0
- package/src/plugin/pluginAbstract.ts +44 -0
- package/src/plugins/font/font.plugin.ts +203 -0
- package/src/plugins/font/index.ts +1 -0
- package/src/plugins/tailwind/index.ts +3 -0
- package/src/plugins/tailwind/main.ts +16 -0
- package/src/plugins/tailwind/plugins-tailwind/font.ts +69 -0
- package/src/plugins/tailwind/plugins-tailwind/index.ts +2 -0
- package/src/plugins/tailwind/plugins-tailwind/state.ts +88 -0
- package/src/plugins/tailwind/plugins-tailwind/themer.ts +137 -0
- package/src/plugins/tailwind/tailwind.plugin.ts +72 -0
- package/src/theme/entities/index.ts +2 -0
- package/src/theme/entities/variant.entity.ts +2 -1
- package/src/theme/index.ts +4 -0
- package/src/theme/models/index.ts +1 -0
- package/src/theme/models/variant.model.ts +7 -0
- package/src/theme/services/index.ts +3 -0
- package/src/theme/services/scheme.service.ts +39 -11
- package/src/theme/services/theme.service.ts +30 -14
- package/src/theme/services/variant.service.ts +40 -5
- package/src/theme/theme.module.ts +8 -9
- package/dist/color/color-manager.service.d.ts +0 -15
- package/dist/color/color.service.d.ts +0 -14
- package/src/color/color.service.ts +0 -58
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { ConfigInterface } from './config.interface';
|
|
2
|
+
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { defaultColors } from '../color';
|
|
5
|
+
import { VariantModel } from '../theme';
|
|
6
|
+
import { AppService } from '../app.service';
|
|
7
|
+
import { existsSync } from 'node:fs';
|
|
8
|
+
|
|
9
|
+
export function defineConfig(configObject: ConfigInterface): ConfigInterface {
|
|
10
|
+
if (!configObject || typeof configObject !== 'object') {
|
|
11
|
+
throw new Error('The configuration is missing or not an object');
|
|
12
|
+
}
|
|
13
|
+
if (!('sourceColor' in configObject)) {
|
|
14
|
+
throw new Error('Invalid configuration');
|
|
15
|
+
}
|
|
16
|
+
return configObject as ConfigInterface;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class ConfigService {
|
|
20
|
+
configPath = './theme.config';
|
|
21
|
+
|
|
22
|
+
private appService: AppService;
|
|
23
|
+
|
|
24
|
+
constructor({ appService }: { appService: AppService }) {
|
|
25
|
+
this.appService = appService;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public loadConfig(config?: ConfigInterface): void {
|
|
29
|
+
const { themeService, colorService, pluginService } = this.appService;
|
|
30
|
+
const {
|
|
31
|
+
sourceColor,
|
|
32
|
+
contrastLevel = 0,
|
|
33
|
+
isDark = false,
|
|
34
|
+
variant = VariantModel.tonalSpot,
|
|
35
|
+
palettes,
|
|
36
|
+
colors,
|
|
37
|
+
useDefaultColors = true,
|
|
38
|
+
plugins,
|
|
39
|
+
} = config ?? this.getConfig();
|
|
40
|
+
themeService.create({
|
|
41
|
+
contrastLevel: contrastLevel,
|
|
42
|
+
isDark: isDark,
|
|
43
|
+
sourceColorHex: sourceColor,
|
|
44
|
+
variant: variant,
|
|
45
|
+
});
|
|
46
|
+
if (palettes) {
|
|
47
|
+
Object.entries(palettes).forEach(([key, value]) =>
|
|
48
|
+
themeService.addCustomPalette(key, value)
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
if (useDefaultColors) {
|
|
52
|
+
colorService.addColors(defaultColors);
|
|
53
|
+
}
|
|
54
|
+
if (colors) {
|
|
55
|
+
colorService.addColors(colors);
|
|
56
|
+
}
|
|
57
|
+
if (plugins) {
|
|
58
|
+
plugins.forEach((plugin) => {
|
|
59
|
+
pluginService.addPlugin(plugin);
|
|
60
|
+
});
|
|
61
|
+
pluginService.loadPlugins(this.appService);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private getConfig(): ConfigInterface {
|
|
66
|
+
if (
|
|
67
|
+
typeof process !== 'undefined' &&
|
|
68
|
+
process.release &&
|
|
69
|
+
process.release.name === 'node'
|
|
70
|
+
) {
|
|
71
|
+
const base = resolve(this.configPath);
|
|
72
|
+
const extensions = ['.js', '.ts', '.jms', '.jcs'];
|
|
73
|
+
let configImport = null;
|
|
74
|
+
|
|
75
|
+
for (const ext of extensions) {
|
|
76
|
+
const path = base + ext;
|
|
77
|
+
if (existsSync(path)) {
|
|
78
|
+
configImport = require(path);
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (!configImport) {
|
|
84
|
+
throw new Error('Configuration file not found');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let config: unknown;
|
|
88
|
+
if ('default' in configImport) {
|
|
89
|
+
config = configImport.default;
|
|
90
|
+
} else {
|
|
91
|
+
config = configImport;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return config as ConfigInterface;
|
|
95
|
+
} else {
|
|
96
|
+
throw new Error(
|
|
97
|
+
'You must provide configuration object when using this library in a browser.'
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1 +1,12 @@
|
|
|
1
|
+
export { default as AppContainer } from './app.container';
|
|
2
|
+
export * from './app.container';
|
|
3
|
+
export * from './app.module';
|
|
4
|
+
export * from './app.service';
|
|
5
|
+
export * from './color';
|
|
6
|
+
export * from './config';
|
|
1
7
|
export * from './main';
|
|
8
|
+
export * from './material-color-utilities';
|
|
9
|
+
export * from './plugin';
|
|
10
|
+
export * from './plugins/font';
|
|
11
|
+
export * from './plugins/tailwind';
|
|
12
|
+
export * from './theme';
|
package/src/main.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { AppModule } from './app.module';
|
|
1
|
+
import AppContainer from './app.container';
|
|
3
2
|
import { AppService } from './app.service';
|
|
3
|
+
import { ConfigInterface, ConfigService } from './config';
|
|
4
4
|
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
export function bootstrap(): AppService {
|
|
6
|
+
return AppContainer.resolve<AppService>('appService');
|
|
7
|
+
}
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
export function bootstrapFromConfig(args?: {
|
|
10
|
+
path?: string;
|
|
11
|
+
config?: ConfigInterface;
|
|
12
|
+
}): AppService {
|
|
13
|
+
const configService = AppContainer.resolve<ConfigService>('configService');
|
|
14
|
+
if (args?.path) configService.configPath = args.path;
|
|
15
|
+
configService.loadConfig(args?.config);
|
|
16
|
+
return AppContainer.resolve<AppService>('appService');
|
|
11
17
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { PluginAbstract } from './pluginAbstract';
|
|
2
|
+
import { AppService } from '../app.service';
|
|
3
|
+
|
|
4
|
+
export class PluginService {
|
|
5
|
+
private plugins = new Map<string, PluginAbstract<any, any>>();
|
|
6
|
+
|
|
7
|
+
public addPlugin(plugin: PluginAbstract<any, any>) {
|
|
8
|
+
this.plugins.set(plugin.name, plugin);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
public loadPlugins(appService: AppService) {
|
|
12
|
+
const plugins = new Map(this.plugins);
|
|
13
|
+
|
|
14
|
+
let size = 0;
|
|
15
|
+
do {
|
|
16
|
+
size = plugins.size;
|
|
17
|
+
plugins.forEach((plugin, key) => {
|
|
18
|
+
const deps = plugin.dependencies.filter(
|
|
19
|
+
(dep) => !this.plugins.has(new dep().name)
|
|
20
|
+
);
|
|
21
|
+
if (deps.length === 0) {
|
|
22
|
+
this.plugins.set(plugin.name, plugin.init(appService));
|
|
23
|
+
plugins.delete(key);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
} while (plugins.size != 0 && plugins.size < size);
|
|
27
|
+
|
|
28
|
+
if (plugins.size > 0)
|
|
29
|
+
throw new Error(
|
|
30
|
+
"Some plugins couldn't be loaded due to missing dependencies: " +
|
|
31
|
+
Array.from(plugins.keys())
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public getPlugin<T extends PluginAbstract<any, any>>(
|
|
36
|
+
plugin: new (...args: any) => T
|
|
37
|
+
): T {
|
|
38
|
+
const pluginInstance = this.plugins.get(new plugin().name);
|
|
39
|
+
if (!pluginInstance) throw new Error(`Plugin ${plugin.name} not found`);
|
|
40
|
+
return pluginInstance as T;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { AppService } from '../app.service';
|
|
2
|
+
|
|
3
|
+
export type PluginConstructor<Plugin extends PluginImplAbstract<any>> = new (
|
|
4
|
+
...args: any
|
|
5
|
+
) => Plugin;
|
|
6
|
+
|
|
7
|
+
export abstract class PluginAbstract<
|
|
8
|
+
Plugin extends PluginImplAbstract<Options>,
|
|
9
|
+
Options extends object
|
|
10
|
+
> {
|
|
11
|
+
public abstract readonly dependencies: (new (...args: any) => PluginAbstract<
|
|
12
|
+
any,
|
|
13
|
+
any
|
|
14
|
+
>)[];
|
|
15
|
+
public abstract readonly name: string;
|
|
16
|
+
public options: Options;
|
|
17
|
+
public abstract readonly pluginClass: PluginConstructor<Plugin>;
|
|
18
|
+
protected pluginInstance: Plugin | undefined;
|
|
19
|
+
|
|
20
|
+
constructor(options: Options) {
|
|
21
|
+
this.options = options;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public init(appService: AppService) {
|
|
25
|
+
this.pluginInstance = new this.pluginClass(appService, this.options);
|
|
26
|
+
this.pluginInstance.onInit();
|
|
27
|
+
return this;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public getInstance(): Plugin {
|
|
31
|
+
if (!this.pluginInstance) {
|
|
32
|
+
throw new Error(`Plugin ${this.name} is not initialized`);
|
|
33
|
+
}
|
|
34
|
+
return this.pluginInstance;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export abstract class PluginImplAbstract<Options extends object> {
|
|
39
|
+
constructor(protected appService: AppService, protected options: Options) {
|
|
40
|
+
this.onInit();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
abstract onInit(): void;
|
|
44
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { PluginAbstract, PluginImplAbstract } from '../../plugin';
|
|
2
|
+
|
|
3
|
+
export enum FontFamily {
|
|
4
|
+
Expressive = 'expressive',
|
|
5
|
+
Neutral = 'neutral',
|
|
6
|
+
}
|
|
7
|
+
export type FontStyle = {
|
|
8
|
+
fontSize: number;
|
|
9
|
+
lineHeight: number;
|
|
10
|
+
fontWeight: number;
|
|
11
|
+
letterSpacing?: number;
|
|
12
|
+
fontFamily: FontFamily;
|
|
13
|
+
};
|
|
14
|
+
export type FontRole = 'display' | 'headline' | 'title' | 'label' | 'body';
|
|
15
|
+
export type FontSize = 'large' | 'medium' | 'small';
|
|
16
|
+
interface FontPluginOptions {
|
|
17
|
+
fontFamily?: {
|
|
18
|
+
expressive?: string[];
|
|
19
|
+
neutral?: string[];
|
|
20
|
+
};
|
|
21
|
+
fontStyles?: Partial<Record<FontRole, Record<FontSize, Partial<FontStyle>>>>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class FontPlugin extends PluginAbstract<
|
|
25
|
+
FontPluginImpl,
|
|
26
|
+
FontPluginOptions
|
|
27
|
+
> {
|
|
28
|
+
dependencies = [];
|
|
29
|
+
name = 'font';
|
|
30
|
+
pluginClass = FontPluginImpl;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
class FontPluginImpl extends PluginImplAbstract<FontPluginOptions> {
|
|
34
|
+
private _fontFamily: { expressive: string[]; neutral: string[] } | undefined;
|
|
35
|
+
|
|
36
|
+
get fontFamily(): { expressive: string[]; neutral: string[] } {
|
|
37
|
+
if (!this._fontFamily) throw new Error('Font family not initialized');
|
|
38
|
+
return this._fontFamily;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
set fontFamily(
|
|
42
|
+
value: { expressive: string[]; neutral: string[] } | undefined
|
|
43
|
+
) {
|
|
44
|
+
this._fontFamily = value;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private _fontStyles:
|
|
48
|
+
| Record<FontRole, Record<FontSize, FontStyle>>
|
|
49
|
+
| undefined;
|
|
50
|
+
|
|
51
|
+
get fontStyles(): Record<FontRole, Record<FontSize, FontStyle>> {
|
|
52
|
+
if (!this._fontStyles) throw new Error('Font styles not initialized');
|
|
53
|
+
return this._fontStyles;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
set fontStyles(
|
|
57
|
+
value: Record<FontRole, Record<FontSize, FontStyle>> | undefined
|
|
58
|
+
) {
|
|
59
|
+
this._fontStyles = value;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
getFonts() {
|
|
63
|
+
return {
|
|
64
|
+
fontStyles: this.fontStyles,
|
|
65
|
+
fontFamily: this.fontFamily,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
onInit(): void {
|
|
70
|
+
this.fontFamily = {
|
|
71
|
+
expressive: this.options?.fontFamily?.expressive ?? [
|
|
72
|
+
'Roboto',
|
|
73
|
+
'sans-serif',
|
|
74
|
+
],
|
|
75
|
+
neutral: this.options?.fontFamily?.neutral ?? ['Roboto', 'sans-serif'],
|
|
76
|
+
};
|
|
77
|
+
this.fontStyles = {
|
|
78
|
+
display: {
|
|
79
|
+
large: {
|
|
80
|
+
fontWeight: 400,
|
|
81
|
+
fontSize: 3.5625,
|
|
82
|
+
lineHeight: 4,
|
|
83
|
+
letterSpacing: -0.015625,
|
|
84
|
+
fontFamily: FontFamily.Expressive,
|
|
85
|
+
},
|
|
86
|
+
medium: {
|
|
87
|
+
fontWeight: 400,
|
|
88
|
+
fontSize: 2.8125,
|
|
89
|
+
lineHeight: 3.25,
|
|
90
|
+
fontFamily: FontFamily.Expressive,
|
|
91
|
+
},
|
|
92
|
+
small: {
|
|
93
|
+
fontWeight: 400,
|
|
94
|
+
fontSize: 2.25,
|
|
95
|
+
lineHeight: 2.75,
|
|
96
|
+
fontFamily: FontFamily.Expressive,
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
headline: {
|
|
100
|
+
large: {
|
|
101
|
+
fontWeight: 400,
|
|
102
|
+
fontSize: 2,
|
|
103
|
+
lineHeight: 2.5,
|
|
104
|
+
fontFamily: FontFamily.Expressive,
|
|
105
|
+
},
|
|
106
|
+
medium: {
|
|
107
|
+
fontWeight: 400,
|
|
108
|
+
fontSize: 1.75,
|
|
109
|
+
lineHeight: 2.25,
|
|
110
|
+
fontFamily: FontFamily.Expressive,
|
|
111
|
+
},
|
|
112
|
+
small: {
|
|
113
|
+
fontWeight: 400,
|
|
114
|
+
fontSize: 1.5,
|
|
115
|
+
lineHeight: 2,
|
|
116
|
+
fontFamily: FontFamily.Expressive,
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
title: {
|
|
120
|
+
large: {
|
|
121
|
+
fontWeight: 400,
|
|
122
|
+
fontSize: 1.375,
|
|
123
|
+
lineHeight: 1.75,
|
|
124
|
+
fontFamily: FontFamily.Neutral,
|
|
125
|
+
},
|
|
126
|
+
medium: {
|
|
127
|
+
fontWeight: 500,
|
|
128
|
+
fontSize: 1,
|
|
129
|
+
lineHeight: 1.5,
|
|
130
|
+
fontFamily: FontFamily.Neutral,
|
|
131
|
+
letterSpacing: 0.009375,
|
|
132
|
+
},
|
|
133
|
+
small: {
|
|
134
|
+
fontWeight: 500,
|
|
135
|
+
fontSize: 0.875,
|
|
136
|
+
lineHeight: 1.25,
|
|
137
|
+
fontFamily: FontFamily.Neutral,
|
|
138
|
+
letterSpacing: 0.00625,
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
label: {
|
|
142
|
+
large: {
|
|
143
|
+
fontWeight: 500,
|
|
144
|
+
fontSize: 0.875,
|
|
145
|
+
lineHeight: 1.25,
|
|
146
|
+
fontFamily: FontFamily.Neutral,
|
|
147
|
+
letterSpacing: 0.00625,
|
|
148
|
+
},
|
|
149
|
+
medium: {
|
|
150
|
+
fontWeight: 500,
|
|
151
|
+
fontSize: 0.75,
|
|
152
|
+
lineHeight: 1,
|
|
153
|
+
fontFamily: FontFamily.Neutral,
|
|
154
|
+
letterSpacing: 0.03125,
|
|
155
|
+
},
|
|
156
|
+
small: {
|
|
157
|
+
fontWeight: 500,
|
|
158
|
+
fontSize: 0.6875,
|
|
159
|
+
lineHeight: 1,
|
|
160
|
+
fontFamily: FontFamily.Neutral,
|
|
161
|
+
letterSpacing: 0.03125,
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
body: {
|
|
165
|
+
large: {
|
|
166
|
+
fontWeight: 400,
|
|
167
|
+
fontSize: 1,
|
|
168
|
+
lineHeight: 1.5625,
|
|
169
|
+
fontFamily: FontFamily.Neutral,
|
|
170
|
+
letterSpacing: 0.03125,
|
|
171
|
+
},
|
|
172
|
+
medium: {
|
|
173
|
+
fontWeight: 400,
|
|
174
|
+
fontSize: 0.875,
|
|
175
|
+
lineHeight: 1.25,
|
|
176
|
+
fontFamily: FontFamily.Neutral,
|
|
177
|
+
letterSpacing: 0.015625,
|
|
178
|
+
},
|
|
179
|
+
small: {
|
|
180
|
+
fontWeight: 400,
|
|
181
|
+
fontSize: 0.75,
|
|
182
|
+
lineHeight: 1,
|
|
183
|
+
fontFamily: FontFamily.Neutral,
|
|
184
|
+
letterSpacing: 0.025,
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
if (this.options && this.options.fontStyles)
|
|
190
|
+
Object.entries(this.options.fontStyles).forEach(([key, fontParam]) => {
|
|
191
|
+
const fontRole: FontRole = key as FontRole;
|
|
192
|
+
Object.entries(fontParam).forEach(([size, fontStyle]) => {
|
|
193
|
+
const fontSize: FontSize = size as FontSize;
|
|
194
|
+
if (fontStyle) {
|
|
195
|
+
this.fontStyles[fontRole][fontSize] = {
|
|
196
|
+
...this.fontStyles[fontRole][fontSize],
|
|
197
|
+
...fontStyle,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './font.plugin';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { PluginsConfig } from 'tailwindcss/types/config';
|
|
2
|
+
import { bootstrapFromConfig } from '../../main';
|
|
3
|
+
import { AppService } from '../../app.service';
|
|
4
|
+
import { TailwindPlugin } from './tailwind.plugin';
|
|
5
|
+
|
|
6
|
+
export type Theme = {
|
|
7
|
+
colors: Record<string, string>;
|
|
8
|
+
fontFamily: { expressive: string[]; neutral: string[] };
|
|
9
|
+
plugins: Partial<PluginsConfig>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const createTheme = (): Theme & { appService: AppService } => {
|
|
13
|
+
const app = bootstrapFromConfig();
|
|
14
|
+
const plugin = app.pluginService.getPlugin(TailwindPlugin).getInstance();
|
|
15
|
+
return { ...plugin.getTheme(), appService: app };
|
|
16
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import plugin from 'tailwindcss/plugin';
|
|
2
|
+
import { CSSRuleObject, PluginAPI } from 'tailwindcss/types/config';
|
|
3
|
+
import { FontRole, FontSize, FontStyle } from '../../font/font.plugin';
|
|
4
|
+
|
|
5
|
+
export const font = (
|
|
6
|
+
fontStyles: Record<FontRole, Record<FontSize, FontStyle>>,
|
|
7
|
+
responsiveBreakPoints: Record<string, number>
|
|
8
|
+
) => {
|
|
9
|
+
const createUtilities = ({
|
|
10
|
+
theme,
|
|
11
|
+
}: Pick<PluginAPI, 'theme'>): CSSRuleObject => {
|
|
12
|
+
const pixelUnit = 'rem';
|
|
13
|
+
const newUtilities: any = {};
|
|
14
|
+
|
|
15
|
+
const baseTextStyle = (sizeValue: FontStyle) => ({
|
|
16
|
+
fontSize: sizeValue.fontSize + pixelUnit,
|
|
17
|
+
fontWeight: sizeValue.fontWeight as unknown as CSSRuleObject,
|
|
18
|
+
lineHeight: sizeValue.lineHeight + pixelUnit,
|
|
19
|
+
letterSpacing: sizeValue.letterSpacing
|
|
20
|
+
? sizeValue.letterSpacing + pixelUnit
|
|
21
|
+
: null,
|
|
22
|
+
fontFamily: theme('fontFamily.' + sizeValue.fontFamily),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const responsiveTextStyle = (
|
|
26
|
+
sizeValue: FontStyle,
|
|
27
|
+
breakPointName: string,
|
|
28
|
+
breakPointRatio: number
|
|
29
|
+
) => ({
|
|
30
|
+
[`@media (min-width: ${theme('screens.' + breakPointName, {})})`]: {
|
|
31
|
+
fontSize: sizeValue.fontSize * breakPointRatio + pixelUnit,
|
|
32
|
+
lineHeight: sizeValue.lineHeight * breakPointRatio + pixelUnit,
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
for (const [roleName, roleValue] of Object.entries(fontStyles)) {
|
|
37
|
+
for (const [sizeName, sizeValue] of Object.entries(roleValue)) {
|
|
38
|
+
newUtilities['.text-' + roleName + '-' + sizeName] = {
|
|
39
|
+
...baseTextStyle(sizeValue),
|
|
40
|
+
...Object.entries(responsiveBreakPoints).reduce(
|
|
41
|
+
(acc, [breakPointName, breakPointRatio]) => {
|
|
42
|
+
acc = {
|
|
43
|
+
...acc,
|
|
44
|
+
...responsiveTextStyle(
|
|
45
|
+
sizeValue,
|
|
46
|
+
breakPointName,
|
|
47
|
+
breakPointRatio
|
|
48
|
+
),
|
|
49
|
+
};
|
|
50
|
+
return acc;
|
|
51
|
+
},
|
|
52
|
+
{}
|
|
53
|
+
),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return newUtilities as CSSRuleObject;
|
|
59
|
+
};
|
|
60
|
+
return plugin(
|
|
61
|
+
({
|
|
62
|
+
addUtilities,
|
|
63
|
+
theme,
|
|
64
|
+
}: Pick<PluginAPI, 'theme'> & Pick<PluginAPI, 'addUtilities'>) => {
|
|
65
|
+
const newUtilities = createUtilities({ theme });
|
|
66
|
+
addUtilities(newUtilities);
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// from tailwindcss src/util/flattenColors
|
|
2
|
+
import plugin from 'tailwindcss/plugin';
|
|
3
|
+
import { PluginAPI } from 'tailwindcss/types/config';
|
|
4
|
+
|
|
5
|
+
type State = {
|
|
6
|
+
statePrefix: string;
|
|
7
|
+
disabledStyles: {
|
|
8
|
+
textOpacity: number;
|
|
9
|
+
backgroundOpacity: number;
|
|
10
|
+
};
|
|
11
|
+
transition: {
|
|
12
|
+
duration: number;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type Components = Record<string, Record<string, {}>>;
|
|
17
|
+
|
|
18
|
+
export const state = (colorkeys: string[]) =>
|
|
19
|
+
plugin((pluginArgs: PluginAPI) => {
|
|
20
|
+
addAllNewComponents(
|
|
21
|
+
pluginArgs,
|
|
22
|
+
{
|
|
23
|
+
statePrefix: 'state',
|
|
24
|
+
disabledStyles: {
|
|
25
|
+
textOpacity: 0.38,
|
|
26
|
+
backgroundOpacity: 0.12,
|
|
27
|
+
},
|
|
28
|
+
transition: {
|
|
29
|
+
duration: 150,
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
colorkeys
|
|
33
|
+
);
|
|
34
|
+
}, {});
|
|
35
|
+
|
|
36
|
+
const addAllNewComponents = (
|
|
37
|
+
{ addComponents }: PluginAPI,
|
|
38
|
+
{ statePrefix, disabledStyles, transition }: State,
|
|
39
|
+
colorKeys: string[]
|
|
40
|
+
) => {
|
|
41
|
+
const newComponents: Components = {};
|
|
42
|
+
|
|
43
|
+
for (const isGroup of [false, true]) {
|
|
44
|
+
const group = isGroup ? 'group-' : '';
|
|
45
|
+
for (const colorName of colorKeys) {
|
|
46
|
+
const className = `.${group}${statePrefix}-${colorName}`;
|
|
47
|
+
newComponents[className] = {
|
|
48
|
+
[`@apply ${group}hover:bg-${colorName}/[0.08]`]: {},
|
|
49
|
+
[`@apply ${group}active:bg-${colorName}/[0.12]`]: {},
|
|
50
|
+
[`@apply ${group}focus-visible:bg-${colorName}/[0.12]`]: {},
|
|
51
|
+
};
|
|
52
|
+
if (transition) {
|
|
53
|
+
newComponents[className][`@apply transition-colors`] = {};
|
|
54
|
+
newComponents[className][`@apply duration-${transition.duration}`] = {};
|
|
55
|
+
}
|
|
56
|
+
if (disabledStyles) {
|
|
57
|
+
newComponents[className][
|
|
58
|
+
`@apply ${group}disabled:text-on-surface/[${disabledStyles.textOpacity}]`
|
|
59
|
+
] = {};
|
|
60
|
+
newComponents[className][
|
|
61
|
+
`@apply ${group}disabled:bg-on-surface/[${disabledStyles.backgroundOpacity}]`
|
|
62
|
+
] = {};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
for (const colorName of colorKeys) {
|
|
67
|
+
for (const stateName of ['hover', 'active', 'focus', 'disabled']) {
|
|
68
|
+
const className = `.${stateName}-${statePrefix}-${colorName}`;
|
|
69
|
+
if (stateName === 'active' || stateName === 'focus') {
|
|
70
|
+
newComponents[className] = {
|
|
71
|
+
[`@apply bg-${colorName}/[0.12]`]: {},
|
|
72
|
+
};
|
|
73
|
+
} else if (stateName === 'hover') {
|
|
74
|
+
newComponents[className] = {
|
|
75
|
+
[`@apply bg-${colorName}/[0.08]`]: {},
|
|
76
|
+
};
|
|
77
|
+
} else if (stateName === 'disabled') {
|
|
78
|
+
newComponents[className] = {
|
|
79
|
+
[`@apply text-on-surface/[${disabledStyles.textOpacity}]`]: {},
|
|
80
|
+
};
|
|
81
|
+
newComponents[className] = {
|
|
82
|
+
[`@apply bg-on-surface/[${disabledStyles.backgroundOpacity}]`]: {},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
addComponents(newComponents);
|
|
88
|
+
};
|