From 1fc39dfebc5688336ebb6f5680b170f770f8e8f2 Mon Sep 17 00:00:00 2001 From: DCCONSTRUCTIONS Date: Fri, 10 Jul 2026 22:02:32 +0300 Subject: [PATCH] Align selects ranges and modal catalog --- apps/catalog/src/CatalogApp.tsx | 92 ++++++++++++++------------ apps/catalog/src/styles.css | 65 +++++++++++++++++- docs/COMPONENTS.md | 6 +- docs/THEMING.md | 2 +- packages/tokens/themes.css | 2 + packages/ui-core/styles.css | 16 ++++- packages/ui-react/src/RangeControl.tsx | 9 +-- packages/ui-react/src/Select.tsx | 6 +- 8 files changed, 138 insertions(+), 60 deletions(-) diff --git a/apps/catalog/src/CatalogApp.tsx b/apps/catalog/src/CatalogApp.tsx index 777cfa3..d1a9924 100644 --- a/apps/catalog/src/CatalogApp.tsx +++ b/apps/catalog/src/CatalogApp.tsx @@ -384,15 +384,11 @@ export function CatalogApp() { updateMaterial({ panelHex })} /> - - `${value}%`} onChange={(panelOpacity) => updateMaterial({ panelOpacity })} /> - + `${value}%`} onChange={(panelOpacity) => updateMaterial({ panelOpacity })} /> updateMaterial({ fieldHex })} /> - - `${value}%`} onChange={(fieldOpacity) => updateMaterial({ fieldOpacity })} /> - + `${value}%`} onChange={(fieldOpacity) => updateMaterial({ fieldOpacity })} /> ), }, @@ -609,48 +605,58 @@ export function CatalogApp() { ); case "modals": return ( -
- -

Поля переходят в одну колонку на узком viewport; footer остаётся видимым, прокручивается только тело окна.

- -
- -

Destructive-цвет появляется у подтверждения, а не окрашивает всё окно заранее.

- -
- -

Участники, роли, приглашение и immutable-владелец. API доступа остаётся в Engine.

- -
- -

Read-only ссылка, copy-state и единая механика focus/Escape/backdrop.

- -
- -

Короткое окно действий над выбранным размером или объектом без отдельной modal-системы.

- -
- -

Большое прокручиваемое data-окно; BIM-таблица остаётся доменной композицией внутри общего Window.

- -
- -

Рабочая detail-modal с контентом и вложениями, которая может расширяться без смены механики слоя.

- -
+
+
+
ОбщиеLauncher / Engine
+
+ +

Одна колонка на узком viewport; footer остаётся видимым.

+ +
+ +

Destructive-цвет появляется только у подтверждения.

+ +
+ +

Участники, роли, приглашение и immutable-владелец.

+ +
+
+
+
+
Работа с моделямиBIM Viewer
+
+ +

Read-only ссылка и copy-state.

+ +
+ +

Действия над выбранным размером или объектом.

+ +
+ +

Прокручиваемая история BIM-версий.

+ +
+ +

Контент, вложения и detail-состояние.

+ +
+
+
); case "icons": return (
- +
- - - - + + + +
-

Иконка наследует `currentColor`; фон, размер кнопки и active-state задаёт компонент поверхности, а не SVG. Glyph оптически уменьшен на 18% без изменения hit target.

+

Все glyph-иконки используют один размер 16 px и один stroke 1.6; surface меняет только фон и hit target.

{iconGroups.map((group) => (
@@ -658,7 +664,7 @@ export function CatalogApp() {
{group.icons.map((name) => (
- + {iconLabels[name]}{name}
))} @@ -699,7 +705,7 @@ export function CatalogApp() { } right={ - + Профиль diff --git a/apps/catalog/src/styles.css b/apps/catalog/src/styles.css index c3c5ddc..e2e99f1 100644 --- a/apps/catalog/src/styles.css +++ b/apps/catalog/src/styles.css @@ -215,6 +215,60 @@ textarea { gap: 0.75rem; } +.catalog-modal-groups { + display: grid; + gap: 1.25rem; +} + +.catalog-modal-group { + display: grid; + gap: 0.65rem; +} + +.catalog-modal-group > header { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 1rem; + padding-inline: 0.15rem; +} + +.catalog-modal-group > header strong { + font-size: 0.9rem; +} + +.catalog-modal-group > header span { + color: var(--nodedc-text-muted); + font-size: 0.68rem; + text-transform: uppercase; +} + +.catalog-modal-grid { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 0.75rem; +} + +.nodedc-glass.catalog-modal-preview { + min-height: 10.5rem; + padding: 1rem; +} + +.catalog-modal-preview .catalog-preview__head { + margin-bottom: 0.7rem; +} + +.catalog-modal-preview .catalog-preview__body { + min-height: 0; + align-content: space-between; + gap: 0.75rem; +} + +.catalog-modal-preview .nodedc-button { + width: 100%; + padding-inline: 0.75rem; +} + .catalog-history-table { display: grid; min-width: 46rem; @@ -447,6 +501,10 @@ textarea { .catalog-icon-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } + + .catalog-modal-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } } @media (max-width: 900px) { @@ -492,7 +550,8 @@ textarea { } .catalog-preview__head, - .catalog-icon-group > header { + .catalog-icon-group > header, + .catalog-modal-group > header { align-items: flex-start; flex-direction: column; gap: 0.25rem; @@ -503,6 +562,10 @@ textarea { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .catalog-modal-grid { + grid-template-columns: 1fr; + } + .catalog-shell-diagram { grid-template-columns: 1fr 1.5fr; grid-template-rows: 3rem 7rem 7rem; diff --git a/docs/COMPONENTS.md b/docs/COMPONENTS.md index ed40e94..e6d959a 100644 --- a/docs/COMPONENTS.md +++ b/docs/COMPONENTS.md @@ -4,13 +4,13 @@ ## GlassSurface -Базовая непрозрачная поверхность для карточки, панели и большого окна. Варианты меняют плотность нейтральной поверхности, но не создают отдельный дизайн. Реальный glass-материал с blur и rim используется только у modal `Window`. +Базовая непрозрачная поверхность для карточки, панели и большого окна. Варианты меняют плотность нейтральной поверхности, но не создают отдельный дизайн. Реальный glass-материал с blur и rim используется только у floating-слоёв: modal `Window` и portal-dropdown/select. - `default` — обычная карточка/панель; - `strong` — dropdown, modal и поверхность над сложным фоном; - `soft` — вложенная или вторичная область. -Material rim допустим только как часть modal-стекла. Жёсткая цветная рамка, случайный browser outline или debug border не являются rim. +Material rim допустим только как часть floating glass. Жёсткая цветная рамка, случайный browser outline или debug border не являются rim. ## Button и IconButton @@ -61,7 +61,7 @@ Dropdown владеет floating-layer поведением: Select добавляет к Dropdown контролируемое значение, options и необязательный поиск. У него две канонические формы: -- `integrated` — единая pill-поверхность Hub/Launcher; +- `integrated` — единая pill-поверхность Hub/Launcher: label слева, chevron строго у правого края; - `split` — форма Engine с отдельной областью значения и отдельной кнопкой раскрытия `46 px`, между ними зазор `8 px`. Native select не используется как видимый runtime UI. Меню рендерится через portal; у Engine-варианта меню имеет радиус `20 px`, а строки — `14 px` и высоту `42 px`. diff --git a/docs/THEMING.md b/docs/THEMING.md index c771bfa..17edd76 100644 --- a/docs/THEMING.md +++ b/docs/THEMING.md @@ -38,7 +38,7 @@ NODE.DC-приложения отличаются цветовой схемой, Accent задаётся RGB tuple. On-accent вычисляется по luminance и не должен вручную прописываться белым или чёрным в отдельном компоненте. -Default accent текущего NODE.DC shell — `#ff2f92`. `ColorField` открывает единый portal-picker с saturation/value plane, hue slider, HEX и быстрыми цветами — системный `input[type=color]` не является runtime UI. Изменение accent сразу меняет Environment Settings, active actions, range fill, media file action и theme-aware window states без создания новой темы или fork компонентов. Blur, gloss и material rim не наследуются application panels: это материал только для modal `Window`. +Default accent текущего NODE.DC shell — `#ff2f92`. `ColorField` открывает единый portal-picker с saturation/value plane, hue slider, HEX и быстрыми цветами — системный `input[type=color]` не является runtime UI. Изменение accent сразу меняет Environment Settings, active actions, range fill, media file action и theme-aware window states без создания новой темы или fork компонентов. Blur, gloss и material rim не наследуются application panels: это материал только для floating modal и portal-dropdown/select. Semantic status не обязан совпадать с accent. Например, ошибка остаётся danger, даже если приложение использует красный брендовый accent. diff --git a/packages/tokens/themes.css b/packages/tokens/themes.css index 70213fe..9a474e1 100644 --- a/packages/tokens/themes.css +++ b/packages/tokens/themes.css @@ -57,6 +57,7 @@ --nodedc-primary-action-shadow: 0 12px 28px rgb(var(--nodedc-accent-rgb) / 0.2); --nodedc-modal-surface: linear-gradient(180deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.014)), rgba(7, 7, 10, 0.54); --nodedc-modal-shadow: 0 34px 120px rgba(0, 0, 0, 0.64), 0 10px 32px rgba(0, 0, 0, 0.32), inset 0 1px 0 rgba(255, 255, 255, 0.045); + --nodedc-floating-surface: linear-gradient(180deg, rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0.012)), rgba(10, 10, 13, 0.94); } [data-nodedc-theme="light"] { @@ -117,4 +118,5 @@ --nodedc-primary-action-shadow: none; --nodedc-modal-surface: linear-gradient(180deg, rgba(255, 255, 255, 0.88), rgba(255, 255, 255, 0.36)), rgba(255, 255, 255, 0.72); --nodedc-modal-shadow: 0 24px 72px rgba(0, 0, 0, 0.24), inset 0 1px 0 rgba(255, 255, 255, 0.8); + --nodedc-floating-surface: linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(255, 255, 255, 0.72)), rgba(255, 255, 255, 0.94); } diff --git a/packages/ui-core/styles.css b/packages/ui-core/styles.css index db60920..c19654a 100644 --- a/packages/ui-core/styles.css +++ b/packages/ui-core/styles.css @@ -1623,10 +1623,12 @@ textarea.nodedc-field__control { overflow: auto; border: 0; border-radius: var(--nodedc-radius-control); - background: var(--nodedc-glass-panel-surface-strong); + background: var(--nodedc-floating-surface); color: var(--nodedc-text-primary); padding: 0.45rem; box-shadow: var(--nodedc-glass-dropdown-shadow); + backdrop-filter: blur(var(--nodedc-blur-dropdown)) saturate(1.12); + -webkit-backdrop-filter: blur(var(--nodedc-blur-dropdown)) saturate(1.12); animation: nodedc-popover-in var(--nodedc-duration-fast) var(--nodedc-ease-standard); } @@ -1782,7 +1784,7 @@ textarea.nodedc-field__control { .nodedc-dropdown-surface.nodedc-select__menu { gap: 0.125rem; border-radius: var(--nodedc-radius-control); - background: var(--nodedc-field-bg); + background: var(--nodedc-floating-surface); padding: 0.4375rem; } @@ -1820,6 +1822,12 @@ textarea.nodedc-field__control { white-space: nowrap; } +.nodedc-select-trigger__icon { + display: inline-grid; + min-width: 0; + place-items: center; +} + .nodedc-select-trigger__chevron { width: 0.5rem; height: 0.5rem; @@ -2077,9 +2085,11 @@ textarea.nodedc-field__control { gap: 0.25rem; overflow-y: auto; border-radius: var(--nodedc-radius-panel); - background: var(--nodedc-glass-panel-surface-strong); + background: var(--nodedc-floating-surface); padding: 0.6rem; box-shadow: var(--nodedc-glass-dropdown-shadow); + backdrop-filter: blur(var(--nodedc-blur-dropdown)) saturate(1.12); + -webkit-backdrop-filter: blur(var(--nodedc-blur-dropdown)) saturate(1.12); } .nodedc-share-access__summary { diff --git a/packages/ui-react/src/RangeControl.tsx b/packages/ui-react/src/RangeControl.tsx index 6e0e7fb..275d844 100644 --- a/packages/ui-react/src/RangeControl.tsx +++ b/packages/ui-react/src/RangeControl.tsx @@ -4,8 +4,6 @@ import { Dropdown } from "./Dropdown.js"; export interface RangeControlProps extends Omit, "type" | "value" | "onChange"> { label: string; - /** Keeps the accessible name while letting a parent ControlRow render the visible label. */ - showLabel?: boolean; value: number; min: number; max: number; @@ -15,7 +13,6 @@ export interface RangeControlProps extends Omit + diff --git a/packages/ui-react/src/Select.tsx b/packages/ui-react/src/Select.tsx index 679d76a..dcb634b 100644 --- a/packages/ui-react/src/Select.tsx +++ b/packages/ui-react/src/Select.tsx @@ -75,8 +75,8 @@ export function Select({ if (variant === "split") { return (
-
-
+
+
{selected?.icon ? {selected.icon} : null} {selected?.label ?? "—"}
@@ -111,7 +111,7 @@ export function Select({ onClick={toggle} onKeyDown={handleKeyDown} > - {selected?.icon ?? null} + {selected?.icon ? {selected.icon} :