@udixio/theme 1.0.0-beta.2

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 (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +73 -0
  3. package/dist/app.module.d.ts +2 -0
  4. package/dist/app.service.d.ts +7 -0
  5. package/dist/color/color-manager.service.d.ts +15 -0
  6. package/dist/color/color.interface.d.ts +9 -0
  7. package/dist/color/color.module.d.ts +2 -0
  8. package/dist/color/color.service.d.ts +13 -0
  9. package/dist/color/entities/color.entity.d.ts +37 -0
  10. package/dist/color/models/default-color.model.d.ts +4 -0
  11. package/dist/index.d.ts +1 -0
  12. package/dist/index.js +8 -0
  13. package/dist/main.d.ts +2 -0
  14. package/dist/material-color-utilities/contrastCurve.d.ts +46 -0
  15. package/dist/material-color-utilities/dynamic_color.d.ts +171 -0
  16. package/dist/material-color-utilities/index.d.ts +2 -0
  17. package/dist/material-color-utilities/toneDeltaPair.d.ts +60 -0
  18. package/dist/theme/entities/scheme.entity.d.ts +15 -0
  19. package/dist/theme/entities/variant.entity.d.ts +6 -0
  20. package/dist/theme/models/variant.model.d.ts +8 -0
  21. package/dist/theme/services/scheme.service.d.ts +12 -0
  22. package/dist/theme/services/theme.service.d.ts +13 -0
  23. package/dist/theme/services/variant.service.d.ts +7 -0
  24. package/dist/theme/theme.module.d.ts +2 -0
  25. package/dist/theme.cjs.development.js +1310 -0
  26. package/dist/theme.cjs.development.js.map +1 -0
  27. package/dist/theme.cjs.production.min.js +2 -0
  28. package/dist/theme.cjs.production.min.js.map +1 -0
  29. package/dist/theme.esm.js +1306 -0
  30. package/dist/theme.esm.js.map +1 -0
  31. package/package.json +101 -0
  32. package/src/app.module.ts +10 -0
  33. package/src/app.service.spec.ts +15 -0
  34. package/src/app.service.ts +11 -0
  35. package/src/color/color-manager.service.ts +186 -0
  36. package/src/color/color.interface.ts +15 -0
  37. package/src/color/color.module.ts +11 -0
  38. package/src/color/color.service.spec.ts +28 -0
  39. package/src/color/color.service.ts +52 -0
  40. package/src/color/entities/color.entity.ts +60 -0
  41. package/src/color/models/default-color.model.ts +297 -0
  42. package/src/index.ts +1 -0
  43. package/src/main.ts +11 -0
  44. package/src/material-color-utilities/contrastCurve.ts +63 -0
  45. package/src/material-color-utilities/dynamic_color.ts +450 -0
  46. package/src/material-color-utilities/index.ts +2 -0
  47. package/src/material-color-utilities/toneDeltaPair.ts +64 -0
  48. package/src/theme/entities/scheme.entity.ts +44 -0
  49. package/src/theme/entities/variant.entity.ts +38 -0
  50. package/src/theme/models/variant.model.ts +56 -0
  51. package/src/theme/services/scheme.service.ts +52 -0
  52. package/src/theme/services/theme.service.ts +58 -0
  53. package/src/theme/services/variant.service.ts +17 -0
  54. package/src/theme/theme.module.ts +10 -0
@@ -0,0 +1,60 @@
1
+ import { hexFromArgb, TonalPalette } from '@material/material-color-utilities';
2
+ import { SchemeEntity } from '../../theme/entities/scheme.entity';
3
+ import { DynamicColor } from '../../material-color-utilities/dynamic_color';
4
+ import { ContrastCurve } from '../../material-color-utilities';
5
+ import mergeDeep from 'merge-deep';
6
+ import { SchemeService } from '../../theme/services/scheme.service';
7
+ import { ColorManagerService } from '../color-manager.service';
8
+
9
+ export interface ColorOptions {
10
+ palette: (scheme: SchemeEntity) => TonalPalette;
11
+ tone: (scheme: SchemeEntity) => number;
12
+ isBackground?: boolean;
13
+ background?: (scheme: SchemeEntity) => DynamicColor;
14
+ secondBackground?: (scheme: SchemeEntity) => DynamicColor;
15
+ contrastCurve?: ContrastCurve;
16
+ toneDeltaPair?: (scheme: SchemeEntity) => {
17
+ roleA: DynamicColor;
18
+ readonly roleB: DynamicColor;
19
+ readonly delta: number;
20
+ readonly polarity: 'darker' | 'lighter' | 'nearer' | 'farther';
21
+ readonly stayTogether: boolean;
22
+ };
23
+ }
24
+
25
+ export class ColorEntity {
26
+ private dynamicColor: DynamicColor | null = null;
27
+
28
+ constructor(
29
+ private option: ColorOptions & { name: string },
30
+ private schemeService: SchemeService,
31
+ private colorService: ColorManagerService
32
+ ) {}
33
+
34
+ update(args: Partial<ColorOptions & { name: string }>) {
35
+ this.dynamicColor = null;
36
+ this.option = mergeDeep(this.option, args);
37
+ }
38
+
39
+ getHex(): string {
40
+ return hexFromArgb(this.getArgb());
41
+ }
42
+
43
+ getArgb() {
44
+ return this.getDynamicColor().getArgb(this.schemeService.get());
45
+ }
46
+
47
+ getName(): string {
48
+ return this.option.name.replace(/([A-Z])/g, '_$1').toLowerCase();
49
+ }
50
+
51
+ getDynamicColor(): DynamicColor {
52
+ if (!this.dynamicColor) {
53
+ this.dynamicColor = DynamicColor.fromPalette({
54
+ ...this.option,
55
+ name: this.getName(),
56
+ });
57
+ }
58
+ return this.dynamicColor;
59
+ }
60
+ }
@@ -0,0 +1,297 @@
1
+ import { DislikeAnalyzer, Hct } from '@material/material-color-utilities';
2
+ import { ContrastCurve, ToneDeltaPair } from '../../material-color-utilities';
3
+ import { DynamicColor } from '../../material-color-utilities/dynamic_color';
4
+ import { ColorOptions } from '../entities/color.entity';
5
+ import { ColorManagerService, highestSurface } from '../color-manager.service';
6
+
7
+ export type DynamicColorKey =
8
+ | 'background'
9
+ | 'onBackground'
10
+ | 'surface'
11
+ | 'surfaceDim'
12
+ | 'surfaceBright'
13
+ | 'surfaceContainerLowest'
14
+ | 'surfaceContainerLow'
15
+ | 'surfaceContainer'
16
+ | 'surfaceContainerHigh'
17
+ | 'surfaceContainerHighest'
18
+ | 'onSurface'
19
+ | 'surfaceVariant'
20
+ | 'onSurfaceVariant'
21
+ | 'inverseSurface'
22
+ | 'inverseOnSurface'
23
+ | 'outline'
24
+ | 'outlineVariant'
25
+ | 'shadow'
26
+ | 'scrim'
27
+ | 'surfaceTint'
28
+ | 'primary'
29
+ | 'onPrimary'
30
+ | 'primaryContainer'
31
+ | 'onPrimaryContainer'
32
+ | 'inversePrimary'
33
+ | 'secondary'
34
+ | 'onSecondary'
35
+ | 'secondaryContainer'
36
+ | 'onSecondaryContainer'
37
+ | 'tertiary'
38
+ | 'onTertiary'
39
+ | 'tertiaryContainer'
40
+ | 'onTertiaryContainer'
41
+ | 'error'
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
+ function findDesiredChromaByTone(
59
+ hue: number,
60
+ chroma: number,
61
+ tone: number,
62
+ byDecreasingTone: boolean
63
+ ): number {
64
+ let answer = tone;
65
+
66
+ let closestToChroma = Hct.from(hue, chroma, tone);
67
+ if (closestToChroma.chroma < chroma) {
68
+ let chromaPeak = closestToChroma.chroma;
69
+ while (closestToChroma.chroma < chroma) {
70
+ answer += byDecreasingTone ? -1.0 : 1.0;
71
+ const potentialSolution = Hct.from(hue, chroma, answer);
72
+ if (chromaPeak > potentialSolution.chroma) {
73
+ break;
74
+ }
75
+ if (Math.abs(potentialSolution.chroma - chroma) < 0.4) {
76
+ break;
77
+ }
78
+
79
+ const potentialDelta = Math.abs(potentialSolution.chroma - chroma);
80
+ const currentDelta = Math.abs(closestToChroma.chroma - chroma);
81
+ if (potentialDelta < currentDelta) {
82
+ closestToChroma = potentialSolution;
83
+ }
84
+ chromaPeak = Math.max(chromaPeak, potentialSolution.chroma);
85
+ }
86
+ }
87
+
88
+ return answer;
89
+ }
90
+
91
+ export const DefaultColors = (
92
+ colorManagerService: ColorManagerService
93
+ ): Partial<Record<DynamicColorKey, Partial<Omit<ColorOptions, 'name'>>>> => ({
94
+ background: {
95
+ palette: (s) => s.getPalette('neutral'),
96
+ tone: (s) => (s.isDark ? 6 : 98),
97
+ isBackground: true,
98
+ },
99
+ onBackground: {
100
+ palette: (s) => s.getPalette('neutral'),
101
+ tone: (s) => (s.isDark ? 90 : 10),
102
+ background: (s) => colorManagerService.get('background').getDynamicColor(),
103
+ contrastCurve: new ContrastCurve(3, 3, 4.5, 7),
104
+ },
105
+ surface: {
106
+ palette: (s) => s.getPalette('neutral'),
107
+ tone: (s) => (s.isDark ? 6 : 98),
108
+ isBackground: true,
109
+ },
110
+ surfaceDim: {
111
+ palette: (s) => s.getPalette('neutral'),
112
+ tone: (s) => (s.isDark ? 6 : 87),
113
+ isBackground: true,
114
+ },
115
+ surfaceBright: {
116
+ palette: (s) => s.getPalette('neutral'),
117
+ tone: (s) => (s.isDark ? 24 : 98),
118
+ isBackground: true,
119
+ },
120
+ surfaceContainerLowest: {
121
+ palette: (s) => s.getPalette('neutral'),
122
+ tone: (s) => (s.isDark ? 4 : 100),
123
+ isBackground: true,
124
+ },
125
+ surfaceContainerLow: {
126
+ palette: (s) => s.getPalette('neutral'),
127
+ tone: (s) => (s.isDark ? 10 : 96),
128
+ isBackground: true,
129
+ },
130
+ surfaceContainer: {
131
+ palette: (s) => s.getPalette('neutral'),
132
+ tone: (s) => (s.isDark ? 12 : 94),
133
+ isBackground: true,
134
+ },
135
+ surfaceContainerHigh: {
136
+ palette: (s) => s.getPalette('neutral'),
137
+ tone: (s) => (s.isDark ? 17 : 92),
138
+ isBackground: true,
139
+ },
140
+ surfaceContainerHighest: {
141
+ palette: (s) => s.getPalette('neutral'),
142
+ tone: (s) => (s.isDark ? 22 : 90),
143
+ isBackground: true,
144
+ },
145
+ onSurface: {
146
+ palette: (s) => s.getPalette('neutral'),
147
+ tone: (s) => (s.isDark ? 90 : 10),
148
+ background: (s) => highestSurface(s, colorManagerService),
149
+ contrastCurve: new ContrastCurve(4.5, 7, 11, 21),
150
+ },
151
+ surfaceVariant: {
152
+ palette: (s) => s.getPalette('neutralVariant'),
153
+ tone: (s) => (s.isDark ? 30 : 90),
154
+ isBackground: true,
155
+ },
156
+ onSurfaceVariant: {
157
+ palette: (s) => s.getPalette('neutralVariant'),
158
+ tone: (s) => (s.isDark ? 80 : 30),
159
+ background: (s) => highestSurface(s, colorManagerService),
160
+ contrastCurve: new ContrastCurve(3, 4.5, 7, 11),
161
+ },
162
+ inverseSurface: {
163
+ palette: (s) => s.getPalette('neutral'),
164
+ tone: (s) => (s.isDark ? 90 : 20),
165
+ },
166
+ inverseOnSurface: {
167
+ palette: (s) => s.getPalette('neutral'),
168
+ tone: (s) => (s.isDark ? 20 : 95),
169
+ background: (s) =>
170
+ colorManagerService.get('inverseSurface').getDynamicColor(),
171
+ contrastCurve: new ContrastCurve(4.5, 7, 11, 21),
172
+ },
173
+ outline: {
174
+ palette: (s) => s.getPalette('neutralVariant'),
175
+ tone: (s) => (s.isDark ? 60 : 50),
176
+ background: (s) => highestSurface(s, colorManagerService),
177
+ contrastCurve: new ContrastCurve(1.5, 3, 4.5, 7),
178
+ },
179
+ outlineVariant: {
180
+ palette: (s) => s.getPalette('neutralVariant'),
181
+ tone: (s) => (s.isDark ? 30 : 80),
182
+ background: (s) => highestSurface(s, colorManagerService),
183
+ contrastCurve: new ContrastCurve(1, 1, 3, 7),
184
+ },
185
+ shadow: {
186
+ palette: (s) => s.getPalette('neutral'),
187
+ tone: (s) => 0,
188
+ },
189
+ scrim: {
190
+ palette: (s) => s.getPalette('neutral'),
191
+ tone: (s) => 0,
192
+ },
193
+ surfaceTint: {
194
+ palette: (s) => s.getPalette('neutral'),
195
+ tone: (s) => (s.isDark ? 80 : 40),
196
+ isBackground: true,
197
+ },
198
+ secondaryContainer: {
199
+ tone: (s) => {
200
+ const initialTone = s.isDark ? 30 : 90;
201
+ return findDesiredChromaByTone(
202
+ s.getPalette('secondary').hue,
203
+ s.getPalette('secondary').chroma,
204
+ initialTone,
205
+ !s.isDark
206
+ );
207
+ },
208
+ },
209
+ onSecondaryContainer: {
210
+ tone: (s) => {
211
+ return DynamicColor.foregroundTone(
212
+ colorManagerService.get('secondaryContainer').getDynamicColor().tone(s),
213
+ 4.5
214
+ );
215
+ },
216
+ },
217
+ tertiaryContainer: {
218
+ palette: (s) => s.getPalette('tertiary'),
219
+ tone: (s) => {
220
+ const proposedHct = s
221
+ .getPalette('tertiary')
222
+ .getHct(s.sourceColorHct.tone);
223
+ return DislikeAnalyzer.fixIfDisliked(proposedHct).tone;
224
+ },
225
+ },
226
+ onTertiaryContainer: {
227
+ palette: (s) => s.getPalette('tertiary'),
228
+ tone: (s) => {
229
+ return DynamicColor.foregroundTone(
230
+ colorManagerService.get('tertiaryContainer').getDynamicColor().tone(s),
231
+ 4.5
232
+ );
233
+ },
234
+ },
235
+ error: {
236
+ palette: (s) => s.getPalette('error'),
237
+ tone: (s) => (s.isDark ? 80 : 40),
238
+ isBackground: true,
239
+ background: (s) => highestSurface(s, colorManagerService),
240
+ contrastCurve: new ContrastCurve(3, 4.5, 7, 11),
241
+ toneDeltaPair: (s) =>
242
+ new ToneDeltaPair(
243
+ colorManagerService.get('errorContainer').getDynamicColor(),
244
+ colorManagerService.get('error').getDynamicColor(),
245
+ 15,
246
+ 'nearer',
247
+ false
248
+ ),
249
+ },
250
+ onError: {
251
+ palette: (s) => s.getPalette('error'),
252
+ tone: (s) => (s.isDark ? 20 : 100),
253
+ background: (s) => colorManagerService.get('error').getDynamicColor(),
254
+ contrastCurve: new ContrastCurve(4.5, 7, 11, 21),
255
+ },
256
+ errorContainer: {
257
+ palette: (s) => s.getPalette('error'),
258
+ tone: (s) => (s.isDark ? 30 : 90),
259
+ isBackground: true,
260
+ background: (s) => highestSurface(s, colorManagerService),
261
+ contrastCurve: new ContrastCurve(1, 1, 3, 7),
262
+ toneDeltaPair: (s) =>
263
+ new ToneDeltaPair(
264
+ colorManagerService.get('errorContainer').getDynamicColor(),
265
+ colorManagerService.get('error').getDynamicColor(),
266
+ 15,
267
+ 'nearer',
268
+ false
269
+ ),
270
+ },
271
+ onErrorContainer: {
272
+ palette: (s) => s.getPalette('error'),
273
+ tone: (s) => (s.isDark ? 90 : 10),
274
+ background: (s) =>
275
+ colorManagerService.get('errorContainer').getDynamicColor(),
276
+ contrastCurve: new ContrastCurve(4.5, 7, 11, 21),
277
+ },
278
+
279
+ onTertiaryFixed: {
280
+ palette: (s) => s.getPalette('tertiary'),
281
+ tone: (s) => 10.0,
282
+ background: (s) =>
283
+ colorManagerService.get('tertiaryFixedDim').getDynamicColor(),
284
+ secondBackground: (s) =>
285
+ colorManagerService.get('tertiaryFixed').getDynamicColor(),
286
+ contrastCurve: new ContrastCurve(4.5, 7, 11, 21),
287
+ },
288
+ onTertiaryFixedVariant: {
289
+ palette: (s) => s.getPalette('tertiary'),
290
+ tone: (s) => 30.0,
291
+ background: (s) =>
292
+ colorManagerService.get('tertiaryFixedDim').getDynamicColor(),
293
+ secondBackground: (s) =>
294
+ colorManagerService.get('tertiaryFixed').getDynamicColor(),
295
+ contrastCurve: new ContrastCurve(3, 4.5, 7, 11),
296
+ },
297
+ });
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './main';
package/src/main.ts ADDED
@@ -0,0 +1,11 @@
1
+ import { NestFactory } from '@nestjs/core';
2
+ import { AppModule } from './app.module';
3
+ import { AppService } from './app.service';
4
+
5
+ export async function main(): Promise<[AppService, () => Promise<void>]> {
6
+ const app = await NestFactory.create(AppModule);
7
+ const appService = app.get(AppService);
8
+
9
+ const close = () => app.close();
10
+ return [appService, close];
11
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2023 Google LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ import { lerp } from '@material/material-color-utilities';
19
+
20
+ /**
21
+ * A class containing a value that changes with the contrast level.
22
+ *
23
+ * Usually represents the contrast requirements for a dynamic color on its
24
+ * background. The four values correspond to values for contrast levels -1.0,
25
+ * 0.0, 0.5, and 1.0, respectively.
26
+ */
27
+ export class ContrastCurve {
28
+ /**
29
+ * Creates a `ContrastCurve` object.
30
+ *
31
+ * @param low Value for contrast level -1.0
32
+ * @param normal Value for contrast level 0.0
33
+ * @param medium Value for contrast level 0.5
34
+ * @param high Value for contrast level 1.0
35
+ */
36
+ constructor(
37
+ readonly low: number,
38
+ readonly normal: number,
39
+ readonly medium: number,
40
+ readonly high: number
41
+ ) {}
42
+
43
+ /**
44
+ * Returns the value at a given contrast level.
45
+ *
46
+ * @param contrastLevel The contrast level. 0.0 is the default (normal); -1.0
47
+ * is the lowest; 1.0 is the highest.
48
+ * @return The value. For contrast ratios, a number between 1.0 and 21.0.
49
+ */
50
+ get(contrastLevel: number): number {
51
+ if (contrastLevel <= -1.0) {
52
+ return this.low;
53
+ } else if (contrastLevel < 0.0) {
54
+ return lerp(this.low, this.normal, (contrastLevel - -1) / 1);
55
+ } else if (contrastLevel < 0.5) {
56
+ return lerp(this.normal, this.medium, (contrastLevel - 0) / 0.5);
57
+ } else if (contrastLevel < 1.0) {
58
+ return lerp(this.medium, this.high, (contrastLevel - 0.5) / 0.5);
59
+ } else {
60
+ return this.high;
61
+ }
62
+ }
63
+ }