@udixio/theme 1.3.1 → 2.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 (144) hide show
  1. package/CHANGELOG.md +23 -2
  2. package/dist/API.d.ts +7 -4
  3. package/dist/API.d.ts.map +1 -1
  4. package/dist/app.container.d.ts +1 -2
  5. package/dist/app.container.d.ts.map +1 -1
  6. package/dist/bin.cjs +1 -1
  7. package/dist/bin.js +1 -1
  8. package/dist/browser.cjs +23 -18
  9. package/dist/browser.js +34 -29
  10. package/dist/color/color.api.d.ts +16 -31
  11. package/dist/color/color.api.d.ts.map +1 -1
  12. package/dist/color/color.d.ts +100 -0
  13. package/dist/color/color.d.ts.map +1 -0
  14. package/dist/color/color.manager.d.ts +9 -18
  15. package/dist/color/color.manager.d.ts.map +1 -1
  16. package/dist/color/color.utils.d.ts +14 -3
  17. package/dist/color/color.utils.d.ts.map +1 -1
  18. package/dist/color/default-color.d.ts +5 -1
  19. package/dist/color/default-color.d.ts.map +1 -1
  20. package/dist/color/index.d.ts +1 -1
  21. package/dist/color/index.d.ts.map +1 -1
  22. package/dist/config/config.interface.d.ts +4 -4
  23. package/dist/config/config.interface.d.ts.map +1 -1
  24. package/dist/context/context.d.ts +41 -0
  25. package/dist/context/context.d.ts.map +1 -0
  26. package/dist/context/context.module.d.ts +3 -0
  27. package/dist/context/context.module.d.ts.map +1 -0
  28. package/dist/context/index.d.ts +3 -0
  29. package/dist/context/index.d.ts.map +1 -0
  30. package/dist/font.plugin-5Xpo-ntw.js +228 -0
  31. package/dist/font.plugin-FPU_gL1Y.cjs +227 -0
  32. package/dist/index.d.ts +6 -4
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/{load-from-path-Bo1kCfh9.js → load-from-path-CBnO8ESw.js} +1 -1
  35. package/dist/{load-from-path-DoZnR1-p.cjs → load-from-path-kuofMGN3.cjs} +2 -2
  36. package/dist/loader/loader.d.ts +1 -1
  37. package/dist/loader/loader.d.ts.map +1 -1
  38. package/dist/loader-BzsrGBu-.cjs +3905 -0
  39. package/dist/loader-CoayTlSl.js +3906 -0
  40. package/dist/material-color-utilities/dynamic_color.d.ts +0 -22
  41. package/dist/material-color-utilities/dynamic_color.d.ts.map +1 -1
  42. package/dist/material-color-utilities/toneDeltaPair.d.ts +8 -8
  43. package/dist/material-color-utilities/toneDeltaPair.d.ts.map +1 -1
  44. package/dist/node.cjs +24 -19
  45. package/dist/node.js +36 -31
  46. package/dist/palette/index.d.ts +4 -0
  47. package/dist/palette/index.d.ts.map +1 -0
  48. package/dist/palette/palette.api.d.ts +13 -0
  49. package/dist/palette/palette.api.d.ts.map +1 -0
  50. package/dist/palette/palette.d.ts +33 -0
  51. package/dist/palette/palette.d.ts.map +1 -0
  52. package/dist/palette/palette.manager.d.ts +20 -0
  53. package/dist/palette/palette.manager.d.ts.map +1 -0
  54. package/dist/palette/palette.module.d.ts +3 -0
  55. package/dist/palette/palette.module.d.ts.map +1 -0
  56. package/dist/plugins/font/font.plugin.d.ts +1 -1
  57. package/dist/plugins/font/font.plugin.d.ts.map +1 -1
  58. package/dist/variant/index.d.ts +3 -0
  59. package/dist/variant/index.d.ts.map +1 -0
  60. package/dist/variant/variant.d.ts +31 -0
  61. package/dist/variant/variant.d.ts.map +1 -0
  62. package/dist/variant/variants/expressive.variant.d.ts +2 -0
  63. package/dist/variant/variants/expressive.variant.d.ts.map +1 -0
  64. package/dist/{theme → variant}/variants/index.d.ts +1 -1
  65. package/dist/variant/variants/index.d.ts.map +1 -0
  66. package/dist/variant/variants/neutral.variant.d.ts.map +1 -0
  67. package/dist/variant/variants/tonal-spot.variant.d.ts.map +1 -0
  68. package/dist/variant/variants/udixio.variant.d.ts +3 -0
  69. package/dist/variant/variants/udixio.variant.d.ts.map +1 -0
  70. package/dist/variant/variants/vibrant.variant.d.ts.map +1 -0
  71. package/package.json +1 -1
  72. package/src/API.ts +12 -5
  73. package/src/app.container.ts +22 -11
  74. package/src/bootstrap.ts +1 -1
  75. package/src/color/color.api.ts +189 -47
  76. package/src/color/color.manager.ts +34 -189
  77. package/src/color/color.ts +295 -0
  78. package/src/color/color.utils.ts +48 -3
  79. package/src/color/default-color.ts +390 -425
  80. package/src/color/index.ts +1 -1
  81. package/src/config/config.interface.ts +5 -4
  82. package/src/context/context.module.ts +7 -0
  83. package/src/context/context.ts +169 -0
  84. package/src/context/index.ts +2 -0
  85. package/src/index.ts +6 -4
  86. package/src/loader/loader.ts +11 -24
  87. package/src/material-color-utilities/dynamic_color.ts +25 -34
  88. package/src/material-color-utilities/toneDeltaPair.ts +44 -41
  89. package/src/palette/index.ts +3 -0
  90. package/src/palette/palette.api.ts +43 -0
  91. package/src/palette/palette.manager.ts +74 -0
  92. package/src/palette/palette.module.ts +9 -0
  93. package/src/palette/palette.ts +206 -0
  94. package/src/plugins/font/font.plugin.ts +1 -1
  95. package/src/variant/index.ts +2 -0
  96. package/src/variant/variant.ts +84 -0
  97. package/src/{theme → variant}/variants/expressive.variant.ts +33 -29
  98. package/src/{theme → variant}/variants/index.ts +2 -2
  99. package/src/variant/variants/neutral.variant.ts +47 -0
  100. package/src/variant/variants/tonal-spot.variant.ts +37 -0
  101. package/src/variant/variants/udixio.variant.ts +846 -0
  102. package/src/{theme → variant}/variants/vibrant.variant.ts +23 -22
  103. package/dist/color/configurable-color.d.ts +0 -31
  104. package/dist/color/configurable-color.d.ts.map +0 -1
  105. package/dist/define-config-BasMdCqD.js +0 -430
  106. package/dist/define-config-CKSsLMnc.cjs +0 -429
  107. package/dist/loader-C8_TyOuS.js +0 -2698
  108. package/dist/loader-R7hccp8_.cjs +0 -2697
  109. package/dist/theme/index.d.ts +0 -8
  110. package/dist/theme/index.d.ts.map +0 -1
  111. package/dist/theme/scheme.d.ts +0 -20
  112. package/dist/theme/scheme.d.ts.map +0 -1
  113. package/dist/theme/scheme.manager.d.ts +0 -31
  114. package/dist/theme/scheme.manager.d.ts.map +0 -1
  115. package/dist/theme/theme.api.d.ts +0 -24
  116. package/dist/theme/theme.api.d.ts.map +0 -1
  117. package/dist/theme/theme.module.d.ts +0 -3
  118. package/dist/theme/theme.module.d.ts.map +0 -1
  119. package/dist/theme/variant.d.ts +0 -36
  120. package/dist/theme/variant.d.ts.map +0 -1
  121. package/dist/theme/variant.manager.d.ts +0 -14
  122. package/dist/theme/variant.manager.d.ts.map +0 -1
  123. package/dist/theme/variants/expressive.variant.d.ts +0 -3
  124. package/dist/theme/variants/expressive.variant.d.ts.map +0 -1
  125. package/dist/theme/variants/fidelity.variant.d.ts +0 -3
  126. package/dist/theme/variants/fidelity.variant.d.ts.map +0 -1
  127. package/dist/theme/variants/index.d.ts.map +0 -1
  128. package/dist/theme/variants/neutral.variant.d.ts.map +0 -1
  129. package/dist/theme/variants/tonal-spot.variant.d.ts.map +0 -1
  130. package/dist/theme/variants/vibrant.variant.d.ts.map +0 -1
  131. package/src/color/configurable-color.ts +0 -67
  132. package/src/theme/index.ts +0 -7
  133. package/src/theme/scheme.manager.ts +0 -100
  134. package/src/theme/scheme.ts +0 -66
  135. package/src/theme/theme.api.ts +0 -82
  136. package/src/theme/theme.module.ts +0 -11
  137. package/src/theme/variant.manager.ts +0 -58
  138. package/src/theme/variant.ts +0 -53
  139. package/src/theme/variants/fidelity.variant.ts +0 -38
  140. package/src/theme/variants/neutral.variant.ts +0 -45
  141. package/src/theme/variants/tonal-spot.variant.ts +0 -35
  142. /package/dist/{theme → variant}/variants/neutral.variant.d.ts +0 -0
  143. /package/dist/{theme → variant}/variants/tonal-spot.variant.d.ts +0 -0
  144. /package/dist/{theme → variant}/variants/vibrant.variant.d.ts +0 -0
@@ -1,5 +1,5 @@
1
1
  export * from './color.api';
2
2
  export * from './color.manager';
3
3
  export * from './color.module';
4
- export * from './configurable-color';
4
+ export * from './color';
5
5
  export * from './default-color';
@@ -1,14 +1,15 @@
1
1
  import { AddColorsOptions } from '../color';
2
- import { Variant } from '../theme';
2
+
3
3
  import { PluginAbstract } from '../plugin';
4
+ import { Variant } from '../variant/variant';
5
+ import { PaletteCallback } from '../palette/palette';
4
6
 
5
7
  export interface ConfigInterface {
6
8
  sourceColor: string;
7
9
  contrastLevel?: number;
8
10
  isDark?: boolean;
9
11
  variant?: Variant;
10
- colors?: AddColorsOptions | AddColorsOptions[];
11
- useDefaultColors?: boolean;
12
- palettes?: Record<string, string>;
12
+ colors?: AddColorsOptions;
13
+ palettes?: Record<string, PaletteCallback | string>;
13
14
  plugins?: PluginAbstract<any, any>[];
14
15
  }
@@ -0,0 +1,7 @@
1
+ import { asClass } from 'awilix';
2
+ import { Module } from '../app.container';
3
+ import { Context } from './context';
4
+
5
+ export const ContextModule: Module = {
6
+ context: asClass(Context).singleton(),
7
+ };
@@ -0,0 +1,169 @@
1
+ import { argbFromHex } from '@material/material-color-utilities';
2
+ import { Hct } from '../material-color-utilities/htc';
3
+ import { Variant } from '../variant/variant';
4
+
5
+ export interface ContextOptions {
6
+ sourceColor: string | Hct;
7
+ contrastLevel: number;
8
+ isDark: boolean;
9
+ variant: Variant;
10
+ }
11
+
12
+ export class Context {
13
+ private _options?: ContextOptions;
14
+ private _temOptions: ContextOptions | null = null;
15
+ private readonly updateCallbacks: Array<
16
+ (changed: (keyof Context)[]) => void
17
+ > = [];
18
+
19
+ constructor() {
20
+ this.onUpdate((changed) => {
21
+ if (changed.includes('variant')) {
22
+ this.variant.init(this);
23
+ }
24
+ });
25
+ }
26
+
27
+ /**
28
+ * Runs the provided callback with a proxied Context and records which Context
29
+ * properties (getters) were accessed during its execution.
30
+ *
31
+ * This helps determine dependencies of the callback on the Context.
32
+ *
33
+ * Example usage:
34
+ * const { result, dependencies } = Context.trackDependencies(ctx, (c) => cb(c));
35
+ */
36
+ static trackDependencies<T>(
37
+ context: Context,
38
+ callback: (ctx: Context) => T,
39
+ ): { result: T; dependencies: (keyof Context)[] } {
40
+ const dependencies = new Set<keyof Context>();
41
+
42
+ const isGetterOnContext = (prop: PropertyKey): boolean => {
43
+ if (typeof prop !== 'string') return false;
44
+ const desc = Object.getOwnPropertyDescriptor(Context.prototype, prop);
45
+ return !!desc && typeof desc.get === 'function';
46
+ };
47
+
48
+ const proxy = new Proxy(context, {
49
+ get(target, prop, receiver) {
50
+ if (isGetterOnContext(prop)) {
51
+ dependencies.add(prop as keyof Context);
52
+ }
53
+ return Reflect.get(target, prop, receiver);
54
+ },
55
+ });
56
+
57
+ const result = callback(proxy as unknown as Context);
58
+ return { result, dependencies: Array.from(dependencies) };
59
+ }
60
+
61
+ set(options: ContextOptions) {
62
+ if (this._options) {
63
+ console.error(this._options);
64
+ throw new Error('Options already set');
65
+ }
66
+ if (typeof options.sourceColor === 'string') {
67
+ options.sourceColor = Hct.fromInt(argbFromHex(options.sourceColor));
68
+ }
69
+
70
+ const changed: (keyof Context)[] = [];
71
+ for (const key of Object.keys(options) as (keyof ContextOptions)[]) {
72
+ changed.push(key);
73
+ }
74
+
75
+ this._options = options;
76
+
77
+ if (changed.length > 0) {
78
+ this.updateCallbacks.forEach((callback) => callback(changed));
79
+ }
80
+ }
81
+
82
+ public update(args: Partial<ContextOptions>) {
83
+ const options = this._options;
84
+ if (!options) {
85
+ throw new Error('Options not found');
86
+ }
87
+ if (typeof args.sourceColor === 'string') {
88
+ args.sourceColor = Hct.fromInt(argbFromHex(args.sourceColor));
89
+ }
90
+
91
+ // compute changed keys
92
+ const changed: (keyof Context)[] = [];
93
+ for (const key of Object.keys(args) as (keyof ContextOptions)[]) {
94
+ if ((args as any)[key] !== (options as any)[key]) {
95
+ changed.push(key);
96
+ }
97
+ }
98
+
99
+ this._options = {
100
+ ...options,
101
+ ...args,
102
+ };
103
+
104
+ // notify listeners with changed keys (if any)
105
+ if (changed.length > 0) {
106
+ this.updateCallbacks.forEach((callback) => callback(changed));
107
+ }
108
+ }
109
+
110
+ private getOptions(): ContextOptions {
111
+ let options;
112
+ if (this._temOptions) {
113
+ options = this._temOptions;
114
+ } else {
115
+ options = this._options;
116
+ }
117
+ if (!options) {
118
+ throw new Error('Options not found');
119
+ }
120
+ return options;
121
+ }
122
+
123
+ set darkMode(isDark: boolean) {
124
+ this.update({ isDark });
125
+ }
126
+ get isDark() {
127
+ return this.getOptions().isDark;
128
+ }
129
+
130
+ set contrastLevel(contrastLevel: number) {
131
+ this.update({ contrastLevel });
132
+ }
133
+ get contrastLevel() {
134
+ return this.getOptions().contrastLevel;
135
+ }
136
+
137
+ set sourceColor(sourceColor: string) {
138
+ this.update({ sourceColor });
139
+ }
140
+ get sourceColor(): Hct {
141
+ let sourceColor = this.getOptions().sourceColor;
142
+ if (typeof sourceColor === 'string') {
143
+ sourceColor = Hct.fromInt(argbFromHex(sourceColor));
144
+ }
145
+ return sourceColor;
146
+ }
147
+
148
+ set variant(variant: Variant) {
149
+ this.update({ variant });
150
+ }
151
+ get variant() {
152
+ return this.getOptions().variant;
153
+ }
154
+
155
+ temp<T>(args: Partial<ContextOptions>, callback: () => T): T {
156
+ const previousOptions = this.getOptions();
157
+ this._temOptions = {
158
+ ...previousOptions,
159
+ ...args,
160
+ };
161
+ const result = callback();
162
+ this._temOptions = null;
163
+ return result;
164
+ }
165
+
166
+ onUpdate(callback: (changed: (keyof Context)[]) => void): void {
167
+ this.updateCallbacks.push(callback);
168
+ }
169
+ }
@@ -0,0 +1,2 @@
1
+ export * from './context.module';
2
+ export * from './context';
package/src/index.ts CHANGED
@@ -1,11 +1,13 @@
1
+ export * from './API';
1
2
  export * from './app.container';
2
3
  export * from './app.module';
3
- export * from './API';
4
+ export * from './bootstrap';
4
5
  export * from './color';
6
+ export * from './config';
7
+ export * from './context';
5
8
  export * from './loader';
6
- export * from './bootstrap';
7
9
  export * from './material-color-utilities';
10
+ export * from './palette';
8
11
  export * from './plugin';
9
12
  export * from './plugins';
10
- export * from './theme';
11
- export * from './config';
13
+ export * from './variant';
@@ -1,45 +1,29 @@
1
- import { tonalSpotVariant } from '../theme';
2
- import { defaultColors } from '../color';
3
1
  import { bootstrap } from '../bootstrap';
4
- import { registerModule } from '../app.container';
5
- import { asValue } from 'awilix';
6
2
  import { ConfigInterface } from '../config';
3
+ import { Variants } from '../variant/variants';
7
4
 
8
- const initializeApi = () => {
5
+ export const loader = async (config: ConfigInterface, load = true) => {
9
6
  const api = bootstrap();
10
- registerModule({
11
- adapter: asValue(this),
12
- });
13
- return api;
14
- };
15
-
16
- export const loader = async (config: ConfigInterface) => {
17
- const api = initializeApi();
18
7
 
19
8
  const init = () => {
20
9
  const {
21
10
  sourceColor,
22
11
  contrastLevel = 0,
23
12
  isDark = false,
24
- variant = tonalSpotVariant,
13
+ variant = Variants.TonalSpot,
25
14
  palettes,
26
15
  colors,
27
- useDefaultColors = true,
28
16
  plugins,
29
17
  } = config;
30
- api.themes.create({
18
+
19
+ api.context.set({
31
20
  contrastLevel: contrastLevel,
32
21
  isDark: isDark,
33
- sourceColorHex: sourceColor,
22
+ sourceColor,
34
23
  variant: variant,
35
24
  });
36
25
  if (palettes) {
37
- Object.entries(palettes).forEach(([key, value]) =>
38
- api.themes.addCustomPalette(key, value),
39
- );
40
- }
41
- if (useDefaultColors) {
42
- api.colors.addColors(defaultColors);
26
+ api.palettes.add(palettes);
43
27
  }
44
28
  if (colors) {
45
29
  api.colors.addColors(colors);
@@ -53,7 +37,10 @@ export const loader = async (config: ConfigInterface) => {
53
37
  };
54
38
 
55
39
  init();
56
- await api.load();
40
+
41
+ if (load) {
42
+ await api.load();
43
+ }
57
44
 
58
45
  return api;
59
46
  };
@@ -50,22 +50,22 @@ import { Hct } from './htc';
50
50
  * constructed. When not provided or resolved as undefined, the tone is
51
51
  * calculated based on other constraints.
52
52
  */
53
- export interface FromPaletteOptions {
54
- name?: string;
55
- palette: (scheme: Scheme) => TonalPalette;
56
- tone?: (scheme: Scheme) => number;
57
- chromaMultiplier?: (scheme: Scheme) => number;
58
- isBackground?: boolean;
59
- background?: (scheme: Scheme) => DynamicColor | undefined;
60
- secondBackground?: (scheme: Scheme) => DynamicColor | undefined;
61
- contrastCurve?: (scheme: Scheme) => ContrastCurve | undefined;
62
- adjustTone?: (scheme: Scheme) => AdjustTone | undefined;
63
- }
53
+ // export interface FromPaletteOptions {
54
+ // name?: string;
55
+ // palette: (scheme: Scheme) => TonalPalette;
56
+ // tone?: (scheme: Scheme) => number;
57
+ // chromaMultiplier?: (scheme: Scheme) => number;
58
+ // isBackground?: boolean;
59
+ // background?: (scheme: Scheme) => DynamicColor | undefined;
60
+ // secondBackground?: (scheme: Scheme) => DynamicColor | undefined;
61
+ // contrastCurve?: (scheme: Scheme) => ContrastCurve | undefined;
62
+ // adjustTone?: (scheme: Scheme) => AdjustTone | undefined;
63
+ // }
64
64
 
65
- export type AdjustTone = (args: {
66
- scheme: Scheme;
67
- dynamicColor: DynamicColor;
68
- }) => number;
65
+ // export type AdjustTone = (args: {
66
+ // scheme: Scheme;
67
+ // dynamicColor: DynamicColor;
68
+ // }) => number;
69
69
 
70
70
  /**
71
71
  * A color that adjusts itself based on UI state provided by DynamicScheme.
@@ -79,8 +79,6 @@ export type AdjustTone = (args: {
79
79
  * DynamicColor.
80
80
  */
81
81
  export class DynamicColor {
82
- private readonly hctCache = new Map<Scheme, Hct>();
83
-
84
82
  /**
85
83
  * The base constructor for DynamicColor.
86
84
  *
@@ -155,7 +153,8 @@ export class DynamicColor {
155
153
  return new DynamicColor(
156
154
  args.name ?? '',
157
155
  args.palette,
158
- args.tone ?? DynamicColor.getInitialToneFromBackground(args.background),
156
+ args.tone ??
157
+ (() => DynamicColor.getInitialToneFromBackground(args.background)),
159
158
  args.isBackground ?? false,
160
159
  args.chromaMultiplier,
161
160
  args.background,
@@ -165,14 +164,14 @@ export class DynamicColor {
165
164
  );
166
165
  }
167
166
 
168
- static getInitialToneFromBackground(
169
- background?: (scheme: Scheme) => DynamicColor | undefined,
170
- ): (scheme: Scheme) => number {
171
- if (background === undefined) {
172
- return (s) => 50;
173
- }
174
- return (s) => (background(s) ? background(s)!.getTone(s) : 50);
175
- }
167
+ // static getInitialToneFromBackground(
168
+ // background?: () => Color | undefined,
169
+ // ): number {
170
+ // if (background === undefined) {
171
+ // return 50;
172
+ // }
173
+ // return (background() ? background()!.getTone() : 50);
174
+ // }
176
175
 
177
176
  /**
178
177
  * Given a background tone, finds a foreground tone, while ensuring they reach
@@ -270,14 +269,6 @@ export class DynamicColor {
270
269
  });
271
270
  }
272
271
 
273
- /**
274
- * Clears the cache of HCT values for this color. For testing or debugging
275
- * purposes.
276
- */
277
- clearCache() {
278
- this.hctCache.clear();
279
- }
280
-
281
272
  /**
282
273
  * Returns a ARGB integer (i.e. a hex code).
283
274
  *
@@ -15,9 +15,14 @@
15
15
  * limitations under the License.
16
16
  */
17
17
 
18
- import { AdjustTone, DynamicColor } from './dynamic_color';
19
- import { clampDouble, Contrast } from '@material/material-color-utilities';
20
- import { Scheme } from '../theme';
18
+ import {
19
+ clampDouble,
20
+ Contrast,
21
+ DynamicColor,
22
+ } from '@material/material-color-utilities';
23
+
24
+ import { AdjustTone, Color, ColorFromPalette } from '../color';
25
+ import { Context } from 'src/context';
21
26
 
22
27
  /**
23
28
  * Describes the different in tone between colors.
@@ -72,8 +77,8 @@ class ToneDeltaPair {
72
77
  * one role has two backgrounds.
73
78
  */
74
79
  constructor(
75
- readonly roleA: DynamicColor,
76
- readonly roleB: DynamicColor,
80
+ readonly roleA: Color,
81
+ readonly roleB: Color,
77
82
  readonly delta: number,
78
83
  readonly polarity: TonePolarity,
79
84
  readonly stayTogether: boolean,
@@ -82,29 +87,28 @@ class ToneDeltaPair {
82
87
  this.constraint = constraint ?? 'exact';
83
88
  }
84
89
 
85
- adjustedTone({
86
- scheme,
87
- dynamicColor,
88
- }: {
89
- scheme: Scheme;
90
- dynamicColor: DynamicColor;
91
- }) {
90
+ adjustedTone({ context: ctx, color }: { context: Context; color: Color }) {
92
91
  const roleA = this.roleA;
93
92
  const roleB = this.roleB;
94
93
  const polarity = this.polarity;
95
94
  const constraint = this.constraint;
96
95
  const absoluteDelta =
97
96
  polarity === 'darker' ||
98
- (polarity === 'relative_lighter' && scheme.isDark) ||
99
- (polarity === 'relative_darker' && !scheme.isDark)
97
+ (polarity === 'relative_lighter' && ctx.isDark) ||
98
+ (polarity === 'relative_darker' && !ctx.isDark)
100
99
  ? -this.delta
101
100
  : this.delta;
102
101
 
103
- const amRoleA = dynamicColor.name === roleA.name;
102
+ const amRoleA = color.name === roleA.name;
104
103
  const selfRole = amRoleA ? roleA : roleB;
105
104
  const refRole = amRoleA ? roleB : roleA;
106
- let selfTone = selfRole.tone(scheme);
107
- const refTone = refRole.getTone(scheme);
105
+
106
+ if (!(selfRole instanceof ColorFromPalette)) {
107
+ throw new Error('selfRole is not a ColorFromPalette');
108
+ }
109
+
110
+ let selfTone = selfRole.options.tone;
111
+ const refTone = refRole.getTone();
108
112
  const relativeDelta = absoluteDelta * (amRoleA ? 1 : -1);
109
113
 
110
114
  if (constraint === 'exact') {
@@ -131,32 +135,31 @@ class ToneDeltaPair {
131
135
  }
132
136
  }
133
137
 
134
- if (dynamicColor.background && dynamicColor.contrastCurve) {
135
- const background = dynamicColor.background(scheme);
136
- const contrastCurve = dynamicColor.contrastCurve(scheme);
137
- if (background && contrastCurve) {
138
- // Adjust the tones for contrast, if background and contrast curve
139
- // are defined.
140
- const bgTone = background.getTone(scheme);
141
- const selfContrast = contrastCurve.get(scheme.contrastLevel);
142
- selfTone =
143
- Contrast.ratioOfTones(bgTone, selfTone) >= selfContrast &&
144
- scheme.contrastLevel >= 0
145
- ? selfTone
146
- : DynamicColor.foregroundTone(bgTone, selfContrast);
138
+ if (color instanceof ColorFromPalette) {
139
+ if (color.options.background && color.options.contrastCurve) {
140
+ const background = color.options.background;
141
+ const contrastCurve = color.options.contrastCurve;
142
+ if (background && contrastCurve) {
143
+ // Adjust the tones for contrast, if background and contrast curve
144
+ // are defined.
145
+ const bgTone = background.getTone();
146
+ const selfContrast = contrastCurve.get(ctx.contrastLevel);
147
+ selfTone =
148
+ Contrast.ratioOfTones(bgTone, selfTone) >= selfContrast &&
149
+ ctx.contrastLevel >= 0
150
+ ? selfTone
151
+ : DynamicColor.foregroundTone(bgTone, selfContrast);
152
+ }
147
153
  }
148
- }
149
154
 
150
- // This can avoid the awkward tones for background colors including the
151
- // access fixed colors. Accent fixed dim colors should not be adjusted.
152
- if (
153
- dynamicColor.isBackground &&
154
- !dynamicColor.name.endsWith('_fixed_dim')
155
- ) {
156
- if (selfTone >= 57) {
157
- selfTone = clampDouble(65, 100, selfTone);
158
- } else {
159
- selfTone = clampDouble(0, 49, selfTone);
155
+ // This can avoid the awkward tones for background colors including the
156
+ // access fixed colors. Accent fixed dim colors should not be adjusted.
157
+ if (color.options.isBackground && !color.name.endsWith('_fixed_dim')) {
158
+ if (selfTone >= 57) {
159
+ selfTone = clampDouble(65, 100, selfTone);
160
+ } else {
161
+ selfTone = clampDouble(0, 49, selfTone);
162
+ }
160
163
  }
161
164
  }
162
165
 
@@ -0,0 +1,3 @@
1
+ export * from './palette';
2
+ export * from './palette.api';
3
+ export * from './palette.module';
@@ -0,0 +1,43 @@
1
+ import { PaletteManager } from './palette.manager';
2
+ import { Palette, PaletteCallback } from './palette';
3
+ import { Hct } from '../material-color-utilities/htc';
4
+ import { argbFromHex } from '@material/material-color-utilities';
5
+
6
+ export type AddPaletteOptions = Record<string, PaletteCallback>;
7
+
8
+ export class PaletteApi {
9
+ private readonly paletteManager: PaletteManager;
10
+ constructor({ paletteManager }: { paletteManager: PaletteManager }) {
11
+ this.paletteManager = paletteManager;
12
+ }
13
+
14
+ add(args: Record<string, PaletteCallback | string>): void {
15
+ Object.entries(args).forEach(([key, callback]) => {
16
+ if (typeof callback === 'string') {
17
+ this.paletteManager.addCustomPalette(
18
+ key,
19
+ Hct.fromInt(argbFromHex(callback)),
20
+ );
21
+ } else {
22
+ this.paletteManager.add(key, callback);
23
+ }
24
+ });
25
+ }
26
+
27
+ get(
28
+ key:
29
+ | 'primary'
30
+ | 'secondary'
31
+ | 'tertiary'
32
+ | 'neutral'
33
+ | 'neutralVariant'
34
+ | 'error'
35
+ | string,
36
+ ): Palette {
37
+ return this.paletteManager.get(key);
38
+ }
39
+
40
+ getAll(): Readonly<Record<string, Palette>> {
41
+ return this.paletteManager.palettes;
42
+ }
43
+ }
@@ -0,0 +1,74 @@
1
+ import { Context } from 'src/context';
2
+ import { Palette, PaletteCallback } from './palette';
3
+ import { Hct } from '../material-color-utilities/htc';
4
+ import { ColorApi } from '../color';
5
+
6
+ export class PaletteManager {
7
+ _palettes: Record<string, Palette> = {};
8
+ context: Context;
9
+ colorApi: ColorApi;
10
+
11
+ get palettes(): Readonly<Record<string, Palette>> {
12
+ return {
13
+ ...this.context.variant.palettes,
14
+ ...this._palettes,
15
+ };
16
+ }
17
+
18
+ constructor(args: { context: Context; colorApi: ColorApi }) {
19
+ this.colorApi = args.colorApi;
20
+ this.context = args.context;
21
+
22
+ this.context.onUpdate((changed) =>
23
+ Object.entries(this.palettes).forEach(([key, value]) => {
24
+ value.update(changed);
25
+ }),
26
+ );
27
+ }
28
+
29
+ addCustomPalette(key: string, color: Hct) {
30
+ const palette = Palette.fromVariant(key, color, this.context);
31
+ this.add(key, palette);
32
+ this.colorApi.addFromCustomPalette(key);
33
+ }
34
+
35
+ add(key: string, palette: PaletteCallback | Palette): void {
36
+ if (this._palettes['key']) {
37
+ throw new Error(`Palette with key ${key} already exists`);
38
+ }
39
+ if (!(palette instanceof Palette))
40
+ palette = new Palette(key, palette, this.context);
41
+ this.set(key, palette);
42
+ }
43
+
44
+ get(
45
+ key:
46
+ | 'primary'
47
+ | 'secondary'
48
+ | 'tertiary'
49
+ | 'neutral'
50
+ | 'neutralVariant'
51
+ | 'error'
52
+ | string,
53
+ ): Palette {
54
+ const palette = this.palettes[key];
55
+ if (!palette) {
56
+ throw new Error(`Palette ${key} not found`);
57
+ }
58
+ return palette;
59
+ }
60
+
61
+ private set(key: string, palette: Palette) {
62
+ this._palettes[key] = palette;
63
+ }
64
+
65
+ update(key: string, args: PaletteCallback | Palette): void {
66
+ if (!this.palettes['key']) {
67
+ throw new Error(`Palette with key ${key} not found`);
68
+ }
69
+ if (!(args instanceof Palette)) {
70
+ args = new Palette(key, args, this.context);
71
+ }
72
+ this.set(key, args);
73
+ }
74
+ }
@@ -0,0 +1,9 @@
1
+ import { asClass } from 'awilix';
2
+ import { Module } from '../app.container';
3
+ import { PaletteManager } from './palette.manager';
4
+ import { PaletteApi } from './palette.api';
5
+
6
+ export const PaletteModule: Module = {
7
+ paletteApi: asClass(PaletteApi).singleton(),
8
+ paletteManager: asClass(PaletteManager).singleton(),
9
+ };