@skbkontur/colors 2.0.0-alpha.6 → 2.0.1

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 (43) hide show
  1. package/.gitignore +10 -0
  2. package/.npmignore +10 -0
  3. package/CHANGELOG.md +117 -0
  4. package/__docs__/Colors.docs.stories.tsx +1578 -0
  5. package/__docs__/Colors.mdx +228 -0
  6. package/__docs__/ColorsAPI.docs.stories.tsx +954 -0
  7. package/__docs__/ColorsAPI.mdx +133 -0
  8. package/__stories__/colors.stories.tsx +452 -0
  9. package/__tests__/convert-color.test.ts +23 -0
  10. package/__tests__/create-tokens-from-figma.test.ts +162 -0
  11. package/__tests__/format-variable.test.ts +16 -0
  12. package/__tests__/get-colors-base.test.ts +55 -0
  13. package/__tests__/get-colors.test.ts +75 -0
  14. package/__tests__/get-interactions.test.ts +37 -0
  15. package/__tests__/get-logo.test.ts +24 -0
  16. package/__tests__/get-palette.test.ts +43 -0
  17. package/__tests__/get-promo.test.ts +32 -0
  18. package/colors-default-dark.d.ts +319 -0
  19. package/colors-default-dark.js +319 -0
  20. package/colors-default-dark.ts +332 -0
  21. package/colors-default-light.d.ts +319 -0
  22. package/colors-default-light.js +319 -0
  23. package/colors-default-light.ts +336 -0
  24. package/package.json +25 -28
  25. package/scripts/create-tokens-files.ts +424 -0
  26. package/scripts/create-tokens-from-figma.ts +376 -0
  27. package/scripts/figma-tokens-base.json +3499 -0
  28. package/scripts/figma-tokens.json +710 -0
  29. package/tokens/brand-blue-deep_accent-brand.css +1 -1
  30. package/tokens/brand-blue-deep_accent-gray.css +1 -1
  31. package/tokens/brand-blue_accent-brand.css +1 -1
  32. package/tokens/brand-blue_accent-gray.css +1 -1
  33. package/tokens/brand-green_accent-brand.css +1 -1
  34. package/tokens/brand-green_accent-gray.css +1 -1
  35. package/tokens/brand-mint_accent-brand.css +1 -1
  36. package/tokens/brand-mint_accent-gray.css +1 -1
  37. package/tokens/brand-orange_accent-gray.css +1 -1
  38. package/tokens/brand-purple_accent-brand.css +1 -1
  39. package/tokens/brand-purple_accent-gray.css +1 -1
  40. package/tokens/brand-red_accent-gray.css +1 -1
  41. package/tokens/brand-violet_accent-brand.css +1 -1
  42. package/tokens/brand-violet_accent-gray.css +1 -1
  43. package/tsconfig.json +8 -0
@@ -0,0 +1,1578 @@
1
+ import React from 'react';
2
+
3
+ import { DropdownMenu, MenuHeader, MenuItem, Select, Toast, ThemeFactory, ThemeContext } from '@skbkontur/react-ui';
4
+ import { BasicThemeClass } from '@skbkontur/react-ui/internal/themes/BasicTheme';
5
+ import { css, injectGlobal } from '@skbkontur/react-ui/lib/theming/Emotion';
6
+ import { SearchLoupeIcon16Regular } from '@skbkontur/icons/icons/SearchLoupeIcon/SearchLoupeIcon16Regular';
7
+ import { SideMenuThemeIn } from '@skbkontur/side-menu';
8
+ import { WeatherMoonIcon16Regular } from '@skbkontur/icons/icons/WeatherMoonIcon/WeatherMoonIcon16Regular';
9
+ import { WeatherSunIcon16Regular } from '@skbkontur/icons/icons/WeatherSunIcon/WeatherSunIcon16Regular';
10
+ import type { Meta } from '@skbkontur/react-ui/typings/stories';
11
+
12
+ import { brand as brandSwatch } from '../lib/consts/default-swatch';
13
+ import { getColors } from '../lib/get-colors';
14
+ import type { ColorFormat } from '../lib/utils/convert-color';
15
+
16
+ import { SideMenu } from '@skbkontur/side-menu';
17
+ import { Kontur } from '@skbkontur/logos/src/Kontur';
18
+ import { Product } from '@skbkontur/logos/src/Product';
19
+ import { Button } from '@skbkontur/react-ui/components/Button';
20
+ import { Checkbox } from '@skbkontur/react-ui/components/Checkbox';
21
+ import { Gapped } from '@skbkontur/react-ui/components/Gapped';
22
+ import { Input } from '@skbkontur/react-ui/components/Input';
23
+ import { Link } from '@skbkontur/react-ui/components/Link';
24
+ import { Radio } from '@skbkontur/react-ui/components/Radio';
25
+ import { RadioGroup } from '@skbkontur/react-ui/components/RadioGroup';
26
+ import { Tabs } from '@skbkontur/react-ui/components/Tabs';
27
+ import { Toggle } from '@skbkontur/react-ui/components/Toggle';
28
+ import { Tooltip } from '@skbkontur/react-ui/components/Tooltip';
29
+ import { IconArrowALeftRegular24 } from '@skbkontur/icons-v2/IconArrowALeftRegular24';
30
+ import { IconQuestionCircleLight20 } from '@skbkontur/icons-v2/IconQuestionCircleLight20';
31
+ import { IconWarningTriangleSolid20 } from '@skbkontur/icons-v2/IconWarningTriangleSolid20';
32
+ import { IconDocTextRegular24 } from '@skbkontur/icons-v2/IconDocTextRegular24';
33
+ import { IconCommentRectTextRegular24 } from '@skbkontur/icons-v2/IconCommentRectTextRegular24';
34
+ import { IconMarketShoppingBasketRegular24 } from '@skbkontur/icons-v2/IconMarketShoppingBasketRegular24';
35
+ import { IconSettingsGearRegular24 } from '@skbkontur/icons-v2/IconSettingsGearRegular24';
36
+ import { IconPeople2Regular24 } from '@skbkontur/icons-v2/IconPeople2Regular24';
37
+ import { IconStackHDownRegular24 } from '@skbkontur/icons-v2/IconStackHDownRegular24';
38
+ import { IconNaturePlantFlowerSolid20 } from '@skbkontur/icons-v2/IconNaturePlantFlowerSolid20';
39
+ import { AddonsTheme } from '@skbkontur/react-ui-addons';
40
+
41
+ interface TokenPair {
42
+ key: string;
43
+ value: {
44
+ light: string;
45
+ dark: string;
46
+ };
47
+ }
48
+
49
+ injectGlobal(`
50
+ [data-role=preview]:has([data-colors-controls]) {
51
+ padding: 0 !important;
52
+ }
53
+ `);
54
+
55
+ export default {
56
+ title: 'Colors/Colors',
57
+ parameters: {
58
+ creevey: {
59
+ skip: true,
60
+ },
61
+ },
62
+ } as Meta;
63
+
64
+ /**
65
+ * Библиотека состоит из семантических переменных, которые настраиваются через 3 параметра:
66
+ * - **brand** — брендовый цвет `red | orange | green | mint | blue | blueDeep | violet | purple | #custom-hex`
67
+ * - **accent** — акцентный цвет `gray | brand | #custom-hex` (по умолчанию gray)
68
+ * - **theme** — тема `light | dark` (по умолчанию light)
69
+ */
70
+ export const ColorsExampleStory = () => {
71
+ const styles: Record<string, string> = {
72
+ colors: css`
73
+ display: grid;
74
+ grid-template-columns: 1fr 1fr 1fr;
75
+ gap: 24px;
76
+ `,
77
+ colorGroup: css`
78
+ margin-bottom: 64px;
79
+ `,
80
+ filterRow: css`
81
+ position: sticky;
82
+ z-index: 10;
83
+ display: flex;
84
+ align-items: center;
85
+ gap: 24px;
86
+ padding: 16px;
87
+ width: calc(100% - 32px);
88
+ top: 0;
89
+ background: white;
90
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
91
+ font-size: 14px;
92
+ `,
93
+ headerRow: css`
94
+ display: flex;
95
+ align-items: center;
96
+ gap: 16px;
97
+ width: calc(100% - 32px);
98
+ margin-bottom: -24px;
99
+ padding: 4px 16px 4px;
100
+ font-weight: 600;
101
+ color: #222;
102
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
103
+ position: sticky;
104
+ top: 57px;
105
+ z-index: 10;
106
+ background: white;
107
+ font-size: 14px;
108
+ font-weight: 700;
109
+ line-height: 1.2;
110
+ `,
111
+ dropdownRow: css`
112
+ display: flex;
113
+ align-items: center;
114
+ gap: 16px;
115
+ width: calc(100% - 16px);
116
+ padding: 8px;
117
+ margin: 0 8px;
118
+ border-radius: 8px;
119
+ cursor: pointer;
120
+ background: none;
121
+ border: none;
122
+ text-align: left;
123
+ transition: background 0.1s ease;
124
+ font-family: inherit;
125
+ &:hover {
126
+ background: rgba(0, 0, 0, 0.06);
127
+ }
128
+ &:active {
129
+ background: rgba(0, 0, 0, 0.1);
130
+ }
131
+ `,
132
+ colorName: css`
133
+ flex: 1;
134
+ `,
135
+ colorTileWrapper: css`
136
+ position: relative;
137
+ z-index: 1;
138
+ display: flex;
139
+ flex-direction: column;
140
+ align-items: center;
141
+ gap: 4px;
142
+ width: 140px;
143
+ `,
144
+ colorTile: css`
145
+ height: 32px;
146
+ width: 32px;
147
+ border: 1px solid rgba(127, 127, 127, 0.3);
148
+ border-radius: 8px;
149
+ flex-shrink: 0;
150
+ `,
151
+ colorHex: css`
152
+ font-size: 12px;
153
+ font-weight: 600;
154
+ white-space: nowrap;
155
+ color: #8b8b8b;
156
+ `,
157
+ groupHeader: css`
158
+ position: sticky;
159
+ top: 57px;
160
+ width: 50%;
161
+ z-index: 10;
162
+ background: white;
163
+ font-size: 14px;
164
+ line-height: 1;
165
+ padding: 4px 16px;
166
+ font-weight: bold;
167
+ `,
168
+ controls: css`
169
+ position: sticky;
170
+ z-index: 10;
171
+ bottom: 0;
172
+ padding: 8px;
173
+ background: white;
174
+ box-shadow: 0 -1px rgba(0, 0, 0, 0.15);
175
+ margin-top: auto;
176
+ `,
177
+ };
178
+
179
+ const defaultColorOptions = Object.keys(brandSwatch);
180
+ const defaultBrandColor = 'mint';
181
+ const defaultAccentColor = 'brand';
182
+ const defaultTheme = 'dark';
183
+ const colorOptions = [...defaultColorOptions, 'custom'];
184
+ const baseAccentOptions = ['gray', 'brand', 'custom'];
185
+
186
+ const [activeTab, setActiveTab] = React.useState<string>('tab-0');
187
+ const [activeRadio, setActiveRadio] = React.useState<number>(1);
188
+ const [checked, setChecked] = React.useState<boolean>(true);
189
+ const [brand, setBrand] = React.useState(defaultBrandColor);
190
+ const [accent, setAccent] = React.useState(defaultAccentColor);
191
+ const [colorTheme, setColorTheme] = React.useState<'light' | 'dark'>(defaultTheme);
192
+
193
+ const [customBrandColor, setCustomBrandColor] = React.useState('#FFDD2D');
194
+ const [customAccentColor, setCustomAccentColor] = React.useState('#FFDD2D');
195
+
196
+ const handleCustomBrandColorValueChange = React.useCallback((newColor: string) => {
197
+ setCustomBrandColor(newColor);
198
+ }, []);
199
+
200
+ const handleCustomAccentColorValueChange = React.useCallback((newColor: string) => {
201
+ setCustomAccentColor(newColor);
202
+ }, []);
203
+
204
+ const getBrandColorForSwatch = React.useCallback(() => {
205
+ if (brand === 'custom') {
206
+ return customBrandColor.trim() !== ''
207
+ ? customBrandColor
208
+ : brandSwatch[defaultBrandColor as keyof typeof brandSwatch];
209
+ }
210
+ return brandSwatch[brand as keyof typeof brandSwatch];
211
+ }, [brand, customBrandColor]);
212
+
213
+ const renderColorItem = (color: string, text: string) => {
214
+ return (
215
+ <div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
216
+ <div
217
+ style={{
218
+ flexShrink: 0,
219
+ background: color,
220
+ width: 12,
221
+ height: 12,
222
+ borderRadius: 4,
223
+ }}
224
+ />
225
+ {text}
226
+ </div>
227
+ );
228
+ };
229
+
230
+ const renderBrandItem = (value: string) => {
231
+ if (value === 'custom') {
232
+ const colorToDisplay = customBrandColor.trim() !== '' ? customBrandColor : '#999';
233
+ return renderColorItem(colorToDisplay, '#custom-hex');
234
+ }
235
+ return renderColorItem(brandSwatch[value as keyof typeof brandSwatch], value);
236
+ };
237
+
238
+ const renderThemeItem = (value: 'light' | 'dark') => {
239
+ return (
240
+ <div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
241
+ {value === 'light' ? <WeatherSunIcon16Regular /> : <WeatherMoonIcon16Regular />}
242
+ {value}
243
+ </div>
244
+ );
245
+ };
246
+
247
+ const safeBrandColor = React.useMemo(() => {
248
+ if (brand !== 'custom') {
249
+ return brand;
250
+ }
251
+ return customBrandColor.trim() !== '' ? customBrandColor : defaultBrandColor;
252
+ }, [brand, customBrandColor]);
253
+
254
+ const safeAccentColor = React.useMemo(() => {
255
+ if (accent !== 'custom') {
256
+ return accent;
257
+ }
258
+ return customAccentColor.trim() !== '' ? customAccentColor : defaultAccentColor;
259
+ }, [accent, customAccentColor]);
260
+
261
+ const isAccentDisabled = (accentValue: string, currentBrand: string) => {
262
+ return accentValue === 'brand' && (currentBrand === 'red' || currentBrand === 'orange');
263
+ };
264
+
265
+ const renderAccentItemValue = (value: string) => {
266
+ if (value === 'custom') {
267
+ const colorToDisplay = customAccentColor.trim() !== '' ? customAccentColor : '#999';
268
+ return renderColorItem(colorToDisplay, '#custom-hex');
269
+ }
270
+ const color = value === 'gray' ? '#3d3d3d' : getBrandColorForSwatch();
271
+ return renderColorItem(color, value);
272
+ };
273
+
274
+ const renderAccentMenuItem = (value: string) => {
275
+ const content = renderAccentItemValue(value);
276
+ return (
277
+ <div key={value} style={{ opacity: isAccentDisabled(value, brand) ? 0.4 : 1 }}>
278
+ {content}
279
+ </div>
280
+ );
281
+ };
282
+
283
+ React.useEffect(() => {
284
+ if (isAccentDisabled(accent, brand)) {
285
+ setAccent('gray');
286
+ Toast.push('Акцент brand недоступен для red и orange', null, 3000);
287
+ }
288
+ }, [brand, accent]);
289
+
290
+ const effectiveAccentColor = React.useMemo(() => {
291
+ if (isAccentDisabled(safeAccentColor, safeBrandColor)) {
292
+ return 'gray';
293
+ }
294
+ return safeAccentColor;
295
+ }, [safeBrandColor, safeAccentColor]);
296
+
297
+ let c = getColors({
298
+ brand: safeBrandColor,
299
+ accent: effectiveAccentColor,
300
+ theme: colorTheme,
301
+ });
302
+
303
+ const theme = ThemeFactory.create<BasicThemeClass | SideMenuThemeIn | AddonsTheme>({
304
+ brand: c.shapeBoldBrandOriginal,
305
+ bgDefault: c.surfaceHigh,
306
+ bgSecondary: c.surfaceHigh,
307
+ bgDisabled: c.shapeOtherDisabled,
308
+ errorText: c.textErrorHeavy,
309
+ borderColorDisabled: c.lineNeutralFaint,
310
+ placeholderColor: c.textNeutralPale,
311
+ outlineColorFocus: c.surfaceBase,
312
+ placeholderColorLight: `color-mix(in srgb, ${c.textNeutralPale}, transparent 40%)`,
313
+ textColorDefault: c.textNeutralHeavy,
314
+ textColorDisabled: c.textNeutralPale,
315
+ borderColorFocus: c.lineAccentBold,
316
+ borderColorError: c.lineErrorBold,
317
+ borderColorWarning: c.lineWarningBold,
318
+ linkColor: c.textAccentHeavy,
319
+ linkHoverColor: c.textAccentHeavyHover,
320
+ linkActiveColor: c.textAccentHeavyPressed,
321
+ linkSuccessColor: c.textSuccessHeavy,
322
+ linkSuccessHoverColor: c.textSuccessHeavyHover,
323
+ linkSuccessActiveColor: c.textSuccessHeavyPressed,
324
+ linkDangerColor: c.textErrorHeavy,
325
+ linkDangerHoverColor: c.textErrorHeavyHover,
326
+ linkDangerActiveColor: c.textErrorHeavyPressed,
327
+ linkDisabledColor: c.textNeutralPale,
328
+ linkGrayedColor: c.textNeutralSoft,
329
+ linkGrayedHoverColor: c.textNeutralHeavy,
330
+ linkGrayedActiveColor: c.textNeutralHeavy,
331
+ linkFocusOutlineColor: c.lineAccentBold,
332
+ tokenDisabledBg: c.shapeOtherDisabled,
333
+ tokenBg: c.shapeFaintNeutralAlpha,
334
+ tokenColor: c.textNeutralHeavy,
335
+ tokenBorderColor: c.lineNeutralPale,
336
+ tokenBgHover: c.shapeFaintNeutralAlphaHover,
337
+ tokenColorHover: c.textNeutralHeavy,
338
+ tokenBorderColorHover: c.lineNeutralPale,
339
+ tokenBgActive: c.shapeBoldAccent,
340
+ tokenColorActive: c.textOnAccentBoldHeavy,
341
+ tokenInputBorderColor: c.lineNeutralPale,
342
+ tokenInputBorderColorHover: c.lineNeutralPaleHover,
343
+ tokenInputBorderColorFocus: c.lineAccentBold,
344
+ tokenInputBorderColorError: c.lineErrorBold,
345
+ tokenInputBorderColorWarning: c.lineWarningBold,
346
+ tokenInputBorderTopColor: c.lineNeutralPale,
347
+ tokenInputPlaceholderColor: c.textNeutralPale,
348
+ tokenInputPlaceholderColorLight: `color-mix(in srgb, ${c.textNeutralPale}, transparent 40%)`,
349
+ tokenInputDisabledBg: c.shapeOtherDisabled,
350
+ tokenInputBg: c.shapeOtherField,
351
+ tokenInputMenuPopupBg: 'transparent',
352
+ loaderBg: c.shapeInvertedNeutralHeavy,
353
+ btnDisabledBorderColor: c.lineNeutralFaint,
354
+ btnCheckedBg: c.shapeBoldAccent,
355
+ btnCheckedDisabledBg: c.shapeOtherAccentBoldDisabled,
356
+ btnCheckedDisabledColor: c.textInvertedNeutralSoft,
357
+ btnCheckedTextColor: c.textOnAccentBoldHeavy,
358
+ btnDefaultBg: c.shapeOtherBase,
359
+ btnDefaultHoverBg: c.shapeOtherBaseHover,
360
+ btnDefaultActiveBg: c.shapeOtherBasePressed,
361
+ btnDefaultHoverTextColor: c.textNeutralHeavy,
362
+ btnDefaultBorderColor: c.lineNeutralPale,
363
+ btnSuccessBg: c.shapeBoldSuccess,
364
+ btnSuccessBorderColor: c.shapeBoldSuccess,
365
+ btnSuccessHoverBg: c.shapeBoldSuccessHover,
366
+ btnSuccessHoverBorderColor: c.shapeBoldSuccessHover,
367
+ btnSuccessHoverTextColor: c.textConstHeavyWhite,
368
+ btnSuccessTextColor: c.textConstHeavyWhite,
369
+ btnSuccessActiveBg: c.shapeBoldSuccessPressed,
370
+ btnSuccessActiveBorderColor: c.shapeBoldSuccessPressed,
371
+ btnPrimaryBg: c.shapeBoldAccent,
372
+ btnPrimaryHoverBg: c.shapeBoldAccentHover,
373
+ btnPrimaryActiveBg: c.shapeBoldAccentPressed,
374
+ btnPrimaryHoverTextColor: '',
375
+ btnPrimaryBorderColor: c.shapeBoldAccent,
376
+ btnPrimaryHoverBorderColor: c.shapeBoldAccentHover,
377
+ btnPrimaryActiveBorderColor: c.shapeBoldAccentPressed,
378
+ btnPrimaryTextColor: c.textOnAccentBoldHeavy,
379
+ btnDangerBg: c.shapeBoldError,
380
+ btnDangerHoverBg: c.shapeBoldErrorHover,
381
+ btnDangerHoverBorderColor: c.shapeBoldErrorHover,
382
+ btnDangerTextColor: c.textConstHeavyWhite,
383
+ btnDangerActiveBg: c.shapeBoldErrorPressed,
384
+ btnDangerActiveBorderColor: c.shapeBoldErrorPressed,
385
+ btnPayBg: c.shapeBoldWarning,
386
+ btnPayBorderColor: c.shapeBoldWarning,
387
+ btnPayHoverBg: c.shapeBoldWarningHover,
388
+ btnPayHoverBorderColor: c.shapeBoldWarningHover,
389
+ btnPayTextColor: c.textConstHeavyBlack,
390
+ btnPayActiveBg: c.shapeBoldWarningPressed,
391
+ btnPayActiveBorderColor: c.shapeBoldWarningPressed,
392
+ btnMenuArrowColor: c.textNeutralSoft,
393
+ btnDisabledBg: c.shapeOtherDisabled,
394
+ btnBorderColorWarning: c.lineWarningBold,
395
+ btnBorderColorError: c.lineErrorBold,
396
+ btnErrorSecondary: c.shapeFaintError,
397
+ btnWarningSecondary: c.shapeFaintWarning,
398
+ btnInsetColor: c.surfaceBase,
399
+ btnDisabledTextColor: c.textNeutralPale,
400
+ btnBacklessBg: 'transparent',
401
+ btnBacklessHoverBg: c.shapeOtherBacklessHover,
402
+ btnBacklessActiveBg: c.shapeOtherBacklessPressed,
403
+ btnBacklessActiveBorderColor: c.lineNeutralPale,
404
+ btnBacklessBorderColor: c.lineNeutralPale,
405
+ btnBacklessDisabledBorderColor: c.lineNeutralFaint,
406
+ btnBacklessHoverBorderColor: c.lineNeutralPale,
407
+ btnBacklessHoverTextColor: '',
408
+ btnTextHoverBg: c.shapeOtherBacklessHover,
409
+ btnTextActiveBg: c.shapeOtherBacklessPressed,
410
+ selectDefaultBg: c.shapeOtherField,
411
+ selectPlaceholderColor: c.textNeutralPale,
412
+ selectPlaceholderColorDisabled: c.textNeutralPale,
413
+ selectMenuArrowColorDisabled: c.textNeutralPale,
414
+ selectBgDisabled: c.shapeOtherDisabled,
415
+ selectBorderColorDisabled: c.lineNeutralFaint,
416
+ tooltipCloseBtnColor: c.textNeutralPale,
417
+ tooltipCloseBtnHoverColor: c.textNeutralHeavy,
418
+ tooltipTextColor: c.textNeutralHeavy,
419
+ tooltipBg: c.surfaceHigh,
420
+ kebabBackgroundHover: c.shapeOtherBacklessHover,
421
+ kebabBackgroundActive: c.shapeOtherBacklessPressed,
422
+ kebabIconColor: c.textNeutralSoft,
423
+ modalWindowShadow: '0px 16px 32px 0px rgba(0, 0, 0, 0.06)',
424
+ modalBackBg: c.surfaceModalBackdrop,
425
+ modalBg: c.surfaceHigh,
426
+ modalBackOpacity: '1',
427
+ modalCloseButtonColor: c.textNeutralPale,
428
+ modalCloseButtonDisabledColor: c.textNeutralPale,
429
+ modalCloseButtonHoverColor: c.textNeutralHeavy,
430
+ modalFixedHeaderBg: c.surfaceHigh,
431
+ modalFooterBg: c.surfaceHigh,
432
+ modalHeaderTextColor: c.textNeutralHeavy,
433
+ modalSeparatorBorderBottom: `1px solid ${c.lineNeutralFaint}`,
434
+ sidePageFooterPanelBg: c.surfaceHigh,
435
+ sidePageBackingBg: c.surfaceModalBackdrop,
436
+ sidePageBackingBgOpacity: '1',
437
+ sidePageCloseButtonColor: c.textNeutralPale,
438
+ sidePageCloseButtonHoverColor: c.textNeutralHeavy,
439
+ sidePageContainerShadow: '0 5px 10px rgba(0, 0, 0, 0.2)',
440
+ sidePageBgDefault: c.surfaceHigh,
441
+ sidePageHeaderTextColor: c.textNeutralHeavy,
442
+ dateInputMaskColor: c.textNeutralPale,
443
+ calendarBottomSeparatorBorderColor: c.lineNeutralFaint,
444
+ calendarBottomSeparatorBorder: `1px solid ${c.lineNeutralFaint}`,
445
+ calendarBg: c.surfaceHigh,
446
+ calendarCellBg: 'transparent',
447
+ calendarCellHoverColor: '',
448
+ calendarCellActiveHoverColor: '',
449
+ calendarCellWeekendColor: c.customizableHeavyRed,
450
+ calendarCellTodayBorder: '1px solid',
451
+ calendarCellSelectedBgColor: c.shapeBoldAccent,
452
+ calendarCellSelectedFontColor: c.textOnAccentBoldHeavy,
453
+ calendarMonthHeaderStickedBgColor: c.surfaceHigh,
454
+ calendarMonthTitleBorderBottomColor: c.lineNeutralFaint,
455
+ calendarCellHoverBgColor: c.shapeOtherBacklessHover,
456
+ datePickerOpenBtnColor: c.textNeutralHeavy,
457
+ rangeCalendarCellBg: c.shapeFaintNeutralAlpha,
458
+ rangeCalendarCellEndBg: c.shapeBoldAccent,
459
+ rangeCalendarCellEndColor: c.textOnAccentBoldHeavy,
460
+ rangeCalendarCellHoverBg: c.shapeOtherBacklessHover,
461
+ dateSelectMenuBg: c.surfaceHigh,
462
+ dateSelectMenuItemBgDisabled: c.surfaceHigh,
463
+ dateSelectMenuItemFontActive: '',
464
+ dateSelectMenuItemFontSelected: c.textNeutralHeavy,
465
+ dateSelectMenuItemFontDisabled: c.textNeutralPale,
466
+ dateSelectTextColorDisabled: c.textNeutralPale,
467
+ dateSelectTextColorDefault: c.textNeutralHeavy,
468
+ dateSelectLinkColor: c.textAccentHeavy,
469
+ dateSelectPopupBoxShadow: '0px 32px 32px -16px rgba(0, 0, 0, 0.08), 0px 0px 24px 0px rgba(0, 0, 0, 0.12)',
470
+ dateSelectTextColorInvert: '',
471
+ pagingPageLinkActiveBg: c.shapeOtherBacklessPressed,
472
+ pagingPageLinkDisabledActiveBg: c.shapeOtherDisabled,
473
+ pagingPageLinkHoverBg: c.shapeOtherBacklessHover,
474
+ pagingDotsColor: c.textNeutralPale,
475
+ pagingPageLinkHintColor: c.textNeutralSoft,
476
+ hintColor: c.textInvertedNeutralHeavy,
477
+ mobileHintColor: c.textInvertedNeutralHeavy,
478
+ hintBgColor: c.shapeHeavyNeutral,
479
+ toastBg: c.shapeHeavyNeutral,
480
+ toastErrorBg: c.shapeBoldError,
481
+ toastColor: c.textInvertedNeutralHeavy,
482
+ toastLinkColor: c.textInvertedNeutralHeavy,
483
+ toastLinkTextDecorationHover: '',
484
+ toastLinkBgHover: c.shapeInvertedBacklessHover,
485
+ toastLinkBgActive: c.shapeInvertedBacklessPressed,
486
+ toastCloseColor: c.textInvertedNeutralSoft,
487
+ toastCloseHoverColor: c.textInvertedNeutralHeavy,
488
+ toastColorError: c.textConstHeavyWhite,
489
+ toastLinkColorError: c.textConstHeavyWhite,
490
+ toastLinkBgHoverError: c.shapeConstBacklessWhiteHover,
491
+ toastLinkBgActiveError: c.shapeConstBacklessWhiteHover,
492
+ toastLinkColorActiveError: c.textConstHeavyWhite,
493
+ toastCloseColorError: c.textConstSoftWhite,
494
+ toastCloseHoverColorError: c.textConstHeavyWhite,
495
+ dropdownDefaultBg: c.shapeOtherBase,
496
+ dropdownBgDisabled: c.shapeOtherDisabled,
497
+ dropdownBorderColorDisabled: c.lineNeutralFaint,
498
+ dropdownTextColorDisabled: c.textNeutralPale,
499
+ menuBgDefault: c.surfaceHigh,
500
+ menuShadow: '0px 32px 32px -16px rgba(0, 0, 0, 0.08), 0px 0px 24px 0px rgba(0, 0, 0, 0.12)',
501
+ menuItemSelectedBg: c.shapeOtherBacklessPressed,
502
+ menuItemHoverBg: c.shapeOtherBacklessHover,
503
+ menuItemLinkColor: c.textAccentHeavy,
504
+ menuItemCommentColor: c.textNeutralSoft,
505
+ menuItemCommentOpacity: '1',
506
+ menuItemDisabledColor: c.textNeutralPale,
507
+ menuMessageTextColor: c.textNeutralPale,
508
+ menuHeaderColor: c.textNeutralSoft,
509
+ menuFooterColor: c.textNeutralSoft,
510
+ menuSeparatorBorderColor: c.lineNeutralFaint,
511
+ toggleTextColor: c.textNeutralHeavy,
512
+ toggleBaseBg: 'transparent',
513
+ toggleBgHover: c.shapeOtherFieldHover,
514
+ toggleBorderColor: c.lineNeutralPale,
515
+ toggleBorderColorDisabled: c.lineNeutralPale,
516
+ toggleBgFocus: 'linear-gradient(-180deg, #f1f1f1, #dedede)',
517
+ toggleShadowColorError: c.lineErrorBold,
518
+ toggleShadowColorWarning: c.lineWarningBold,
519
+ toggleFocusShadowColor: c.lineAccentBold,
520
+ toggleContainerBg: c.shapeOtherField,
521
+ toggleHandleBg: c.shapeOtherBase,
522
+ toggleHandleBoxShadow: `0 0 0 1px ${c.lineNeutralPale}`,
523
+ toggleContainerBoxShadow: `inset 0 0 0 1px ${c.lineNeutralPale}`,
524
+ toggleContainerBoxShadowHover: `inset 0 0 0 1px ${c.lineNeutralPaleHover}`,
525
+ toggleHandleBgHover: c.shapeOtherBase,
526
+ toggleHandleBoxShadowHover: `0 0 0 1px ${c.lineNeutralPale}`,
527
+ toggleContainerBgHover: c.shapeOtherFieldHover,
528
+ toggleContainerBoxShadowChecked: 'none',
529
+ toggleHandleBoxShadowChecked: 'none',
530
+ toggleHandleBgChecked: c.shapeInvertedNeutralHeavy,
531
+ toggleBgChecked: c.shapeBoldAccent,
532
+ toggleContainerBgChecked: c.shapeBoldAccent,
533
+ toggleContainerBoxShadowCheckedHover: 'none',
534
+ toggleContainerBgCheckedHover: c.shapeBoldAccentHover,
535
+ toggleHandleBoxShadowCheckedHover: 'none',
536
+ toggleHandleBgCheckedHover: c.shapeInvertedNeutralHeavyHover,
537
+ toggleContainerBgDisabled: c.shapeOtherDisabled,
538
+ toggleHandleBgDisabled: 'transparent',
539
+ toggleContainerBoxShadowDisabled: `inset 0 0 0 1px ${c.lineNeutralFaint}`,
540
+ toggleHandleBoxShadowDisabled: `0 0 0 1px ${c.lineNeutralFaint}`,
541
+ toggleDisabledHandleBg: 'transparent',
542
+ toggleBgDisabled: c.shapeOtherDisabled,
543
+ toggleContainerBgDisabledChecked: c.shapeOtherAccentBoldDisabled,
544
+ toggleHandleBgDisabledChecked: c.shapeInvertedNeutralHeavy,
545
+ toggleContainerBoxShadowDisabledChecked: 'none',
546
+ toggleHandleBoxShadowDisabledChecked: 'none',
547
+ toggleBorderColorDisabledChecked: c.lineNeutralPale,
548
+ popupDropShadow: 'drop-shadow(0px 32px 32px rgba(0, 0, 0, 0.08)) drop-shadow(0px 0px 24px rgba(0, 0, 0, 0.12))',
549
+ popupBoxShadow: '0px 32px 32px -16px rgba(0, 0, 0, 0.08), 0px 0px 24px 0px rgba(0, 0, 0, 0.12)',
550
+ popupTextColor: c.textNeutralHeavy,
551
+ popupBackground: c.surfaceHigh,
552
+ inputBg: c.shapeOtherField,
553
+ inputIconColor: c.textNeutralSoft,
554
+ inputDisabledBg: c.shapeOtherDisabled,
555
+ inputBorderColor: c.lineNeutralPale,
556
+ inputBorderColorHover: c.lineNeutralPaleHover,
557
+ inputBorderTopColor: c.lineNeutralPale,
558
+ inputPlaceholderColor: c.textNeutralPale,
559
+ inputPlaceholderColorLight: `color-mix(in srgb, ${c.textNeutralPale}, transparent 40%)`,
560
+ inputBlinkColor: c.shapeFaintNeutralAlpha,
561
+ inputColorScheme: 'light',
562
+ checkboxTextColorDefault: c.textNeutralHeavy,
563
+ checkboxTextColorDisabled: c.textNeutralPale,
564
+ checkboxShadowDisabled: `0 0 0 1px ${c.lineNeutralFaint}`,
565
+ checkboxBorder: 'none',
566
+ checkboxShadow: `0 0 0 1px ${c.lineNeutralPale}`,
567
+ checkboxShadowHover: `0 0 0 1px ${c.lineNeutralPaleHover}`,
568
+ checkboxCheckedColor: c.shapeInvertedNeutralHeavy,
569
+ checkboxBorderColorWarning: c.lineWarningBold,
570
+ checkboxBorderColorError: c.lineErrorBold,
571
+ checkboxCheckedHoverShadow: `0 0 0 1px ${c.shapeBoldAccentHover}`,
572
+ checkboxCheckedShadow: `0 0 0 1px ${c.shapeBoldAccent}`,
573
+ checkboxCheckedActiveShadow: `0 0 0 1px ${c.shapeBoldAccentPressed}`,
574
+ checkboxBorderColorFocus: c.lineAccentBold,
575
+ checkboxBg: c.shapeOtherField,
576
+ checkboxHoverBg: c.shapeOtherFieldHover,
577
+ checkboxActiveBg: c.shapeOtherFieldPressed,
578
+ checkboxCheckedBg: c.shapeBoldAccent,
579
+ checkboxBgDisabled: c.shapeOtherDisabled,
580
+ checkboxCheckedHoverBg: c.shapeBoldAccentHover,
581
+ checkboxCheckedActiveBg: c.shapeBoldAccentPressed,
582
+ checkboxShadowActive: `0 0 0 1px ${c.lineNeutralPalePressed}`,
583
+ textareaBg: c.shapeOtherField,
584
+ textareaColor: c.textNeutralHeavy,
585
+ textareaTextColorDisabled: c.textNeutralPale,
586
+ textareaPlaceholderColorLight: `color-mix(in srgb, ${c.textNeutralPale}, transparent 40%)`,
587
+ textareaPlaceholderColor: c.textNeutralPale,
588
+ textareaPlaceholderColorDisabled: c.textNeutralPale,
589
+ textareaShadow: 'none',
590
+ textareaBorderColor: c.lineNeutralPale,
591
+ textareaBorderTopColor: c.lineNeutralPale,
592
+ textareaBorderColorFocus: c.lineAccentBold,
593
+ textareaBorderColorHover: c.lineNeutralPaleHover,
594
+ textareaBorderColorWarning: c.lineWarningBold,
595
+ textareaBorderColorError: c.lineErrorBold,
596
+ textareaDisabledBg: c.shapeOtherDisabled,
597
+ textareaDisabledBorderColor: c.lineNeutralFaint,
598
+ textareaCounterColor: c.textNeutralSoft,
599
+ textareaCounterBg: 'transparent',
600
+ textareaCounterErrorColor: c.textErrorHeavy,
601
+ textareaCounterHelpIconColor: c.textNeutralHeavy,
602
+ radioBgColor: c.shapeOtherField,
603
+ radioHoverBg: c.shapeOtherFieldHover,
604
+ radioActiveBg: c.shapeOtherFieldPressed,
605
+ radioBorderColor: c.lineNeutralPale,
606
+ radioBoxShadow: 'none',
607
+ radioBorder: `1px solid ${c.lineNeutralPale}`,
608
+ radioBorderColorFocus: c.lineAccentBold,
609
+ radioBorderColorWarning: c.lineWarningBold,
610
+ radioBorderColorError: c.lineErrorBold,
611
+ radioHoverShadow: 'none',
612
+ radioActiveShadow: 'none',
613
+ radioCheckedBgColor: c.shapeBoldAccent,
614
+ radioCheckedBorderColor: 'transparent',
615
+ radioCheckedBulletColor: c.shapeInvertedNeutralHeavy,
616
+ radioCheckedHoverBgColor: c.shapeBoldAccentHover,
617
+ radioDisabledBg: c.shapeOtherDisabled,
618
+ radioDisabledShadow: `0 0 0 1px ${c.lineNeutralFaint}`,
619
+ radioCheckedDisabledBulletBg: c.textNeutralPale,
620
+ tabTextColorDefault: c.textNeutralHeavy,
621
+ tabColorFocus: c.lineAccentBold,
622
+ tabColorError: c.shapeBoldError,
623
+ tabColorWarning: c.shapeBoldWarning,
624
+ tabColorSuccess: c.shapeBoldSuccess,
625
+ tabColorPrimary: c.shapeBoldAccent,
626
+ tabColorHover: c.lineNeutralPale,
627
+ tabColorHoverError: `color-mix(in srgb, ${c.shapeBoldError}, transparent 50%)`,
628
+ tabColorHoverWarning: `color-mix(in srgb, ${c.shapeBoldWarning}, transparent 50%)`,
629
+ tabColorHoverSuccess: `color-mix(in srgb, ${c.shapeBoldSuccess}, transparent 50%)`,
630
+ tabColorHoverPrimary: c.lineAccentPale,
631
+ spinnerColor: c.customizableBoldRed,
632
+ spinnerDimmedColor: c.customizableBoldGray,
633
+ spinnerCaptionColor: c.textNeutralSoft,
634
+ switcherTextColor: c.textNeutralHeavy,
635
+ switcherBtnDisabledBorderColor: c.lineNeutralPale,
636
+ switcherButtonDisabledBorderColor: c.lineNeutralPale,
637
+ scrollContainerScrollBarColor: c.shapeSoftNeutralAlpha,
638
+ scrollContainerScrollBarInvertColor: c.shapeInvertedNeutralSoftAlpha,
639
+ passwordInputVisibilityIconColor: c.textNeutralHeavy,
640
+ passwordInputVisibilityIconOpacity: '0.64',
641
+ passwordInputVisibilityIconHoverColor: c.textNeutralHeavy,
642
+ passwordInputVisibilityIconHoverOpacity: '1',
643
+ globalLoaderColor: c.shapeBoldBrandOriginal,
644
+ fileUploaderBg: '',
645
+ fileUploaderUploadButtonBg: 'transparent',
646
+ fileUploaderTextColorDefault: c.textNeutralHeavy,
647
+ fileUploaderBorderColor: c.lineNeutralPale,
648
+ fileUploaderDisabledBorder: `1px ${'dashed'} ${c.lineNeutralFaint}`,
649
+ fileUploaderBorderColorFocus: c.lineAccentBold,
650
+ fileUploaderLinkColor: c.textNeutralHeavy,
651
+ fileUploaderAfterLinkColor: c.textNeutralSoft,
652
+ fileUploaderIconColor: c.textNeutralPale,
653
+ fileUploaderIconHoverColor: c.textNeutralHeavy,
654
+ fileUploaderBorderColorError: c.lineErrorBold,
655
+ fileUploaderBorderColorWarning: c.lineWarningBold,
656
+ fileUploaderDisabledBg: c.shapeOtherDisabled,
657
+ fileUploaderDisabledBorderColor: c.lineNeutralFaint,
658
+ fileUploaderDisabledTextColor: c.textNeutralPale,
659
+ fileUploaderDisabledLinkColor: c.textNeutralPale,
660
+ fileUploaderDisabledIconColor: c.textNeutralPale,
661
+ fileUploaderHoveredBg: c.shapeOtherBacklessHover,
662
+ fileUploaderHoveredBorderColor: 'transparent',
663
+ fileUploaderDragOverBorderColor: c.lineAccentBold,
664
+ clearCrossIconColor: c.textNeutralSoft,
665
+ clearCrossIconHoverColor: c.textNeutralHeavy,
666
+ closeBtnIconColor: c.textNeutralPale,
667
+ closeBtnIconDisabledColor: c.textNeutralPale,
668
+ closeBtnIconHoverColor: c.textNeutralHeavy,
669
+ validationsTextColorError: c.textErrorHeavy,
670
+ validationsTextColorWarning: c.textWarningHeavy,
671
+ sideMenuBgColor: c.surfaceLow,
672
+ sideMenuProductColor: c.shapeBoldBrandOriginal,
673
+ sideMenuNotificationsMarkerBg: c.shapeHeavyNeutral,
674
+ addonsUserAvatarBorderColor: c.shapeHeavyNeutral,
675
+ addonsUserAvatarColor: c.shapeHeavyNeutral,
676
+ sideMenuItemActiveBg: c.shapeOtherBase,
677
+ sideMenuItemHoverBg: c.shapeOtherBacklessHover,
678
+ });
679
+
680
+ return (
681
+ <div data-colors-controls>
682
+ <div className={styles.filterRow}>
683
+ <Gapped>
684
+ <label htmlFor="example-brand">Brand</label>
685
+ <Select
686
+ id="example-brand"
687
+ items={colorOptions}
688
+ value={brand}
689
+ width={140}
690
+ onValueChange={setBrand}
691
+ renderValue={renderBrandItem}
692
+ renderItem={renderBrandItem}
693
+ />
694
+
695
+ {brand === 'custom' && (
696
+ <Input
697
+ maxLength={7}
698
+ width={80}
699
+ value={customBrandColor}
700
+ onValueChange={handleCustomBrandColorValueChange}
701
+ placeholder="#RRGGBB"
702
+ />
703
+ )}
704
+ </Gapped>
705
+
706
+ <Gapped>
707
+ <label htmlFor="example-accent">Accent</label>
708
+ <Select
709
+ id="example-accent"
710
+ width={140}
711
+ items={baseAccentOptions}
712
+ value={accent}
713
+ onValueChange={setAccent}
714
+ renderValue={renderAccentItemValue}
715
+ renderItem={renderAccentMenuItem}
716
+ />
717
+
718
+ {accent === 'custom' && (
719
+ <Input
720
+ maxLength={7}
721
+ width={80}
722
+ value={customAccentColor}
723
+ onValueChange={handleCustomAccentColorValueChange}
724
+ placeholder="#RRGGBB"
725
+ />
726
+ )}
727
+ </Gapped>
728
+ <Gapped style={{ marginLeft: 'auto' }}>
729
+ <label htmlFor="example-theme">Theme</label>
730
+
731
+ <Select
732
+ id="example-theme"
733
+ width={140}
734
+ items={['light', 'dark']}
735
+ value={colorTheme}
736
+ onValueChange={setColorTheme}
737
+ renderValue={renderThemeItem}
738
+ renderItem={renderThemeItem}
739
+ />
740
+ </Gapped>
741
+ </div>
742
+ <ThemeContext.Provider value={theme}>
743
+ <div style={{ color: c.textNeutralHeavy, background: c.surfaceHigh }}>
744
+ <div className="wrapper">
745
+ <style>{`
746
+ .wrapper {
747
+ display: flex;
748
+ height: 100%;
749
+ }
750
+
751
+ .wrapper > * {
752
+ height: auto;
753
+ }
754
+
755
+ .container {
756
+ overflow-y: auto;
757
+ display: flex;
758
+ flex-direction: column;
759
+ gap: 16px;
760
+
761
+ box-sizing: border-box;
762
+ padding-top: 24px;
763
+ height: 100%;
764
+ width: 100%;
765
+ color: ${c.textNeutralHeavy};
766
+ background: ${c.surfaceBase};
767
+ }
768
+
769
+ .header {
770
+ display: flex;
771
+ align-items: center;
772
+ }
773
+
774
+ .header__title {
775
+ font-weight: 700;
776
+ font-size: 32px;
777
+ line-height: 48px;
778
+ }
779
+
780
+ .panel {
781
+ display: flex;
782
+ align-items: center;
783
+ gap: 8px;
784
+
785
+ width: fit-content;
786
+
787
+ margin-left: 40px;
788
+ padding: 16px;
789
+ border-radius: 8px;
790
+
791
+ color: ${c.textOnBrandOriginalHeavy};
792
+ background-color: ${c.shapeBoldBrandOriginal};
793
+ }
794
+
795
+ .content {
796
+ display: flex;
797
+ flex-direction: column;
798
+ gap: 24px;
799
+
800
+ padding: 0 48px 48px;
801
+ }
802
+
803
+ .item {
804
+ display: flex;
805
+ gap: 8px;
806
+ }
807
+
808
+ .item__title {
809
+ box-sizing: border-box;
810
+ width: 88px;
811
+ height: 40px;
812
+ padding: 9px 0;
813
+
814
+ font-weight: 700;
815
+ font-size: 16px;
816
+ line-height: 22px;
817
+ }
818
+
819
+ .item__body {
820
+ display: flex;
821
+ flex-direction: column;
822
+ gap: 8px;
823
+ }
824
+
825
+ .item__toggle {
826
+ box-sizing: border-box;
827
+ height: 40px;
828
+ padding: 9px 0;
829
+ }
830
+
831
+ .item__text {
832
+ width: 376px;
833
+ margin: 0;
834
+
835
+ font-weight: 400;
836
+ font-size: 16px;
837
+ line-height: 22px;
838
+ }
839
+
840
+ .footer {
841
+ position: sticky;
842
+ bottom: 0;
843
+
844
+ display: flex;
845
+ flex-direction: column;
846
+
847
+ padding: 0 72px 0 48px;
848
+ background-color: ${c.surfaceBase};
849
+ border-top: 1px solid ${c.lineNeutralPale};
850
+ }
851
+
852
+ .footer__controls {
853
+ display: flex;
854
+ gap: 8px;
855
+
856
+ padding: 16px 0;
857
+ }
858
+
859
+ .footer__warning-panel {
860
+ display: flex;
861
+ gap: 8px;
862
+
863
+ margin-left: 16px;
864
+ padding: 13px 16px;
865
+ border-radius: 8px;
866
+
867
+ background-color: ${c.shapeFaintWarning};
868
+ }
869
+ `}</style>
870
+ <SideMenu>
871
+ <SideMenu.Header
872
+ konturLogo={<Kontur color={c.textNeutralHeavy} />}
873
+ productLogo={<Product color={c.shapeBoldBrandOriginal} />}
874
+ />
875
+ <SideMenu.Body>
876
+ <SideMenu.Item icon={<IconDocTextRegular24 />} caption="Документы" />
877
+ <SideMenu.Item icon={<IconPeople2Regular24 />} caption="Команда" />
878
+ <SideMenu.Item icon={<IconCommentRectTextRegular24 />} caption="Сообщения" marker={1} />
879
+ <SideMenu.Item icon={<IconMarketShoppingBasketRegular24 />} caption="Товары" />
880
+ </SideMenu.Body>
881
+ <SideMenu.Footer>
882
+ <SideMenu.Organisations icon={<IconStackHDownRegular24 />} />
883
+ <SideMenu.Item icon={<IconSettingsGearRegular24 />} caption="Настройки" />
884
+ <SideMenu.Avatar userName="Кирилл Лаптев" />
885
+ </SideMenu.Footer>
886
+ </SideMenu>
887
+
888
+ <div className="container">
889
+ <header className="header">
890
+ <Button use="text" icon={<IconArrowALeftRegular24 color={c.textNeutralHeavy} />} size="large" />
891
+ <span className="header__title">Иванов Иван Иванович</span>
892
+ </header>
893
+ <div className="panel">
894
+ <IconNaturePlantFlowerSolid20 />
895
+ Теперь появилась возможность разукрасить интерфейс!
896
+ </div>
897
+ <main className="content">
898
+ <Tabs value={activeTab} onValueChange={setActiveTab}>
899
+ <Tabs.Tab id="tab-0">Настройки доступа</Tabs.Tab>
900
+ <Tabs.Tab id="tab-1">Безопасность</Tabs.Tab>
901
+ <Tabs.Tab id="tab-2">Подписки</Tabs.Tab>
902
+ <Tabs.Tab id="tab-3">Документы</Tabs.Tab>
903
+ </Tabs>
904
+ <div className="item">
905
+ <span className="item__title">Почта</span>
906
+ <Input size="medium" width={288} />
907
+ </div>
908
+ <div className="item">
909
+ <span className="item__title">Доступ</span>
910
+ <div className="item__body">
911
+ <div className="item__toggle">
912
+ <Toggle size="medium">Разрешить</Toggle>
913
+ </div>
914
+ <p className="item__text">
915
+ Мы обновили анкету участника, чтобы встречи становились ещё комфортнее.{' '}
916
+ <Link>Подробнее в статье</Link>
917
+ </p>
918
+ </div>
919
+ </div>
920
+ <div className="item">
921
+ <span className="item__title">Уровень</span>
922
+ <RadioGroup value={activeRadio} onValueChange={setActiveRadio}>
923
+ <Gapped vertical gap={0}>
924
+ <Radio size="medium" value={1}>
925
+ Администратор
926
+ </Radio>
927
+ <Radio size="medium" value={2}>
928
+ Пользователь
929
+ </Radio>
930
+ <Radio size="medium" value={3}>
931
+ Только чтение
932
+ </Radio>
933
+ </Gapped>
934
+ </RadioGroup>
935
+ </div>
936
+ <div className="item">
937
+ <span className="item__title">Другое</span>
938
+ <Checkbox size="medium" checked={checked} onValueChange={setChecked}>
939
+ Входить по умолчанию
940
+ </Checkbox>
941
+ <Tooltip
942
+ render={() => (
943
+ <div style={{ width: 200 }}>При входе в сервис сотруднику не придётся проходить авторизацию.</div>
944
+ )}
945
+ >
946
+ <IconQuestionCircleLight20 color={c.textNeutralHeavy} />
947
+ </Tooltip>
948
+ </div>
949
+ </main>
950
+ <footer className="footer">
951
+ <div className="footer__controls">
952
+ <Button use="primary" size="large">
953
+ Сохранить
954
+ </Button>
955
+ <Button use="backless" size="large">
956
+ Отменить
957
+ </Button>
958
+ <div className="footer__warning-panel">
959
+ <IconWarningTriangleSolid20 color={c.shapeBoldWarning} />
960
+ Укажите все данные для сохранения
961
+ </div>
962
+ </div>
963
+ </footer>
964
+ </div>
965
+ </div>
966
+ </div>
967
+ </ThemeContext.Provider>
968
+ </div>
969
+ );
970
+ };
971
+
972
+ ColorsExampleStory.storyName = 'Пример интерфейса';
973
+
974
+ /**
975
+ * Токены цветов состоят из 6 основных групп:
976
+ * - **text** — переменные для текстов и иконок, которые являются частью текста
977
+ * - **shape** — подложки, плашки, фоны и заливки
978
+ * - **line** — линии, разделители, обводки, подчеркивания
979
+ * - **surface** — крупные контентные области; поверхности, которые образуют лейаут
980
+ * - **illustration** — иллюстрации, промо-материалы и всё связанное с коммуникационным дизайном
981
+ * - **customizable** — произвольные цвета из палитры для схем/графиков и других сценариев
982
+ *
983
+ * ---
984
+ *
985
+ * Для покраски элемента необходимо подбирать токены по заложенному в них смыслу. Подробнее в [устройстве семантики →](https://www.figma.com/design/XVgPCAAFhEbIiQDDznkdat/%E2%9A%A1%EF%B8%8F-Kontur-Colors-2.0?node-id=6256-369539&t=t9J25uypIjGTMtLQ-4)
986
+ */
987
+ export const ColorsPaletteStory = () => {
988
+ const GROUPING_ROOTS = [
989
+ 'text',
990
+ 'texInverted',
991
+ 'textConst',
992
+ 'textOnAccent',
993
+ 'textOnBrand',
994
+ 'shape',
995
+ 'shapeInverted',
996
+ 'shapeConst',
997
+ 'line',
998
+ 'lineInverted',
999
+ 'lineConst',
1000
+ 'surface',
1001
+ 'illustration',
1002
+ 'customizable',
1003
+ ];
1004
+
1005
+ const generateTokenList = (tokens: any): TokenPair[] => {
1006
+ const { light, dark } = tokens;
1007
+ const tokenList: TokenPair[] = [];
1008
+
1009
+ for (const key in light) {
1010
+ if (dark[key] && typeof light[key] === 'string' && typeof dark[key] === 'string') {
1011
+ tokenList.push({
1012
+ key: key,
1013
+ value: {
1014
+ light: light[key],
1015
+ dark: dark[key],
1016
+ },
1017
+ });
1018
+ }
1019
+ }
1020
+
1021
+ return tokenList;
1022
+ };
1023
+
1024
+ const extractRootFromCamelCase = (key: string, roots: string[]): string | null => {
1025
+ const sortedRoots = [...roots].sort((a, b) => b.length - a.length);
1026
+
1027
+ for (const root of sortedRoots) {
1028
+ if (key === root) {
1029
+ return root;
1030
+ }
1031
+ const rootEndIndex = root.length;
1032
+ if (key.startsWith(root) && key.length > rootEndIndex) {
1033
+ const nextChar = key[rootEndIndex];
1034
+
1035
+ if (nextChar === nextChar.toUpperCase() && nextChar !== nextChar.toLowerCase()) {
1036
+ return root;
1037
+ }
1038
+ }
1039
+ }
1040
+ return null;
1041
+ };
1042
+
1043
+ const groupTokensByRoot = (tokens: TokenPair[]): Record<string, TokenPair[]> => {
1044
+ const groupedByRoot: Record<string, TokenPair[]> = {};
1045
+ const allRoots = GROUPING_ROOTS;
1046
+
1047
+ const sortedTokens = tokens.sort((a, b) => a.key.localeCompare(b.key));
1048
+
1049
+ for (const token of sortedTokens) {
1050
+ const tokenKey = token.key;
1051
+ let rootKey = 'other';
1052
+
1053
+ const foundRoot = extractRootFromCamelCase(tokenKey, allRoots);
1054
+
1055
+ if (foundRoot) {
1056
+ rootKey = foundRoot;
1057
+ } else {
1058
+ const match = tokenKey.match(/^[a-z]+/);
1059
+ if (match) {
1060
+ rootKey = match[0];
1061
+ } else if (tokenKey.includes('.')) {
1062
+ rootKey = tokenKey.split('.')[0];
1063
+ }
1064
+ }
1065
+
1066
+ if (!rootKey || rootKey.length === 0) {
1067
+ rootKey = 'other';
1068
+ }
1069
+
1070
+ if (!groupedByRoot[rootKey]) {
1071
+ groupedByRoot[rootKey] = [];
1072
+ }
1073
+ groupedByRoot[rootKey].push(token);
1074
+ }
1075
+
1076
+ const finalGrouped: Record<string, TokenPair[]> = {};
1077
+
1078
+ for (const key of GROUPING_ROOTS) {
1079
+ if (groupedByRoot[key]) {
1080
+ finalGrouped[key] = groupedByRoot[key];
1081
+ delete groupedByRoot[key];
1082
+ }
1083
+ }
1084
+
1085
+ for (const key in groupedByRoot) {
1086
+ if (groupedByRoot[key].length > 0) {
1087
+ finalGrouped[key] = groupedByRoot[key];
1088
+ }
1089
+ }
1090
+
1091
+ return finalGrouped;
1092
+ };
1093
+
1094
+ const styles: Record<string, string> = {
1095
+ colors: css`
1096
+ display: flex;
1097
+ flex-direction: column;
1098
+ height: 900px;
1099
+ min-height: 400px;
1100
+ max-height: calc(100vh - 120px);
1101
+ overflow-y: scroll;
1102
+ `,
1103
+ colorGroup: css`
1104
+ margin-bottom: 64px;
1105
+ `,
1106
+ filterRow: css`
1107
+ position: sticky;
1108
+ z-index: 10;
1109
+ display: flex;
1110
+ align-items: center;
1111
+ gap: 24px;
1112
+ padding: 16px;
1113
+ width: calc(100% - 32px);
1114
+ top: 0;
1115
+ background: white;
1116
+ font-size: 14px;
1117
+ `,
1118
+ headerRow: css`
1119
+ display: flex;
1120
+ align-items: center;
1121
+ gap: 8px;
1122
+ width: calc(100% - 32px);
1123
+ padding: 4px 16px 4px;
1124
+ font-weight: 600;
1125
+ color: #222;
1126
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
1127
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
1128
+ position: sticky;
1129
+ top: 57px;
1130
+ z-index: 10;
1131
+ background: white;
1132
+ font-size: 14px;
1133
+ font-weight: 700;
1134
+ line-height: 1.2;
1135
+ `,
1136
+ dropdownRow: css`
1137
+ display: flex;
1138
+ align-items: center;
1139
+ gap: 8px;
1140
+ width: calc(100% - 16px);
1141
+ padding: 3px 8px;
1142
+ margin: 0 8px;
1143
+ border-radius: 8px;
1144
+ cursor: pointer;
1145
+ background: none;
1146
+ border: none;
1147
+ text-align: left;
1148
+ transition: background 0.1s ease;
1149
+ font-family: inherit;
1150
+ &:hover {
1151
+ background: rgba(0, 0, 0, 0.06);
1152
+ }
1153
+ &:active {
1154
+ background: rgba(0, 0, 0, 0.1);
1155
+ }
1156
+ `,
1157
+ colorName: css`
1158
+ flex: 1;
1159
+ `,
1160
+ colorTileWrapper: css`
1161
+ position: relative;
1162
+ z-index: 1;
1163
+ display: flex;
1164
+ flex-direction: column;
1165
+ align-items: center;
1166
+ gap: 4px;
1167
+ width: 140px;
1168
+ `,
1169
+ colorTile: css`
1170
+ height: 32px;
1171
+ width: 32px;
1172
+ border: 1px solid rgba(127, 127, 127, 0.3);
1173
+ border-radius: 8px;
1174
+ flex-shrink: 0;
1175
+ `,
1176
+ colorHex: css`
1177
+ font-size: 12px;
1178
+ font-weight: 600;
1179
+ white-space: nowrap;
1180
+ color: #8b8b8b;
1181
+ `,
1182
+ groupHeader: css`
1183
+ position: sticky;
1184
+ top: 62px;
1185
+ width: 50%;
1186
+ z-index: 10;
1187
+ background: white;
1188
+ font-size: 14px;
1189
+ line-height: 1;
1190
+ padding: 2px 16px;
1191
+ font-weight: bold;
1192
+ `,
1193
+ controls: css`
1194
+ position: sticky;
1195
+ z-index: 10;
1196
+ bottom: 0;
1197
+ padding: 8px;
1198
+ background: white;
1199
+ box-shadow: 0 -1px rgba(0, 0, 0, 0.15);
1200
+ margin-top: auto;
1201
+ `,
1202
+ };
1203
+
1204
+ const defaultColorOptions = Object.keys(brandSwatch);
1205
+ const defaultBrandColor = 'red';
1206
+ const defaultAccentColor = 'gray';
1207
+
1208
+ const colorOptions = [...defaultColorOptions, 'custom'];
1209
+ const baseAccentOptions = ['gray', 'brand', 'custom'];
1210
+
1211
+ const colorFormatOptions = ['Web (hex/rgba)', 'Web (oklch)', 'iOS/Android (hex-aarrggbb)'];
1212
+
1213
+ const [brand, setBrand] = React.useState(defaultBrandColor);
1214
+ const [accent, setAccent] = React.useState(defaultAccentColor);
1215
+ const [filter, setFilter] = React.useState('');
1216
+ const [colorFormat, setColorFormat] = React.useState(colorFormatOptions[0]);
1217
+
1218
+ const [customBrandColor, setCustomBrandColor] = React.useState('#FFDD2D');
1219
+ const [customAccentColor, setCustomAccentColor] = React.useState('#FFDD2D');
1220
+
1221
+ const handleCustomBrandColorValueChange = React.useCallback((newColor: string) => {
1222
+ setCustomBrandColor(newColor);
1223
+ }, []);
1224
+
1225
+ const handleCustomAccentColorValueChange = React.useCallback((newColor: string) => {
1226
+ setCustomAccentColor(newColor);
1227
+ }, []);
1228
+
1229
+ const getColorsFormat = (tokenKey: string) => {
1230
+ const kebabCaseKey = tokenKey.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
1231
+
1232
+ return {
1233
+ CSS: `var(--k-color-${kebabCaseKey})`,
1234
+ SCSS: `$color-${kebabCaseKey}`,
1235
+ Less: `@color-${kebabCaseKey}`,
1236
+ 'JS/TS': `colors.${tokenKey}`,
1237
+ 'Android Compose': `KonturTheme.colors.${tokenKey}`,
1238
+ };
1239
+ };
1240
+
1241
+ const copyColor = (colorValue: string) => {
1242
+ window.navigator.clipboard.writeText(colorValue);
1243
+ Toast.push('Цвет скопирован', null, 1000);
1244
+ };
1245
+
1246
+ const getOutputFormatParam = (format: string): ColorFormat => {
1247
+ switch (format) {
1248
+ case 'iOS/Android (hex-aarrggbb)':
1249
+ return 'hex-aarrggbb';
1250
+ case 'Web (oklch)':
1251
+ return 'oklch';
1252
+ case 'Web (hex/rgba)':
1253
+ default:
1254
+ return 'hex/rgba';
1255
+ }
1256
+ };
1257
+
1258
+ const outputFormatParam = getOutputFormatParam(colorFormat);
1259
+
1260
+ const safeBrandColor = React.useMemo(() => {
1261
+ if (brand !== 'custom') {
1262
+ return brand;
1263
+ }
1264
+ return customBrandColor.trim() !== '' ? customBrandColor : defaultBrandColor;
1265
+ }, [brand, customBrandColor]);
1266
+
1267
+ const safeAccentColor = React.useMemo(() => {
1268
+ if (accent !== 'custom') {
1269
+ return accent;
1270
+ }
1271
+ return customAccentColor.trim() !== '' ? customAccentColor : defaultAccentColor;
1272
+ }, [accent, customAccentColor]);
1273
+
1274
+ const isAccentDisabled = (accentValue: string, currentBrand: string) => {
1275
+ return accentValue === 'brand' && (currentBrand === 'red' || currentBrand === 'orange');
1276
+ };
1277
+
1278
+ const getBrandColorForSwatch = React.useCallback(() => {
1279
+ if (brand === 'custom') {
1280
+ return customBrandColor.trim() !== ''
1281
+ ? customBrandColor
1282
+ : brandSwatch[defaultBrandColor as keyof typeof brandSwatch];
1283
+ }
1284
+ return brandSwatch[brand as keyof typeof brandSwatch];
1285
+ }, [brand, customBrandColor]);
1286
+
1287
+ const renderAccentItemValuePalette = (value: string) => {
1288
+ if (value === 'custom') {
1289
+ const colorToDisplay = customAccentColor.trim() !== '' ? customAccentColor : '#999';
1290
+ return renderColorItem(colorToDisplay, '#custom-hex');
1291
+ }
1292
+ const color = value === 'gray' ? '#3d3d3d' : getBrandColorForSwatch();
1293
+ return renderColorItem(color, value);
1294
+ };
1295
+
1296
+ const renderAccentMenuItemPalette = (value: string) => {
1297
+ const content = renderAccentItemValuePalette(value);
1298
+ return (
1299
+ <div key={value} style={{ opacity: isAccentDisabled(value, brand) ? 0.4 : 1 }}>
1300
+ {content}
1301
+ </div>
1302
+ );
1303
+ };
1304
+
1305
+ const handleBrandChange = React.useCallback((newBrand: string) => {
1306
+ setBrand(newBrand);
1307
+ }, []);
1308
+
1309
+ React.useEffect(() => {
1310
+ if (isAccentDisabled(accent, brand)) {
1311
+ setAccent('gray');
1312
+ Toast.push('Акцент brand недоступен для red и orange', null, 3000);
1313
+ }
1314
+ }, [brand, accent]);
1315
+
1316
+ const effectiveAccentColor = React.useMemo(() => {
1317
+ if (isAccentDisabled(safeAccentColor, safeBrandColor)) {
1318
+ return 'gray';
1319
+ }
1320
+ return safeAccentColor;
1321
+ }, [safeBrandColor, safeAccentColor]);
1322
+
1323
+ let tokenList;
1324
+
1325
+ try {
1326
+ tokenList = generateTokenList({
1327
+ light: getColors({
1328
+ brand: safeBrandColor,
1329
+ accent: effectiveAccentColor,
1330
+ theme: 'light',
1331
+ format: outputFormatParam,
1332
+ }),
1333
+ dark: getColors({
1334
+ brand: safeBrandColor,
1335
+ accent: effectiveAccentColor,
1336
+ theme: 'dark',
1337
+ format: outputFormatParam,
1338
+ }),
1339
+ });
1340
+ } catch (error) {
1341
+ tokenList = generateTokenList({
1342
+ light: getColors({
1343
+ brand: '#FFDD2D',
1344
+ accent: '#FFDD2D',
1345
+ theme: 'light',
1346
+ format: outputFormatParam,
1347
+ }),
1348
+ dark: getColors({
1349
+ brand: '#FFDD2D',
1350
+ accent: '#FFDD2D',
1351
+ theme: 'dark',
1352
+ format: outputFormatParam,
1353
+ }),
1354
+ });
1355
+ }
1356
+
1357
+ const filterTokens = (tokens: TokenPair[]) => {
1358
+ if (!filter) return tokens;
1359
+
1360
+ const filterLower = filter.toLowerCase();
1361
+
1362
+ return tokens.filter((token) => {
1363
+ if (token.key.toLowerCase().includes(filterLower)) {
1364
+ return true;
1365
+ }
1366
+
1367
+ if (
1368
+ token.value.light.toLowerCase().includes(filterLower) ||
1369
+ token.value.dark.toLowerCase().includes(filterLower)
1370
+ ) {
1371
+ return true;
1372
+ }
1373
+
1374
+ const figmaNameToToken = (figmanName: string): string => {
1375
+ const prefixMatch = figmanName.match(/^(Hover|Pressed)\//);
1376
+
1377
+ const [processedString, extractedPrefix] = prefixMatch
1378
+ ? [
1379
+ figmanName.substring(prefixMatch[0].length),
1380
+ prefixMatch[1].charAt(0).toUpperCase() + prefixMatch[1].slice(1),
1381
+ ]
1382
+ : [figmanName, ''];
1383
+
1384
+ const parts = processedString.split(/[/ ]+/).filter(Boolean);
1385
+
1386
+ const baseCamelCaseString = parts.reduce((acc, part, index) => {
1387
+ return (
1388
+ acc +
1389
+ (index === 0 ? part.charAt(0).toLowerCase() + part.slice(1) : part.charAt(0).toUpperCase() + part.slice(1))
1390
+ );
1391
+ }, '');
1392
+
1393
+ return baseCamelCaseString + extractedPrefix;
1394
+ };
1395
+
1396
+ if (token.key.includes(figmaNameToToken(filter))) {
1397
+ return true;
1398
+ }
1399
+
1400
+ return false;
1401
+ });
1402
+ };
1403
+
1404
+ const groupedByRoot = groupTokensByRoot(filterTokens(tokenList));
1405
+
1406
+ const renderColorItem = (color: string, text: string) => {
1407
+ return (
1408
+ <div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
1409
+ <div
1410
+ style={{
1411
+ flexShrink: 0,
1412
+ background: color,
1413
+ width: 12,
1414
+ height: 12,
1415
+ borderRadius: 4,
1416
+ }}
1417
+ />
1418
+ {text}
1419
+ </div>
1420
+ );
1421
+ };
1422
+
1423
+ const renderBrandItem = (value: string) => {
1424
+ if (value === 'custom') {
1425
+ const colorToDisplay = customBrandColor.trim() !== '' ? customBrandColor : '#999';
1426
+ return renderColorItem(colorToDisplay, '#custom-hex');
1427
+ }
1428
+ return renderColorItem(brandSwatch[value as keyof typeof brandSwatch], value);
1429
+ };
1430
+
1431
+ return (
1432
+ <div className={styles.colors} data-colors-controls>
1433
+ <div className={styles.filterRow}>
1434
+ <Gapped>
1435
+ <label htmlFor="palette-brand">Brand</label>
1436
+ <Select
1437
+ id="palette-brand"
1438
+ items={colorOptions}
1439
+ value={brand}
1440
+ width={140}
1441
+ onValueChange={handleBrandChange}
1442
+ renderValue={renderBrandItem}
1443
+ renderItem={renderBrandItem}
1444
+ />
1445
+ {brand === 'custom' && (
1446
+ <Input
1447
+ maxLength={7}
1448
+ width={80}
1449
+ value={customBrandColor}
1450
+ onValueChange={handleCustomBrandColorValueChange}
1451
+ placeholder="#RRGGBB"
1452
+ />
1453
+ )}
1454
+ </Gapped>
1455
+
1456
+ <Gapped>
1457
+ <label htmlFor="palette-accent">Accent</label>
1458
+ <Select
1459
+ id="palette-accent"
1460
+ width={140}
1461
+ items={baseAccentOptions}
1462
+ value={accent}
1463
+ onValueChange={setAccent}
1464
+ renderValue={renderAccentItemValuePalette}
1465
+ renderItem={renderAccentMenuItemPalette}
1466
+ />
1467
+ {accent === 'custom' && (
1468
+ <Input
1469
+ maxLength={7}
1470
+ width={80}
1471
+ value={customAccentColor}
1472
+ onValueChange={handleCustomAccentColorValueChange}
1473
+ placeholder="#RRGGBB"
1474
+ />
1475
+ )}
1476
+ </Gapped>
1477
+
1478
+ <Gapped style={{ paddingLeft: 16, marginLeft: 'auto' }}>
1479
+ <Gapped>
1480
+ <label htmlFor="palette-format">Format</label>
1481
+ <Select
1482
+ width={238}
1483
+ id="palette-format"
1484
+ items={colorFormatOptions}
1485
+ value={colorFormat}
1486
+ onValueChange={setColorFormat}
1487
+ />
1488
+ </Gapped>
1489
+ </Gapped>
1490
+ </div>
1491
+ <div className={styles.headerRow}>
1492
+ <span className={styles.colorName}>Token</span>
1493
+ <div className={styles.colorTileWrapper} style={{ borderLeft: '1px solid rgba(0, 0, 0, 0.08)' }}>
1494
+ <span>Light</span>
1495
+ </div>
1496
+ <div className={styles.colorTileWrapper} style={{ borderLeft: '1px solid rgba(0, 0, 0, 0.08)' }}>
1497
+ <span>Dark</span>
1498
+ </div>
1499
+ </div>
1500
+ {Object.entries(groupedByRoot).map(([rootKey, tokens], i) => (
1501
+ <React.Fragment key={rootKey}>
1502
+ <div className={styles.groupHeader} style={{ margin: i !== 0 ? '24px 0 16px' : '12px 0 0' }}>
1503
+ {rootKey}
1504
+ </div>
1505
+ {tokens.map(({ key, value }) => {
1506
+ const convertHexAlphaToWebFormat = (color: string) =>
1507
+ color.length === 9 ? '#' + color.slice(3, 9) + color.slice(1, 3) : color;
1508
+ const displayLightValue = value?.light;
1509
+ const displayDarkValue = value?.dark;
1510
+
1511
+ return (
1512
+ <DropdownMenu
1513
+ key={key}
1514
+ width="100%"
1515
+ caption={
1516
+ <button className={styles.dropdownRow}>
1517
+ <span className={styles.colorName}>{key}</span>
1518
+ <div className={styles.colorTileWrapper}>
1519
+ <div
1520
+ className={styles.colorTile}
1521
+ style={{
1522
+ backgroundColor:
1523
+ colorFormat === 'iOS/Android (hex-aarrggbb)'
1524
+ ? convertHexAlphaToWebFormat(displayLightValue)
1525
+ : value?.light,
1526
+ }}
1527
+ />
1528
+ <span className={styles.colorHex}>{displayLightValue}</span>
1529
+ </div>
1530
+ <div
1531
+ className={styles.colorTileWrapper}
1532
+ style={{
1533
+ background: '#3d3d3d',
1534
+ borderRadius: 4,
1535
+ boxShadow: '0 0 0 2px #3d3d3d',
1536
+ }}
1537
+ >
1538
+ <div
1539
+ className={styles.colorTile}
1540
+ style={{
1541
+ backgroundColor:
1542
+ colorFormat === 'iOS/Android (hex-aarrggbb)'
1543
+ ? convertHexAlphaToWebFormat(displayDarkValue)
1544
+ : value?.dark,
1545
+ }}
1546
+ />
1547
+ <span className={styles.colorHex} style={{ color: 'white' }}>
1548
+ {displayDarkValue}
1549
+ </span>
1550
+ </div>
1551
+ </button>
1552
+ }
1553
+ >
1554
+ <MenuHeader>Скопировать переменную</MenuHeader>
1555
+ {Object.entries(getColorsFormat(key)).map(([lang, colorVar]) => (
1556
+ <MenuItem key={lang} onClick={() => copyColor(colorVar)} comment={lang}>
1557
+ <div style={{ minWidth: 270 }}>{colorVar}</div>
1558
+ </MenuItem>
1559
+ ))}
1560
+ </DropdownMenu>
1561
+ );
1562
+ })}
1563
+ </React.Fragment>
1564
+ ))}
1565
+ <div className={styles.controls}>
1566
+ <Input
1567
+ width="50%"
1568
+ value={filter}
1569
+ onValueChange={setFilter}
1570
+ placeholder="Введите название токена или цвет"
1571
+ rightIcon={<SearchLoupeIcon16Regular />}
1572
+ />
1573
+ </div>
1574
+ </div>
1575
+ );
1576
+ };
1577
+
1578
+ ColorsPaletteStory.storyName = 'Список всех токенов';