Establish NODE.DC design system baseline
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
# Подключение приложений
|
||||
|
||||
## Текущее состояние
|
||||
|
||||
Первый этап не меняет существующие приложения. Репозиторий фиксирует baseline и создаёт пакетный контракт. Это позволяет продолжать текущую разработку без одномоментной переделки Engine, Ops, BIM, CMS, SEO и Launcher.
|
||||
|
||||
## Рекомендуемый порядок
|
||||
|
||||
### 1. Catalog validation
|
||||
|
||||
Сначала компоненты проверяются как самостоятельная система в dark/light и нескольких accent. До этого они не объявляются заменой production-кода.
|
||||
|
||||
### 2. Launcher/Hub pilot
|
||||
|
||||
Первый consumer — Launcher/Hub как основной visual reference. Подключение выполняется по одному вертикальному набору:
|
||||
|
||||
- tokens/theme;
|
||||
- Button/IconButton;
|
||||
- Dropdown/Select;
|
||||
- Window/Confirmation;
|
||||
- AppHeader.
|
||||
|
||||
Локальные реализации удаляются только после функционального и визуального сравнения.
|
||||
|
||||
### 3. CMS и SEO
|
||||
|
||||
Используют те же компоненты и механику окон. CMS подключает DOM-layer, SEO — React-layer. Цветовые различия оформляются theme overrides.
|
||||
|
||||
### 4. Engine
|
||||
|
||||
Сначала библиотека заменяет компоненты только в уже новом Environment Settings и NDC Agent Inspector. Затем остальные инспекторы переводятся на Inspector/ControlRow постепенно. Legacy UI не переносится в библиотеку и не используется как reference.
|
||||
|
||||
### 5. Task Manager / Ops
|
||||
|
||||
Подключение начинается с floating-layer и modal primitives, где уже сформулирован канон. Массовая замена Plane UI не выполняется одним изменением.
|
||||
|
||||
### 6. BIM Viewer
|
||||
|
||||
Подключает tokens/styles и `@nodedc/ui-dom`. React в BIM ради дизайн-системы не добавляется.
|
||||
|
||||
## Новые приложения
|
||||
|
||||
Шаблон приложения пока не входит в scope. До его появления новый проект должен:
|
||||
|
||||
- установить опубликованные NODE.DC UI packages;
|
||||
- выбрать theme и accent;
|
||||
- использовать AppHeader и Window/Dropdown primitives;
|
||||
- проверять registry перед созданием локального control;
|
||||
- хранить доменные компоненты у себя.
|
||||
|
||||
## Adoption matrix
|
||||
|
||||
| Приложение | Текущий источник | Целевой adapter | Первый набор |
|
||||
| --- | --- | --- | --- |
|
||||
| Launcher/Hub | React/local shared | React | header, button, dropdown, window |
|
||||
| SEO | React/local overrides | React | theme, window, field, select |
|
||||
| CMS | Vanilla DOM | DOM | theme, header, modal, select |
|
||||
| Engine | React/mixed generations | React | new inspector and environment settings only |
|
||||
| Ops | React/Plane packages | React | floating behavior and modal primitives |
|
||||
| BIM Viewer | Vanilla JS | DOM | tokens, glass select, settings surfaces |
|
||||
|
||||
## Не делать
|
||||
|
||||
- не переписывать все приложения одновременно;
|
||||
- не копировать `packages/ui-react/src` в consumer;
|
||||
- не поддерживать отдельную тему путём fork компонента;
|
||||
- не использовать legacy Engine inspector как промежуточный канон;
|
||||
- не удалять production local component до проверки package replacement.
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
# Архитектура дизайн-системы
|
||||
|
||||
## Цель
|
||||
|
||||
NODE.DC состоит из самостоятельных репозиториев и разных frontend-стеков. Поэтому общий UI не может существовать как папка, которую вручную копируют из последнего приложения. Дизайн-система поставляется как версионируемые пакеты, а приложения сохраняют собственную доменную логику.
|
||||
|
||||
Репозиторий физически отделён от `NODEDC_PLATFORM`, но логически относится к платформенному слою. Он не является backend-сервисом и не владеет данными приложений.
|
||||
|
||||
## Слои
|
||||
|
||||
### Tokens
|
||||
|
||||
`@nodedc/tokens` хранит геометрию, слои, motion и переменные темы. Геометрия не зависит от выбранного цвета приложения.
|
||||
|
||||
### Core
|
||||
|
||||
`@nodedc/ui-core` хранит:
|
||||
|
||||
- общий CSS-контракт;
|
||||
- расчёт контрастного текста на акцентной заливке;
|
||||
- расчёт позиции dropdown/popover относительно viewport;
|
||||
- применение темы к DOM-контейнеру.
|
||||
|
||||
Core не зависит от React.
|
||||
|
||||
### React
|
||||
|
||||
`@nodedc/ui-react` предоставляет компоненты для Launcher/Hub, Engine, Ops и React-модулей. Компоненты контролируемые: бизнес-состояние остаётся у приложения, а визуальное и базовое интерактивное поведение принадлежит библиотеке.
|
||||
|
||||
### DOM
|
||||
|
||||
`@nodedc/ui-dom` предоставляет контроллеры для CMS, BIM Viewer и других приложений без React. DOM-слой не создаёт вторую стилизацию: он использует те же классы и токены `@nodedc/ui-core`.
|
||||
|
||||
### Catalog
|
||||
|
||||
Каталог является исполняемой документацией. Компонент не считается зафиксированным, если его состояния нельзя увидеть и проверить в каталоге.
|
||||
|
||||
## Граница shared/domain
|
||||
|
||||
В дизайн-системе живут элементы, значение которых не зависит от предметной области:
|
||||
|
||||
- поверхности;
|
||||
- поля и действия;
|
||||
- floating layers;
|
||||
- окна;
|
||||
- навигационная геометрия;
|
||||
- inspector/settings layout;
|
||||
- состояния загрузки, пустоты и ошибки.
|
||||
|
||||
В приложениях остаются:
|
||||
|
||||
- определение агентной ноды;
|
||||
- схема SEO pipeline;
|
||||
- BIM tree/model tools;
|
||||
- модели задач и карточки с уникальной бизнес-логикой;
|
||||
- запросы к API, права, маршруты и сохранение данных.
|
||||
|
||||
Если элемент встречается только в одном приложении, это ещё не причина переносить его сюда. Он переносится, когда имеет независимый UI-контракт или нужен новому приложению как готовый строительный блок.
|
||||
|
||||
## Зависимости
|
||||
|
||||
Направление зависимостей одностороннее:
|
||||
|
||||
1. tokens;
|
||||
2. core;
|
||||
3. React/DOM adapters;
|
||||
4. applications.
|
||||
|
||||
Дизайн-система никогда не импортирует исходники приложения. Исторические реализации используются только как audit/reference и фиксируются в `registry/sources.json`.
|
||||
|
||||
## Версионирование
|
||||
|
||||
- `0.x` — baseline и первоначальная проверка API на реальных приложениях;
|
||||
- minor — новый компонент или обратно совместимое расширение;
|
||||
- patch — исправление поведения/стиля без изменения публичного контракта;
|
||||
- major — удаление/переименование экспорта или несовместимая геометрия/семантика.
|
||||
|
||||
Приложение должно зависеть от опубликованной версии пакета, а не от копии файлов или произвольного commit URL.
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
# Следующие компоненты
|
||||
|
||||
Baseline намеренно отделяет готовые exports от известного инвентаря. Элемент из этого списка нельзя считать отсутствующим и начинать с нуля в новом приложении: сначала проверяется его запись в `registry/candidates.json` и существующие reference sources.
|
||||
|
||||
## P1 — следующий reusable слой
|
||||
|
||||
- CalendarPopover — согласовать Launcher и Task Manager, включая keyboard/date range.
|
||||
- ProfileMenu — отделить визуальную карточку от identity/role API приложения.
|
||||
- ActionDropdown — добавить типизированные command rows поверх готового Dropdown.
|
||||
- DataTable — вынести toolbar, cells, loading/empty и row actions без entity schemas Launcher/Plane.
|
||||
- SideNavigation — route-aware навигация отдельно от уже готового Inspector accordion.
|
||||
- AvatarStack — согласовать overflow, presence и picker mode.
|
||||
|
||||
## P2 — продуктовые patterns
|
||||
|
||||
- ServiceRailCard — зрелый Launcher pattern, пока связанный с service catalog data.
|
||||
- WorkItemCard — зрелый Ops pattern, который нужно разделить на общий card shell и task-domain content.
|
||||
- MediaSourceField — общий preview/picker, но storage adapter остаётся в приложении.
|
||||
- Toast.
|
||||
- Tooltip.
|
||||
- Empty/Loading/Error states.
|
||||
|
||||
## Как использовать список
|
||||
|
||||
Если новый проект требует candidate:
|
||||
|
||||
1. не копировать reference implementation целиком;
|
||||
2. поднять candidate до package component в этом репозитории;
|
||||
3. определить независимые props/DOM contract;
|
||||
4. добавить catalog examples и состояния;
|
||||
5. обновить `components.json`, удалив запись из candidates;
|
||||
6. выпустить minor version.
|
||||
|
||||
Так список сохраняет уже проделанную работу, но не превращает незавершённый локальный код в ложный канон.
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
# Канонические компоненты
|
||||
|
||||
Машинный список находится в `registry/components.json`. Ниже зафиксирован смысл компонентов и границы их использования.
|
||||
|
||||
## GlassSurface
|
||||
|
||||
Базовая поверхность для карточки, панели и большого окна. Варианты меняют плотность материала, но не создают отдельный дизайн.
|
||||
|
||||
- `default` — обычная карточка/панель;
|
||||
- `strong` — dropdown, modal и поверхность над сложным фоном;
|
||||
- `soft` — вложенная или вторичная область.
|
||||
|
||||
Material rim допустим только как часть стекла. Жёсткая цветная рамка, случайный browser outline или debug border не являются rim.
|
||||
|
||||
## Button и IconButton
|
||||
|
||||
Button используется для всех текстовых действий. IconButton — для действия без текста.
|
||||
|
||||
- primary/accent — главное действие текущего контекста;
|
||||
- secondary — обычное действие на glass surface;
|
||||
- ghost — малозаметное действие без собственной подложки;
|
||||
- danger — разрушительное действие, обычно в окне подтверждения.
|
||||
|
||||
Icon-only action по умолчанию круглый. Квадратная кнопка с маленьким радиусом допустима только как кнопка закрытия окна или плотный инструмент, где это зафиксировано контрактом.
|
||||
|
||||
## Field
|
||||
|
||||
FieldFrame объединяет label, control, hint и description. TextField/TextAreaField реализуют стандартные текстовые поля.
|
||||
|
||||
Приложение не должно вручную собирать label и input, если подходит Field. Ошибка и validation state будут расширением этого контракта, а не локальным классом приложения.
|
||||
|
||||
## Checker
|
||||
|
||||
Круглый бинарный контрол из нового Engine settings/agent inspector. Он использует checkbox semantics, но не копирует квадратный системный checkbox.
|
||||
|
||||
## RangeControl
|
||||
|
||||
Pill-range с заполнением акцентным цветом, встроенной подписью и значением. Домен определяет min/max/step и формат числа.
|
||||
|
||||
## ColorField
|
||||
|
||||
Связка круглого color picker и текстового значения. Валидация конкретного формата остаётся у формы приложения.
|
||||
|
||||
## Dropdown
|
||||
|
||||
Dropdown владеет floating-layer поведением:
|
||||
|
||||
- рендерится в `document.body`;
|
||||
- позиционируется относительно viewport;
|
||||
- переворачивается вверх при недостатке места;
|
||||
- ограничивает размеры viewport;
|
||||
- обновляется при resize и scroll;
|
||||
- закрывается по outside pointer и Escape;
|
||||
- закрывает другой открытый dropdown.
|
||||
|
||||
Содержимое меню передаётся приложением. Selection, action menu и filter menu используют один engine.
|
||||
|
||||
## Select
|
||||
|
||||
Select добавляет к Dropdown контролируемое значение, options и необязательный поиск. Native select не используется как видимый runtime UI, но может использоваться как fallback только в специально оговорённом legacy-контуре.
|
||||
|
||||
## Window
|
||||
|
||||
Window — единая механика открытия modal и правой panel:
|
||||
|
||||
- приложение контролирует `open`;
|
||||
- библиотека управляет portal, focus entry/trap/restore, Escape, backdrop и scroll lock;
|
||||
- `center` — modal;
|
||||
- `end` — inspector/settings/detail panel.
|
||||
|
||||
Содержание, сохранение и запросы принадлежат приложению.
|
||||
|
||||
## ConfirmationModal
|
||||
|
||||
ConfirmationModal оборачивает Window и защищает async-confirm от повторного запуска. До завершения операции закрытие можно заблокировать.
|
||||
|
||||
## SegmentedControl
|
||||
|
||||
Pill navigation для верхней панели и компактного переключения режимов. Active segment использует активную поверхность темы; это не обязательно основной accent приложения.
|
||||
|
||||
## AppHeader
|
||||
|
||||
Трёхосевая верхняя панель:
|
||||
|
||||
- слева фиксированная область логотипа и optional context;
|
||||
- по центру workspace/navigation;
|
||||
- справа actions/profile.
|
||||
|
||||
Компонент фиксирует положение логотипа, высоту и выравнивание, но не знает маршруты конкретного приложения.
|
||||
|
||||
## StatusBadge
|
||||
|
||||
Статус использует semantic tone: neutral, success, warning, danger или accent. Бизнес-статус приложения маппится на tone в самом приложении.
|
||||
|
||||
## Inspector и ControlRow
|
||||
|
||||
Единая accordion-панель для settings и definition controls. Источник — только новый Environment Settings и NDC Agent Inspector.
|
||||
|
||||
Inspector владеет:
|
||||
|
||||
- заголовками разделов;
|
||||
- открытием/закрытием секций;
|
||||
- active state;
|
||||
- вертикальным ритмом;
|
||||
- раскладкой label/control.
|
||||
|
||||
Engine продолжает владеть определениями полей, типами нод и сохранением значений.
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
# Правила развития
|
||||
|
||||
## Единственный источник истины
|
||||
|
||||
Публичный компонент, его CSS, документация, registry entry и catalog example изменяются одним pull/merge request в этом репозитории.
|
||||
|
||||
Приложения не должны исправлять общий компонент локальным копированием. Если срочный adapter неизбежен, он должен:
|
||||
|
||||
- оборачивать публичный компонент;
|
||||
- не копировать его внутреннюю реализацию;
|
||||
- иметь ссылку на issue/design-system change;
|
||||
- быть внесён в `docs/ADOPTION.md` как временный долг.
|
||||
|
||||
## Добавление компонента
|
||||
|
||||
Компонент принимается в систему, если:
|
||||
|
||||
1. он независим от доменной модели;
|
||||
2. его API можно описать без упоминания конкретной таблицы/ноды/проекта;
|
||||
3. он использует существующую тему и layer scale;
|
||||
4. определены keyboard и disabled/pending states;
|
||||
5. добавлен React или DOM adapter в зависимости от потребителей;
|
||||
6. есть catalog example;
|
||||
7. обновлён registry.
|
||||
|
||||
## Изменение геометрии
|
||||
|
||||
Геометрия меняется централизованно. Нельзя исправлять высоту/радиус в одном приложении, если изменение относится к общему контролу.
|
||||
|
||||
Допустимы density variants, если они имеют устойчивое назначение (`default`, `compact`) и тестируются как часть API.
|
||||
|
||||
## Deprecated
|
||||
|
||||
Перед удалением export:
|
||||
|
||||
- он получает статус deprecated в registry;
|
||||
- документация указывает replacement;
|
||||
- минимум один minor release сохраняет совместимость;
|
||||
- после миграции известных consumers export удаляется в major release.
|
||||
|
||||
## Проверки
|
||||
|
||||
Минимум для merge:
|
||||
|
||||
- typecheck всех пакетов;
|
||||
- registry validation;
|
||||
- production build каталога;
|
||||
- ручная визуальная проверка dark/light и нескольких accent;
|
||||
- keyboard smoke test для Dropdown, Select, Window и Inspector.
|
||||
|
||||
Следующий уровень зрелости — автоматические screenshot regression и interaction tests. Они добавляются до массовой миграции приложений.
|
||||
|
||||
## Владение
|
||||
|
||||
У дизайн-системы должен быть явный code owner. Product team может предлагать компоненты, но общий API и theme contract проходят отдельное review, потому что изменение распространяется на все будущие приложения.
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
# Baseline источников
|
||||
|
||||
Дата аудита: 10 июля 2026 года. Точные repository revisions находятся в `registry/sources.json`.
|
||||
|
||||
## Launcher / Hub
|
||||
|
||||
Основной визуальный источник:
|
||||
|
||||
- общая верхняя панель;
|
||||
- позиция логотипа;
|
||||
- segmented navigation;
|
||||
- dark glass family;
|
||||
- button/modal/dropdown primitives;
|
||||
- общая логика admin windows.
|
||||
|
||||
Существовавший `dc-ui-guideline` использован как исходный инвентарь. Он больше не должен развиваться как независимая копия после подключения центрального репозитория.
|
||||
|
||||
## SEO и CMS
|
||||
|
||||
Рассматриваются как одна продуктовая семья с Launcher/Hub. Их различия должны выражаться темой и содержанием, а не отдельной реализацией окон и controls.
|
||||
|
||||
SEO содержит большой объём накопленных локальных overrides. Они являются материалом аудита, но не переносятся автоматически. В библиотеку попадает очищенный общий контракт.
|
||||
|
||||
CMS подтверждает необходимость DOM-пакета без React.
|
||||
|
||||
## Engine
|
||||
|
||||
Разрешён только строгий subset:
|
||||
|
||||
- Environment Settings;
|
||||
- NdcGlassChecker;
|
||||
- NdcGlassSelect;
|
||||
- NdcGlassModal;
|
||||
- новый NDC Agent Inspector;
|
||||
- styles, непосредственно обслуживающие эти элементы.
|
||||
|
||||
Все остальные legacy inspectors и старые plates не участвуют в принятии решений. Их будущее приведение к новому дизайну должно идти через эту библиотеку.
|
||||
|
||||
## Task Manager / Ops
|
||||
|
||||
Используются только уже стандартизированные shared patterns и документы по dropdown/modal behavior. Известные legacy CustomMenu и screen-local wrappers не являются источником компонентов.
|
||||
|
||||
## BIM Viewer
|
||||
|
||||
Используется как ограничение архитектуры: общий UI должен работать без React. В baseline включены общие требования к toolbar, settings menu и glass select, но не BIM-domain controls.
|
||||
|
||||
## Почему код не копируется целиком
|
||||
|
||||
Исторические реализации содержат:
|
||||
|
||||
- зависимости от доменных типов;
|
||||
- локальные классы;
|
||||
- дублированные токены;
|
||||
- наслоившиеся overrides;
|
||||
- разные z-index;
|
||||
- различную степень accessibility.
|
||||
|
||||
Поэтому библиотека фиксирует очищенный API и поведение, сохраняя происхождение решения в реестре.
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
# Темы и цвет приложения
|
||||
|
||||
## Основное правило
|
||||
|
||||
NODE.DC-приложения отличаются цветовой схемой, а не набором заново нарисованных компонентов. Тема задаёт значения CSS custom properties. Компоненты сохраняют геометрию, состояния и поведение.
|
||||
|
||||
## Что меняет тема
|
||||
|
||||
- canvas/background;
|
||||
- основной и вторичный текст;
|
||||
- accent и автоматически вычисляемый on-accent;
|
||||
- плотность/цвет glass surfaces;
|
||||
- field/control surfaces;
|
||||
- overlay;
|
||||
- shadows и material rim;
|
||||
- semantic success/warning/danger tones при необходимости.
|
||||
|
||||
## Что тема не меняет
|
||||
|
||||
- высоту контролов;
|
||||
- радиусы по классам компонентов;
|
||||
- положение логотипа;
|
||||
- механику окна и dropdown;
|
||||
- DOM anatomy;
|
||||
- keyboard behavior;
|
||||
- расположение footer actions;
|
||||
- API React/DOM компонентов.
|
||||
|
||||
## Presets
|
||||
|
||||
В baseline включены `dark` и `light`. Это не два отдельных продукта, а примеры одного контракта:
|
||||
|
||||
- dark соответствует текущей основной семье Launcher/Hub/CMS;
|
||||
- light фиксирует возможность светлой схемы, наблюдаемой в SEO;
|
||||
- конкретный продукт может поверх preset задать свой accent и surface variables.
|
||||
|
||||
## Accent
|
||||
|
||||
Accent задаётся RGB tuple. On-accent вычисляется по luminance и не должен вручную прописываться белым или чёрным в отдельном компоненте.
|
||||
|
||||
Semantic status не обязан совпадать с accent. Например, ошибка остаётся danger, даже если приложение использует красный брендовый accent.
|
||||
|
||||
## Область применения
|
||||
|
||||
Тема может быть установлена:
|
||||
|
||||
- на корневом `html/body` для всего приложения;
|
||||
- на контейнере embedded-модуля;
|
||||
- на catalog preview для сравнения схем.
|
||||
|
||||
Portal-компоненты рендерятся в `document.body`, поэтому приложение должно либо задавать тему на `document.documentElement`, либо передавать согласованные переменные на body. Локальная тема глубоко внутри React subtree не сможет автоматически охватить portal без отдельного theme portal root.
|
||||
|
||||
## Совместимость
|
||||
|
||||
Существующие `--nodedc-*` имена сохранены намеренно. Это уменьшает стоимость будущего подключения Launcher, Engine, Task Manager и BIM Viewer.
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# Окна, dropdown и слои
|
||||
|
||||
## Зачем отдельный контракт
|
||||
|
||||
В NODE.DC повторяется одна механика: пользователь нажимает trigger, открывается окно или floating surface, взаимодействует и закрывает его. Если каждое приложение реализует outside click, Escape, portal и z-index отдельно, одинаковые окна начинают вести себя по-разному.
|
||||
|
||||
## Layer scale
|
||||
|
||||
- base: `0`;
|
||||
- panel: `100`;
|
||||
- header: `400`;
|
||||
- overlay/window: `800`;
|
||||
- dropdown/popover: `900`;
|
||||
- toast: `950`.
|
||||
|
||||
Произвольные значения вроде `30030` допускаются только во временном legacy adapter. Новый код использует scale.
|
||||
|
||||
## Dropdown/popover
|
||||
|
||||
Обязательное поведение:
|
||||
|
||||
1. trigger сообщает `aria-expanded`;
|
||||
2. surface переносится в body;
|
||||
3. координаты рассчитываются как fixed;
|
||||
4. горизонтальная позиция ограничивается viewport;
|
||||
5. при недостатке места surface меняет bottom/top placement;
|
||||
6. scroll/resize пересчитывают позицию;
|
||||
7. outside pointer и Escape закрывают surface;
|
||||
8. после Escape фокус возвращается на trigger;
|
||||
9. открытие другого dropdown закрывает предыдущий.
|
||||
|
||||
Inline absolute dropdown внутри card/sidebar/sticky container является дефектом.
|
||||
|
||||
## Modal window
|
||||
|
||||
Обязательное поведение:
|
||||
|
||||
1. window рендерится в body;
|
||||
2. при открытии запоминается текущий focus;
|
||||
3. focus переносится на первый интерактивный элемент или dialog;
|
||||
4. Tab не выходит за пределы dialog;
|
||||
5. Escape закрывает окно, если операция не блокирует закрытие;
|
||||
6. backdrop закрывает окно только при клике именно на backdrop;
|
||||
7. body scroll блокируется;
|
||||
8. после закрытия focus возвращается на предыдущий trigger.
|
||||
|
||||
## Side window / inspector
|
||||
|
||||
Side window использует ту же механику, но placement `end` и ограниченную ширину. Это канон для:
|
||||
|
||||
- настроек окружения;
|
||||
- инспектора агентной ноды;
|
||||
- будущих detail/settings panels;
|
||||
- CMS/SEO side editors, если они не требуют отдельного полноэкранного workflow.
|
||||
|
||||
## Управление состоянием
|
||||
|
||||
Библиотека не создаёт глобальный store окон. Приложение владеет тем, какое окно открыто и какие данные в нём загружены. Библиотека владеет одинаковым поведением самого слоя.
|
||||
|
||||
Если приложению позже потребуется stack нескольких modeless-окон, это будет отдельный пакетный контракт, а не расширение локального z-index.
|
||||
|
||||
Reference in New Issue
Block a user