@udixio/theme 1.0.0-beta.9 → 1.1.0

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 (205) hide show
  1. package/.eslintrc.mjs +22 -0
  2. package/CHANGELOG.md +186 -0
  3. package/README.md +6 -68
  4. package/dist/API.d.ts +14 -0
  5. package/dist/API.d.ts.map +1 -0
  6. package/dist/LICENSE +202 -0
  7. package/dist/adapter/adapter.abstract.d.ts +10 -0
  8. package/dist/adapter/adapter.abstract.d.ts.map +1 -0
  9. package/dist/{config → adapter}/config.interface.d.ts +6 -5
  10. package/dist/adapter/config.interface.d.ts.map +1 -0
  11. package/dist/adapter/define-config.d.ts +3 -0
  12. package/dist/adapter/define-config.d.ts.map +1 -0
  13. package/dist/adapter/file-adapter.mixin.d.ts +18 -0
  14. package/dist/adapter/file-adapter.mixin.d.ts.map +1 -0
  15. package/dist/adapter/index.d.ts +4 -0
  16. package/dist/adapter/index.d.ts.map +1 -0
  17. package/dist/adapters/index.d.ts +3 -0
  18. package/dist/adapters/index.d.ts.map +1 -0
  19. package/dist/adapters/node.adapter.d.ts +7 -0
  20. package/dist/adapters/node.adapter.d.ts.map +1 -0
  21. package/dist/adapters/vite.adapter.d.ts +3 -0
  22. package/dist/adapters/vite.adapter.d.ts.map +1 -0
  23. package/dist/app.container.d.ts +5 -5
  24. package/dist/app.container.d.ts.map +1 -0
  25. package/dist/app.module.d.ts +1 -0
  26. package/dist/app.module.d.ts.map +1 -0
  27. package/dist/bootstrap.d.ts +3 -0
  28. package/dist/bootstrap.d.ts.map +1 -0
  29. package/dist/color/color.api.d.ts +39 -0
  30. package/dist/color/color.api.d.ts.map +1 -0
  31. package/dist/color/color.manager.d.ts +24 -0
  32. package/dist/color/color.manager.d.ts.map +1 -0
  33. package/dist/color/color.module.d.ts +1 -0
  34. package/dist/color/color.module.d.ts.map +1 -0
  35. package/dist/color/color.utils.d.ts +8 -0
  36. package/dist/color/color.utils.d.ts.map +1 -0
  37. package/dist/color/configurable-color.d.ts +31 -0
  38. package/dist/color/configurable-color.d.ts.map +1 -0
  39. package/dist/color/default-color.d.ts +3 -0
  40. package/dist/color/default-color.d.ts.map +1 -0
  41. package/dist/color/index.d.ts +5 -4
  42. package/dist/color/index.d.ts.map +1 -0
  43. package/dist/index.cjs +3224 -0
  44. package/dist/index.d.ts +7 -4
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +3183 -7
  47. package/dist/material-color-utilities/contrastCurve.d.ts +1 -0
  48. package/dist/material-color-utilities/contrastCurve.d.ts.map +1 -0
  49. package/dist/material-color-utilities/dynamic_color.d.ts +89 -76
  50. package/dist/material-color-utilities/dynamic_color.d.ts.map +1 -0
  51. package/dist/material-color-utilities/hct_solver.d.ts +131 -0
  52. package/dist/material-color-utilities/hct_solver.d.ts.map +1 -0
  53. package/dist/material-color-utilities/htc.d.ts +77 -0
  54. package/dist/material-color-utilities/htc.d.ts.map +1 -0
  55. package/dist/material-color-utilities/index.d.ts +1 -0
  56. package/dist/material-color-utilities/index.d.ts.map +1 -0
  57. package/dist/material-color-utilities/toneDeltaPair.d.ts +19 -25
  58. package/dist/material-color-utilities/toneDeltaPair.d.ts.map +1 -0
  59. package/dist/plugin/index.d.ts +4 -0
  60. package/dist/plugin/index.d.ts.map +1 -0
  61. package/dist/plugin/plugin.abstract.d.ts +19 -5
  62. package/dist/plugin/plugin.abstract.d.ts.map +1 -0
  63. package/dist/plugin/plugin.api.d.ts +10 -0
  64. package/dist/plugin/plugin.api.d.ts.map +1 -0
  65. package/dist/plugin/plugin.module.d.ts +1 -0
  66. package/dist/plugin/plugin.module.d.ts.map +1 -0
  67. package/dist/plugins/font/font.plugin.d.ts +50 -0
  68. package/dist/plugins/font/font.plugin.d.ts.map +1 -0
  69. package/dist/plugins/font/index.d.ts +2 -0
  70. package/dist/plugins/font/index.d.ts.map +1 -0
  71. package/dist/plugins/index.d.ts +2 -0
  72. package/dist/plugins/index.d.ts.map +1 -0
  73. package/dist/theme/index.d.ts +7 -3
  74. package/dist/theme/index.d.ts.map +1 -0
  75. package/dist/theme/scheme.d.ts +20 -0
  76. package/dist/theme/scheme.d.ts.map +1 -0
  77. package/dist/theme/scheme.manager.d.ts +31 -0
  78. package/dist/theme/scheme.manager.d.ts.map +1 -0
  79. package/dist/theme/theme.api.d.ts +23 -0
  80. package/dist/theme/theme.api.d.ts.map +1 -0
  81. package/dist/theme/theme.module.d.ts +1 -0
  82. package/dist/theme/theme.module.d.ts.map +1 -0
  83. package/dist/theme/variant.d.ts +36 -0
  84. package/dist/theme/variant.d.ts.map +1 -0
  85. package/dist/theme/variant.manager.d.ts +14 -0
  86. package/dist/theme/variant.manager.d.ts.map +1 -0
  87. package/dist/theme/variants/expressive.variant.d.ts +3 -0
  88. package/dist/theme/variants/expressive.variant.d.ts.map +1 -0
  89. package/dist/theme/variants/index.d.ts +11 -0
  90. package/dist/theme/variants/index.d.ts.map +1 -0
  91. package/dist/theme/variants/neutral.variant.d.ts +3 -0
  92. package/dist/theme/variants/neutral.variant.d.ts.map +1 -0
  93. package/dist/theme/variants/tonal-spot.variant.d.ts +3 -0
  94. package/dist/theme/variants/tonal-spot.variant.d.ts.map +1 -0
  95. package/dist/theme/variants/vibrant.variant.d.ts +3 -0
  96. package/dist/theme/variants/vibrant.variant.d.ts.map +1 -0
  97. package/package.json +26 -87
  98. package/src/API.ts +23 -0
  99. package/src/adapter/adapter.abstract.ts +64 -0
  100. package/src/{config → adapter}/config.interface.ts +5 -5
  101. package/src/adapter/define-config.ts +11 -0
  102. package/src/adapter/file-adapter.mixin.ts +72 -0
  103. package/src/adapter/index.ts +3 -0
  104. package/src/adapters/index.ts +2 -0
  105. package/src/adapters/node.adapter.ts +57 -0
  106. package/src/adapters/vite.adapter.ts +79 -0
  107. package/src/app.container.ts +12 -36
  108. package/src/app.module.ts +2 -2
  109. package/src/bootstrap.ts +6 -0
  110. package/src/color/color.api.ts +75 -0
  111. package/src/color/color.manager.ts +213 -0
  112. package/src/color/color.module.ts +4 -4
  113. package/src/color/color.utils.ts +126 -0
  114. package/src/color/configurable-color.ts +67 -0
  115. package/src/color/default-color.ts +832 -0
  116. package/src/color/index.ts +4 -4
  117. package/src/index.test.ts +5 -0
  118. package/src/index.ts +6 -4
  119. package/src/material-color-utilities/dynamic_color.ts +286 -222
  120. package/src/material-color-utilities/hct_solver.ts +536 -0
  121. package/src/material-color-utilities/htc.ts +198 -0
  122. package/src/material-color-utilities/toneDeltaPair.ts +29 -11
  123. package/src/plugin/index.ts +3 -0
  124. package/src/plugin/plugin.abstract.ts +45 -5
  125. package/src/plugin/plugin.api.ts +51 -0
  126. package/src/plugin/plugin.module.ts +2 -2
  127. package/src/plugins/font/font.plugin.ts +203 -0
  128. package/src/plugins/font/index.ts +1 -0
  129. package/src/plugins/index.ts +1 -0
  130. package/src/theme/index.ts +6 -3
  131. package/src/theme/{services/scheme.service.ts → scheme.manager.ts} +39 -19
  132. package/src/theme/{entities/scheme.entity.ts → scheme.ts} +20 -4
  133. package/src/theme/{services/theme.service.ts → theme.api.ts} +23 -19
  134. package/src/theme/theme.module.ts +6 -4
  135. package/src/theme/variant.manager.ts +58 -0
  136. package/src/theme/variant.ts +53 -0
  137. package/src/theme/variants/expressive.variant.ts +81 -0
  138. package/src/theme/variants/index.ts +16 -0
  139. package/src/theme/variants/neutral.variant.ts +45 -0
  140. package/src/theme/variants/tonal-spot.variant.ts +35 -0
  141. package/src/theme/variants/vibrant.variant.ts +72 -0
  142. package/tsconfig.json +13 -0
  143. package/tsconfig.lib.json +33 -0
  144. package/tsconfig.spec.json +36 -0
  145. package/vite.config.ts +54 -0
  146. package/LICENSE +0 -21
  147. package/dist/app.service.d.ts +0 -13
  148. package/dist/color/color.interface.d.ts +0 -8
  149. package/dist/color/entities/color.entity.d.ts +0 -42
  150. package/dist/color/entities/index.d.ts +0 -1
  151. package/dist/color/models/default-color.model.d.ts +0 -3
  152. package/dist/color/models/index.d.ts +0 -1
  153. package/dist/color/services/color-manager.service.d.ts +0 -18
  154. package/dist/color/services/color.service.d.ts +0 -21
  155. package/dist/color/services/index.d.ts +0 -2
  156. package/dist/config/config.module.d.ts +0 -2
  157. package/dist/config/config.service.d.ts +0 -12
  158. package/dist/config/index.d.ts +0 -2
  159. package/dist/main.d.ts +0 -3
  160. package/dist/plugin/plugin.service.d.ts +0 -10
  161. package/dist/plugins/tailwind/Tailwind.plugin.d.ts +0 -14
  162. package/dist/plugins/tailwind/main.d.ts +0 -10
  163. package/dist/plugins/tailwind/plugins-tailwind/state.d.ts +0 -4
  164. package/dist/plugins/tailwind/plugins-tailwind/themer.d.ts +0 -4
  165. package/dist/theme/entities/index.d.ts +0 -2
  166. package/dist/theme/entities/scheme.entity.d.ts +0 -15
  167. package/dist/theme/entities/variant.entity.d.ts +0 -7
  168. package/dist/theme/models/index.d.ts +0 -1
  169. package/dist/theme/models/variant.model.d.ts +0 -8
  170. package/dist/theme/services/index.d.ts +0 -3
  171. package/dist/theme/services/scheme.service.d.ts +0 -17
  172. package/dist/theme/services/theme.service.d.ts +0 -22
  173. package/dist/theme/services/variant.service.d.ts +0 -13
  174. package/dist/theme.cjs.development.js +0 -1983
  175. package/dist/theme.cjs.development.js.map +0 -1
  176. package/dist/theme.cjs.production.min.js +0 -2
  177. package/dist/theme.cjs.production.min.js.map +0 -1
  178. package/dist/theme.esm.js +0 -1955
  179. package/dist/theme.esm.js.map +0 -1
  180. package/src/app.service.spec.ts +0 -15
  181. package/src/app.service.ts +0 -23
  182. package/src/color/color.interface.ts +0 -13
  183. package/src/color/entities/color.entity.ts +0 -71
  184. package/src/color/entities/index.ts +0 -1
  185. package/src/color/models/default-color.model.ts +0 -300
  186. package/src/color/models/index.ts +0 -1
  187. package/src/color/services/color-manager.service.ts +0 -191
  188. package/src/color/services/color.service.spec.ts +0 -28
  189. package/src/color/services/color.service.ts +0 -75
  190. package/src/color/services/index.ts +0 -2
  191. package/src/config/config.module.ts +0 -7
  192. package/src/config/config.service.ts +0 -74
  193. package/src/config/index.ts +0 -2
  194. package/src/main.ts +0 -14
  195. package/src/plugin/plugin.service.ts +0 -30
  196. package/src/plugins/tailwind/Tailwind.plugin.ts +0 -53
  197. package/src/plugins/tailwind/main.ts +0 -18
  198. package/src/plugins/tailwind/plugins-tailwind/state.ts +0 -88
  199. package/src/plugins/tailwind/plugins-tailwind/themer.ts +0 -53
  200. package/src/theme/entities/index.ts +0 -2
  201. package/src/theme/entities/variant.entity.ts +0 -39
  202. package/src/theme/models/index.ts +0 -1
  203. package/src/theme/models/variant.model.ts +0 -63
  204. package/src/theme/services/index.ts +0 -3
  205. package/src/theme/services/variant.service.ts +0 -52
@@ -0,0 +1,57 @@
1
+ import { AdapterAbstract, ConfigInterface } from '../adapter';
2
+ import { resolve } from 'pathe';
3
+ import { createJiti } from 'jiti';
4
+
5
+ export class NodeAdapter extends AdapterAbstract {
6
+ constructor(public configPath = './theme.config') {
7
+ super();
8
+ }
9
+
10
+ async getConfig() {
11
+ if (
12
+ typeof process !== 'undefined' &&
13
+ process.release &&
14
+ process.release.name === 'node'
15
+ ) {
16
+ const fs = (await import('fs')).default;
17
+
18
+ // Créer une instance jiti pour importer des fichiers TypeScript
19
+ const jiti = createJiti(import.meta.url, {
20
+ // Options optionnelles
21
+ debug: process.env.NODE_ENV === 'development',
22
+ cache: true, // Cache les transpilations
23
+ interopDefault: true, // Gère automatiquement les exports par défaut
24
+ });
25
+
26
+ const base = resolve(this.configPath);
27
+ const extensions = ['.ts', '.js', '.mjs', '.cjs']; // .ts en premier
28
+ let config: ConfigInterface | null = null;
29
+
30
+ for (const ext of extensions) {
31
+ const configFilePath = base + ext;
32
+ if (fs.existsSync(configFilePath)) {
33
+ try {
34
+ // jiti peut importer directement des fichiers .ts
35
+ config = await jiti.import(configFilePath, { default: true });
36
+ break;
37
+ } catch (error) {
38
+ console.warn(`Failed to load ${configFilePath}:`, error);
39
+ continue;
40
+ }
41
+ }
42
+ }
43
+
44
+ if (!config) {
45
+ throw new Error(
46
+ `Configuration file not found, looked for: ${base} with extensions: ${extensions.join(', ')}`,
47
+ );
48
+ }
49
+
50
+ return config as ConfigInterface;
51
+ } else {
52
+ throw new Error(
53
+ 'You must provide configuration object when using this library in a browser.',
54
+ );
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,79 @@
1
+ import { loadConfigFromFile, Plugin } from 'vite';
2
+ import { AdapterAbstract, ConfigInterface } from '../adapter';
3
+ import * as path from 'node:path';
4
+ import * as fs from 'node:fs';
5
+
6
+ export const udixioVite = async (
7
+ configPath = './theme.config',
8
+ ): Promise<Plugin | undefined> => {
9
+ // @ts-expect-error - NX_GRAPH_CREATION is a global variable set by Nx
10
+ if (global.NX_GRAPH_CREATION) {
11
+ return;
12
+ }
13
+
14
+ class ViteAdapter extends AdapterAbstract {
15
+ public configExtension?: string;
16
+
17
+ constructor(public configPath: string) {
18
+ super();
19
+ }
20
+
21
+ getConfigPath() {
22
+ if (!this.configExtension) {
23
+ throw new Error('config extension not found');
24
+ }
25
+ return path.resolve(this.configPath + this.configExtension);
26
+ }
27
+
28
+ async getConfig() {
29
+ const resolvedPath = path.resolve(this.configPath);
30
+
31
+ const result = await loadConfigFromFile(
32
+ { command: 'serve', mode: 'development' }, // ou 'build'
33
+ resolvedPath,
34
+ );
35
+ if (!result?.config) {
36
+ throw new Error('config not found');
37
+ }
38
+ if (!this.configExtension) {
39
+ this.configExtension = path.extname(result.dependencies[0]);
40
+ }
41
+ return result.config as unknown as ConfigInterface;
42
+ }
43
+ }
44
+
45
+ const adapter = new ViteAdapter(configPath);
46
+
47
+ await adapter.init();
48
+
49
+ configPath = adapter.getConfigPath();
50
+ return {
51
+ name: 'vite:udixio-theme',
52
+ async buildStart() {
53
+ if (fs.existsSync(configPath)) {
54
+ this.addWatchFile(configPath);
55
+ }
56
+ adapter.load();
57
+ },
58
+
59
+ async generateBundle() {
60
+ adapter.load();
61
+ },
62
+
63
+ // Handles Hot Module Replacement in dev server
64
+ async handleHotUpdate({ server, file, modules }) {
65
+ // Vérifie si le fichier modifié correspond au chemin du fichier de config
66
+ if (configPath === file) {
67
+ const adapter = new ViteAdapter(configPath);
68
+ await adapter.init();
69
+ adapter.load();
70
+
71
+ server.ws.send({ type: 'full-reload', path: '*' });
72
+
73
+ return modules;
74
+ }
75
+
76
+ return;
77
+ },
78
+ };
79
+ };
@@ -1,46 +1,22 @@
1
- import {
2
- AwilixContainer,
3
- BuildResolver,
4
- createContainer,
5
- DisposableResolver,
6
- InjectionMode,
7
- } from 'awilix';
8
- import { ColorModule } from './color/color.module';
9
- import { ThemeModule } from './theme/theme.module';
1
+ import { createContainer, InjectionMode, Resolver } from 'awilix';
2
+ import { ColorModule } from './color';
3
+ import { ThemeModule } from './theme';
10
4
  import { AppModule } from './app.module';
11
- import { ConfigModule } from './config/config.module';
12
- import { PluginModule } from './plugin/plugin.module';
5
+ import { PluginModule } from './plugin';
13
6
 
14
- export type Module = Record<
15
- string,
16
- BuildResolver<any> & DisposableResolver<any>
17
- >;
7
+ export type Module = Record<string, Resolver<any>>;
18
8
 
19
- export function importContainer(
20
- container: AwilixContainer,
21
- services: Module[]
22
- ) {
23
- services.forEach((service) => {
24
- Object.entries(service).forEach(([name, serviceClass]) => {
25
- container.register(name, serviceClass);
9
+ export function registerModule(...modules: Module[]) {
10
+ modules.forEach((module) => {
11
+ Object.entries(module).forEach(([name, moduleClass]) => {
12
+ AppContainer.register(name, moduleClass);
26
13
  });
27
14
  });
28
- return container;
15
+ return AppContainer;
29
16
  }
30
17
 
31
- const AppContainer = createContainer({
18
+ export const AppContainer = createContainer({
32
19
  injectionMode: InjectionMode.PROXY,
33
20
  });
34
21
 
35
- importContainer(AppContainer, [
36
- ConfigModule,
37
- AppModule,
38
- PluginModule,
39
- ColorModule,
40
- ThemeModule,
41
- ]);
42
-
43
- // AppContainer.register(ColorModule.cradle);
44
- // AppContainer.register(ThemeModule.cradle);
45
-
46
- export default AppContainer;
22
+ registerModule(AppModule, PluginModule, ColorModule, ThemeModule);
package/src/app.module.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { asClass } from 'awilix';
2
- import { AppService } from './app.service';
2
+ import { API } from './API';
3
3
  import { Module } from './app.container';
4
4
 
5
5
  export const AppModule: Module = {
6
- appService: asClass(AppService).singleton(),
6
+ api: asClass(API).singleton(),
7
7
  };
@@ -0,0 +1,6 @@
1
+ import { AppContainer } from './app.container';
2
+ import { API } from './API';
3
+
4
+ export function bootstrap(): API {
5
+ return AppContainer.resolve<API>('api');
6
+ }
@@ -0,0 +1,75 @@
1
+ import { ColorOptions, ConfigurableColor } from './configurable-color';
2
+ import { ColorManager } from './color.manager';
3
+ import { DynamicColorKey } from './color.utils';
4
+
5
+ export type AddColors = {
6
+ colors?: Record<
7
+ DynamicColorKey | string,
8
+ | (Partial<ColorOptions> & { alias?: never })
9
+ | { alias: string; palette?: never; tone?: never }
10
+ >;
11
+ fromPalettes?: string[] | string;
12
+ };
13
+
14
+ export type AddColorsOptions =
15
+ | AddColors
16
+ | ((colorService: ColorApi) => AddColors);
17
+
18
+ export class ColorApi {
19
+ private readonly colorManager: ColorManager;
20
+
21
+ constructor({ colorManager }: { colorManager: ColorManager }) {
22
+ this.colorManager = colorManager;
23
+ }
24
+
25
+ getColors() {
26
+ return this.colorManager.getAll();
27
+ }
28
+
29
+ addColor(
30
+ key: string,
31
+ color:
32
+ | (Partial<ColorOptions> & { alias?: never })
33
+ | { alias: string; palette?: never; tone?: never },
34
+ ): ConfigurableColor {
35
+ return this.colorManager.createOrUpdate(key, color);
36
+ }
37
+
38
+ addColors(args: AddColorsOptions | AddColorsOptions[]) {
39
+ if (!Array.isArray(args)) args = [args];
40
+ args.forEach((args) => {
41
+ if (typeof args === 'function') {
42
+ args = args(this);
43
+ }
44
+ if (args.fromPalettes) {
45
+ if (!Array.isArray(args.fromPalettes))
46
+ args.fromPalettes = [args.fromPalettes];
47
+ args.fromPalettes.map((paletteKey) => {
48
+ this.colorManager.addFromPalette(paletteKey);
49
+ });
50
+ }
51
+ if (args.colors) {
52
+ Object.keys(args.colors).map((key) =>
53
+ this.addColor(key, args.colors![key]),
54
+ );
55
+ }
56
+ });
57
+ }
58
+
59
+ getColor(key: DynamicColorKey | string): ConfigurableColor {
60
+ return this.colorManager.get(key);
61
+ }
62
+
63
+ removeColor(key: string): boolean {
64
+ return this.colorManager.remove(key);
65
+ }
66
+
67
+ updateColor(
68
+ key: string,
69
+ newColor:
70
+ | (Partial<ColorOptions> & { alias?: never })
71
+ | { alias: string; palette?: never; tone?: never },
72
+ ): ConfigurableColor {
73
+ return this.colorManager.createOrUpdate(key, newColor);
74
+ }
75
+ }
@@ -0,0 +1,213 @@
1
+ import { DynamicColor, ToneDeltaPair } from '../material-color-utilities';
2
+ import { Scheme, SchemeManager } from '../theme';
3
+
4
+ import { ColorOptions, ConfigurableColor } from './configurable-color';
5
+ import { DynamicColorKey, getCurve, tMaxC, tMinC } from './color.utils';
6
+ import { ColorApi } from './color.api';
7
+
8
+ function capitalizeFirstLetter(string: string) {
9
+ return string.charAt(0).toUpperCase() + string.slice(1);
10
+ }
11
+
12
+ export const highestSurface = (
13
+ s: Scheme,
14
+ colorService: ColorManager | ColorApi,
15
+ ): DynamicColor => {
16
+ if (colorService instanceof ColorApi) {
17
+ return s.isDark
18
+ ? colorService.getColor('surfaceBright').getMaterialColor()
19
+ : colorService.getColor('surfaceDim').getMaterialColor();
20
+ } else {
21
+ return s.isDark
22
+ ? colorService.get('surfaceBright').getMaterialColor()
23
+ : colorService.get('surfaceDim').getMaterialColor();
24
+ }
25
+ };
26
+
27
+ export class ColorManager {
28
+ private colorMap = new Map<string, ConfigurableColor>();
29
+ private readonly schemeManager: SchemeManager;
30
+
31
+ constructor({ schemeManager }: { schemeManager: SchemeManager }) {
32
+ this.schemeManager = schemeManager;
33
+ }
34
+
35
+ createOrUpdate(
36
+ key: string,
37
+ args:
38
+ | (Partial<ColorOptions> & { alias?: never })
39
+ | { alias: string; palette?: never; tone?: never },
40
+ ): ConfigurableColor {
41
+ let colorEntity = this.colorMap.get(key);
42
+ if (!colorEntity) {
43
+ const { palette, alias } = args;
44
+ if (palette) {
45
+ colorEntity = new ConfigurableColor(
46
+ { ...args, palette: palette, name: key },
47
+ this.schemeManager,
48
+ this,
49
+ );
50
+ this.colorMap.set(key, colorEntity);
51
+ } else if (alias) {
52
+ colorEntity = new ConfigurableColor(
53
+ { ...args, alias: alias, name: key },
54
+ this.schemeManager,
55
+ this,
56
+ );
57
+ this.colorMap.set(key, colorEntity);
58
+ } else {
59
+ throw new Error(`Palette ${palette} does not exist from ${key}`);
60
+ }
61
+ } else {
62
+ colorEntity.update({ ...args, name: key });
63
+ this.colorMap.set(key, colorEntity);
64
+ }
65
+ return colorEntity;
66
+ }
67
+
68
+ public remove(key: string) {
69
+ return this.colorMap.delete(key);
70
+ }
71
+
72
+ public get(key: string): ConfigurableColor {
73
+ const colorEntity = this.colorMap.get(key);
74
+ if (colorEntity) {
75
+ return colorEntity;
76
+ } else {
77
+ throw new Error(`Color ${key} does not exist`);
78
+ }
79
+ }
80
+
81
+ public getAll(): ReadonlyMap<string, ConfigurableColor> {
82
+ return this.colorMap;
83
+ }
84
+
85
+ addFromPalette(key: string): void {
86
+ const colorKey = key as DynamicColorKey;
87
+ const colorDimKey = (colorKey + 'Dim') as DynamicColorKey;
88
+ const ColorKey = capitalizeFirstLetter(key);
89
+ const onColorKey = ('on' + ColorKey) as DynamicColorKey;
90
+ const colorContainerKey = (colorKey + 'Container') as DynamicColorKey;
91
+ const onColorContainerKey = ('on' +
92
+ ColorKey +
93
+ 'Container') as DynamicColorKey;
94
+ const inverseColorKey = ('inverse' + ColorKey) as DynamicColorKey;
95
+ const colorFixedKey = (colorKey + 'Fixed') as DynamicColorKey;
96
+ const colorFixedDimKey = (colorKey + 'FixedDim') as DynamicColorKey;
97
+ const onColorFixedKey = ('on' + ColorKey + 'Fixed') as DynamicColorKey;
98
+ const onColorFixedVariantKey = ('on' +
99
+ ColorKey +
100
+ 'FixedVariant') as DynamicColorKey;
101
+
102
+ this.createOrUpdate(colorKey, {
103
+ palette: (s) => s.getPalette(colorKey),
104
+ tone: (s) => {
105
+ if (s.variant === 'neutral') {
106
+ return s.isDark
107
+ ? tMinC(s.getPalette(colorKey), 0, 98)
108
+ : tMaxC(s.getPalette(colorKey));
109
+ } else if (s.variant === 'vibrant') {
110
+ return tMaxC(s.getPalette(colorKey), 0, s.isDark ? 90 : 98);
111
+ } else {
112
+ return s.isDark ? 80 : tMaxC(s.getPalette(colorKey));
113
+ }
114
+ },
115
+ isBackground: true,
116
+ background: (s) => highestSurface(s, this),
117
+ contrastCurve: (s) => getCurve(4.5),
118
+ toneDeltaPair: (s) =>
119
+ new ToneDeltaPair(
120
+ this.get(colorContainerKey).getMaterialColor(),
121
+ this.get(colorKey).getMaterialColor(),
122
+ 5,
123
+ 'relative_lighter',
124
+ true,
125
+ 'farther',
126
+ ),
127
+ });
128
+ this.createOrUpdate(colorDimKey, {
129
+ palette: (s) => s.getPalette(colorKey),
130
+ tone: (s) => {
131
+ if (s.variant === 'neutral') {
132
+ return 85;
133
+ } else {
134
+ return tMaxC(s.getPalette(colorKey), 0, 90);
135
+ }
136
+ },
137
+ isBackground: true,
138
+ background: (s) => this.get('surfaceContainerHigh').getMaterialColor(),
139
+ contrastCurve: (s) => getCurve(4.5),
140
+ toneDeltaPair: (s) =>
141
+ new ToneDeltaPair(
142
+ this.get(colorDimKey).getMaterialColor(),
143
+ this.get(colorKey).getMaterialColor(),
144
+ 5,
145
+ 'darker',
146
+ true,
147
+ 'farther',
148
+ ),
149
+ });
150
+ this.createOrUpdate(onColorKey, {
151
+ palette: (s) => s.getPalette(colorKey),
152
+ background: (s) => this.get(colorKey).getMaterialColor(),
153
+ contrastCurve: (s) => getCurve(6),
154
+ });
155
+ this.createOrUpdate(colorContainerKey, {
156
+ palette: (s) => s.getPalette(colorKey),
157
+ tone: (s) => {
158
+ if (s.variant === 'vibrant') {
159
+ return s.isDark
160
+ ? tMinC(s.getPalette(colorKey), 30, 40)
161
+ : tMaxC(s.getPalette(colorKey), 84, 90);
162
+ } else if (s.variant === 'expressive') {
163
+ return s.isDark ? 15 : tMaxC(s.getPalette(colorKey), 90, 95);
164
+ } else {
165
+ return s.isDark ? 25 : 90;
166
+ }
167
+ },
168
+ isBackground: true,
169
+ background: (s) => highestSurface(s, this),
170
+ toneDeltaPair: (s) => undefined,
171
+ contrastCurve: (s) => (s.contrastLevel > 0 ? getCurve(1.5) : undefined),
172
+ });
173
+ this.createOrUpdate(onColorContainerKey, {
174
+ palette: (s) => s.getPalette(colorKey),
175
+ background: (s) => this.get(colorContainerKey).getMaterialColor(),
176
+ contrastCurve: (s) => getCurve(6),
177
+ });
178
+ this.createOrUpdate(colorFixedKey, {
179
+ palette: (s) => s.getPalette(colorKey),
180
+ tone: (s) => {
181
+ const tempS = Object.assign({}, s, { isDark: false, contrastLevel: 0 });
182
+ return this.get(colorContainerKey).getMaterialColor().getTone(tempS);
183
+ },
184
+ isBackground: true,
185
+ background: (s) => highestSurface(s, this),
186
+ contrastCurve: (s) => (s.contrastLevel > 0 ? getCurve(1.5) : undefined),
187
+ });
188
+ this.createOrUpdate(colorFixedDimKey, {
189
+ palette: (s) => s.getPalette(colorKey),
190
+ tone: (s) => this.get(colorFixedKey).getMaterialColor().getTone(s),
191
+ isBackground: true,
192
+ toneDeltaPair: (s) =>
193
+ new ToneDeltaPair(
194
+ this.get(colorFixedDimKey).getMaterialColor(),
195
+ this.get(colorFixedKey).getMaterialColor(),
196
+ 5,
197
+ 'darker',
198
+ true,
199
+ 'exact',
200
+ ),
201
+ });
202
+ this.createOrUpdate(onColorFixedKey, {
203
+ palette: (s) => s.getPalette(colorKey),
204
+ background: (s) => this.get(colorFixedDimKey).getMaterialColor(),
205
+ contrastCurve: (s) => getCurve(7),
206
+ });
207
+ this.createOrUpdate(onColorFixedVariantKey, {
208
+ palette: (s) => s.getPalette(colorKey),
209
+ background: (s) => this.get(colorFixedDimKey).getMaterialColor(),
210
+ contrastCurve: (s) => getCurve(4.5),
211
+ });
212
+ }
213
+ }
@@ -1,9 +1,9 @@
1
- import { ColorService } from './services/color.service';
2
- import { ColorManagerService } from './services/color-manager.service';
1
+ import { ColorApi } from './color.api';
2
+ import { ColorManager } from './color.manager';
3
3
  import { asClass } from 'awilix';
4
4
  import { Module } from '../app.container';
5
5
 
6
6
  export const ColorModule: Module = {
7
- colorManagerService: asClass(ColorManagerService).singleton(),
8
- colorService: asClass(ColorService).singleton(),
7
+ colorManager: asClass(ColorManager).singleton(),
8
+ colorApi: asClass(ColorApi).singleton(),
9
9
  };
@@ -0,0 +1,126 @@
1
+ import { clampDouble, TonalPalette } from '@material/material-color-utilities';
2
+ import { Hct } from '../material-color-utilities/htc';
3
+ import { ContrastCurve } from '../material-color-utilities/contrastCurve';
4
+
5
+ export type DynamicColorKey =
6
+ | 'background'
7
+ | 'onBackground'
8
+ | 'surface'
9
+ | 'surfaceDim'
10
+ | 'surfaceBright'
11
+ | 'surfaceContainerLowest'
12
+ | 'surfaceContainerLow'
13
+ | 'surfaceContainer'
14
+ | 'surfaceContainerHigh'
15
+ | 'surfaceContainerHighest'
16
+ | 'onSurface'
17
+ | 'surfaceVariant'
18
+ | 'onSurfaceVariant'
19
+ | 'inverseSurface'
20
+ | 'inverseOnSurface'
21
+ | 'outline'
22
+ | 'outlineVariant'
23
+ | 'surfaceTint'
24
+ | 'primary'
25
+ | 'primaryDim'
26
+ | 'onPrimary'
27
+ | 'primaryContainer'
28
+ | 'onPrimaryContainer'
29
+ | 'inversePrimary'
30
+ | 'secondary'
31
+ | 'secondaryDim'
32
+ | 'onSecondary'
33
+ | 'secondaryContainer'
34
+ | 'onSecondaryContainer'
35
+ | 'tertiary'
36
+ | 'tertiaryDim'
37
+ | 'onTertiary'
38
+ | 'tertiaryContainer'
39
+ | 'onTertiaryContainer'
40
+ | 'error'
41
+ | 'errorDim'
42
+ | 'onError'
43
+ | 'errorContainer'
44
+ | 'onErrorContainer'
45
+ | 'primaryFixed'
46
+ | 'primaryFixedDim'
47
+ | 'onPrimaryFixed'
48
+ | 'onPrimaryFixedVariant'
49
+ | 'secondaryFixed'
50
+ | 'secondaryFixedDim'
51
+ | 'onSecondaryFixed'
52
+ | 'onSecondaryFixedVariant'
53
+ | 'tertiaryFixed'
54
+ | 'tertiaryFixedDim'
55
+ | 'onTertiaryFixed'
56
+ | 'onTertiaryFixedVariant';
57
+
58
+ export function getCurve(defaultContrast: number): ContrastCurve {
59
+ if (defaultContrast === 1.5) {
60
+ return new ContrastCurve(1.5, 1.5, 3, 4.5);
61
+ } else if (defaultContrast === 3) {
62
+ return new ContrastCurve(3, 3, 4.5, 7);
63
+ } else if (defaultContrast === 4.5) {
64
+ return new ContrastCurve(4.5, 4.5, 7, 11);
65
+ } else if (defaultContrast === 6) {
66
+ return new ContrastCurve(6, 6, 7, 11);
67
+ } else if (defaultContrast === 7) {
68
+ return new ContrastCurve(7, 7, 11, 21);
69
+ } else if (defaultContrast === 9) {
70
+ return new ContrastCurve(9, 9, 11, 21);
71
+ } else if (defaultContrast === 11) {
72
+ return new ContrastCurve(11, 11, 21, 21);
73
+ } else if (defaultContrast === 21) {
74
+ return new ContrastCurve(21, 21, 21, 21);
75
+ } else {
76
+ // Shouldn't happen.
77
+ return new ContrastCurve(defaultContrast, defaultContrast, 7, 21);
78
+ }
79
+ }
80
+
81
+ export function tMaxC(
82
+ palette: TonalPalette,
83
+ lowerBound = 0,
84
+ upperBound = 100,
85
+ chromaMultiplier = 1,
86
+ ): number {
87
+ const answer = findBestToneForChroma(
88
+ palette.hue,
89
+ palette.chroma * chromaMultiplier,
90
+ 100,
91
+ true,
92
+ );
93
+ return clampDouble(lowerBound, upperBound, answer);
94
+ }
95
+
96
+ export function tMinC(
97
+ palette: TonalPalette,
98
+ lowerBound = 0,
99
+ upperBound = 100,
100
+ ): number {
101
+ const answer = findBestToneForChroma(palette.hue, palette.chroma, 0, false);
102
+ return clampDouble(lowerBound, upperBound, answer);
103
+ }
104
+
105
+ export function findBestToneForChroma(
106
+ hue: number,
107
+ chroma: number,
108
+ tone: number,
109
+ byDecreasingTone: boolean,
110
+ ): number {
111
+ let answer = tone;
112
+ let bestCandidate = Hct.from(hue, chroma, answer);
113
+ while (bestCandidate.chroma < chroma) {
114
+ if (tone < 0 || tone > 100) {
115
+ break;
116
+ }
117
+ tone += byDecreasingTone ? -1.0 : 1.0;
118
+ const newCandidate = Hct.from(hue, chroma, tone);
119
+ if (bestCandidate.chroma < newCandidate.chroma) {
120
+ bestCandidate = newCandidate;
121
+ answer = tone;
122
+ }
123
+ }
124
+
125
+ return answer;
126
+ }