@sberbusiness/triplex-next-mcp-server 1.41.0-beta → 1.42.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.
@@ -1,11 +1,9 @@
1
1
  ---
2
2
  component: MediaWidth
3
- category: Utilities
4
- figma-node: ""
5
- figma-file: ""
6
- related: [MobileView, IslandWidget]
3
+ category: MediaWidth
4
+ related: [MediaMinWidth, MediaMaxWidth, MediaBetweenWidth, useMatchMedia, MobileView]
7
5
  tokens: []
8
- stories: ""
6
+ stories: stories/MediaWidth/MediaWidth.stories.tsx
9
7
  version: "1.0"
10
8
  ---
11
9
 
@@ -13,157 +11,297 @@ version: "1.0"
13
11
 
14
12
  ## Назначение
15
13
 
16
- Набор компонентов + хук для условного рендеринга по ширине окна:
17
- - **`useMatchMedia(query, initial)`** — хук, обёртка над `window.matchMedia` с live-обновлением через `change`-listener.
18
- - **`MediaMinWidth` / `MediaMaxWidth` / `MediaBetweenWidth`** — рендерят `children` / `fallback` по результату media-query.
19
- - **`MediaWidth`** — универсальная обёртка; по наличию `minWidth`/`maxWidth` выбирает нужный из трёх.
20
-
21
- Используй когда: нужно рендерить разный UI на разных брейкпоинтах.
22
- Для бинарного "mobile vs desktop" — `MobileView`. Для стилей — CSS `@media`
23
- напрямую (без JS-условия).
24
-
25
- ---
26
-
27
- ## Файловая структура
28
-
29
- ```text
30
- src/components/MediaWidth/
31
- ├── MediaWidth.tsx # Компонент-диспетчер
32
- ├── MediaMinWidth.tsx # min-width only
33
- ├── MediaMaxWidth.tsx # max-width only
34
- ├── MediaBetweenWidth.tsx # between min and max
35
- ├── useMatchMedia.ts # Хук
36
- ├── index.ts # Barrel (все 4 компонента + хук)
37
- └── __tests__/
38
- ```
14
+ Сервисный headless-компонент адаптивности и точка входа в семейство `MediaWidth`.
15
+ Рендерит `children`, когда ширина окна браузера попадает в диапазон, заданный
16
+ `minWidth` и/или `maxWidth`, и `fallback` — во всех остальных случаях.
17
+ Собственной разметки не добавляет: в DOM попадает ровно тот элемент, который
18
+ выбран по медиа-запросу.
19
+
20
+ Сам подписку на медиа-запрос не выполняет — по набору переданных props делегирует
21
+ рендер в `MediaBetweenWidth`, `MediaMinWidth` или `MediaMaxWidth`, а те уже
22
+ используют `useMatchMedia`.
23
+
24
+ Используй когда: на определённой ширине окна нужно отрендерить **другое дерево**
25
+ компонентов, а не просто другие стили — мобильный вариант дропдауна вместо
26
+ десктопного, список вместо таблицы, компактная панель вместо развёрнутой.
27
+
28
+ Не используй когда:
29
+ - Разница между ширинами чисто визуальная (отступы, шрифт, раскладка) — это
30
+ дешевле и надёжнее решается медиа-запросом в `*.module.less`, без
31
+ перемонтирования поддерева.
32
+ - Граница — мобильная (`<768px`): для этого есть `MobileView`, частный случай с
33
+ `maxWidth={EScreenWidth.SM_MAX}`.
34
+ - Нужно булево значение для props или условной разметки вокруг, а не выбор между
35
+ двумя готовыми элементами — используй хук `useMatchMedia` (или `useMobileView`
36
+ для мобильной границы).
39
37
 
40
38
  ---
41
39
 
42
40
  ## Варианты и props
43
41
 
44
- ### MediaWidth — `IMediaProps`
42
+ ### Обязательные props
45
43
 
46
- | Prop | Тип | Обязателен | Описание |
47
- |---|---|---|---|
48
- | `children` | `ReactElement \| null` | Да | Элемент рендерится при совпадении с media-query. |
49
- | `fallback` | `ReactElement \| null` | Да | Рендерится, если не совпало. |
50
- | `minWidth` | `EScreenWidth` | Нет | Нижняя граница. |
51
- | `maxWidth` | `EScreenWidth` | Нет | Верхняя граница. |
44
+ | Prop | Тип | Описание |
45
+ |---|---|---|
46
+ | `children` | `React.ReactElement \| null` | Элемент для ширины внутри диапазона. `null` — внутри диапазона не рендерится ничего |
47
+ | `fallback` | `React.ReactElement \| null` | Элемент для ширины вне диапазона. Обязателен: prop без значения по умолчанию, для «ничего» передаётся явный `null` |
52
48
 
53
- Логика выбора:
54
- - `minWidth && maxWidth` → `MediaBetweenWidth`
55
- - `minWidth` → `MediaMinWidth`
56
- - `maxWidth` → `MediaMaxWidth`
57
- - Ни один — возвращает `fallback`.
49
+ ### Опциональные props
58
50
 
59
- ### MediaMinWidth / MediaMaxWidth / MediaBetweenWidth
51
+ | Prop | Тип | По умолчанию | Описание |
52
+ |---|---|---|---|
53
+ | `minWidth` | `EScreenWidth` | — | Нижняя граница диапазона, включительно |
54
+ | `maxWidth` | `EScreenWidth` | — | Верхняя граница диапазона, включительно |
55
+
56
+ ### Как комбинация props выбирает реализацию
57
+
58
+ | Переданы | Делегирует в | Медиа-запрос |
59
+ |---|---|---|
60
+ | `minWidth` и `maxWidth` | `MediaBetweenWidth` | `(max-width: {maxWidth}) and (min-width: {minWidth})` |
61
+ | только `minWidth` | `MediaMinWidth` | `(min-width: {minWidth})` |
62
+ | только `maxWidth` | `MediaMaxWidth` | `(max-width: {maxWidth})` |
63
+ | ничего | — | запрос не строится, `window.matchMedia` не вызывается |
64
+
65
+ ### Особенности поведения
66
+
67
+ - **Без `minWidth` и `maxWidth` всегда рендерится `fallback`, а `children` — никогда.**
68
+ Это штатное поведение, а не деградация: ветка «нет ограничений» трактуется как
69
+ «условие не выполнено». Story `Fallback` фиксирует его специально. Если ширины
70
+ приходят из props потребителя и могут быть `undefined` — учитывай, что
71
+ «ограничений нет» здесь означает «показать `fallback`».
72
+ - **Границы включительные.** Значения `EScreenWidth` подобраны парами
73
+ (`XS_MAX = 575px` / `SM_MIN = 576px`, `SM_MAX = 767px` / `MD_MIN = 768px` и
74
+ т.д.), поэтому в `minWidth` передаётся `*_MIN`, в `maxWidth` — `*_MAX`. Так
75
+ соседние диапазоны стыкуются без перекрытия и без «дыр» в один пиксель.
76
+ - **Согласованность `minWidth` и `maxWidth` не проверяется.** Если `minWidth`
77
+ больше `maxWidth`, запрос просто никогда не сматчится и всегда будет рендериться
78
+ `fallback` — ни предупреждения, ни ошибки не будет.
79
+ - **Ветки взаимоисключающие.** При пересечении границы одна ветка размонтируется,
80
+ вторая монтируется: локальный state, фокус, значения неконтролируемых полей и
81
+ текущие анимации внутри поддерева теряются. Состояние, которое должно
82
+ переживать смену ширины, храни выше `MediaWidth` (или передавай его как props
83
+ в обе ветки).
84
+ - Значение медиа-запроса читается синхронно на первом рендере (`useMatchMedia`
85
+ вызывает `window.matchMedia` в инициализаторе `useState`), поэтому «мигания»
86
+ неправильной ветки на первом кадре нет.
87
+ - Пересчёт при ресайзе идёт по событию `change` у `MediaQueryList`, а не по
88
+ `resize` окна — слушатель снимается при размонтировании и переподписывается при
89
+ смене строки запроса.
90
+ - **Компонент не SSR-safe:** `window.matchMedia` вызывается на первом рендере.
91
+ Это поведение унаследовано от `useMatchMedia` и общее для всего семейства.
60
92
 
61
- Аналогичные props (`children`, `fallback`, соответствующие `min`/`max`).
93
+ ---
62
94
 
63
- ### useMatchMedia — `(query: string, initial: boolean) => boolean`
95
+ ## Дизайн-токены
64
96
 
65
- | Параметр | Описание |
66
- |---|---|
67
- | `query` | Полный media-query (например, `"(max-width: 767px)"`). |
68
- | `initial` | Начальное значение до первого рендера — критично для SSR/первого кадра. |
97
+ Собственных стилей и CSS-переменных нет — компонент не рендерит разметку.
98
+ Границы диапазонов задаются enum `EScreenWidth` (`src/helpers/breakpoints.ts`),
99
+ согласованным с LESS-переменными брейкпоинтов.
69
100
 
70
- Возвращает `true`, если query совпадает. Подписывается на `change`, возвращает актуальное значение.
101
+ ---
71
102
 
72
- ### EScreenWidth (из `@sberbusiness/triplex-next/helpers/breakpoints`)
103
+ ## Инварианты
73
104
 
74
- Breakpoint-значения: `XS_MAX`, `SM_MIN`, `SM_MAX`, `MD_MIN`, `MD_MAX`, `LG_MIN`, `LG_MAX`, `XL_MIN`, и т.д. (строковые — в px). Используются как значения `minWidth`/`maxWidth`.
105
+ - **Без `forwardRef` — осознанное исключение** из общего правила библиотеки:
106
+ компонент не рендерит собственный host-элемент, форвардить `ref` некуда.
107
+ Не добавлять и не убирать без отдельного решения — это публичный контракт.
108
+ - **Интерфейс props называется `IMediaProps`, а не `IMediaWidthProps`.**
109
+ Рассинхрон с именем компонента исторический, интерфейс экспортируется из
110
+ barrel — переименование будет breaking change. Не «чинить» походя: только
111
+ отдельным решением с записью в release notes.
112
+ - **Имена и типы props `children`, `minWidth`, `maxWidth`, `fallback`** —
113
+ публичное API, не менять. `fallback` остаётся обязательным: сделать его
114
+ опциональным можно только осознанно, это меняет контракт у потребителей.
115
+ - **Значения `EScreenWidth`** — публичное API и общая сетка дизайн-системы. Не
116
+ хардкодить пиксели в props и не заводить локальные брейкпоинты.
117
+ - **Делегирование в `MediaBetweenWidth` / `MediaMinWidth` / `MediaMaxWidth`** —
118
+ не инлайнить `useMatchMedia` в `MediaWidth.tsx`. Строки медиа-запросов живут
119
+ в трёх реализациях, и это единственное место, где они собираются.
120
+ - **Порядок веток в `if`/`else` значим:** проверка «оба props» идёт первой,
121
+ иначе комбинация `minWidth + maxWidth` схлопнулась бы до одной границы.
122
+ - **Barrel `src/components/MediaWidth/index.ts`** экспортирует все четыре
123
+ компонента и `useMatchMedia` — состав экспортов не сокращать.
124
+ - Реализация остаётся React 17-совместимой (без `useId`,
125
+ `useSyncExternalStore` и прочих React 18-only API): код синхронизируется
126
+ в `release-0`.
127
+ - **Fallback на устаревшие `addListener` / `removeListener`** в `useMatchMedia` —
128
+ это поддержка старых браузеров, где `MediaQueryList` не наследует `EventTarget`
129
+ и не имеет `addEventListener` (Safari < 14). К React 17 и синхронизации
130
+ в `release-0` он отношения не имеет: удалять его вместе с React 17-совместимостью
131
+ нельзя — это наблюдаемая регрессия у потребителей.
75
132
 
76
133
  ---
77
134
 
78
- ## Ключевые особенности реализации
135
+ ## Accessibility
79
136
 
80
- ### `children` / `fallback` обязательны — нет "пустого рендера"
137
+ - Собственной ARIA-семантики нет — компонент прозрачен для accessibility-дерева.
138
+ - Невыбранная ветка **не рендерится в DOM**, а не скрывается визуально: скринридер
139
+ и последовательность табуляции видят только актуальный вариант. Дублирования
140
+ контента для ассистивных технологий не возникает, но и «широкая» разметка на
141
+ узком экране недоступна.
142
+ - Смена ветки при ресайзе удаляет из DOM сфокусированный элемент — фокус
143
+ переходит на `<body>`. Если это критично (открытый оверлей, форма), верни
144
+ фокус явно на стороне потребителя.
145
+ - Медиа-запрос завязан на ширину окна, а не на тип ввода: на десктопе с узким
146
+ окном тоже рендерится «мобильная» ветка. Не завязывай на `MediaWidth` наличие
147
+ touch-специфичных обработчиков как единственного способа взаимодействия.
81
148
 
82
- ```ts
83
- children: React.ReactElement | null;
84
- fallback: React.ReactElement | null;
85
- ```
149
+ ---
86
150
 
87
- - Оба slot'а должны быть заданы (даже если один — `null`). Не `children?: ReactNode`.
88
- - **Render-props стиль "if/else"**, не "return null if mismatch".
151
+ ## Связанные компоненты
89
152
 
90
- ### `window.innerWidth` на первом рендере — НЕ SSR-safe
153
+ Все три реализации живут в той же папке, экспортируются из того же barrel и
154
+ собственного AI.md не имеют — их контракт описан здесь.
155
+
156
+ > **Объём AI-рефакторинга.** В рамках TRI-58 отрефакторен только `MediaWidth.tsx`:
157
+ > соседние реализации и `useMatchMedia` были вне scope задачи. В их JSDoc
158
+ > остаются недочёты (опечатка «ширина экран» в `MediaMaxWidth` / `MediaMinWidth` /
159
+ > `MediaBetweenWidth`, `@param initial` без соответствующего параметра
160
+ > в `useMatchMedia`) — семейство ещё не вычищено целиком, это отдельная задача.
161
+ > Туда же — тест на fallback `addListener` / `removeListener`: инвариант ниже
162
+ > запрещает его удалять, но ни один тест эту ветку не исполняет (во всех моках
163
+ > `MediaQueryList` есть `addEventListener`).
164
+
165
+ - `MediaBetweenWidth` (`src/components/MediaWidth/MediaBetweenWidth.tsx`) —
166
+ диапазон между двумя границами. Props: `minWidth` и `maxWidth` **обязательные**,
167
+ плюс `children` и `fallback`. Интерфейс `IMediaBetweenWidthProps`.
168
+ - `MediaMinWidth` (`src/components/MediaWidth/MediaMinWidth.tsx`) — только нижняя
169
+ граница, `minWidth` обязателен. Интерфейс `IMediaMinWidthProps`.
170
+ - `MediaMaxWidth` (`src/components/MediaWidth/MediaMaxWidth.tsx`) — только верхняя
171
+ граница, `maxWidth` обязателен. Интерфейс `IMediaMaxWidthProps`.
172
+ - `useMatchMedia` (`src/components/MediaWidth/useMatchMedia.ts`) — хук подписки на
173
+ произвольный медиа-запрос, основа всего семейства. Принимает строку запроса,
174
+ возвращает `boolean`. Годится и для не-ширинных запросов: `CarouselButton`
175
+ использует его с `(pointer: coarse)`.
176
+ - `MobileView` (`src/components/MobileView/`) — частный случай `MediaWidth` с
177
+ `maxWidth={EScreenWidth.SM_MAX}`, со своим AI.md. Для мобильной границы
178
+ используется он, а не `MediaWidth` напрямую.
179
+
180
+ Разница между `MediaWidth` и тремя реализациями: у `MediaWidth` обе границы
181
+ опциональны и выбор реализации делается за потребителя; у реализаций
182
+ соответствующие границы обязательны. Прямое использование реализации оправдано,
183
+ когда набор границ известен статически и «пустая» комбинация невозможна по
184
+ построению.
185
+
186
+ ### Потребители внутри библиотеки
187
+
188
+ Компонентом `MediaWidth`: `MobileView` (и, транзитивно, все его потребители).
189
+ Хуком `useMatchMedia`: `CarouselButton`, `useMobileView`.
190
+
191
+ В unit-тестах потребителей ветка задаётся подменой `window.matchMedia` (как в
192
+ `MediaWidth.test.tsx` и `Dropdown.test.tsx`) либо мокированием модуля
193
+ `useMatchMedia` (как в `IslandWidget.test.tsx`).
91
194
 
92
- ```ts
93
- useMatchMedia(`(min-width: ${minWidth})`, window.innerWidth >= parseInt(minWidth));
94
- ```
195
+ ---
95
196
 
96
- - `window.innerWidth` в initial-аргументе выполняется **при рендере**, не в `useEffect` — упадёт на SSR.
97
- - Потребители в SSR должны оборачивать в ClientOnly или подавать `false`/`true` безопасный default через свой wrapper.
197
+ ## Stories
98
198
 
99
- ### `useMatchMedia` — `addEventListener("change")`, не deprecated `addListener`
199
+ Основные истории: `stories/MediaWidth/MediaWidth.stories.tsx`
200
+ Файлы примеров: `stories/MediaWidth/examples/`
100
201
 
101
- ```ts
102
- mediaQueryList.addEventListener("change", handleChangeMatches);
103
- ```
202
+ | Story | Example file | Что демонстрирует |
203
+ |---|---|---|
204
+ | `Playground` | — | Интерактивный подбор `minWidth` / `maxWidth`, включая значение `undefined` |
205
+ | `Default` | `DefaultExample.tsx` | Обе границы: контент виден в диапазоне 768px–1199px |
206
+ | `MinWidth` | `MinWidthExample.tsx` | Только нижняя граница: контент от 768px и шире |
207
+ | `MaxWidth` | `MaxWidthExample.tsx` | Только верхняя граница: контент до 1199px включительно |
208
+ | `Fallback` | `FallbackExample.tsx` | Без границ: всегда рендерится `fallback` |
209
+ | `Example: MobileView` | `MobileViewExample.tsx` | Связь общего случая с частным — `MobileView` вместо ручной мобильной границы |
104
210
 
105
- - Современный API. Safari 13.1+, Chrome 76+ — ок.
106
- - **Legacy `addListener` не используется** — если нужна поддержка старых Safari, нужен polyfill.
211
+ Скриншот-тестами покрыта только story `Example: MobileView` (baseline
212
+ `mediawidth--mobile-view-story--xs.png` и `--xl.png`): на viewport `xs` (575px)
213
+ рендерится мобильная ветка, на `xl` (1200px) — `fallback`. Остальные stories
214
+ помечены `testRunner: { skip: true }` — их результат целиком определяется
215
+ шириной viewport и уже проверяется этой парой скриншотов.
107
216
 
108
- ### `useEffect` зависит только от `query`
217
+ ---
218
+
219
+ ## История изменений
109
220
 
110
- Колбэк `handleChangeMatches` всегда зовёт `setMatches(event.matches)` — `initial` игнорируется после первого рендера.
221
+ | Дата | Изменение |
222
+ |---|---|
223
+ | 2026-08-11 | Создан документ. AI-рефакторинг (TRI-58): `displayName` у `MediaWidth`, исправлены опечатки и дополнен JSDoc props и компонента, unit-тесты расширены с 7 до 19 кейсов (проверяются строки медиа-запросов, выбор реализации, `null`-ветки, переподписка и отписка). Публичный API не изменён (props, `IMediaProps`, barrel-экспорты; `forwardRef` по-прежнему осознанно отсутствует). |
111
224
 
112
- ### `MediaBetweenWidth` query — AND двух conditions
225
+ ---
113
226
 
114
- ```ts
115
- `(max-width: ${maxWidth}) and (min-width: ${minWidth})`
116
- ```
227
+ ## Примеры использования
117
228
 
118
- Оба условия должны выполняться. `between` — это **пересечение**.
229
+ ### Default
119
230
 
120
- ### `MediaWidth` — не "матчит всё" по умолчанию
231
+ `stories/MediaWidth/examples/DefaultExample.tsx`
121
232
 
122
- Если ни `minWidth`, ни `maxWidth` не переданы — возвращает `fallback`. Это неочевидно: потребители могут ожидать, что пустая media-query рендерит `children`. Потому всегда **передавай хотя бы одну границу**.
233
+ ```tsx
234
+ import React from "react";
235
+ import { MediaWidth, EScreenWidth } from "@sberbusiness/triplex-next";
123
236
 
124
- ---
237
+ export const DefaultExample = () => (
238
+ <MediaWidth
239
+ minWidth={EScreenWidth.MD_MIN}
240
+ maxWidth={EScreenWidth.LG_MAX}
241
+ fallback={<div>Fallback: ширина экрана за пределами диапазона 768px–1199px</div>}
242
+ >
243
+ <div>Контент виден только на экранах шириной от 768px до 1199px включительно</div>
244
+ </MediaWidth>
245
+ );
246
+ ```
125
247
 
126
- ## Инварианты
248
+ ### MinWidth
127
249
 
128
- - **`children` и `fallback` — обязательные `ReactElement | null`**, не `ReactNode`. Не менять на optional.
129
- - **`EScreenWidth` как тип `minWidth`/`maxWidth`** — потребители завязаны на брейкпоинты библиотеки; произвольные строки не принимаются.
130
- - **`useMatchMedia(initial)` аргумент обязателен** — отсутствие initial ломает SSR-совместимость (которая и так условная).
131
- - **`window.innerWidth` в initial** — legacy-approach. Не вставлять в SSR-контекст без защиты.
132
- - **`addEventListener("change")`** — современный API; не переключать на deprecated `addListener`.
133
- - **`MediaWidth` без границ → `fallback`** — документированное поведение, не менять.
250
+ `stories/MediaWidth/examples/MinWidthExample.tsx`
134
251
 
135
- ---
252
+ ```tsx
253
+ import React from "react";
254
+ import { MediaWidth, EScreenWidth } from "@sberbusiness/triplex-next";
136
255
 
137
- ## Accessibility
256
+ export const MinWidthExample = () => (
257
+ <MediaWidth minWidth={EScreenWidth.MD_MIN} fallback={<div>Fallback: ширина экрана менее 768px</div>}>
258
+ <div>Контент виден на экранах шириной 768px и более</div>
259
+ </MediaWidth>
260
+ );
261
+ ```
138
262
 
139
- - JS-условный рендеринг → **содержимое DOM меняется на брейкпоинте**. При переключении фокуса/viewport/скринридера это может вызвать нежелательный повторный анонс.
140
- - Предпочитай CSS `@media` + `display: none` там, где возможно: DOM стабилен, скринридеры видят весь контент (если это желаемое поведение) или выбираешь, какой показать через `aria-hidden`.
141
- - Используй `MediaWidth`/`MobileView` только тогда, когда действительно нужно **разное дерево React'а** (разные компоненты, не CSS-вариации).
263
+ ### MaxWidth
142
264
 
143
- ---
265
+ `stories/MediaWidth/examples/MaxWidthExample.tsx`
144
266
 
145
- ## Дизайн-токены
267
+ ```tsx
268
+ import React from "react";
269
+ import { MediaWidth, EScreenWidth } from "@sberbusiness/triplex-next";
146
270
 
147
- Нет. Брейкпоинты — `EScreenWidth` (строковые px).
271
+ export const MaxWidthExample = () => (
272
+ <MediaWidth maxWidth={EScreenWidth.LG_MAX} fallback={<div>Fallback: ширина экрана более 1199px</div>}>
273
+ <div>Контент виден на экранах шириной до 1199px включительно</div>
274
+ </MediaWidth>
275
+ );
276
+ ```
148
277
 
149
- ---
278
+ ### Fallback
150
279
 
151
- ## Связанные компоненты
280
+ `stories/MediaWidth/examples/FallbackExample.tsx`
152
281
 
153
- - `MobileView` — частный случай (mobile vs desktop), use-case определён.
154
- - `IslandWidget` — использует `useMatchMedia` для адаптивного сворачивания.
155
- - `helpers/breakpoints.EScreenWidth` — источник брейкпоинтов.
282
+ ```tsx
283
+ import React from "react";
284
+ import { MediaWidth } from "@sberbusiness/triplex-next";
156
285
 
157
- ---
286
+ export const FallbackExample = () => (
287
+ <MediaWidth fallback={<div>Fallback отображается всегда, если не заданы minWidth и maxWidth</div>}>
288
+ {null}
289
+ </MediaWidth>
290
+ );
291
+ ```
158
292
 
159
- ## Stories
293
+ ### Example: MobileView
160
294
 
161
- Нет. Компонент утилитарный; покрытие в тестах и через потребителей.
295
+ `stories/MediaWidth/examples/MobileViewExample.tsx`
162
296
 
163
- ---
297
+ ```tsx
298
+ import React from "react";
299
+ import { MobileView } from "@sberbusiness/triplex-next";
164
300
 
165
- ## История изменений
301
+ export const MobileViewExample = () => (
302
+ <MobileView fallback={<div>Десктоп: ширина экрана 768px и более</div>}>
303
+ <div>Мобильный: ширина экрана менее 768px</div>
304
+ </MobileView>
305
+ );
306
+ ```
166
307
 
167
- | Дата | Изменение |
168
- |---|---|
169
- | 2026-04-18 | Создан документ |