@udixio/theme 1.0.0-beta.2 → 1.0.0-beta.20

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.
Files changed (97) hide show
  1. package/dist/app.container.d.ts +5 -0
  2. package/dist/app.module.d.ts +2 -2
  3. package/dist/app.service.d.ts +9 -3
  4. package/dist/color/color.interface.d.ts +1 -2
  5. package/dist/color/color.module.d.ts +2 -2
  6. package/dist/color/entities/color.entity.d.ts +6 -1
  7. package/dist/color/entities/index.d.ts +1 -0
  8. package/dist/color/index.d.ts +5 -0
  9. package/dist/color/models/default-color.model.d.ts +2 -3
  10. package/dist/color/models/index.d.ts +1 -0
  11. package/dist/color/services/color-manager.service.d.ts +18 -0
  12. package/dist/color/services/color.service.d.ts +21 -0
  13. package/dist/color/services/index.d.ts +2 -0
  14. package/dist/config/config.interface.d.ts +13 -0
  15. package/dist/config/config.module.d.ts +2 -0
  16. package/dist/config/config.service.d.ts +12 -0
  17. package/dist/config/index.d.ts +3 -0
  18. package/dist/index.d.ts +11 -0
  19. package/dist/main.d.ts +2 -1
  20. package/dist/material-color-utilities/index.d.ts +1 -0
  21. package/dist/plugin/index.d.ts +3 -0
  22. package/dist/plugin/plugin.abstract.d.ts +7 -0
  23. package/dist/plugin/plugin.module.d.ts +2 -0
  24. package/dist/plugin/plugin.service.d.ts +12 -0
  25. package/dist/plugins/font/font.plugin.d.ts +38 -0
  26. package/dist/plugins/font/index.d.ts +1 -0
  27. package/dist/plugins/tailwind/index.d.ts +3 -0
  28. package/dist/plugins/tailwind/main.d.ts +13 -0
  29. package/dist/plugins/tailwind/plugins-tailwind/font.d.ts +5 -0
  30. package/dist/plugins/tailwind/plugins-tailwind/index.d.ts +2 -0
  31. package/dist/plugins/tailwind/plugins-tailwind/state.d.ts +4 -0
  32. package/dist/plugins/tailwind/plugins-tailwind/themer.d.ts +4 -0
  33. package/dist/plugins/tailwind/tailwind.plugin.d.ts +17 -0
  34. package/dist/theme/entities/index.d.ts +2 -0
  35. package/dist/theme/entities/variant.entity.d.ts +2 -1
  36. package/dist/theme/index.d.ts +4 -0
  37. package/dist/theme/models/index.d.ts +1 -0
  38. package/dist/theme/services/index.d.ts +3 -0
  39. package/dist/theme/services/scheme.service.d.ts +7 -2
  40. package/dist/theme/services/theme.service.d.ts +16 -7
  41. package/dist/theme/services/variant.service.d.ts +8 -2
  42. package/dist/theme/theme.module.d.ts +2 -2
  43. package/dist/theme.cjs.development.js +1316 -579
  44. package/dist/theme.cjs.development.js.map +1 -1
  45. package/dist/theme.cjs.production.min.js +1 -1
  46. package/dist/theme.cjs.production.min.js.map +1 -1
  47. package/dist/theme.esm.js +1284 -580
  48. package/dist/theme.esm.js.map +1 -1
  49. package/package.json +21 -22
  50. package/src/app.container.ts +46 -0
  51. package/src/app.module.ts +5 -8
  52. package/src/app.service.spec.ts +1 -1
  53. package/src/app.service.ts +20 -8
  54. package/src/color/color.interface.ts +1 -3
  55. package/src/color/color.module.ts +8 -10
  56. package/src/color/entities/color.entity.ts +14 -3
  57. package/src/color/entities/index.ts +1 -0
  58. package/src/color/index.ts +5 -0
  59. package/src/color/models/default-color.model.ts +205 -202
  60. package/src/color/models/index.ts +1 -0
  61. package/src/color/{color-manager.service.ts → services/color-manager.service.ts} +22 -17
  62. package/src/color/{color.service.spec.ts → services/color.service.spec.ts} +1 -1
  63. package/src/color/services/color.service.ts +75 -0
  64. package/src/color/services/index.ts +2 -0
  65. package/src/config/config.interface.ts +14 -0
  66. package/src/config/config.module.ts +7 -0
  67. package/src/config/config.service.ts +95 -0
  68. package/src/config/index.ts +3 -0
  69. package/src/index.ts +11 -0
  70. package/src/main.ts +10 -7
  71. package/src/material-color-utilities/index.ts +1 -0
  72. package/src/plugin/index.ts +3 -0
  73. package/src/plugin/plugin.abstract.ts +9 -0
  74. package/src/plugin/plugin.module.ts +7 -0
  75. package/src/plugin/plugin.service.ts +48 -0
  76. package/src/plugins/font/font.plugin.ts +172 -0
  77. package/src/plugins/font/index.ts +1 -0
  78. package/src/plugins/tailwind/index.ts +3 -0
  79. package/src/plugins/tailwind/main.ts +19 -0
  80. package/src/plugins/tailwind/plugins-tailwind/font.ts +69 -0
  81. package/src/plugins/tailwind/plugins-tailwind/index.ts +2 -0
  82. package/src/plugins/tailwind/plugins-tailwind/state.ts +88 -0
  83. package/src/plugins/tailwind/plugins-tailwind/themer.ts +53 -0
  84. package/src/plugins/tailwind/tailwind.plugin.ts +64 -0
  85. package/src/theme/entities/index.ts +2 -0
  86. package/src/theme/entities/variant.entity.ts +2 -1
  87. package/src/theme/index.ts +4 -0
  88. package/src/theme/models/index.ts +1 -0
  89. package/src/theme/models/variant.model.ts +7 -0
  90. package/src/theme/services/index.ts +3 -0
  91. package/src/theme/services/scheme.service.ts +39 -11
  92. package/src/theme/services/theme.service.ts +30 -14
  93. package/src/theme/services/variant.service.ts +40 -5
  94. package/src/theme/theme.module.ts +8 -9
  95. package/dist/color/color-manager.service.d.ts +0 -15
  96. package/dist/color/color.service.d.ts +0 -13
  97. package/src/color/color.service.ts +0 -52
@@ -0,0 +1,3 @@
1
+ export * from './config.interface';
2
+ export * from './config.module';
3
+ export * from './config.service';
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,14 @@
1
- import { NestFactory } from '@nestjs/core';
2
- import { AppModule } from './app.module';
1
+ import AppContainer from './app.container';
3
2
  import { AppService } from './app.service';
3
+ import { ConfigService } from './config';
4
4
 
5
- export async function main(): Promise<[AppService, () => Promise<void>]> {
6
- const app = await NestFactory.create(AppModule);
7
- const appService = app.get(AppService);
5
+ export function bootstrap(): AppService {
6
+ return AppContainer.resolve<AppService>('appService');
7
+ }
8
8
 
9
- const close = () => app.close();
10
- return [appService, close];
9
+ export function bootstrapFromConfig(path?: string): AppService {
10
+ const configService = AppContainer.resolve<ConfigService>('configService');
11
+ if (path) configService.configPath = path;
12
+ configService.loadConfig();
13
+ return AppContainer.resolve<AppService>('appService');
11
14
  }
@@ -1,2 +1,3 @@
1
1
  export * from './contrastCurve';
2
+ export * from './dynamic_color';
2
3
  export * from './toneDeltaPair';
@@ -0,0 +1,3 @@
1
+ export * from './plugin.abstract';
2
+ export * from './plugin.module';
3
+ export * from './plugin.service';
@@ -0,0 +1,9 @@
1
+ import { AppService } from '../app.service';
2
+ import { PluginConstructor } from './plugin.service';
3
+
4
+ export abstract class PluginAbstract {
5
+ static dependencies: PluginConstructor[] = [];
6
+
7
+ protected abstract appService: AppService;
8
+ protected abstract options: object;
9
+ }
@@ -0,0 +1,7 @@
1
+ import { asClass } from 'awilix';
2
+ import { Module } from '../app.container';
3
+ import { PluginService } from './plugin.service';
4
+
5
+ export const PluginModule: Module = {
6
+ pluginService: asClass(PluginService).singleton(),
7
+ };
@@ -0,0 +1,48 @@
1
+ import { PluginAbstract } from './plugin.abstract';
2
+ import { AppService } from '../app.service';
3
+
4
+ export type PluginConstructor = (new (
5
+ appService: AppService,
6
+ options: any
7
+ ) => PluginAbstract) & { dependencies: PluginConstructor[] };
8
+
9
+ export class PluginService {
10
+ private pluginInstances = new Map<string, PluginAbstract>();
11
+ private pluginConstructors = new Map<string, [PluginConstructor, object]>();
12
+
13
+ public addPlugin(plugin: PluginConstructor, config: object) {
14
+ this.pluginConstructors.set(plugin.name, [plugin, config]);
15
+ }
16
+
17
+ public loadPlugins(appService: AppService) {
18
+ const plugins = new Map(this.pluginConstructors);
19
+
20
+ let size = 0;
21
+ do {
22
+ size = plugins.size;
23
+ plugins.forEach(([plugin, option], key) => {
24
+ const deps = plugin.dependencies.filter(
25
+ (dep) => !this.pluginInstances.has(dep.name)
26
+ );
27
+ if (deps.length === 0) {
28
+ this.pluginInstances.set(plugin.name, new plugin(appService, option));
29
+ plugins.delete(key);
30
+ }
31
+ });
32
+ } while (plugins.size != 0 && plugins.size < size);
33
+
34
+ if (plugins.size > 0)
35
+ console.log(
36
+ "Some plugins couldn't be loaded due to missing dependencies: ",
37
+ Array.from(plugins.keys())
38
+ );
39
+ }
40
+
41
+ public getPlugin<T extends PluginAbstract>(
42
+ plugin: new (appService: AppService, options: any) => T
43
+ ): T {
44
+ const pluginInstance = this.pluginInstances.get(plugin.name);
45
+ if (!pluginInstance) throw new Error(`Plugin ${plugin.name} not found`);
46
+ return pluginInstance as T;
47
+ }
48
+ }
@@ -0,0 +1,172 @@
1
+ import { PluginAbstract } from '../../plugin';
2
+ import { AppService } from '../../app.service';
3
+
4
+ export enum FontFamily {
5
+ Expressive = 'expressive',
6
+ Neutral = 'neutral',
7
+ }
8
+ export type FontStyle = {
9
+ fontSize: number;
10
+ lineHeight: number;
11
+ fontWeight: number;
12
+ letterSpacing?: number;
13
+ fontFamily: FontFamily;
14
+ };
15
+ export type FontRole = 'display' | 'headline' | 'title' | 'label' | 'body';
16
+ export type FontSize = 'large' | 'medium' | 'small';
17
+ interface FontPluginOptions {
18
+ fontFamily?: {
19
+ expressive?: string[];
20
+ neutral?: string[];
21
+ };
22
+ fontStyles?: Partial<Record<FontRole, Record<FontSize, Partial<FontStyle>>>>;
23
+ }
24
+
25
+ export class FontPlugin extends PluginAbstract {
26
+ private readonly fontFamily: { expressive: string[]; neutral: string[] };
27
+ private readonly fontStyles: Record<FontRole, Record<FontSize, FontStyle>>;
28
+
29
+ constructor(
30
+ protected appService: AppService,
31
+ protected options: FontPluginOptions
32
+ ) {
33
+ super();
34
+ this.fontFamily = {
35
+ expressive: options?.fontFamily?.expressive ?? ['Roboto', 'sans-serif'],
36
+ neutral: options?.fontFamily?.neutral ?? ['Roboto', 'sans-serif'],
37
+ };
38
+ this.fontStyles = {
39
+ display: {
40
+ large: {
41
+ fontWeight: 400,
42
+ fontSize: 3.5625,
43
+ lineHeight: 4,
44
+ letterSpacing: -0.015625,
45
+ fontFamily: FontFamily.Expressive,
46
+ },
47
+ medium: {
48
+ fontWeight: 400,
49
+ fontSize: 2.8125,
50
+ lineHeight: 3.25,
51
+ fontFamily: FontFamily.Expressive,
52
+ },
53
+ small: {
54
+ fontWeight: 400,
55
+ fontSize: 2.25,
56
+ lineHeight: 2.75,
57
+ fontFamily: FontFamily.Expressive,
58
+ },
59
+ },
60
+ headline: {
61
+ large: {
62
+ fontWeight: 400,
63
+ fontSize: 2,
64
+ lineHeight: 2.5,
65
+ fontFamily: FontFamily.Expressive,
66
+ },
67
+ medium: {
68
+ fontWeight: 400,
69
+ fontSize: 1.75,
70
+ lineHeight: 2.25,
71
+ fontFamily: FontFamily.Expressive,
72
+ },
73
+ small: {
74
+ fontWeight: 400,
75
+ fontSize: 1.5,
76
+ lineHeight: 2,
77
+ fontFamily: FontFamily.Expressive,
78
+ },
79
+ },
80
+ title: {
81
+ large: {
82
+ fontWeight: 400,
83
+ fontSize: 1.375,
84
+ lineHeight: 1.75,
85
+ fontFamily: FontFamily.Neutral,
86
+ },
87
+ medium: {
88
+ fontWeight: 500,
89
+ fontSize: 1,
90
+ lineHeight: 1.5,
91
+ fontFamily: FontFamily.Neutral,
92
+ letterSpacing: 0.009375,
93
+ },
94
+ small: {
95
+ fontWeight: 500,
96
+ fontSize: 0.875,
97
+ lineHeight: 1.25,
98
+ fontFamily: FontFamily.Neutral,
99
+ letterSpacing: 0.00625,
100
+ },
101
+ },
102
+ label: {
103
+ large: {
104
+ fontWeight: 500,
105
+ fontSize: 0.875,
106
+ lineHeight: 1.25,
107
+ fontFamily: FontFamily.Neutral,
108
+ letterSpacing: 0.00625,
109
+ },
110
+ medium: {
111
+ fontWeight: 500,
112
+ fontSize: 0.75,
113
+ lineHeight: 1,
114
+ fontFamily: FontFamily.Neutral,
115
+ letterSpacing: 0.03125,
116
+ },
117
+ small: {
118
+ fontWeight: 500,
119
+ fontSize: 0.6875,
120
+ lineHeight: 1,
121
+ fontFamily: FontFamily.Neutral,
122
+ letterSpacing: 0.03125,
123
+ },
124
+ },
125
+ body: {
126
+ large: {
127
+ fontWeight: 400,
128
+ fontSize: 1,
129
+ lineHeight: 1.5625,
130
+ fontFamily: FontFamily.Neutral,
131
+ letterSpacing: 0.03125,
132
+ },
133
+ medium: {
134
+ fontWeight: 400,
135
+ fontSize: 0.875,
136
+ lineHeight: 1.25,
137
+ fontFamily: FontFamily.Neutral,
138
+ letterSpacing: 0.015625,
139
+ },
140
+ small: {
141
+ fontWeight: 400,
142
+ fontSize: 0.75,
143
+ lineHeight: 1,
144
+ fontFamily: FontFamily.Neutral,
145
+ letterSpacing: 0.025,
146
+ },
147
+ },
148
+ };
149
+ if (options && options.fontStyles)
150
+ Object.entries(options.fontStyles).forEach(([key, fontParam]) => {
151
+ const fontRole: FontRole = key as FontRole;
152
+ Object.entries(fontParam).forEach(([size, fontStyle]) => {
153
+ const fontSize: FontSize = size as FontSize;
154
+ if (fontStyle) {
155
+ this.fontStyles[fontRole][fontSize] = {
156
+ ...this.fontStyles[fontRole][fontSize],
157
+ ...fontStyle,
158
+ };
159
+ }
160
+ });
161
+ });
162
+ }
163
+ static config(options: FontPluginOptions): FontPluginOptions {
164
+ return options;
165
+ }
166
+ getFonts() {
167
+ return {
168
+ fontStyles: this.fontStyles,
169
+ fontFamily: this.fontFamily,
170
+ };
171
+ }
172
+ }
@@ -0,0 +1 @@
1
+ export * from './font.plugin';
@@ -0,0 +1,3 @@
1
+ export * from './main';
2
+ export * from './plugins-tailwind';
3
+ export * from './tailwind.plugin';
@@ -0,0 +1,19 @@
1
+ import { PluginsConfig } from 'tailwindcss/types/config';
2
+ import { bootstrapFromConfig } from '../../main';
3
+ import { TailwindPlugin } from './tailwind.plugin';
4
+ import { AppService } from '../../app.service';
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);
15
+ if (!plugin) {
16
+ throw new Error('Tailwind plugin not found');
17
+ }
18
+ return { ...plugin.getTheme(), appService: app };
19
+ };
@@ -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,2 @@
1
+ export * from './state';
2
+ export * from './themer';
@@ -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
+ };
@@ -0,0 +1,53 @@
1
+ export const themer = (
2
+ colors: Record<
3
+ string,
4
+ {
5
+ light: string;
6
+ dark: string;
7
+ }
8
+ >,
9
+ darkMode: 'class' | 'media'
10
+ ) => {
11
+ const options: {
12
+ defaultTheme: {
13
+ extend: {
14
+ colors: Record<string, string>;
15
+ };
16
+ };
17
+ themes: [
18
+ {
19
+ name: 'darkTheme';
20
+ selectors?: ['.dark-mode', '[data-theme="dark"]'];
21
+ mediaQuery?: '@media (prefers-color-scheme: dark)';
22
+ extend: {
23
+ colors: Record<string, string>;
24
+ };
25
+ }
26
+ ];
27
+ } = {
28
+ defaultTheme: {
29
+ extend: {
30
+ colors: {},
31
+ },
32
+ },
33
+ themes: [
34
+ {
35
+ name: 'darkTheme',
36
+ extend: {
37
+ colors: {},
38
+ },
39
+ },
40
+ ],
41
+ };
42
+
43
+ Object.entries(colors).forEach(([key, value]) => {
44
+ options.defaultTheme.extend.colors[key] = value.light;
45
+ options.themes[0].extend.colors[key] = value.dark;
46
+ });
47
+ options.themes[0].selectors =
48
+ darkMode === 'class' ? ['.dark-mode', '[data-theme="dark"]'] : undefined;
49
+ options.themes[0].mediaQuery =
50
+ darkMode === 'media' ? '@media (prefers-color-scheme: dark)' : undefined;
51
+
52
+ return require('tailwindcss-themer')(options);
53
+ };
@@ -0,0 +1,64 @@
1
+ import { PluginAbstract } from '../../plugin/plugin.abstract';
2
+ import { AppService } from '../../app.service';
3
+ import { Theme } from './main';
4
+ import { state } from './plugins-tailwind/state';
5
+ import { themer } from './plugins-tailwind/themer';
6
+ import { FontPlugin } from '../font/font.plugin';
7
+ import { font } from './plugins-tailwind/font';
8
+
9
+ interface TailwindPluginOptions {
10
+ darkMode?: 'class' | 'media';
11
+ responsiveBreakPoints?: Record<string, number>;
12
+ }
13
+
14
+ export class TailwindPlugin extends PluginAbstract {
15
+ static dependencies = [FontPlugin];
16
+ constructor(
17
+ protected appService: AppService,
18
+ protected options: TailwindPluginOptions
19
+ ) {
20
+ options.darkMode ??= 'class';
21
+ options.responsiveBreakPoints ??= {
22
+ lg: 1.125,
23
+ };
24
+ super();
25
+ }
26
+ static config(options: TailwindPluginOptions): TailwindPluginOptions {
27
+ return options;
28
+ }
29
+ getTheme(): Theme {
30
+ const colors: Record<
31
+ string,
32
+ {
33
+ light: string;
34
+ dark: string;
35
+ }
36
+ > = {};
37
+
38
+ for (const isDark of [false, true]) {
39
+ this.appService.themeService.update({ isDark: isDark });
40
+ for (const [key, value] of this.appService.colorService
41
+ .getColors()
42
+ .entries()) {
43
+ const newKey = key
44
+ .replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2')
45
+ .toLowerCase();
46
+ colors[newKey] ??= { light: '', dark: '' };
47
+ colors[newKey][isDark ? 'dark' : 'light'] = value.getHex();
48
+ }
49
+ }
50
+
51
+ const { fontStyles, fontFamily } = this.appService.pluginService
52
+ .getPlugin(FontPlugin)
53
+ .getFonts();
54
+ return {
55
+ colors: {},
56
+ fontFamily: fontFamily,
57
+ plugins: [
58
+ state(Object.keys(colors)),
59
+ themer(colors, this.options.darkMode!),
60
+ font(fontStyles, this.options.responsiveBreakPoints!),
61
+ ],
62
+ };
63
+ }
64
+ }
@@ -0,0 +1,2 @@
1
+ export * from './scheme.entity';
2
+ export * from './variant.entity';
@@ -33,6 +33,7 @@ export const getRotatedHue = (
33
33
 
34
34
  export class VariantEntity {
35
35
  constructor(
36
- public palettes: Record<string, (sourceColorHct: Hct) => TonalPalette> = {}
36
+ public palettes: Record<string, (sourceColorHct: Hct) => TonalPalette> = {},
37
+ public customPalettes?: (colorHct: Hct) => TonalPalette
37
38
  ) {}
38
39
  }
@@ -0,0 +1,4 @@
1
+ export * from './entities';
2
+ export * from './models';
3
+ export * from './services';
4
+ export * from './theme.module';
@@ -0,0 +1 @@
1
+ export * from './variant.model';
@@ -21,6 +21,8 @@ export class VariantModel {
21
21
  neutralVariant: (sourceColorHct) =>
22
22
  TonalPalette.fromHueAndChroma(sourceColorHct.hue, 8.0),
23
23
  },
24
+ customPalettes: (colorHct) =>
25
+ TonalPalette.fromHueAndChroma(colorHct.hue, 16),
24
26
  };
25
27
  static vibrant: VariantEntity = {
26
28
  palettes: {
@@ -41,6 +43,11 @@ export class VariantModel {
41
43
  neutralVariant: (sourceColorHct) =>
42
44
  TonalPalette.fromHueAndChroma(sourceColorHct.hue, 8.0),
43
45
  },
46
+ customPalettes: (colorHct) =>
47
+ TonalPalette.fromHueAndChroma(
48
+ getRotatedHue(colorHct, this.hues, this.secondaryRotations),
49
+ 24.0
50
+ ),
44
51
  };
45
52
 
46
53
  private static readonly hues = [
@@ -0,0 +1,3 @@
1
+ export * from './scheme.service';
2
+ export * from './theme.service';
3
+ export * from './variant.service';