Align selects ranges and modal catalog
This commit is contained in:
parent
41e491191c
commit
1fc39dfebc
|
|
@ -384,15 +384,11 @@ export function CatalogApp() {
|
||||||
<ControlRow label="Плашки">
|
<ControlRow label="Плашки">
|
||||||
<ColorField label="Цвет плашек" value={material.panelHex} onChange={(panelHex) => updateMaterial({ panelHex })} />
|
<ColorField label="Цвет плашек" value={material.panelHex} onChange={(panelHex) => updateMaterial({ panelHex })} />
|
||||||
</ControlRow>
|
</ControlRow>
|
||||||
<ControlRow label="Прозрачность плашки">
|
<RangeControl label="Прозрачность плашки" value={material.panelOpacity} min={18} max={96} formatValue={(value) => `${value}%`} onChange={(panelOpacity) => updateMaterial({ panelOpacity })} />
|
||||||
<RangeControl label="Прозрачность плашки" showLabel={false} value={material.panelOpacity} min={18} max={96} formatValue={(value) => `${value}%`} onChange={(panelOpacity) => updateMaterial({ panelOpacity })} />
|
|
||||||
</ControlRow>
|
|
||||||
<ControlRow label="Поля ввода">
|
<ControlRow label="Поля ввода">
|
||||||
<ColorField label="Цвет полей ввода" value={material.fieldHex} onChange={(fieldHex) => updateMaterial({ fieldHex })} />
|
<ColorField label="Цвет полей ввода" value={material.fieldHex} onChange={(fieldHex) => updateMaterial({ fieldHex })} />
|
||||||
</ControlRow>
|
</ControlRow>
|
||||||
<ControlRow label="Прозрачность полей">
|
<RangeControl label="Прозрачность полей" value={material.fieldOpacity} min={18} max={100} formatValue={(value) => `${value}%`} onChange={(fieldOpacity) => updateMaterial({ fieldOpacity })} />
|
||||||
<RangeControl label="Прозрачность полей" showLabel={false} value={material.fieldOpacity} min={18} max={100} formatValue={(value) => `${value}%`} onChange={(fieldOpacity) => updateMaterial({ fieldOpacity })} />
|
|
||||||
</ControlRow>
|
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
@ -609,48 +605,58 @@ export function CatalogApp() {
|
||||||
);
|
);
|
||||||
case "modals":
|
case "modals":
|
||||||
return (
|
return (
|
||||||
<div className="catalog-grid">
|
<div className="catalog-modal-groups">
|
||||||
<Preview title="Создание" note="форма адаптируется по ширине">
|
<section className="catalog-modal-group">
|
||||||
<p className="catalog-preview__explanation">Поля переходят в одну колонку на узком viewport; footer остаётся видимым, прокручивается только тело окна.</p>
|
<header><strong>Общие</strong><span>Launcher / Engine</span></header>
|
||||||
|
<div className="catalog-modal-grid">
|
||||||
|
<Preview title="Создание" note="адаптивная форма" className="catalog-modal-preview">
|
||||||
|
<p className="catalog-preview__explanation">Одна колонка на узком viewport; footer остаётся видимым.</p>
|
||||||
<Button variant="primary" shape="pill" icon={<Icon name="plus" />} onClick={() => setCreateModalOpen(true)}>Создать проект</Button>
|
<Button variant="primary" shape="pill" icon={<Icon name="plus" />} onClick={() => setCreateModalOpen(true)}>Создать проект</Button>
|
||||||
</Preview>
|
</Preview>
|
||||||
<Preview title="Подтверждение удаления" note="async-safe">
|
<Preview title="Подтверждение" note="async-safe" className="catalog-modal-preview">
|
||||||
<p className="catalog-preview__explanation">Destructive-цвет появляется у подтверждения, а не окрашивает всё окно заранее.</p>
|
<p className="catalog-preview__explanation">Destructive-цвет появляется только у подтверждения.</p>
|
||||||
<Button variant="danger" shape="pill" icon={<Icon name="trash" />} onClick={() => setConfirmOpen(true)}>Проверить удаление</Button>
|
<Button variant="danger" shape="pill" icon={<Icon name="trash" />} onClick={() => setConfirmOpen(true)}>Проверить удаление</Button>
|
||||||
</Preview>
|
</Preview>
|
||||||
<Preview title="Workflow sharing" note="ENGINE / access modal">
|
<Preview title="Workflow sharing" note="ENGINE / access" className="catalog-modal-preview">
|
||||||
<p className="catalog-preview__explanation">Участники, роли, приглашение и immutable-владелец. API доступа остаётся в Engine.</p>
|
<p className="catalog-preview__explanation">Участники, роли, приглашение и immutable-владелец.</p>
|
||||||
<Button variant="primary" shape="pill" icon={<Icon name="users" />} onClick={() => setShareAccessOpen(true)}>Поделиться графом</Button>
|
<Button variant="primary" shape="pill" icon={<Icon name="users" />} onClick={() => setShareAccessOpen(true)}>Поделиться графом</Button>
|
||||||
</Preview>
|
</Preview>
|
||||||
<Preview title="Ссылка на модель" note="BIM / share link">
|
</div>
|
||||||
<p className="catalog-preview__explanation">Read-only ссылка, copy-state и единая механика focus/Escape/backdrop.</p>
|
</section>
|
||||||
|
<section className="catalog-modal-group">
|
||||||
|
<header><strong>Работа с моделями</strong><span>BIM Viewer</span></header>
|
||||||
|
<div className="catalog-modal-grid">
|
||||||
|
<Preview title="Ссылка на модель" note="share link" className="catalog-modal-preview">
|
||||||
|
<p className="catalog-preview__explanation">Read-only ссылка и copy-state.</p>
|
||||||
<Button shape="pill" icon={<Icon name="copy" />} onClick={() => setShareLinkOpen(true)}>Открыть ссылку</Button>
|
<Button shape="pill" icon={<Icon name="copy" />} onClick={() => setShareLinkOpen(true)}>Открыть ссылку</Button>
|
||||||
</Preview>
|
</Preview>
|
||||||
<Preview title="Контекстные действия" note="BIM / measurement & object">
|
<Preview title="Контекстные действия" note="measurement / object" className="catalog-modal-preview">
|
||||||
<p className="catalog-preview__explanation">Короткое окно действий над выбранным размером или объектом без отдельной modal-системы.</p>
|
<p className="catalog-preview__explanation">Действия над выбранным размером или объектом.</p>
|
||||||
<Button shape="pill" icon={<Icon name="sliders" />} onClick={() => setContextActionOpen(true)}>Действия размера</Button>
|
<Button shape="pill" icon={<Icon name="sliders" />} onClick={() => setContextActionOpen(true)}>Действия размера</Button>
|
||||||
</Preview>
|
</Preview>
|
||||||
<Preview title="История версий" note="BIM / large data modal">
|
<Preview title="История версий" note="large data" className="catalog-modal-preview">
|
||||||
<p className="catalog-preview__explanation">Большое прокручиваемое data-окно; BIM-таблица остаётся доменной композицией внутри общего Window.</p>
|
<p className="catalog-preview__explanation">Прокручиваемая история BIM-версий.</p>
|
||||||
<Button shape="pill" icon={<Icon name="list" />} onClick={() => setHistoryOpen(true)}>Открыть историю</Button>
|
<Button shape="pill" icon={<Icon name="list" />} onClick={() => setHistoryOpen(true)}>Открыть историю</Button>
|
||||||
</Preview>
|
</Preview>
|
||||||
<Preview title="Комментарий к модели" note="BIM / expandable detail">
|
<Preview title="Комментарий" note="expandable detail" className="catalog-modal-preview">
|
||||||
<p className="catalog-preview__explanation">Рабочая detail-modal с контентом и вложениями, которая может расширяться без смены механики слоя.</p>
|
<p className="catalog-preview__explanation">Контент, вложения и detail-состояние.</p>
|
||||||
<Button shape="pill" icon={<Icon name="clipboard" />} onClick={() => setDetailOpen(true)}>Открыть комментарий</Button>
|
<Button shape="pill" icon={<Icon name="clipboard" />} onClick={() => setDetailOpen(true)}>Открыть комментарий</Button>
|
||||||
</Preview>
|
</Preview>
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
case "icons":
|
case "icons":
|
||||||
return (
|
return (
|
||||||
<div className="catalog-icon-catalog">
|
<div className="catalog-icon-catalog">
|
||||||
<Preview title="Размеры и поверхности" note="16 / 18 / 20 px" className="catalog-preview--wide">
|
<Preview title="Размеры и поверхности" note="glyph 16 px" className="catalog-preview--wide">
|
||||||
<div className="catalog-window-actions-demo">
|
<div className="catalog-window-actions-demo">
|
||||||
<IconButton label="Добавить"><Icon name="plus" size={16} /></IconButton>
|
<IconButton label="Добавить"><Icon name="plus" /></IconButton>
|
||||||
<IconButton label="Обновить"><Icon name="refresh" size={18} /></IconButton>
|
<IconButton label="Обновить"><Icon name="refresh" /></IconButton>
|
||||||
<IconButton label="Развернуть"><Icon name="expand" size={20} /></IconButton>
|
<IconButton label="Развернуть"><Icon name="expand" /></IconButton>
|
||||||
<IconButton label="Закрыть"><Icon name="close" size={18} /></IconButton>
|
<IconButton label="Закрыть"><Icon name="close" /></IconButton>
|
||||||
</div>
|
</div>
|
||||||
<p className="catalog-preview__explanation">Иконка наследует `currentColor`; фон, размер кнопки и active-state задаёт компонент поверхности, а не SVG. Glyph оптически уменьшен на 18% без изменения hit target.</p>
|
<p className="catalog-preview__explanation">Все glyph-иконки используют один размер 16 px и один stroke 1.6; surface меняет только фон и hit target.</p>
|
||||||
</Preview>
|
</Preview>
|
||||||
{iconGroups.map((group) => (
|
{iconGroups.map((group) => (
|
||||||
<section className="catalog-icon-group" key={group.title}>
|
<section className="catalog-icon-group" key={group.title}>
|
||||||
|
|
@ -658,7 +664,7 @@ export function CatalogApp() {
|
||||||
<div className="catalog-icon-grid">
|
<div className="catalog-icon-grid">
|
||||||
{group.icons.map((name) => (
|
{group.icons.map((name) => (
|
||||||
<div className="catalog-icon-tile" key={name}>
|
<div className="catalog-icon-tile" key={name}>
|
||||||
<span className="catalog-icon-tile__surface"><Icon name={name} size={20} /></span>
|
<span className="catalog-icon-tile__surface"><Icon name={name} /></span>
|
||||||
<span><strong>{iconLabels[name]}</strong><code>{name}</code></span>
|
<span><strong>{iconLabels[name]}</strong><code>{name}</code></span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
@ -699,7 +705,7 @@ export function CatalogApp() {
|
||||||
}
|
}
|
||||||
right={
|
right={
|
||||||
<HeaderProfile>
|
<HeaderProfile>
|
||||||
<IconButton label="Уведомления"><Icon name="inbox" size={20} /></IconButton>
|
<IconButton label="Уведомления"><Icon name="inbox" /></IconButton>
|
||||||
<HeaderProfileButton>Профиль</HeaderProfileButton>
|
<HeaderProfileButton>Профиль</HeaderProfileButton>
|
||||||
<HeaderAvatar label="DC" />
|
<HeaderAvatar label="DC" />
|
||||||
</HeaderProfile>
|
</HeaderProfile>
|
||||||
|
|
|
||||||
|
|
@ -215,6 +215,60 @@ textarea {
|
||||||
gap: 0.75rem;
|
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 {
|
.catalog-history-table {
|
||||||
display: grid;
|
display: grid;
|
||||||
min-width: 46rem;
|
min-width: 46rem;
|
||||||
|
|
@ -447,6 +501,10 @@ textarea {
|
||||||
.catalog-icon-grid {
|
.catalog-icon-grid {
|
||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.catalog-modal-grid {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 900px) {
|
@media (max-width: 900px) {
|
||||||
|
|
@ -492,7 +550,8 @@ textarea {
|
||||||
}
|
}
|
||||||
|
|
||||||
.catalog-preview__head,
|
.catalog-preview__head,
|
||||||
.catalog-icon-group > header {
|
.catalog-icon-group > header,
|
||||||
|
.catalog-modal-group > header {
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.25rem;
|
gap: 0.25rem;
|
||||||
|
|
@ -503,6 +562,10 @@ textarea {
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.catalog-modal-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
.catalog-shell-diagram {
|
.catalog-shell-diagram {
|
||||||
grid-template-columns: 1fr 1.5fr;
|
grid-template-columns: 1fr 1.5fr;
|
||||||
grid-template-rows: 3rem 7rem 7rem;
|
grid-template-rows: 3rem 7rem 7rem;
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,13 @@
|
||||||
|
|
||||||
## GlassSurface
|
## GlassSurface
|
||||||
|
|
||||||
Базовая непрозрачная поверхность для карточки, панели и большого окна. Варианты меняют плотность нейтральной поверхности, но не создают отдельный дизайн. Реальный glass-материал с blur и rim используется только у modal `Window`.
|
Базовая непрозрачная поверхность для карточки, панели и большого окна. Варианты меняют плотность нейтральной поверхности, но не создают отдельный дизайн. Реальный glass-материал с blur и rim используется только у floating-слоёв: modal `Window` и portal-dropdown/select.
|
||||||
|
|
||||||
- `default` — обычная карточка/панель;
|
- `default` — обычная карточка/панель;
|
||||||
- `strong` — dropdown, modal и поверхность над сложным фоном;
|
- `strong` — dropdown, modal и поверхность над сложным фоном;
|
||||||
- `soft` — вложенная или вторичная область.
|
- `soft` — вложенная или вторичная область.
|
||||||
|
|
||||||
Material rim допустим только как часть modal-стекла. Жёсткая цветная рамка, случайный browser outline или debug border не являются rim.
|
Material rim допустим только как часть floating glass. Жёсткая цветная рамка, случайный browser outline или debug border не являются rim.
|
||||||
|
|
||||||
## Button и IconButton
|
## Button и IconButton
|
||||||
|
|
||||||
|
|
@ -61,7 +61,7 @@ Dropdown владеет floating-layer поведением:
|
||||||
|
|
||||||
Select добавляет к Dropdown контролируемое значение, options и необязательный поиск. У него две канонические формы:
|
Select добавляет к Dropdown контролируемое значение, options и необязательный поиск. У него две канонические формы:
|
||||||
|
|
||||||
- `integrated` — единая pill-поверхность Hub/Launcher;
|
- `integrated` — единая pill-поверхность Hub/Launcher: label слева, chevron строго у правого края;
|
||||||
- `split` — форма Engine с отдельной областью значения и отдельной кнопкой раскрытия `46 px`, между ними зазор `8 px`.
|
- `split` — форма Engine с отдельной областью значения и отдельной кнопкой раскрытия `46 px`, между ними зазор `8 px`.
|
||||||
|
|
||||||
Native select не используется как видимый runtime UI. Меню рендерится через portal; у Engine-варианта меню имеет радиус `20 px`, а строки — `14 px` и высоту `42 px`.
|
Native select не используется как видимый runtime UI. Меню рендерится через portal; у Engine-варианта меню имеет радиус `20 px`, а строки — `14 px` и высоту `42 px`.
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ NODE.DC-приложения отличаются цветовой схемой,
|
||||||
|
|
||||||
Accent задаётся RGB tuple. On-accent вычисляется по luminance и не должен вручную прописываться белым или чёрным в отдельном компоненте.
|
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.
|
Semantic status не обязан совпадать с accent. Например, ошибка остаётся danger, даже если приложение использует красный брендовый accent.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@
|
||||||
--nodedc-primary-action-shadow: 0 12px 28px rgb(var(--nodedc-accent-rgb) / 0.2);
|
--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-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-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"] {
|
[data-nodedc-theme="light"] {
|
||||||
|
|
@ -117,4 +118,5 @@
|
||||||
--nodedc-primary-action-shadow: none;
|
--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-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-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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1623,10 +1623,12 @@ textarea.nodedc-field__control {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: var(--nodedc-radius-control);
|
border-radius: var(--nodedc-radius-control);
|
||||||
background: var(--nodedc-glass-panel-surface-strong);
|
background: var(--nodedc-floating-surface);
|
||||||
color: var(--nodedc-text-primary);
|
color: var(--nodedc-text-primary);
|
||||||
padding: 0.45rem;
|
padding: 0.45rem;
|
||||||
box-shadow: var(--nodedc-glass-dropdown-shadow);
|
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);
|
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 {
|
.nodedc-dropdown-surface.nodedc-select__menu {
|
||||||
gap: 0.125rem;
|
gap: 0.125rem;
|
||||||
border-radius: var(--nodedc-radius-control);
|
border-radius: var(--nodedc-radius-control);
|
||||||
background: var(--nodedc-field-bg);
|
background: var(--nodedc-floating-surface);
|
||||||
padding: 0.4375rem;
|
padding: 0.4375rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1820,6 +1822,12 @@ textarea.nodedc-field__control {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nodedc-select-trigger__icon {
|
||||||
|
display: inline-grid;
|
||||||
|
min-width: 0;
|
||||||
|
place-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
.nodedc-select-trigger__chevron {
|
.nodedc-select-trigger__chevron {
|
||||||
width: 0.5rem;
|
width: 0.5rem;
|
||||||
height: 0.5rem;
|
height: 0.5rem;
|
||||||
|
|
@ -2077,9 +2085,11 @@ textarea.nodedc-field__control {
|
||||||
gap: 0.25rem;
|
gap: 0.25rem;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
border-radius: var(--nodedc-radius-panel);
|
border-radius: var(--nodedc-radius-panel);
|
||||||
background: var(--nodedc-glass-panel-surface-strong);
|
background: var(--nodedc-floating-surface);
|
||||||
padding: 0.6rem;
|
padding: 0.6rem;
|
||||||
box-shadow: var(--nodedc-glass-dropdown-shadow);
|
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 {
|
.nodedc-share-access__summary {
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,6 @@ import { Dropdown } from "./Dropdown.js";
|
||||||
|
|
||||||
export interface RangeControlProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "value" | "onChange"> {
|
export interface RangeControlProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "value" | "onChange"> {
|
||||||
label: string;
|
label: string;
|
||||||
/** Keeps the accessible name while letting a parent ControlRow render the visible label. */
|
|
||||||
showLabel?: boolean;
|
|
||||||
value: number;
|
value: number;
|
||||||
min: number;
|
min: number;
|
||||||
max: number;
|
max: number;
|
||||||
|
|
@ -15,7 +13,6 @@ export interface RangeControlProps extends Omit<InputHTMLAttributes<HTMLInputEle
|
||||||
|
|
||||||
export function RangeControl({
|
export function RangeControl({
|
||||||
label,
|
label,
|
||||||
showLabel = true,
|
|
||||||
value,
|
value,
|
||||||
min,
|
min,
|
||||||
max,
|
max,
|
||||||
|
|
@ -29,7 +26,7 @@ export function RangeControl({
|
||||||
const progress = Math.max(0, Math.min(100, ((value - min) / (safeMax - min)) * 100));
|
const progress = Math.max(0, Math.min(100, ((value - min) / (safeMax - min)) * 100));
|
||||||
const style = { "--nodedc-range-progress": `${progress}%` } as CSSProperties;
|
const style = { "--nodedc-range-progress": `${progress}%` } as CSSProperties;
|
||||||
return (
|
return (
|
||||||
<label className={cn("nodedc-range", className)} data-label-hidden={showLabel ? undefined : "true"} style={style}>
|
<label className={cn("nodedc-range", className)} style={style}>
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
value={value}
|
value={value}
|
||||||
|
|
@ -40,10 +37,10 @@ export function RangeControl({
|
||||||
onChange={(event) => onChange(Number(event.target.value))}
|
onChange={(event) => onChange(Number(event.target.value))}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
{showLabel ? <span className="nodedc-range__label">{label}</span> : null}
|
<span className="nodedc-range__label">{label}</span>
|
||||||
<span className="nodedc-range__value">{formatValue(value)}</span>
|
<span className="nodedc-range__value">{formatValue(value)}</span>
|
||||||
<span className="nodedc-range__fill-text" aria-hidden="true">
|
<span className="nodedc-range__fill-text" aria-hidden="true">
|
||||||
{showLabel ? <span className="nodedc-range__label">{label}</span> : null}
|
<span className="nodedc-range__label">{label}</span>
|
||||||
<span className="nodedc-range__value">{formatValue(value)}</span>
|
<span className="nodedc-range__value">{formatValue(value)}</span>
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
|
||||||
|
|
@ -75,8 +75,8 @@ export function Select<T extends string>({
|
||||||
if (variant === "split") {
|
if (variant === "split") {
|
||||||
return (
|
return (
|
||||||
<div className={cn("nodedc-select", triggerClassName)}>
|
<div className={cn("nodedc-select", triggerClassName)}>
|
||||||
<div className="nodedc-select__control">
|
<div ref={setTriggerRef} className="nodedc-select__control">
|
||||||
<div ref={setTriggerRef} className="nodedc-select__value">
|
<div className="nodedc-select__value">
|
||||||
{selected?.icon ? <span className="nodedc-select__value-icon">{selected.icon}</span> : null}
|
{selected?.icon ? <span className="nodedc-select__value-icon">{selected.icon}</span> : null}
|
||||||
<span>{selected?.label ?? "—"}</span>
|
<span>{selected?.label ?? "—"}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -111,7 +111,7 @@ export function Select<T extends string>({
|
||||||
onClick={toggle}
|
onClick={toggle}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
>
|
>
|
||||||
{selected?.icon ?? null}
|
{selected?.icon ? <span className="nodedc-select-trigger__icon">{selected.icon}</span> : <span className="nodedc-select-trigger__icon" aria-hidden="true" />}
|
||||||
<span className="nodedc-select-trigger__label">{selected?.label ?? "—"}</span>
|
<span className="nodedc-select-trigger__label">{selected?.label ?? "—"}</span>
|
||||||
<span className="nodedc-select-trigger__chevron" aria-hidden="true" />
|
<span className="nodedc-select-trigger__chevron" aria-hidden="true" />
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue