@sberbusiness/triplex-next-mcp-server 1.40.0-beta → 1.41.0-beta
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.
- package/bundle/components/Calendar.md +183 -368
- package/bundle/components/Chip.md +310 -314
- package/bundle/components/FormField.md +468 -232
- package/bundle/components/IconWrapper.md +142 -89
- package/bundle/components/Island.md +221 -129
- package/bundle/components/KeyDownListener.md +215 -102
- package/bundle/components/LoaderMiddle.md +265 -0
- package/bundle/components/LoaderScreen.md +178 -81
- package/bundle/components/ThemeProvider.md +305 -117
- package/bundle/components/Tooltip.md +421 -181
- package/bundle/guides/commits.md +14 -6
- package/bundle/manifest.json +22 -11
- package/bundle/release-notes/1.41.0.mdx +76 -0
- package/bundle/release-notes/1.42.0.mdx +7 -0
- package/package.json +1 -1
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
component: ThemeProvider
|
|
3
|
-
category:
|
|
4
|
-
|
|
5
|
-
figma-file: ""
|
|
6
|
-
related: [DesignTokens]
|
|
3
|
+
category: ThemeProvider
|
|
4
|
+
related: [useToken]
|
|
7
5
|
tokens: []
|
|
8
|
-
stories:
|
|
6
|
+
stories: stories/ThemeProvider/ThemeProvider.stories.tsx
|
|
9
7
|
version: "1.0"
|
|
10
8
|
---
|
|
11
9
|
|
|
@@ -13,169 +11,359 @@ version: "1.0"
|
|
|
13
11
|
|
|
14
12
|
## Назначение
|
|
15
13
|
|
|
16
|
-
Провайдер
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
контекст (`useToken`).
|
|
14
|
+
Провайдер темы: рендерит css-переменные всех дизайн-токенов выбранной темы в тег `<style>`,
|
|
15
|
+
ограничивает их областью видимости элемента из `scopeRef` и передаёт тему и итоговые токены
|
|
16
|
+
дочерним компонентам через контекст (читается хуком `useToken`).
|
|
20
17
|
|
|
21
|
-
Используй когда:
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
Используй когда: приложению нужна тема Triplex Next (светлая или тёмная) либо точечное
|
|
19
|
+
переопределение дизайн-токенов — глобально на корне приложения или локально для части дерева.
|
|
20
|
+
|
|
21
|
+
Не используй когда: нужно поменять внешний вид одного компонента — для этого есть props
|
|
22
|
+
компонента (`theme`, `size`, `status`) и его собственные токены, а не подмена всей темы.
|
|
24
23
|
|
|
25
24
|
---
|
|
26
25
|
|
|
27
|
-
##
|
|
28
|
-
|
|
29
|
-
```text
|
|
30
|
-
src/components/ThemeProvider/
|
|
31
|
-
├── ThemeProvider.tsx # FC<IThemeProviderProps>; updateCSS/removeCSS + ThemeProviderView
|
|
32
|
-
├── ThemeProviderContext.ts # IThemeProviderContext + контекст с дефолтом LIGHT
|
|
33
|
-
├── ETriplexNextTheme.ts # enum LIGHT/DARK
|
|
34
|
-
├── useToken.ts # хук-обёртка над контекстом
|
|
35
|
-
├── components/
|
|
36
|
-
│ └── ThemeProviderView.tsx # классname на scopeRef + Context.Provider с merged-токенами
|
|
37
|
-
├── __tests__/
|
|
38
|
-
└── index.ts
|
|
39
|
-
```
|
|
26
|
+
## Варианты и props
|
|
40
27
|
|
|
41
|
-
|
|
28
|
+
### Обязательные props
|
|
42
29
|
|
|
43
|
-
|
|
30
|
+
| Prop | Тип | Описание |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| `children` | `React.ReactNode` | Контент, внутри которого действуют тема и токены |
|
|
33
|
+
| `scopeRef` | `React.RefObject<HTMLElement>` | Ref на элемент, на который навешивается `scopeClassName`. Именно этот элемент и его потомки видят css-переменные темы |
|
|
44
34
|
|
|
45
|
-
###
|
|
35
|
+
### Опциональные props
|
|
46
36
|
|
|
47
|
-
| Prop | Тип |
|
|
37
|
+
| Prop | Тип | По умолчанию | Описание |
|
|
48
38
|
|---|---|---|---|
|
|
49
|
-
| `
|
|
50
|
-
| `
|
|
51
|
-
| `
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
39
|
+
| `theme` | `ETriplexNextTheme` | `ETriplexNextTheme.LIGHT` | Дизайн-тема: `LIGHT` / `DARK` |
|
|
40
|
+
| `scopeClassName` | `string` | `uniqueId("triplex-next-theme-")` | Класс области видимости css-переменных. Задавай явно, если класс нужен снаружи (например, для портала, который рендерится вне `scopeRef`) |
|
|
41
|
+
| `tokens` | `TDesignTokensPartial` | `{}` | Частичное переопределение токенов поверх токенов темы |
|
|
42
|
+
|
|
43
|
+
### Особенности
|
|
44
|
+
|
|
45
|
+
- `scopeRef` должен указывать на элемент **внутри** `children` (или на предка, существующего
|
|
46
|
+
на момент эффекта). Класс навешивается в `useEffect`, поэтому до первого эффекта переменных
|
|
47
|
+
на элементе ещё нет.
|
|
48
|
+
- `tokens` мерджится через `defaultsDeep` в порядке «переопределения потребителя → core-токены
|
|
49
|
+
темы → токены компонентов темы»: значения потребителя всегда выигрывают.
|
|
50
|
+
- `tokens` сравнивается по ссылке. Инлайн-литерал в JSX пересобирает `<style>` на каждый рендер —
|
|
51
|
+
выноси объект в константу или мемоизируй.
|
|
52
|
+
- Провайдеры вкладываются: `useToken` возвращает значения ближайшего `ThemeProvider`.
|
|
53
|
+
- Вне `ThemeProvider` контекст отдаёт светлую тему, токены по умолчанию и пустой `scopeClassName`.
|
|
63
54
|
|
|
64
|
-
|
|
55
|
+
---
|
|
65
56
|
|
|
66
|
-
|
|
67
|
-
{
|
|
68
|
-
scopeClassName: string;
|
|
69
|
-
theme: ETriplexNextTheme;
|
|
70
|
-
tokens: TDesignTokens; // полные токены (Core + Components, по теме + override)
|
|
71
|
-
}
|
|
72
|
-
```
|
|
57
|
+
## Дизайн-токены
|
|
73
58
|
|
|
74
|
-
|
|
59
|
+
Собственных токенов у компонента нет — он **источник** css-переменных для всех остальных
|
|
60
|
+
компонентов. В `<style>` с ключом `triplex-next-dynamic-tokens-{scopeClassName}` выводятся все
|
|
61
|
+
токены выбранной темы (core + компонентные) в виде:
|
|
75
62
|
|
|
76
|
-
```
|
|
77
|
-
|
|
63
|
+
```
|
|
64
|
+
--triplex-next-{Группа}-{Токен}-{версия}: {значение};
|
|
78
65
|
```
|
|
79
66
|
|
|
80
|
-
|
|
67
|
+
Версия в имени переменной — версия пакета с точками, заменёнными на дефисы
|
|
68
|
+
(см. `DesignTokenUtils.getCSSVariableByTokenGroup`).
|
|
81
69
|
|
|
82
70
|
---
|
|
83
71
|
|
|
84
|
-
##
|
|
72
|
+
## Инварианты
|
|
85
73
|
|
|
86
|
-
|
|
74
|
+
- `ThemeProvider` не рендерит собственный DOM-элемент — `forwardRef` неприменим,
|
|
75
|
+
ref на DOM передаётся потребителем через `scopeRef`.
|
|
76
|
+
- Публичные экспорты `src/components/ThemeProvider/index.ts` — `ThemeProvider`,
|
|
77
|
+
`IThemeProviderProps`, `ThemeProviderContext`, `IThemeProviderContext`, `ETriplexNextTheme`,
|
|
78
|
+
`useToken`. Ни один не удалять и не переименовывать.
|
|
79
|
+
- Значения `ETriplexNextTheme` (`"light"` / `"dark"`) — часть публичного API.
|
|
80
|
+
- Ключ тега стилей `triplex-next-dynamic-tokens-{scopeClassName}` и префикс автогенерируемого
|
|
81
|
+
класса `triplex-next-theme-` менять нельзя: по ключу стили обновляются и удаляются,
|
|
82
|
+
а класс попадает в разметку потребителя.
|
|
83
|
+
- Порядок аргументов `defaultsDeep` в `ThemeProviderView` менять нельзя — иначе токены темы
|
|
84
|
+
начнут переопределять пользовательские.
|
|
85
|
+
- `ThemeProviderContext` должен оставаться единственным экземпляром модуля: `useToken`
|
|
86
|
+
импортирует его по пути пакета (`@sberbusiness/triplex-next/components/ThemeProvider/...`).
|
|
87
|
+
Дублирование модуля через разные спецификаторы разорвёт связь провайдера и потребителей.
|
|
88
|
+
- Компоненты, рендерящиеся в портал вне `scopeRef`, сами переносят `scopeClassName`
|
|
89
|
+
из `useToken` на свой корневой узел — иначе css-переменные до них не доходят.
|
|
90
|
+
Новые портальные компоненты должны делать так же, и способ зависит от контейнера:
|
|
91
|
+
- **собственный контейнер** (`Dropdown`, `ModalWindow`, `LightBox`) — класс просто
|
|
92
|
+
добавляется в `clsx` корневого элемента;
|
|
93
|
+
- **общий контейнер, разделяемый несколькими экземплярами** (`Tooltip`,
|
|
94
|
+
см. `utils/useTooltipTheme.ts`) — класс навешивается через `classList` в эффекте
|
|
95
|
+
со счётчиком использований в data-атрибуте и снимается с задержкой после закрытия.
|
|
96
|
+
Без счётчика соседние экземпляры на том же контейнере затирали бы тему друг другу,
|
|
97
|
+
без задержки класс снимался бы до конца анимации закрытия.
|
|
98
|
+
- При размонтировании и при смене `scopeClassName` тег стилей удаляется, а класс снимается
|
|
99
|
+
с элемента — утечка стилей на страницу недопустима.
|
|
87
100
|
|
|
88
|
-
|
|
89
|
-
1. `DesignTokenUtils.getStyle(theme, tokens)` → строка `--var-name: value;`.
|
|
90
|
-
2. `updateCSS(`.${scopeCssClassName} {…}`, ключ `triplex-next-dynamic-tokens-${scopeCssClassName}`)` — вставляет/обновляет `<style>` тег (через `rc-util/Dom/dynamicCSS`).
|
|
91
|
-
3. SSR: `canUseDom() === false` → `console.log` warning, без вставки.
|
|
101
|
+
---
|
|
92
102
|
|
|
93
|
-
|
|
103
|
+
## Accessibility
|
|
94
104
|
|
|
95
|
-
|
|
105
|
+
Компонент не рендерит DOM и не влияет на семантику, фокус и клавиатуру. Отдельных требований
|
|
106
|
+
нет. Контрастность цветов — ответственность значений токенов темы.
|
|
96
107
|
|
|
97
|
-
|
|
98
|
-
- Добавляет `scopeClassName` в `scopeRef.current.classList`.
|
|
99
|
-
- При cleanup — удаляет `prevScopeClassName.current` (хранит предыдущий класс для корректного removal).
|
|
108
|
+
---
|
|
100
109
|
|
|
101
|
-
|
|
110
|
+
## Связанные компоненты
|
|
102
111
|
|
|
103
|
-
`
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
+
- `useToken` — хук доступа к текущей теме, токенам и `scopeClassName`. Основной способ
|
|
113
|
+
прочитать тему внутри компонента.
|
|
114
|
+
- `ThemeProviderContext` — контекст темы; экспортируется для нестандартных сценариев,
|
|
115
|
+
в обычном коде используй `useToken`.
|
|
116
|
+
- `ETriplexNextTheme` — enum доступных тем.
|
|
117
|
+
- `ThemeProviderView` — внутренний компонент (навешивает класс, собирает значение контекста),
|
|
118
|
+
не экспортируется из barrel.
|
|
119
|
+
- `DesignTokens` (`DesignTokensCore`, `DesignTokensComponents` и их тёмные варианты) —
|
|
120
|
+
источник значений токенов; `DesignTokenUtils.getStyle` формирует css-переменные.
|
|
121
|
+
- `Dropdown`, `ModalWindow`, `LightBox`, `Tooltip` — потребители `useToken`: переносят
|
|
122
|
+
`scopeClassName` на свои портальные контейнеры.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Stories
|
|
112
127
|
|
|
113
|
-
|
|
114
|
-
|
|
128
|
+
Основные истории: `stories/ThemeProvider/ThemeProvider.stories.tsx`
|
|
129
|
+
Файлы примеров: `stories/ThemeProvider/examples/`
|
|
130
|
+
|
|
131
|
+
| Story | Example file | Что демонстрирует |
|
|
132
|
+
|---|---|---|
|
|
133
|
+
| `Playground` | `Playground.tsx` | Интерактивный подбор темы, `scopeClassName` и переопределяемых токенов |
|
|
134
|
+
| `Default` | `Default.tsx` | Минимальный пример: светлая тема по умолчанию |
|
|
135
|
+
| `DarkTheme` | `DarkTheme.tsx` | Тёмная тема |
|
|
136
|
+
| `CustomTokens` | `CustomTokens.tsx` | Переопределение токена поверх темы |
|
|
137
|
+
| `ThemeSwitcher` | `ThemeSwitcher.tsx` | Переключение темы на лету |
|
|
138
|
+
| `ScopedTheme` | `ScopedTheme.tsx` | Область действия темы: контент вне `scopeRef` её не видит |
|
|
115
139
|
|
|
116
140
|
---
|
|
117
141
|
|
|
118
|
-
##
|
|
142
|
+
## История изменений
|
|
119
143
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
-
|
|
123
|
-
-
|
|
124
|
-
- **Cleanup `removeCSS`** при размонтировании — иначе утечки `<style>` тегов в `<head>`.
|
|
125
|
-
- **Порядок `defaultsDeep` зафиксирован комментарием** — `tokensProps` идёт первым (приоритет), не менять без обсуждения.
|
|
126
|
-
- **`prevScopeClassName: useRef`** — нужен для удаления старого класса при изменении `scopeClassName` (обычный сценарий — не возникает, но защита от смены через props).
|
|
127
|
-
- **SSR не поддержан** — фоллбек на `console.log`. Реальная тема применится только после hydration на клиенте.
|
|
128
|
-
- **Дефолтный контекст содержит `LIGHT` токены** (`DesignTokensCore + DesignTokensComponents`), не пустой объект. Компоненты вне `ThemeProvider` получают дефолтную светлую тему через `useToken`.
|
|
129
|
-
- **Контекст имеет полные токены `TDesignTokens`**, а не `Partial` — `useToken` гарантированно возвращает все ключи.
|
|
144
|
+
| Дата | Изменение |
|
|
145
|
+
|---|---|
|
|
146
|
+
| 2026-08-05 | Создан документ. AI-рефакторинг: JSDoc на props и экспортах, ключ тега стилей вынесен в хелпер, упрощён эффект `ThemeProviderView`, добавлены unit-тесты `ThemeProviderView` и `useToken`, stories переведены на modern pattern |
|
|
147
|
+
| 2026-08-05 | `related` сокращён до `useToken`. Удалён неактуальный комментарий про относительный импорт в `ThemeProviderView` |
|
|
130
148
|
|
|
131
149
|
---
|
|
132
150
|
|
|
133
|
-
##
|
|
151
|
+
## Примеры использования
|
|
152
|
+
|
|
153
|
+
### Default
|
|
154
|
+
|
|
155
|
+
`stories/ThemeProvider/examples/Default.tsx`
|
|
134
156
|
|
|
135
157
|
```tsx
|
|
136
|
-
|
|
137
|
-
|
|
158
|
+
import React, { useRef } from "react";
|
|
159
|
+
import {
|
|
160
|
+
ThemeProvider,
|
|
161
|
+
ETriplexNextTheme,
|
|
162
|
+
Button,
|
|
163
|
+
EButtonTheme,
|
|
164
|
+
EComponentSize,
|
|
165
|
+
Text,
|
|
166
|
+
ETextSize,
|
|
167
|
+
} from "@sberbusiness/triplex-next";
|
|
168
|
+
|
|
169
|
+
export const Default = () => {
|
|
170
|
+
const scopeRef = useRef<HTMLDivElement>(null);
|
|
138
171
|
|
|
139
172
|
return (
|
|
140
|
-
<
|
|
141
|
-
<
|
|
142
|
-
<
|
|
143
|
-
|
|
144
|
-
|
|
173
|
+
<ThemeProvider theme={ETriplexNextTheme.LIGHT} scopeRef={scopeRef}>
|
|
174
|
+
<div ref={scopeRef} style={{ display: "flex", flexDirection: "column", alignItems: "flex-start", gap: 16 }}>
|
|
175
|
+
<Text size={ETextSize.B1}>Светлая тема — значение по умолчанию.</Text>
|
|
176
|
+
<Button theme={EButtonTheme.GENERAL} size={EComponentSize.MD}>
|
|
177
|
+
Button text
|
|
178
|
+
</Button>
|
|
179
|
+
</div>
|
|
180
|
+
</ThemeProvider>
|
|
145
181
|
);
|
|
146
182
|
};
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### DarkTheme
|
|
186
|
+
|
|
187
|
+
`stories/ThemeProvider/examples/DarkTheme.tsx`
|
|
147
188
|
|
|
148
|
-
|
|
149
|
-
|
|
189
|
+
```tsx
|
|
190
|
+
import React, { useRef } from "react";
|
|
191
|
+
import {
|
|
192
|
+
ThemeProvider,
|
|
193
|
+
ETriplexNextTheme,
|
|
194
|
+
Button,
|
|
195
|
+
EButtonTheme,
|
|
196
|
+
EComponentSize,
|
|
197
|
+
CardStatic,
|
|
198
|
+
ECardContentPaddingSize,
|
|
199
|
+
Text,
|
|
200
|
+
ETextSize,
|
|
201
|
+
} from "@sberbusiness/triplex-next";
|
|
202
|
+
|
|
203
|
+
export const DarkTheme = () => {
|
|
204
|
+
const scopeRef = useRef<HTMLDivElement>(null);
|
|
205
|
+
|
|
206
|
+
return (
|
|
207
|
+
<ThemeProvider theme={ETriplexNextTheme.DARK} scopeRef={scopeRef}>
|
|
208
|
+
{/*
|
|
209
|
+
ThemeProvider задаёт только css-переменные и не красит подложку — фон под тёмную тему
|
|
210
|
+
даёт компонент с собственным токеном фона, иначе светлый текст темы будет не виден.
|
|
211
|
+
*/}
|
|
212
|
+
<div ref={scopeRef}>
|
|
213
|
+
<CardStatic>
|
|
214
|
+
<CardStatic.Content paddingSize={ECardContentPaddingSize.MD}>
|
|
215
|
+
<div style={{ display: "flex", flexDirection: "column", alignItems: "flex-start", gap: 16 }}>
|
|
216
|
+
<Text size={ETextSize.B1}>Тёмная тема.</Text>
|
|
217
|
+
<Button theme={EButtonTheme.GENERAL} size={EComponentSize.MD}>
|
|
218
|
+
Button text
|
|
219
|
+
</Button>
|
|
220
|
+
<Button theme={EButtonTheme.SECONDARY} size={EComponentSize.MD}>
|
|
221
|
+
Button text
|
|
222
|
+
</Button>
|
|
223
|
+
</div>
|
|
224
|
+
</CardStatic.Content>
|
|
225
|
+
</CardStatic>
|
|
226
|
+
</div>
|
|
227
|
+
</ThemeProvider>
|
|
228
|
+
);
|
|
229
|
+
};
|
|
150
230
|
```
|
|
151
231
|
|
|
152
|
-
|
|
153
|
-
внешний внутри своего scope.
|
|
232
|
+
### CustomTokens
|
|
154
233
|
|
|
155
|
-
|
|
234
|
+
`stories/ThemeProvider/examples/CustomTokens.tsx`
|
|
156
235
|
|
|
157
|
-
|
|
236
|
+
```tsx
|
|
237
|
+
import React, { useRef } from "react";
|
|
238
|
+
import {
|
|
239
|
+
ThemeProvider,
|
|
240
|
+
ETriplexNextTheme,
|
|
241
|
+
Button,
|
|
242
|
+
EButtonTheme,
|
|
243
|
+
EComponentSize,
|
|
244
|
+
Text,
|
|
245
|
+
ETextSize,
|
|
246
|
+
} from "@sberbusiness/triplex-next";
|
|
247
|
+
|
|
248
|
+
// tokens сравнивается по ссылке — объект вынесен из рендера, чтобы style-тег не пересобирался.
|
|
249
|
+
const CUSTOM_TOKENS = {
|
|
250
|
+
ColorBrand: {
|
|
251
|
+
50: { value: "blue" },
|
|
252
|
+
},
|
|
253
|
+
};
|
|
158
254
|
|
|
159
|
-
|
|
160
|
-
|
|
255
|
+
export const CustomTokens = () => {
|
|
256
|
+
const scopeRef = useRef<HTMLDivElement>(null);
|
|
161
257
|
|
|
162
|
-
|
|
258
|
+
return (
|
|
259
|
+
<ThemeProvider theme={ETriplexNextTheme.LIGHT} tokens={CUSTOM_TOKENS} scopeRef={scopeRef}>
|
|
260
|
+
<div ref={scopeRef} style={{ display: "flex", flexDirection: "column", alignItems: "flex-start", gap: 16 }}>
|
|
261
|
+
<Text size={ETextSize.B1}>Токен ColorBrand.50 переопределён — кнопка перекрашена.</Text>
|
|
262
|
+
<Button theme={EButtonTheme.GENERAL} size={EComponentSize.MD}>
|
|
263
|
+
Button text
|
|
264
|
+
</Button>
|
|
265
|
+
</div>
|
|
266
|
+
</ThemeProvider>
|
|
267
|
+
);
|
|
268
|
+
};
|
|
269
|
+
```
|
|
163
270
|
|
|
164
|
-
|
|
271
|
+
### ThemeSwitcher
|
|
165
272
|
|
|
166
|
-
|
|
167
|
-
- Все компоненты дизайн-системы — потребители токенов через CSS-переменные `--triplex-next-*`.
|
|
273
|
+
`stories/ThemeProvider/examples/ThemeSwitcher.tsx`
|
|
168
274
|
|
|
169
|
-
|
|
275
|
+
```tsx
|
|
276
|
+
import React, { useRef, useState } from "react";
|
|
277
|
+
import {
|
|
278
|
+
ThemeProvider,
|
|
279
|
+
ETriplexNextTheme,
|
|
280
|
+
Button,
|
|
281
|
+
EButtonTheme,
|
|
282
|
+
EComponentSize,
|
|
283
|
+
CardStatic,
|
|
284
|
+
ECardContentPaddingSize,
|
|
285
|
+
Text,
|
|
286
|
+
ETextSize,
|
|
287
|
+
} from "@sberbusiness/triplex-next";
|
|
288
|
+
|
|
289
|
+
export const ThemeSwitcher = () => {
|
|
290
|
+
const scopeRef = useRef<HTMLDivElement>(null);
|
|
291
|
+
const [theme, setTheme] = useState(ETriplexNextTheme.LIGHT);
|
|
292
|
+
|
|
293
|
+
const handleToggleClick = () => {
|
|
294
|
+
setTheme((prevTheme) =>
|
|
295
|
+
prevTheme === ETriplexNextTheme.LIGHT ? ETriplexNextTheme.DARK : ETriplexNextTheme.LIGHT,
|
|
296
|
+
);
|
|
297
|
+
};
|
|
170
298
|
|
|
171
|
-
|
|
299
|
+
return (
|
|
300
|
+
<ThemeProvider theme={theme} scopeRef={scopeRef}>
|
|
301
|
+
{/* Подложку под тему даёт компонент с собственным токеном фона — ThemeProvider её не красит. */}
|
|
302
|
+
<div ref={scopeRef}>
|
|
303
|
+
<CardStatic>
|
|
304
|
+
<CardStatic.Content paddingSize={ECardContentPaddingSize.MD}>
|
|
305
|
+
<div style={{ display: "flex", flexDirection: "column", alignItems: "flex-start", gap: 16 }}>
|
|
306
|
+
<Text size={ETextSize.B1}>
|
|
307
|
+
Текущая тема: {theme === ETriplexNextTheme.LIGHT ? "светлая" : "тёмная"}
|
|
308
|
+
</Text>
|
|
309
|
+
<Button theme={EButtonTheme.GENERAL} size={EComponentSize.MD} onClick={handleToggleClick}>
|
|
310
|
+
Переключить тему
|
|
311
|
+
</Button>
|
|
312
|
+
</div>
|
|
313
|
+
</CardStatic.Content>
|
|
314
|
+
</CardStatic>
|
|
315
|
+
</div>
|
|
316
|
+
</ThemeProvider>
|
|
317
|
+
);
|
|
318
|
+
};
|
|
319
|
+
```
|
|
172
320
|
|
|
173
|
-
|
|
321
|
+
### ScopedTheme
|
|
174
322
|
|
|
175
|
-
|
|
323
|
+
`stories/ThemeProvider/examples/ScopedTheme.tsx`
|
|
176
324
|
|
|
177
|
-
|
|
325
|
+
```tsx
|
|
326
|
+
import React, { useRef } from "react";
|
|
327
|
+
import {
|
|
328
|
+
ThemeProvider,
|
|
329
|
+
ETriplexNextTheme,
|
|
330
|
+
Button,
|
|
331
|
+
EButtonTheme,
|
|
332
|
+
EComponentSize,
|
|
333
|
+
CardStatic,
|
|
334
|
+
ECardContentPaddingSize,
|
|
335
|
+
Text,
|
|
336
|
+
ETextSize,
|
|
337
|
+
} from "@sberbusiness/triplex-next";
|
|
338
|
+
|
|
339
|
+
export const ScopedTheme = () => {
|
|
340
|
+
const darkScopeRef = useRef<HTMLDivElement>(null);
|
|
341
|
+
|
|
342
|
+
// Обе области — одинаковая разметка. Отличается только тема: вторая обёрнута в ThemeProvider.
|
|
343
|
+
const content = (label: string) => (
|
|
344
|
+
<CardStatic>
|
|
345
|
+
<CardStatic.Content paddingSize={ECardContentPaddingSize.MD}>
|
|
346
|
+
<div style={{ display: "flex", flexDirection: "column", alignItems: "flex-start", gap: 16 }}>
|
|
347
|
+
<Text size={ETextSize.B1}>{label}</Text>
|
|
348
|
+
<Button theme={EButtonTheme.SECONDARY} size={EComponentSize.MD}>
|
|
349
|
+
Button text
|
|
350
|
+
</Button>
|
|
351
|
+
</div>
|
|
352
|
+
</CardStatic.Content>
|
|
353
|
+
</CardStatic>
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
return (
|
|
357
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 24 }}>
|
|
358
|
+
{content("Область вне ThemeProvider — тема приложения.")}
|
|
359
|
+
|
|
360
|
+
<ThemeProvider theme={ETriplexNextTheme.DARK} scopeRef={darkScopeRef}>
|
|
361
|
+
<div ref={darkScopeRef}>
|
|
362
|
+
{content("Область с тёмной темой — css-переменные действуют только здесь.")}
|
|
363
|
+
</div>
|
|
364
|
+
</ThemeProvider>
|
|
365
|
+
</div>
|
|
366
|
+
);
|
|
367
|
+
};
|
|
368
|
+
```
|
|
178
369
|
|
|
179
|
-
| Дата | Изменение |
|
|
180
|
-
|---|---|
|
|
181
|
-
| 2026-04-18 | Создан документ |
|