@udixio/theme 1.0.0-beta.8 → 1.0.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 (199) hide show
  1. package/.eslintrc.mjs +22 -0
  2. package/CHANGELOG.md +174 -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/adapter/config.interface.d.ts +14 -0
  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 +3215 -0
  44. package/dist/index.d.ts +7 -4
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +3175 -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 +24 -86
  98. package/src/API.ts +23 -0
  99. package/src/adapter/adapter.abstract.ts +64 -0
  100. package/src/adapter/config.interface.ts +14 -0
  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 +49 -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 -4
  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.interface.d.ts +0 -14
  157. package/dist/config/config.module.d.ts +0 -2
  158. package/dist/config/config.service.d.ts +0 -12
  159. package/dist/config/index.d.ts +0 -2
  160. package/dist/main.d.ts +0 -3
  161. package/dist/plugin/plugin.service.d.ts +0 -9
  162. package/dist/theme/entities/index.d.ts +0 -2
  163. package/dist/theme/entities/scheme.entity.d.ts +0 -15
  164. package/dist/theme/entities/variant.entity.d.ts +0 -7
  165. package/dist/theme/models/index.d.ts +0 -1
  166. package/dist/theme/models/variant.model.d.ts +0 -8
  167. package/dist/theme/services/index.d.ts +0 -3
  168. package/dist/theme/services/scheme.service.d.ts +0 -17
  169. package/dist/theme/services/theme.service.d.ts +0 -22
  170. package/dist/theme/services/variant.service.d.ts +0 -13
  171. package/dist/theme.cjs.development.js +0 -1975
  172. package/dist/theme.cjs.development.js.map +0 -1
  173. package/dist/theme.cjs.production.min.js +0 -2
  174. package/dist/theme.cjs.production.min.js.map +0 -1
  175. package/dist/theme.esm.js +0 -1947
  176. package/dist/theme.esm.js.map +0 -1
  177. package/src/app.service.spec.ts +0 -15
  178. package/src/app.service.ts +0 -23
  179. package/src/color/color.interface.ts +0 -13
  180. package/src/color/entities/color.entity.ts +0 -71
  181. package/src/color/entities/index.ts +0 -1
  182. package/src/color/models/default-color.model.ts +0 -300
  183. package/src/color/models/index.ts +0 -1
  184. package/src/color/services/color-manager.service.ts +0 -191
  185. package/src/color/services/color.service.spec.ts +0 -28
  186. package/src/color/services/color.service.ts +0 -75
  187. package/src/color/services/index.ts +0 -2
  188. package/src/config/config.interface.ts +0 -15
  189. package/src/config/config.module.ts +0 -7
  190. package/src/config/config.service.ts +0 -68
  191. package/src/config/index.ts +0 -2
  192. package/src/main.ts +0 -14
  193. package/src/plugin/plugin.service.ts +0 -26
  194. package/src/theme/entities/index.ts +0 -2
  195. package/src/theme/entities/variant.entity.ts +0 -39
  196. package/src/theme/models/index.ts +0 -1
  197. package/src/theme/models/variant.model.ts +0 -63
  198. package/src/theme/services/index.ts +0 -3
  199. package/src/theme/services/variant.service.ts +0 -52
@@ -0,0 +1,198 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2021 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
+ /**
19
+ * A color system built using CAM16 hue and chroma, and L* from
20
+ * L*a*b*.
21
+ *
22
+ * Using L* creates a link between the color system, contrast, and thus
23
+ * accessibility. Contrast ratio depends on relative luminance, or Y in the XYZ
24
+ * color space. L*, or perceptual luminance can be calculated from Y.
25
+ *
26
+ * Unlike Y, L* is linear to human perception, allowing trivial creation of
27
+ * accurate color tones.
28
+ *
29
+ * Unlike contrast ratio, measuring contrast in L* is linear, and simple to
30
+ * calculate. A difference of 40 in HCT tone guarantees a contrast ratio >= 3.0,
31
+ * and a difference of 50 guarantees a contrast ratio >= 4.5.
32
+ */
33
+
34
+ import { Cam16, lstarFromArgb, lstarFromY, ViewingConditions } from '@material/material-color-utilities';
35
+ import { HctSolver } from './hct_solver';
36
+
37
+ /**
38
+ * HCT, hue, chroma, and tone. A color system that provides a perceptually
39
+ * accurate color measurement system that can also accurately render what colors
40
+ * will appear as in different lighting environments.
41
+ */
42
+ export class Hct {
43
+ /**
44
+ * @param hue 0 <= hue < 360; invalid values are corrected.
45
+ * @param chroma 0 <= chroma < ?; Informally, colorfulness. The color
46
+ * returned may be lower than the requested chroma. Chroma has a different
47
+ * maximum for any given hue and tone.
48
+ * @param tone 0 <= tone <= 100; invalid values are corrected.
49
+ * @return HCT representation of a color in default viewing conditions.
50
+ */
51
+
52
+ internalHue: number;
53
+ internalChroma: number;
54
+ internalTone: number;
55
+
56
+ private constructor(private argb: number) {
57
+ const cam = Cam16.fromInt(argb);
58
+ this.internalHue = cam.hue;
59
+ this.internalChroma = cam.chroma;
60
+ this.internalTone = lstarFromArgb(argb);
61
+ this.argb = argb;
62
+ }
63
+
64
+ /**
65
+ * A number, in degrees, representing ex. red, orange, yellow, etc.
66
+ * Ranges from 0 <= hue < 360.
67
+ */
68
+ get hue(): number {
69
+ return this.internalHue;
70
+ }
71
+
72
+ /**
73
+ * @param newHue 0 <= newHue < 360; invalid values are corrected.
74
+ * Chroma may decrease because chroma has a different maximum for any given
75
+ * hue and tone.
76
+ */
77
+ set hue(newHue: number) {
78
+ this.setInternalState(
79
+ HctSolver.solveToInt(newHue, this.internalChroma, this.internalTone),
80
+ );
81
+ }
82
+
83
+ get chroma(): number {
84
+ return this.internalChroma;
85
+ }
86
+
87
+ /**
88
+ * @param newChroma 0 <= newChroma < ?
89
+ * Chroma may decrease because chroma has a different maximum for any given
90
+ * hue and tone.
91
+ */
92
+ set chroma(newChroma: number) {
93
+ this.setInternalState(
94
+ HctSolver.solveToInt(this.internalHue, newChroma, this.internalTone),
95
+ );
96
+ }
97
+
98
+ /** Lightness. Ranges from 0 to 100. */
99
+ get tone(): number {
100
+ return this.internalTone;
101
+ }
102
+
103
+ /**
104
+ * @param newTone 0 <= newTone <= 100; invalid valids are corrected.
105
+ * Chroma may decrease because chroma has a different maximum for any given
106
+ * hue and tone.
107
+ */
108
+ set tone(newTone: number) {
109
+ this.setInternalState(
110
+ HctSolver.solveToInt(this.internalHue, this.internalChroma, newTone),
111
+ );
112
+ }
113
+
114
+ static from(hue: number, chroma: number, tone: number) {
115
+ return new Hct(HctSolver.solveToInt(hue, chroma, tone));
116
+ }
117
+
118
+ /**
119
+ * @param argb ARGB representation of a color.
120
+ * @return HCT representation of a color in default viewing conditions
121
+ */
122
+ static fromInt(argb: number) {
123
+ return new Hct(argb);
124
+ }
125
+
126
+ static isBlue(hue: number): boolean {
127
+ return hue >= 250 && hue < 270;
128
+ }
129
+
130
+ static isYellow(hue: number): boolean {
131
+ return hue >= 105 && hue < 125;
132
+ }
133
+
134
+ static isCyan(hue: number): boolean {
135
+ return hue >= 170 && hue < 207;
136
+ }
137
+
138
+ toInt(): number {
139
+ return this.argb;
140
+ }
141
+
142
+ /** Sets a property of the Hct object. */
143
+ setValue(propertyName: string, value: number) {
144
+ (this as any)[propertyName] = value;
145
+ }
146
+
147
+ toString(): string {
148
+ return `HCT(${this.hue.toFixed(0)}, ${this.chroma.toFixed(0)}, ${this.tone.toFixed(
149
+ 0,
150
+ )})`;
151
+ }
152
+
153
+ /**
154
+ * Translates a color into different [ViewingConditions].
155
+ *
156
+ * Colors change appearance. They look different with lights on versus off,
157
+ * the same color, as in hex code, on white looks different when on black.
158
+ * This is called color relativity, most famously explicated by Josef Albers
159
+ * in Interaction of Color.
160
+ *
161
+ * In color science, color appearance models can account for this and
162
+ * calculate the appearance of a color in different settings. HCT is based on
163
+ * CAM16, a color appearance model, and uses it to make these calculations.
164
+ *
165
+ * See [ViewingConditions.make] for parameters affecting color appearance.
166
+ */
167
+ inViewingConditions(vc: ViewingConditions): Hct {
168
+ // 1. Use CAM16 to find XYZ coordinates of color in specified VC.
169
+ const cam = Cam16.fromInt(this.toInt());
170
+ const viewedInVc = cam.xyzInViewingConditions(vc);
171
+
172
+ // 2. Create CAM16 of those XYZ coordinates in default VC.
173
+ const recastInVc = Cam16.fromXyzInViewingConditions(
174
+ viewedInVc[0],
175
+ viewedInVc[1],
176
+ viewedInVc[2],
177
+ ViewingConditions.make(),
178
+ );
179
+
180
+ // 3. Create HCT from:
181
+ // - CAM16 using default VC with XYZ coordinates in specified VC.
182
+ // - L* converted from Y in XYZ coordinates in specified VC.
183
+ const recastHct = Hct.from(
184
+ recastInVc.hue,
185
+ recastInVc.chroma,
186
+ lstarFromY(viewedInVc[1]),
187
+ );
188
+ return recastHct;
189
+ }
190
+
191
+ private setInternalState(argb: number) {
192
+ const cam = Cam16.fromInt(argb);
193
+ this.internalHue = cam.hue;
194
+ this.internalChroma = cam.chroma;
195
+ this.internalTone = lstarFromArgb(argb);
196
+ this.argb = argb;
197
+ }
198
+ }
@@ -19,8 +19,21 @@ import { DynamicColor } from './dynamic_color';
19
19
 
20
20
  /**
21
21
  * Describes the different in tone between colors.
22
+ *
23
+ * nearer and farther are deprecated. Use DeltaConstraint instead.
24
+ */
25
+ export type TonePolarity =
26
+ | 'darker'
27
+ | 'lighter'
28
+ | 'nearer'
29
+ | 'farther'
30
+ | 'relative_darker'
31
+ | 'relative_lighter';
32
+
33
+ /**
34
+ * Describes how to fulfill a tone delta pair constraint.
22
35
  */
23
- export type TonePolarity = 'darker' | 'lighter' | 'nearer' | 'farther';
36
+ export type DeltaConstraint = 'exact' | 'nearer' | 'farther';
24
37
 
25
38
  /**
26
39
  * Documents a constraint between two DynamicColors, in which their tones must
@@ -36,13 +49,14 @@ export class ToneDeltaPair {
36
49
  *
37
50
  * The polarity is an adjective that describes "A", compared to "B".
38
51
  *
39
- * For instance, ToneDeltaPair(A, B, 15, 'darker', stayTogether) states that
40
- * A's tone should be at least 15 darker than B's.
52
+ * For instance, ToneDeltaPair(A, B, 15, 'darker', 'exact') states that
53
+ * A's tone should be exactly 15 darker than B's.
41
54
  *
42
- * 'nearer' and 'farther' describes closeness to the surface roles. For
43
- * instance, ToneDeltaPair(A, B, 10, 'nearer', stayTogether) states that A
44
- * should be 10 lighter than B in light mode, and 10 darker than B in dark
45
- * mode.
55
+ * 'relative_darker' and 'relative_lighter' describes the tone adjustment
56
+ * relative to the surface color trend (white in light mode; black in dark
57
+ * mode). For instance, ToneDeltaPair(A, B, 10, 'relative_lighter',
58
+ * 'farther') states that A should be at least 10 lighter than B in light
59
+ * mode, and at least 10 darker than B in dark mode.
46
60
  *
47
61
  * @param roleA The first role in a pair.
48
62
  * @param roleB The second role in a pair.
@@ -50,8 +64,9 @@ export class ToneDeltaPair {
50
64
  * values have undefined behavior.
51
65
  * @param polarity The relative relation between tones of roleA and roleB,
52
66
  * as described above.
53
- * @param stayTogether Whether these two roles should stay on the same side of
54
- * the "awkward zone" (T50-59). This is necessary for certain cases where
67
+ * @param constraint How to fulfill the tone delta pair constraint.
68
+ * @param stayTogether Whether these two roles should stay on the same side
69
+ * of the "awkward zone" (T50-59). This is necessary for certain cases where
55
70
  * one role has two backgrounds.
56
71
  */
57
72
  constructor(
@@ -59,6 +74,9 @@ export class ToneDeltaPair {
59
74
  readonly roleB: DynamicColor,
60
75
  readonly delta: number,
61
76
  readonly polarity: TonePolarity,
62
- readonly stayTogether: boolean
63
- ) {}
77
+ readonly stayTogether: boolean,
78
+ readonly constraint?: DeltaConstraint,
79
+ ) {
80
+ this.constraint = constraint ?? 'exact';
81
+ }
64
82
  }
@@ -0,0 +1,3 @@
1
+ export * from './plugin.abstract';
2
+ export * from './plugin.module';
3
+ export * from './plugin.api';
@@ -1,7 +1,48 @@
1
- import { AppService } from '../app.service';
1
+ import { API } from '../API';
2
2
 
3
- export abstract class PluginAbstract {
4
- static dependencies: (new () => PluginAbstract)[];
3
+ export type PluginConstructor<Plugin extends PluginImplAbstract<any>> = new (
4
+ ...args: any
5
+ ) => Plugin;
5
6
 
6
- protected constructor(protected appService: AppService) {}
7
+ export abstract class PluginAbstract<
8
+ Plugin extends PluginImplAbstract<Options>,
9
+ Options extends object,
10
+ > {
11
+ public abstract readonly dependencies: (new (
12
+ ...args: any
13
+ ) => PluginAbstract<any, any>)[];
14
+ public abstract readonly name: string;
15
+ public options: Options;
16
+ public abstract readonly pluginClass: PluginConstructor<Plugin>;
17
+ protected pluginInstance: Plugin | undefined;
18
+
19
+ constructor(options: Options) {
20
+ this.options = options;
21
+ }
22
+
23
+ public init(api: API) {
24
+ this.pluginInstance = new this.pluginClass(api, this.options);
25
+ this.pluginInstance.onInit?.();
26
+ return this;
27
+ }
28
+
29
+ public getInstance(): Plugin {
30
+ if (!this.pluginInstance) {
31
+ throw new Error(`Plugin ${this.name} is not initialized`);
32
+ }
33
+ return this.pluginInstance;
34
+ }
35
+ }
36
+
37
+ export abstract class PluginImplAbstract<Options extends object> {
38
+ constructor(
39
+ protected api: API,
40
+ protected options: Options,
41
+ ) {
42
+ this.onInit?.();
43
+ }
44
+
45
+ abstract onInit?(): void;
46
+
47
+ abstract onLoad?(): void;
7
48
  }
@@ -0,0 +1,51 @@
1
+ import { PluginAbstract } from './plugin.abstract';
2
+ import { API } from '../API';
3
+
4
+ export class PluginApi {
5
+ private plugins = new Map<string, PluginAbstract<any, any>>();
6
+
7
+ public addPlugin(plugin: PluginAbstract<any, any>) {
8
+ this.plugins.set(plugin.name, plugin);
9
+ }
10
+
11
+ public initPlugins(api: API) {
12
+ const plugins = new Map(this.plugins);
13
+
14
+ let size = 0;
15
+
16
+ this.plugins = new Map();
17
+
18
+ do {
19
+ size = plugins.size;
20
+ plugins.forEach((plugin, key) => {
21
+ const deps = plugin.dependencies.filter(
22
+ (dep) => !this.plugins.has(new dep().name),
23
+ );
24
+ if (deps.length === 0) {
25
+ this.plugins.set(plugin.name, plugin.init(api));
26
+ plugins.delete(key);
27
+ }
28
+ });
29
+ } while (plugins.size != 0 && plugins.size < size);
30
+
31
+ if (plugins.size > 0)
32
+ throw new Error(
33
+ "Some plugins couldn't be loaded due to missing dependencies: " +
34
+ Array.from(plugins.keys()),
35
+ );
36
+ }
37
+
38
+ public loadPlugins() {
39
+ this.plugins.forEach((plugin) => {
40
+ plugin.getInstance().onLoad?.();
41
+ });
42
+ }
43
+
44
+ public getPlugin<T extends PluginAbstract<any, any>>(
45
+ plugin: new (...args: any) => T,
46
+ ): T {
47
+ const pluginInstance = this.plugins.get(new plugin().name);
48
+ if (!pluginInstance) throw new Error(`Plugin ${plugin.name} not found`);
49
+ return pluginInstance as T;
50
+ }
51
+ }
@@ -1,7 +1,7 @@
1
1
  import { asClass } from 'awilix';
2
2
  import { Module } from '../app.container';
3
- import { PluginService } from './plugin.service';
3
+ import { PluginApi } from './plugin.api';
4
4
 
5
5
  export const PluginModule: Module = {
6
- pluginService: asClass(PluginService).singleton(),
6
+ pluginApi: asClass(PluginApi).singleton(),
7
7
  };
@@ -0,0 +1,203 @@
1
+ import { PluginAbstract, PluginImplAbstract } from '../../plugin';
2
+
3
+ export enum FontFamily {
4
+ Expressive = 'expressive',
5
+ Neutral = 'neutral',
6
+ }
7
+ export type FontStyle = {
8
+ fontSize: number;
9
+ lineHeight: number;
10
+ fontWeight: number;
11
+ letterSpacing?: number;
12
+ fontFamily: FontFamily;
13
+ };
14
+ export type FontRole = 'display' | 'headline' | 'title' | 'label' | 'body';
15
+ export type FontSize = 'large' | 'medium' | 'small';
16
+ interface FontPluginOptions {
17
+ fontFamily?: {
18
+ expressive?: string[];
19
+ neutral?: string[];
20
+ };
21
+ fontStyles?: Partial<Record<FontRole, Record<FontSize, Partial<FontStyle>>>>;
22
+ }
23
+
24
+ export class FontPlugin extends PluginAbstract<
25
+ FontPluginImpl,
26
+ FontPluginOptions
27
+ > {
28
+ dependencies = [];
29
+ name = 'font';
30
+ pluginClass = FontPluginImpl;
31
+ }
32
+
33
+ class FontPluginImpl extends PluginImplAbstract<FontPluginOptions> {
34
+ private _fontFamily: { expressive: string[]; neutral: string[] } | undefined;
35
+
36
+ get fontFamily(): { expressive: string[]; neutral: string[] } {
37
+ if (!this._fontFamily) throw new Error('Font family not initialized');
38
+ return this._fontFamily;
39
+ }
40
+
41
+ set fontFamily(
42
+ value: { expressive: string[]; neutral: string[] } | undefined
43
+ ) {
44
+ this._fontFamily = value;
45
+ }
46
+
47
+ private _fontStyles:
48
+ | Record<FontRole, Record<FontSize, FontStyle>>
49
+ | undefined;
50
+
51
+ get fontStyles(): Record<FontRole, Record<FontSize, FontStyle>> {
52
+ if (!this._fontStyles) throw new Error('Font styles not initialized');
53
+ return this._fontStyles;
54
+ }
55
+
56
+ set fontStyles(
57
+ value: Record<FontRole, Record<FontSize, FontStyle>> | undefined
58
+ ) {
59
+ this._fontStyles = value;
60
+ }
61
+
62
+ getFonts() {
63
+ return {
64
+ fontStyles: this.fontStyles,
65
+ fontFamily: this.fontFamily,
66
+ };
67
+ }
68
+
69
+ onInit(): void {
70
+ this.fontFamily = {
71
+ expressive: this.options?.fontFamily?.expressive ?? [
72
+ 'Roboto',
73
+ 'sans-serif',
74
+ ],
75
+ neutral: this.options?.fontFamily?.neutral ?? ['Roboto', 'sans-serif'],
76
+ };
77
+ this.fontStyles = {
78
+ display: {
79
+ large: {
80
+ fontWeight: 400,
81
+ fontSize: 3.5625,
82
+ lineHeight: 4,
83
+ letterSpacing: -0.015625,
84
+ fontFamily: FontFamily.Expressive,
85
+ },
86
+ medium: {
87
+ fontWeight: 400,
88
+ fontSize: 2.8125,
89
+ lineHeight: 3.25,
90
+ fontFamily: FontFamily.Expressive,
91
+ },
92
+ small: {
93
+ fontWeight: 400,
94
+ fontSize: 2.25,
95
+ lineHeight: 2.75,
96
+ fontFamily: FontFamily.Expressive,
97
+ },
98
+ },
99
+ headline: {
100
+ large: {
101
+ fontWeight: 400,
102
+ fontSize: 2,
103
+ lineHeight: 2.5,
104
+ fontFamily: FontFamily.Expressive,
105
+ },
106
+ medium: {
107
+ fontWeight: 400,
108
+ fontSize: 1.75,
109
+ lineHeight: 2.25,
110
+ fontFamily: FontFamily.Expressive,
111
+ },
112
+ small: {
113
+ fontWeight: 400,
114
+ fontSize: 1.5,
115
+ lineHeight: 2,
116
+ fontFamily: FontFamily.Expressive,
117
+ },
118
+ },
119
+ title: {
120
+ large: {
121
+ fontWeight: 400,
122
+ fontSize: 1.375,
123
+ lineHeight: 1.75,
124
+ fontFamily: FontFamily.Neutral,
125
+ },
126
+ medium: {
127
+ fontWeight: 500,
128
+ fontSize: 1,
129
+ lineHeight: 1.5,
130
+ fontFamily: FontFamily.Neutral,
131
+ letterSpacing: 0.009375,
132
+ },
133
+ small: {
134
+ fontWeight: 500,
135
+ fontSize: 0.875,
136
+ lineHeight: 1.25,
137
+ fontFamily: FontFamily.Neutral,
138
+ letterSpacing: 0.00625,
139
+ },
140
+ },
141
+ label: {
142
+ large: {
143
+ fontWeight: 500,
144
+ fontSize: 0.875,
145
+ lineHeight: 1.25,
146
+ fontFamily: FontFamily.Neutral,
147
+ letterSpacing: 0.00625,
148
+ },
149
+ medium: {
150
+ fontWeight: 500,
151
+ fontSize: 0.75,
152
+ lineHeight: 1,
153
+ fontFamily: FontFamily.Neutral,
154
+ letterSpacing: 0.03125,
155
+ },
156
+ small: {
157
+ fontWeight: 500,
158
+ fontSize: 0.6875,
159
+ lineHeight: 1,
160
+ fontFamily: FontFamily.Neutral,
161
+ letterSpacing: 0.03125,
162
+ },
163
+ },
164
+ body: {
165
+ large: {
166
+ fontWeight: 400,
167
+ fontSize: 1,
168
+ lineHeight: 1.5625,
169
+ fontFamily: FontFamily.Neutral,
170
+ letterSpacing: 0.03125,
171
+ },
172
+ medium: {
173
+ fontWeight: 400,
174
+ fontSize: 0.875,
175
+ lineHeight: 1.25,
176
+ fontFamily: FontFamily.Neutral,
177
+ letterSpacing: 0.015625,
178
+ },
179
+ small: {
180
+ fontWeight: 400,
181
+ fontSize: 0.75,
182
+ lineHeight: 1,
183
+ fontFamily: FontFamily.Neutral,
184
+ letterSpacing: 0.025,
185
+ },
186
+ },
187
+ };
188
+
189
+ if (this.options && this.options.fontStyles)
190
+ Object.entries(this.options.fontStyles).forEach(([key, fontParam]) => {
191
+ const fontRole: FontRole = key as FontRole;
192
+ Object.entries(fontParam).forEach(([size, fontStyle]) => {
193
+ const fontSize: FontSize = size as FontSize;
194
+ if (fontStyle) {
195
+ this.fontStyles[fontRole][fontSize] = {
196
+ ...this.fontStyles[fontRole][fontSize],
197
+ ...fontStyle,
198
+ };
199
+ }
200
+ });
201
+ });
202
+ }
203
+ }
@@ -0,0 +1 @@
1
+ export * from './font.plugin';
@@ -0,0 +1 @@
1
+ export * from './font';
@@ -1,4 +1,7 @@
1
- export * from './entities';
2
- export * from './models';
3
- export * from './services';
4
1
  export * from './theme.module';
2
+ export * from './variants';
3
+ export * from './scheme.manager';
4
+ export * from './scheme';
5
+ export * from './theme.api';
6
+ export * from './variant';
7
+ export * from './variant.manager';
@@ -1,9 +1,6 @@
1
- import { SchemeEntity, SchemeOptions } from '../entities/scheme.entity';
2
- import {
3
- argbFromHex,
4
- Hct,
5
- TonalPalette,
6
- } from '@material/material-color-utilities';
1
+ import { Scheme, SchemeOptions } from './scheme';
2
+ import { argbFromHex, TonalPalette } from '@material/material-color-utilities';
3
+ import { Hct } from '../material-color-utilities/htc';
7
4
 
8
5
  export type SchemeServiceOptions = Omit<
9
6
  SchemeOptions,
@@ -12,15 +9,31 @@ export type SchemeServiceOptions = Omit<
12
9
  sourcesColorHex: Record<string, string> & { primary?: string };
13
10
  palettes: Record<
14
11
  string,
15
- {
16
- sourceColorkey?: string;
17
- tonalPalette: (sourceColorHct: Hct) => TonalPalette;
18
- }
12
+ | {
13
+ sourceColorkey: string;
14
+ tonalPalette: CustomPaletteFunction;
15
+ }
16
+ | {
17
+ sourceColorkey?: never;
18
+ tonalPalette: PaletteFunction;
19
+ }
19
20
  >;
20
21
  };
21
22
 
22
- export class SchemeService {
23
- private schemeEntity?: SchemeEntity;
23
+ type PaletteFunctionArgs = {
24
+ isDark: boolean;
25
+ sourceColorHct: Hct;
26
+ };
27
+
28
+ export type PaletteFunction = (args: PaletteFunctionArgs) => TonalPalette;
29
+ export type CustomPaletteFunction = (
30
+ args: PaletteFunctionArgs & {
31
+ colorHct: Hct;
32
+ },
33
+ ) => TonalPalette;
34
+
35
+ export class SchemeManager {
36
+ private schemeEntity?: Scheme;
24
37
  private options?: SchemeServiceOptions;
25
38
 
26
39
  createOrUpdate(options: Partial<SchemeServiceOptions>) {
@@ -53,25 +66,32 @@ export class SchemeService {
53
66
  { sourceColorkey, tonalPalette: paletteFunction },
54
67
  ] of Object.entries(this.options.palettes)) {
55
68
  let palette: TonalPalette;
56
- if (!sourceColorkey) {
57
- palette = paletteFunction(sourceColorHct);
69
+ if (typeof sourceColorkey != 'string') {
70
+ palette = paletteFunction({
71
+ sourceColorHct: sourceColorHct,
72
+ isDark: options.isDark ?? false,
73
+ });
58
74
  } else {
59
75
  const sourceColorArgb = argbFromHex(
60
- this.options.sourcesColorHex[sourceColorkey]
76
+ this.options.sourcesColorHex[sourceColorkey],
61
77
  );
62
- const sourceColorHct: Hct = Hct.fromInt(sourceColorArgb);
63
- palette = paletteFunction(sourceColorHct);
78
+ const colorHct: Hct = Hct.fromInt(sourceColorArgb);
79
+ palette = paletteFunction({
80
+ sourceColorHct: sourceColorHct,
81
+ colorHct: colorHct,
82
+ isDark: options.isDark ?? false,
83
+ });
64
84
  }
65
85
  palettes.set(key, palette);
66
86
  }
67
- this.schemeEntity = new SchemeEntity({
87
+ this.schemeEntity = new Scheme({
68
88
  ...this.options,
69
89
  palettes: palettes,
70
90
  sourceColorArgb: sourceColorArgb,
71
91
  });
72
92
  }
73
93
 
74
- get(): SchemeEntity {
94
+ get(): Scheme {
75
95
  if (!this.schemeEntity) {
76
96
  throw new Error('Scheme is not created');
77
97
  }