@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.
- package/.eslintrc.mjs +22 -0
- package/CHANGELOG.md +186 -0
- package/README.md +6 -68
- package/dist/API.d.ts +14 -0
- package/dist/API.d.ts.map +1 -0
- package/dist/LICENSE +202 -0
- package/dist/adapter/adapter.abstract.d.ts +10 -0
- package/dist/adapter/adapter.abstract.d.ts.map +1 -0
- package/dist/{config → adapter}/config.interface.d.ts +6 -5
- package/dist/adapter/config.interface.d.ts.map +1 -0
- package/dist/adapter/define-config.d.ts +3 -0
- package/dist/adapter/define-config.d.ts.map +1 -0
- package/dist/adapter/file-adapter.mixin.d.ts +18 -0
- package/dist/adapter/file-adapter.mixin.d.ts.map +1 -0
- package/dist/adapter/index.d.ts +4 -0
- package/dist/adapter/index.d.ts.map +1 -0
- package/dist/adapters/index.d.ts +3 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/node.adapter.d.ts +7 -0
- package/dist/adapters/node.adapter.d.ts.map +1 -0
- package/dist/adapters/vite.adapter.d.ts +3 -0
- package/dist/adapters/vite.adapter.d.ts.map +1 -0
- package/dist/app.container.d.ts +5 -5
- package/dist/app.container.d.ts.map +1 -0
- package/dist/app.module.d.ts +1 -0
- package/dist/app.module.d.ts.map +1 -0
- package/dist/bootstrap.d.ts +3 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/color/color.api.d.ts +39 -0
- package/dist/color/color.api.d.ts.map +1 -0
- package/dist/color/color.manager.d.ts +24 -0
- package/dist/color/color.manager.d.ts.map +1 -0
- package/dist/color/color.module.d.ts +1 -0
- package/dist/color/color.module.d.ts.map +1 -0
- package/dist/color/color.utils.d.ts +8 -0
- package/dist/color/color.utils.d.ts.map +1 -0
- package/dist/color/configurable-color.d.ts +31 -0
- package/dist/color/configurable-color.d.ts.map +1 -0
- package/dist/color/default-color.d.ts +3 -0
- package/dist/color/default-color.d.ts.map +1 -0
- package/dist/color/index.d.ts +5 -4
- package/dist/color/index.d.ts.map +1 -0
- package/dist/index.cjs +3224 -0
- package/dist/index.d.ts +7 -4
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3183 -7
- package/dist/material-color-utilities/contrastCurve.d.ts +1 -0
- package/dist/material-color-utilities/contrastCurve.d.ts.map +1 -0
- package/dist/material-color-utilities/dynamic_color.d.ts +89 -76
- package/dist/material-color-utilities/dynamic_color.d.ts.map +1 -0
- package/dist/material-color-utilities/hct_solver.d.ts +131 -0
- package/dist/material-color-utilities/hct_solver.d.ts.map +1 -0
- package/dist/material-color-utilities/htc.d.ts +77 -0
- package/dist/material-color-utilities/htc.d.ts.map +1 -0
- package/dist/material-color-utilities/index.d.ts +1 -0
- package/dist/material-color-utilities/index.d.ts.map +1 -0
- package/dist/material-color-utilities/toneDeltaPair.d.ts +19 -25
- package/dist/material-color-utilities/toneDeltaPair.d.ts.map +1 -0
- package/dist/plugin/index.d.ts +4 -0
- package/dist/plugin/index.d.ts.map +1 -0
- package/dist/plugin/plugin.abstract.d.ts +19 -5
- package/dist/plugin/plugin.abstract.d.ts.map +1 -0
- package/dist/plugin/plugin.api.d.ts +10 -0
- package/dist/plugin/plugin.api.d.ts.map +1 -0
- package/dist/plugin/plugin.module.d.ts +1 -0
- package/dist/plugin/plugin.module.d.ts.map +1 -0
- package/dist/plugins/font/font.plugin.d.ts +50 -0
- package/dist/plugins/font/font.plugin.d.ts.map +1 -0
- package/dist/plugins/font/index.d.ts +2 -0
- package/dist/plugins/font/index.d.ts.map +1 -0
- package/dist/plugins/index.d.ts +2 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/theme/index.d.ts +7 -3
- package/dist/theme/index.d.ts.map +1 -0
- package/dist/theme/scheme.d.ts +20 -0
- package/dist/theme/scheme.d.ts.map +1 -0
- package/dist/theme/scheme.manager.d.ts +31 -0
- package/dist/theme/scheme.manager.d.ts.map +1 -0
- package/dist/theme/theme.api.d.ts +23 -0
- package/dist/theme/theme.api.d.ts.map +1 -0
- package/dist/theme/theme.module.d.ts +1 -0
- package/dist/theme/theme.module.d.ts.map +1 -0
- package/dist/theme/variant.d.ts +36 -0
- package/dist/theme/variant.d.ts.map +1 -0
- package/dist/theme/variant.manager.d.ts +14 -0
- package/dist/theme/variant.manager.d.ts.map +1 -0
- package/dist/theme/variants/expressive.variant.d.ts +3 -0
- package/dist/theme/variants/expressive.variant.d.ts.map +1 -0
- package/dist/theme/variants/index.d.ts +11 -0
- package/dist/theme/variants/index.d.ts.map +1 -0
- package/dist/theme/variants/neutral.variant.d.ts +3 -0
- package/dist/theme/variants/neutral.variant.d.ts.map +1 -0
- package/dist/theme/variants/tonal-spot.variant.d.ts +3 -0
- package/dist/theme/variants/tonal-spot.variant.d.ts.map +1 -0
- package/dist/theme/variants/vibrant.variant.d.ts +3 -0
- package/dist/theme/variants/vibrant.variant.d.ts.map +1 -0
- package/package.json +26 -87
- package/src/API.ts +23 -0
- package/src/adapter/adapter.abstract.ts +64 -0
- package/src/{config → adapter}/config.interface.ts +5 -5
- package/src/adapter/define-config.ts +11 -0
- package/src/adapter/file-adapter.mixin.ts +72 -0
- package/src/adapter/index.ts +3 -0
- package/src/adapters/index.ts +2 -0
- package/src/adapters/node.adapter.ts +57 -0
- package/src/adapters/vite.adapter.ts +79 -0
- package/src/app.container.ts +12 -36
- package/src/app.module.ts +2 -2
- package/src/bootstrap.ts +6 -0
- package/src/color/color.api.ts +75 -0
- package/src/color/color.manager.ts +213 -0
- package/src/color/color.module.ts +4 -4
- package/src/color/color.utils.ts +126 -0
- package/src/color/configurable-color.ts +67 -0
- package/src/color/default-color.ts +832 -0
- package/src/color/index.ts +4 -4
- package/src/index.test.ts +5 -0
- package/src/index.ts +6 -4
- package/src/material-color-utilities/dynamic_color.ts +286 -222
- package/src/material-color-utilities/hct_solver.ts +536 -0
- package/src/material-color-utilities/htc.ts +198 -0
- package/src/material-color-utilities/toneDeltaPair.ts +29 -11
- package/src/plugin/index.ts +3 -0
- package/src/plugin/plugin.abstract.ts +45 -5
- package/src/plugin/plugin.api.ts +51 -0
- package/src/plugin/plugin.module.ts +2 -2
- package/src/plugins/font/font.plugin.ts +203 -0
- package/src/plugins/font/index.ts +1 -0
- package/src/plugins/index.ts +1 -0
- package/src/theme/index.ts +6 -3
- package/src/theme/{services/scheme.service.ts → scheme.manager.ts} +39 -19
- package/src/theme/{entities/scheme.entity.ts → scheme.ts} +20 -4
- package/src/theme/{services/theme.service.ts → theme.api.ts} +23 -19
- package/src/theme/theme.module.ts +6 -4
- package/src/theme/variant.manager.ts +58 -0
- package/src/theme/variant.ts +53 -0
- package/src/theme/variants/expressive.variant.ts +81 -0
- package/src/theme/variants/index.ts +16 -0
- package/src/theme/variants/neutral.variant.ts +45 -0
- package/src/theme/variants/tonal-spot.variant.ts +35 -0
- package/src/theme/variants/vibrant.variant.ts +72 -0
- package/tsconfig.json +13 -0
- package/tsconfig.lib.json +33 -0
- package/tsconfig.spec.json +36 -0
- package/vite.config.ts +54 -0
- package/LICENSE +0 -21
- package/dist/app.service.d.ts +0 -13
- package/dist/color/color.interface.d.ts +0 -8
- package/dist/color/entities/color.entity.d.ts +0 -42
- package/dist/color/entities/index.d.ts +0 -1
- package/dist/color/models/default-color.model.d.ts +0 -3
- package/dist/color/models/index.d.ts +0 -1
- package/dist/color/services/color-manager.service.d.ts +0 -18
- package/dist/color/services/color.service.d.ts +0 -21
- package/dist/color/services/index.d.ts +0 -2
- package/dist/config/config.module.d.ts +0 -2
- package/dist/config/config.service.d.ts +0 -12
- package/dist/config/index.d.ts +0 -2
- package/dist/main.d.ts +0 -3
- package/dist/plugin/plugin.service.d.ts +0 -10
- package/dist/plugins/tailwind/Tailwind.plugin.d.ts +0 -14
- package/dist/plugins/tailwind/main.d.ts +0 -10
- package/dist/plugins/tailwind/plugins-tailwind/state.d.ts +0 -4
- package/dist/plugins/tailwind/plugins-tailwind/themer.d.ts +0 -4
- package/dist/theme/entities/index.d.ts +0 -2
- package/dist/theme/entities/scheme.entity.d.ts +0 -15
- package/dist/theme/entities/variant.entity.d.ts +0 -7
- package/dist/theme/models/index.d.ts +0 -1
- package/dist/theme/models/variant.model.d.ts +0 -8
- package/dist/theme/services/index.d.ts +0 -3
- package/dist/theme/services/scheme.service.d.ts +0 -17
- package/dist/theme/services/theme.service.d.ts +0 -22
- package/dist/theme/services/variant.service.d.ts +0 -13
- package/dist/theme.cjs.development.js +0 -1983
- package/dist/theme.cjs.development.js.map +0 -1
- package/dist/theme.cjs.production.min.js +0 -2
- package/dist/theme.cjs.production.min.js.map +0 -1
- package/dist/theme.esm.js +0 -1955
- package/dist/theme.esm.js.map +0 -1
- package/src/app.service.spec.ts +0 -15
- package/src/app.service.ts +0 -23
- package/src/color/color.interface.ts +0 -13
- package/src/color/entities/color.entity.ts +0 -71
- package/src/color/entities/index.ts +0 -1
- package/src/color/models/default-color.model.ts +0 -300
- package/src/color/models/index.ts +0 -1
- package/src/color/services/color-manager.service.ts +0 -191
- package/src/color/services/color.service.spec.ts +0 -28
- package/src/color/services/color.service.ts +0 -75
- package/src/color/services/index.ts +0 -2
- package/src/config/config.module.ts +0 -7
- package/src/config/config.service.ts +0 -74
- package/src/config/index.ts +0 -2
- package/src/main.ts +0 -14
- package/src/plugin/plugin.service.ts +0 -30
- package/src/plugins/tailwind/Tailwind.plugin.ts +0 -53
- package/src/plugins/tailwind/main.ts +0 -18
- package/src/plugins/tailwind/plugins-tailwind/state.ts +0 -88
- package/src/plugins/tailwind/plugins-tailwind/themer.ts +0 -53
- package/src/theme/entities/index.ts +0 -2
- package/src/theme/entities/variant.entity.ts +0 -39
- package/src/theme/models/index.ts +0 -1
- package/src/theme/models/variant.model.ts +0 -63
- package/src/theme/services/index.ts +0 -3
- 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
|
+
};
|
package/src/app.container.ts
CHANGED
|
@@ -1,46 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
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 {
|
|
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
|
|
20
|
-
|
|
21
|
-
|
|
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
|
|
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
|
-
|
|
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 {
|
|
2
|
+
import { API } from './API';
|
|
3
3
|
import { Module } from './app.container';
|
|
4
4
|
|
|
5
5
|
export const AppModule: Module = {
|
|
6
|
-
|
|
6
|
+
api: asClass(API).singleton(),
|
|
7
7
|
};
|
package/src/bootstrap.ts
ADDED
|
@@ -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 {
|
|
2
|
-
import {
|
|
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
|
-
|
|
8
|
-
|
|
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
|
+
}
|