@skbkontur/colors 2.0.1 → 2.0.3

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 (44) hide show
  1. package/colors.less +319 -319
  2. package/colors.scss +319 -319
  3. package/lib/get-colors.js +19 -1
  4. package/lib/get-colors.ts +9 -1
  5. package/package.json +2 -2
  6. package/tokens-mobile/brand-blue-deep_accent-brand.json +117 -117
  7. package/tokens-mobile/brand-blue-deep_accent-gray.json +135 -135
  8. package/tokens-mobile/brand-blue_accent-brand.json +117 -117
  9. package/tokens-mobile/brand-blue_accent-gray.json +135 -135
  10. package/tokens-mobile/brand-green_accent-brand.json +117 -117
  11. package/tokens-mobile/brand-green_accent-gray.json +135 -135
  12. package/tokens-mobile/brand-mint_accent-brand.json +117 -117
  13. package/tokens-mobile/brand-mint_accent-gray.json +135 -135
  14. package/tokens-mobile/brand-orange_accent-gray.json +135 -135
  15. package/tokens-mobile/brand-purple_accent-brand.json +117 -117
  16. package/tokens-mobile/brand-purple_accent-gray.json +135 -135
  17. package/tokens-mobile/brand-red_accent-gray.json +135 -135
  18. package/tokens-mobile/brand-violet_accent-brand.json +117 -117
  19. package/tokens-mobile/brand-violet_accent-gray.json +135 -135
  20. package/.gitignore +0 -10
  21. package/.npmignore +0 -10
  22. package/CHANGELOG.md +0 -117
  23. package/__docs__/Colors.docs.stories.tsx +0 -1578
  24. package/__docs__/Colors.mdx +0 -228
  25. package/__docs__/ColorsAPI.docs.stories.tsx +0 -954
  26. package/__docs__/ColorsAPI.mdx +0 -133
  27. package/__stories__/colors.stories.tsx +0 -452
  28. package/__tests__/convert-color.test.ts +0 -23
  29. package/__tests__/create-tokens-from-figma.test.ts +0 -162
  30. package/__tests__/format-variable.test.ts +0 -16
  31. package/__tests__/get-colors-base.test.ts +0 -55
  32. package/__tests__/get-colors.test.ts +0 -75
  33. package/__tests__/get-interactions.test.ts +0 -37
  34. package/__tests__/get-logo.test.ts +0 -24
  35. package/__tests__/get-palette.test.ts +0 -43
  36. package/__tests__/get-promo.test.ts +0 -32
  37. package/colors-default-dark.ts +0 -332
  38. package/colors-default-light.ts +0 -336
  39. package/colors.ts +0 -319
  40. package/scripts/create-tokens-files.ts +0 -424
  41. package/scripts/create-tokens-from-figma.ts +0 -376
  42. package/scripts/figma-tokens-base.json +0 -3499
  43. package/scripts/figma-tokens.json +0 -710
  44. package/tsconfig.json +0 -8
@@ -1,376 +0,0 @@
1
- import * as fs from 'fs';
2
- import * as path from 'path';
3
-
4
- import { customizable } from '../lib/consts/default-swatch';
5
-
6
- interface Value {
7
- light: string;
8
- dark: string;
9
- }
10
-
11
- type Input = Record<string, string>;
12
- type Groups = Record<string, Partial<Value>>;
13
- type Output = Record<string, Value>;
14
- type Entries = Array<[string, Value]>;
15
-
16
- interface Rule {
17
- rule: string;
18
- fn: (data: any) => any;
19
- }
20
-
21
- const INPUT_FIGMA_BASE_PATH = path.join(import.meta.dirname, 'figma-tokens-base.json');
22
- const INPUT_FIGMA_SEMANTIC_PATH = path.join(import.meta.dirname, 'figma-tokens.json');
23
- const OUTPUT_TOKENS_PATH = './lib/get-colors-default-tokens.ts';
24
- const TOKENS_BASE_MAP: Record<string, string> = JSON.parse(fs.readFileSync(INPUT_FIGMA_BASE_PATH, 'utf-8'));
25
-
26
- export const transformations: Rule[] = [
27
- {
28
- rule: 'Filter',
29
- fn: (inputMap: Input): Input => {
30
- const MANUAL_PATTERNS: RegExp[] = [];
31
- const filtered: Input = {};
32
- for (const [key, value] of Object.entries(inputMap)) {
33
- if (key.startsWith('Effect/')) {
34
- continue;
35
- }
36
- if (MANUAL_PATTERNS.some((pattern) => pattern.test(value))) {
37
- continue;
38
- }
39
- if (value.includes('rgba(') || value.includes('#')) {
40
- continue;
41
- }
42
- filtered[key] = value;
43
- }
44
- return filtered;
45
- },
46
- },
47
- {
48
- rule: 'Group by Theme',
49
- fn: (inputMap: Input): Groups => {
50
- const groups: Groups = {};
51
- for (const [key, value] of Object.entries(inputMap)) {
52
- const parts = key.split(' / ');
53
- const theme = parts.pop()?.toLowerCase() as 'light' | 'dark';
54
- const baseName = parts.join(' / ');
55
-
56
- if (!theme || !['light', 'dark'].includes(theme)) {
57
- continue;
58
- }
59
-
60
- if (!groups[baseName]) {
61
- groups[baseName] = {};
62
- }
63
- groups[baseName][theme] = value;
64
- }
65
-
66
- for (const baseName in groups) {
67
- if (!groups[baseName].light || !groups[baseName].dark) {
68
- delete groups[baseName];
69
- }
70
- }
71
- return groups;
72
- },
73
- },
74
- {
75
- rule: 'Reorder States',
76
- fn: (groups: Groups): Groups => {
77
- const STATE_SUFFIXES = ['Hover', 'Pressed', 'Disabled'];
78
- const reordered: Groups = {};
79
-
80
- for (const [baseName, values] of Object.entries(groups)) {
81
- const parts: string[] = baseName.split('/');
82
- let newParts = [...parts];
83
-
84
- const state = parts.find((p: string) => STATE_SUFFIXES.includes(p));
85
-
86
- if (state) {
87
- newParts = parts.filter((p: string) => p !== state);
88
- newParts.push(state);
89
- }
90
-
91
- reordered[newParts.join('/')] = values;
92
- }
93
- return reordered;
94
- },
95
- },
96
- {
97
- rule: 'Apply Naming',
98
- fn: (groups: Groups): Output => {
99
- const output: Output = {};
100
- for (const [baseName, values] of Object.entries(groups)) {
101
- const tokenPath = baseName;
102
- output[slashToCamelCase(tokenPath)] = values as Value;
103
- }
104
- return output;
105
- },
106
- },
107
- {
108
- rule: 'Map to BaseTokens',
109
- fn: (tokens: Output): Output => {
110
- const CUSTOMIZABLE_KEYS = Object.keys(customizable);
111
- const DIRECT_SCALE_BASE_NAMES = ['onbrand', 'gray', 'whitealpha', 'blackalpha'];
112
-
113
- const transformToken = (rawValue: string): string => {
114
- const parts = rawValue.split('/');
115
- if (parts.length >= 2) {
116
- const last = parts[parts.length - 1];
117
- const path = parts.slice(0, -1);
118
- const mainPart = path[0];
119
- const camelMainPart = slashToCamelCase(mainPart);
120
- let expression: string = CUSTOMIZABLE_KEYS.includes(camelMainPart) ? 'base.customizable' : 'base';
121
-
122
- for (const part of path) {
123
- const camelPart = slashToCamelCase(part);
124
-
125
- if (part === mainPart && expression === 'base') {
126
- if (camelPart === 'brand') {
127
- expression += '.' + camelPart + '.palette?';
128
- continue;
129
- } else if (camelPart === 'accent') {
130
- expression += '.' + camelPart + '?.palette?';
131
- continue;
132
- } else if (camelPart === 'onAccent') {
133
- expression += '.onAccent';
134
- continue;
135
- } else if (DIRECT_SCALE_BASE_NAMES.includes(camelPart)) {
136
- expression += '.' + camelPart;
137
- continue;
138
- }
139
- }
140
-
141
- expression += '.' + camelPart;
142
- }
143
-
144
- const lastIsNumber = last.match(/^\d+$/);
145
- if (lastIsNumber) {
146
- const isIndexAfterOptionalBase = camelMainPart === 'onAccent' && path.length === 1;
147
-
148
- if (isIndexAfterOptionalBase) {
149
- expression += `?.[${last}]`;
150
- } else {
151
- expression += `[${last}]`;
152
- }
153
- } else {
154
- expression += '.' + last.toLowerCase();
155
- }
156
- return expression;
157
- }
158
- return rawValue;
159
- };
160
-
161
- const valueToExpression = (value: string): string => {
162
- let rawValue = value;
163
- const camelValue = slashToCamelCase(rawValue);
164
-
165
- if (/^Light\//.test(rawValue) || /^Dark\//.test(rawValue)) {
166
- const brandValue = TOKENS_BASE_MAP[`${rawValue.replace(/'/g, '')} / Brand`];
167
- const grayValue = TOKENS_BASE_MAP[`${rawValue.replace(/'/g, '')} / Gray`];
168
-
169
- rawValue = `${brandValue.replace('Brand', 'Accent')} || ${grayValue}`;
170
- }
171
-
172
- return rawValue
173
- .split(' || ')
174
- .map((item: string) => {
175
- if (CUSTOMIZABLE_KEYS.includes(camelValue)) {
176
- return `base.customizable.${camelValue}`;
177
- }
178
-
179
- if (item === 'Brand/Normal/0') {
180
- return 'base.brand.promo';
181
- }
182
-
183
- if (item === 'Brand/Normal/100') {
184
- return 'base.brand.original';
185
- }
186
-
187
- if (item.includes('Semantic Tokens/Light') && item.includes('Logo')) {
188
- return 'base.brand.logo.light';
189
- }
190
- if (item.includes('Semantic Tokens/Dark') && item.includes('Logo')) {
191
- return 'base.brand.logo.dark';
192
- }
193
-
194
- if (item.includes('Brand')) {
195
- if (item.includes('Semantic Tokens/Light') && item.includes('Default')) {
196
- return 'base.brand.original';
197
- }
198
-
199
- if (item.includes('Semantic Tokens/Dark') && item.includes('Default')) {
200
- return 'base.brand.original';
201
- }
202
-
203
- if (item.includes('Semantic Tokens/Light') && item.includes('Hover')) {
204
- return 'base.brand.interactions.hover.light';
205
- }
206
-
207
- if (item.includes('Semantic Tokens/Dark') && item.includes('Hover')) {
208
- return 'base.brand.interactions.hover.dark';
209
- }
210
-
211
- if (item.includes('Semantic Tokens/Light') && item.includes('Pressed')) {
212
- return 'base.brand.interactions.pressed.light';
213
- }
214
-
215
- if (item.includes('Semantic Tokens/Dark') && item.includes('Pressed')) {
216
- return 'base.brand.interactions.pressed.dark';
217
- }
218
- }
219
-
220
- if (item.includes('Accent')) {
221
- if (item.includes('Semantic Tokens/Light') && item.includes('Default')) {
222
- return 'base.accent?.original.light';
223
- }
224
-
225
- if (item.includes('Semantic Tokens/Dark') && item.includes('Default')) {
226
- return 'base.accent?.original.dark';
227
- }
228
-
229
- if (item.includes('Semantic Tokens/Light') && item.includes('Hover')) {
230
- return 'base.accent?.interactions.hover.light';
231
- }
232
-
233
- if (item.includes('Semantic Tokens/Dark') && item.includes('Hover')) {
234
- return 'base.accent?.interactions.hover.dark';
235
- }
236
-
237
- if (item.includes('Semantic Tokens/Light') && item.includes('Pressed')) {
238
- return 'base.accent?.interactions.pressed.light';
239
- }
240
-
241
- if (item.includes('Semantic Tokens/Dark') && item.includes('Pressed')) {
242
- return 'base.accent?.interactions.pressed.dark';
243
- }
244
- }
245
-
246
- return transformToken(item);
247
- })
248
- .join(' || ');
249
- };
250
-
251
- const result: Output = {};
252
- for (const [key, values] of Object.entries(tokens)) {
253
- result[key] = {
254
- light: valueToExpression(values.light),
255
- dark: valueToExpression(values.dark),
256
- };
257
- }
258
- return result;
259
- },
260
- },
261
- {
262
- rule: 'Sort Keys',
263
- fn: (tokens: Output): Entries => {
264
- const SORT_ORDER = [
265
- 'text',
266
- 'texInverted',
267
- 'textConst',
268
- 'textOnAccent',
269
- 'textOnBrand',
270
- 'shape',
271
- 'shapeInverted',
272
- 'shapeConst',
273
- 'line',
274
- 'lineInverted',
275
- 'lineConst',
276
- 'surface',
277
- 'illustration',
278
- 'customizable',
279
- ];
280
-
281
- const getSortIndex = (key: string): number => {
282
- let bestIndex = SORT_ORDER.length;
283
- let longestMatchLength = 0;
284
-
285
- for (let i = 0; i < SORT_ORDER.length; i++) {
286
- const prefix = SORT_ORDER[i];
287
- const index = i;
288
-
289
- if (key.startsWith(prefix)) {
290
- if (key === prefix) {
291
- return index;
292
- }
293
-
294
- if (prefix.length > longestMatchLength) {
295
- longestMatchLength = prefix.length;
296
- bestIndex = index;
297
- }
298
- }
299
- }
300
- return bestIndex;
301
- };
302
-
303
- return Object.entries(tokens).sort(([keyA], [keyB]) => {
304
- const indexA = getSortIndex(keyA);
305
- const indexB = getSortIndex(keyB);
306
-
307
- if (indexA !== indexB) {
308
- return indexA - indexB;
309
- }
310
- return keyA.localeCompare(keyB);
311
- }) as Entries;
312
- },
313
- },
314
- {
315
- rule: 'Generate Code',
316
- fn: (sorted: Entries): string => {
317
- const generateBody = (theme: 'light' | 'dark') =>
318
- sorted
319
- .map(([key, values], i) => ` ${key}: ${values[theme]}${i === sorted.length - 1 ? '' : ','}`)
320
- .join('\n');
321
-
322
- const lightBody = generateBody('light');
323
- const darkBody = generateBody('dark');
324
-
325
- return `import type { TokensBase } from './types/tokens-base.js';
326
-
327
- export const getColorsDefaultTokens = (base: TokensBase) => ({
328
- light: {
329
- ${lightBody}
330
- },
331
- dark: {
332
- ${darkBody}
333
- }
334
- });
335
- `;
336
- },
337
- },
338
- ];
339
-
340
- try {
341
- const inputData = JSON.parse(fs.readFileSync(INPUT_FIGMA_SEMANTIC_PATH, 'utf-8')) as Input;
342
- fs.writeFileSync(OUTPUT_TOKENS_PATH, extractTokensFromFigma(inputData));
343
- } catch (error) {
344
- console.error('An error occurred during token transformation:', error);
345
- }
346
-
347
- export function extractTokensFromFigma(figmaJson: Input): string {
348
- let currentData: any = figmaJson;
349
- for (const rule of transformations) {
350
- currentData = rule.fn(currentData);
351
- }
352
- return currentData as string;
353
- }
354
-
355
- export function slashToCamelCase(str: string): string {
356
- let processedStr = str.replace(/onbrand/gi, 'On Brand');
357
- processedStr = processedStr.replace(/[/-]/g, ' ');
358
- processedStr = processedStr.replace(/([a-z])([A-Z])/g, '$1 $2');
359
- const parts = processedStr.split(/\s+/).filter(Boolean);
360
-
361
- if (parts.length === 0) {
362
- return '';
363
- }
364
-
365
- return parts
366
- .map((part, index) => {
367
- const lowerPart = part.toLowerCase();
368
-
369
- if (index === 0) {
370
- return lowerPart;
371
- }
372
-
373
- return lowerPart.charAt(0).toUpperCase() + lowerPart.slice(1);
374
- })
375
- .join('');
376
- }