mirror of
https://github.com/dartdavros/chatballs.git
synced 2026-10-05 09:14:58 +03:00
♻️ refactor(ui): один селект и один скин полей на всё приложение
Карточка агента рисовала свои выпадающие селекты: «Группа» и «Провайдер» жили на собственной вёрстке и своём CSS, а рядом, в модалке создания агента, стоял общий SelectField. Два стандарта одного элемента успели разойтись в высоте, подложке, размере текста, подписи и фокусе. Общий SelectField научился тому, из-за чего карточка писала свой: метке перед значением (точка группы), невалидному состоянию и режиму без прав правки, где значение показывается текстом. Форм у него две — обычный селект и бокс, который держит рамку, когда внутрь нужно поставить точку или текст вместо контрола. Карточка перешла на него, локальный скин удалён, кольцо фокуса кадра G2 сохранено. Геометрия базлайна v2 (поле 36px на подложке n-10, текст 13.5px, подпись 12.5/600) стала дефолтом shared/form-controls.css. До этого дефолтом были 38px на surface-card, а базлайн добирали поверх профиль, настройки и модалка создания агента — вместе с карточкой четыре копии одного поля. Копии сняты, мобильные 44px тоже общие. Заодно шеврон встал по центру поля: его отступ считался от прежних 38px. Справочник дизайн-системы обновлён следом, чтобы не описывать геометрию, которой в приложении больше нет. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
54c35e281a
commit
eca0e52667
8 files changed
+228
-161
No files matched your search
@@ -5,6 +5,7 @@ import { LANGUAGES } from "@chatballs/shared";
|
||||
|
||||
import { api, ApiError } from "../../api/client";
|
||||
import { ChannelGlyph } from "../../shared/badges";
|
||||
import { SelectField } from "../../shared/form-controls";
|
||||
import { Icon } from "../../shared/icons";
|
||||
import { EmptyState, ErrorScreen, LoadingState } from "../../shared/ui";
|
||||
import { BackLink, Button, CopyButton } from "../../shared/ui-controls";
|
||||
@@ -509,25 +510,16 @@ function AssignmentCard({ card, groups, canManage, busy, apply }: {
|
||||
? <input value={name} onChange={(event) => setName(event.target.value)} onBlur={() => { if (dirty) void apply({ name: name.trim() }); }} />
|
||||
: <span className="agent-field-static">{card.name}</span>}
|
||||
</label>
|
||||
<label className="agent-field is-select">
|
||||
<span>{t("common.group")}</span>
|
||||
<span className="agent-field-control">
|
||||
<i className="agent-group-dot" style={{ background: card.groupId === null ? "var(--n-5)" : groupColorOf(card.groupId, card.groupColor) }} />
|
||||
{canManage ? (
|
||||
<select
|
||||
disabled={busy}
|
||||
value={card.groupId === null ? "" : String(card.groupId)}
|
||||
onChange={(event) => void apply({ groupId: event.target.value ? Number(event.target.value) : null })}
|
||||
>
|
||||
<option value="">{t("common.no_group")}</option>
|
||||
{groups.map((group) => <option value={String(group.id)} key={group.id}>{group.name}</option>)}
|
||||
</select>
|
||||
) : (
|
||||
<span className="agent-field-static">{card.groupName ?? t("common.no_group")}</span>
|
||||
)}
|
||||
<Icon name="chevron" size={14} strokeWidth={2.2} />
|
||||
</span>
|
||||
</label>
|
||||
<SelectField
|
||||
adornment={<i className="agent-group-dot" style={{ background: card.groupId === null ? "var(--n-5)" : groupColorOf(card.groupId, card.groupColor) }} />}
|
||||
disabled={busy}
|
||||
label={t("common.group")}
|
||||
readOnly={!canManage}
|
||||
readOnlyText={card.groupName ?? t("common.no_group")}
|
||||
value={card.groupId === null ? "" : String(card.groupId)}
|
||||
onChange={(next) => void apply({ groupId: next ? Number(next) : null })}
|
||||
options={[["", t("common.no_group")], ...groups.map((group) => [String(group.id), group.name] as [string, string])]}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
@@ -558,24 +550,16 @@ function ModelCard({ card, providers, canManage, busy, apply }: {
|
||||
<h3>{t("common.model")}</h3>
|
||||
<p>{t("ai.provider_key_lives_under_settings")}</p>
|
||||
<div className="agent-side-fields">
|
||||
<label className={`agent-field is-select ${missingProvider ? "is-invalid" : ""}`}>
|
||||
<span>{t("ai.provider")}</span>
|
||||
<span className="agent-field-control">
|
||||
{canManage ? (
|
||||
<select
|
||||
disabled={busy}
|
||||
value={card.providerIntegrationId ? String(card.providerIntegrationId) : ""}
|
||||
onChange={(event) => void apply({ providerIntegrationId: event.target.value ? Number(event.target.value) : null })}
|
||||
>
|
||||
<option value="">{t("ai.not_selected")}</option>
|
||||
{providers.map((item) => <option value={String(item.id)} key={item.id}>{item.name}</option>)}
|
||||
</select>
|
||||
) : (
|
||||
<span className="agent-field-static">{providerName || t("ai.not_selected")}</span>
|
||||
)}
|
||||
<Icon name="chevron" size={14} strokeWidth={2.2} />
|
||||
</span>
|
||||
</label>
|
||||
<SelectField
|
||||
disabled={busy}
|
||||
invalid={missingProvider}
|
||||
label={t("ai.provider")}
|
||||
readOnly={!canManage}
|
||||
readOnlyText={providerName || t("ai.not_selected")}
|
||||
value={card.providerIntegrationId ? String(card.providerIntegrationId) : ""}
|
||||
onChange={(next) => void apply({ providerIntegrationId: next ? Number(next) : null })}
|
||||
options={[["", t("ai.not_selected")], ...providers.map((item) => [String(item.id), item.name] as [string, string])]}
|
||||
/>
|
||||
<label className="agent-field is-model">
|
||||
<span>{t("common.model")}</span>
|
||||
<span className="agent-field-control">
|
||||
|
||||
@@ -352,23 +352,6 @@
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.agent-create-modal .readonly-field span,
|
||||
.agent-knowledge-modal .readonly-field span {
|
||||
margin-bottom: 6px;
|
||||
color: var(--n-3);
|
||||
font-size: 12.5px;
|
||||
font-weight: 600;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.agent-create-modal .readonly-field input,
|
||||
.agent-create-modal .select-like select {
|
||||
height: 36px;
|
||||
background: var(--n-10);
|
||||
font-size: 13.5px;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.agent-create-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
@@ -800,7 +783,6 @@
|
||||
|
||||
.agent-field textarea,
|
||||
.agent-field input,
|
||||
.agent-field select,
|
||||
.agent-field .agent-field-static {
|
||||
width: 100%;
|
||||
padding: 0 12px;
|
||||
@@ -814,7 +796,6 @@
|
||||
}
|
||||
|
||||
.agent-field input,
|
||||
.agent-field select,
|
||||
.agent-field > .agent-field-static {
|
||||
height: 36px;
|
||||
line-height: 34px;
|
||||
@@ -859,7 +840,6 @@
|
||||
/* Фокус поля — как в кадре G2: акцентная рамка, кольцо 3px и подложка карточки. */
|
||||
.agent-field textarea:focus,
|
||||
.agent-field input:focus,
|
||||
.agent-field select:focus,
|
||||
.agent-create-modal .form-field.is-editable input:focus,
|
||||
.agent-create-modal .select-like select:focus {
|
||||
border-color: var(--primary);
|
||||
@@ -867,7 +847,9 @@
|
||||
box-shadow: 0 0 0 3px var(--primary-bg);
|
||||
}
|
||||
|
||||
.agent-field-control:focus-within {
|
||||
/* Тот же фокус для общего селекта карточки: с боксом (группа) и без (провайдер). */
|
||||
.agent-side-fields .select-like select:focus,
|
||||
.agent-side-fields .select-box:focus-within {
|
||||
border-color: var(--primary);
|
||||
background: var(--surface-card);
|
||||
box-shadow: 0 0 0 3px var(--primary-bg);
|
||||
@@ -1028,23 +1010,6 @@
|
||||
font-size: 13.5px;
|
||||
}
|
||||
|
||||
.agent-field.is-invalid .agent-field-control {
|
||||
border-color: var(--error-border);
|
||||
color: var(--error-text);
|
||||
}
|
||||
|
||||
.agent-field-control select {
|
||||
flex: 1;
|
||||
height: 34px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
font-size: 13.5px;
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.agent-field-control .agent-field-static {
|
||||
flex: 1;
|
||||
height: auto;
|
||||
|
||||
@@ -154,23 +154,8 @@
|
||||
color: var(--error-text);
|
||||
}
|
||||
|
||||
/* Поля профиля: подпись 12.5/600 --n-3, поле 36px на подложке --n-10. */
|
||||
|
||||
.profile-page .readonly-field span {
|
||||
margin-bottom: 6px;
|
||||
color: var(--n-3);
|
||||
font-size: 12.5px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.profile-page .readonly-field input,
|
||||
.profile-page .form-field.is-editable input {
|
||||
height: 36px;
|
||||
color: var(--n-1);
|
||||
background: var(--n-10);
|
||||
border-color: var(--n-7);
|
||||
font-size: 13.5px;
|
||||
}
|
||||
/* Поле профиля моноширинным набором (идентификаторы) — на полступени мельче.
|
||||
Сам скин поля общий, из shared/form-controls.css. */
|
||||
|
||||
.profile-page .readonly-field input.mono {
|
||||
font-size: 13px;
|
||||
@@ -866,11 +851,6 @@
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.profile-page.is-mobile .readonly-field input {
|
||||
height: 44px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.profile-page.is-mobile .readonly-field input.mono {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
@@ -229,33 +229,14 @@
|
||||
font-size: 12.5px;
|
||||
}
|
||||
|
||||
/* Карточка и поля раздела: подпись 12.5/600 --n-3, поле 36px на подложке
|
||||
--n-10 (кадры N1/N6). */
|
||||
/* Карточка раздела (кадры N1/N6). Поля — общий скин из
|
||||
shared/form-controls.css, своей копии у раздела больше нет. */
|
||||
|
||||
.settings-content .administration-card {
|
||||
padding: 22px 24px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.settings-content .readonly-field span {
|
||||
margin-bottom: 6px;
|
||||
color: var(--n-3);
|
||||
font-size: 12.5px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.settings-content .readonly-field input,
|
||||
.settings-content .readonly-field select,
|
||||
.settings-content .select-like input,
|
||||
.settings-content .select-like select,
|
||||
.settings-content .form-field.is-editable input {
|
||||
height: 36px;
|
||||
color: var(--n-1);
|
||||
background: var(--n-10);
|
||||
border-color: var(--n-7);
|
||||
font-size: 13.5px;
|
||||
}
|
||||
|
||||
/* Кнопки раздела: primary и вторичная 34px радиусом 8 (кадры N1–N7). */
|
||||
|
||||
.settings-content .primary-button,
|
||||
@@ -913,9 +894,6 @@
|
||||
padding-inline: 14px;
|
||||
}
|
||||
|
||||
.settings-content .readonly-field input,
|
||||
.settings-content .readonly-field select,
|
||||
.settings-content .select-like select,
|
||||
.settings-content .settings-group-create input {
|
||||
height: 44px;
|
||||
font-size: 15px;
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/* Поля формы приложения. Геометрия — дизайн-базлайн v2 (кадры G3, N1/N6, P1):
|
||||
подпись 12.5/600 цветом --n-3, поле 36px на подложке --n-10 рамкой --n-7,
|
||||
текст 13.5px. Скин был общим только на бумаге: здесь стояли 38px на
|
||||
--surface-card, а базлайн добирали поверх в профиле, настройках и модалке
|
||||
создания агента — три копии одного поля. Копий больше нет, скин один и
|
||||
живёт здесь. line-height задан явно: внутри antd Modal свой reset. */
|
||||
.readonly-field {
|
||||
display: block;
|
||||
}
|
||||
@@ -6,24 +12,26 @@
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
.readonly-field span {
|
||||
.readonly-field > span:first-child {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
color: var(--n-4);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--n-3);
|
||||
font-size: 12.5px;
|
||||
font-weight: 600;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.readonly-field input {
|
||||
width: 100%;
|
||||
height: 38px;
|
||||
height: 36px;
|
||||
padding: 0 12px;
|
||||
color: var(--n-2);
|
||||
background: var(--surface-card);
|
||||
color: var(--n-1);
|
||||
background: var(--n-10);
|
||||
border: 1px solid var(--n-7);
|
||||
border-radius: 8px;
|
||||
outline: none;
|
||||
font-size: 13px;
|
||||
font-size: 13.5px;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.form-field.is-editable:focus-within input,
|
||||
@@ -31,7 +39,7 @@
|
||||
.form-field.is-editable:focus-within textarea,
|
||||
.form-field.is-editable textarea:focus,
|
||||
.select-like:focus-within select,
|
||||
.select-like select:focus {
|
||||
.select-like .select-box:focus-within {
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
@@ -57,45 +65,102 @@
|
||||
.form-field.is-readonly input,
|
||||
.readonly-field input:disabled {
|
||||
color: var(--n-4);
|
||||
background: var(--n-10);
|
||||
border-color: var(--n-8);
|
||||
}
|
||||
|
||||
.form-field.is-editable input {
|
||||
background: var(--surface-card);
|
||||
}
|
||||
|
||||
.select-like {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.select-like input,
|
||||
.select-like select {
|
||||
width: 100%;
|
||||
height: 38px;
|
||||
padding-right: 32px;
|
||||
height: 36px;
|
||||
padding: 0 32px 0 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.select-like select {
|
||||
padding-left: 12px;
|
||||
color: var(--n-2);
|
||||
background: var(--surface-card);
|
||||
color: var(--n-1);
|
||||
background: var(--n-10);
|
||||
border: 1px solid var(--n-7);
|
||||
border-radius: 8px;
|
||||
outline: none;
|
||||
appearance: none;
|
||||
font-size: 13px;
|
||||
font-size: 13.5px;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.select-like svg {
|
||||
/* Шеврон по центру поля: 36px бокса минус 14px иконки. Отступ справа — те же
|
||||
12px внутреннего поля плюс 1px рамки, что и у шеврона внутри `.select-box`. */
|
||||
.select-like > svg {
|
||||
position: absolute;
|
||||
right: 11px;
|
||||
bottom: 12px;
|
||||
right: 13px;
|
||||
bottom: 11px;
|
||||
color: var(--n-4);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Бокс селекта — для метки перед значением (точка группы на карточке агента) и
|
||||
для режима без прав правки. Рамку держит бокс, селект внутри без своей. */
|
||||
.select-like .select-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
height: 36px;
|
||||
padding: 0 12px;
|
||||
color: var(--n-1);
|
||||
background: var(--n-10);
|
||||
border: 1px solid var(--n-7);
|
||||
border-radius: 8px;
|
||||
font-size: 13.5px;
|
||||
}
|
||||
|
||||
.select-like .select-box select {
|
||||
flex: 1;
|
||||
height: 34px;
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.select-like .select-box > svg {
|
||||
flex: none;
|
||||
color: var(--n-4);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.select-like .select-value {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.select-like.is-invalid select,
|
||||
.select-like.is-invalid .select-box {
|
||||
color: var(--error-text);
|
||||
border-color: var(--error-border);
|
||||
}
|
||||
|
||||
/* Узкий экран (мобильные кадры профиля и настроек): поле под палец — 44px и
|
||||
текст 15px. Это тоже лежало копиями в профиле и настройках. */
|
||||
@media (max-width: 900px) {
|
||||
.readonly-field input,
|
||||
.select-like select,
|
||||
.select-like .select-box {
|
||||
height: 44px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.select-like .select-box select {
|
||||
height: 42px;
|
||||
}
|
||||
|
||||
.select-like > svg {
|
||||
bottom: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Единый переключатель приложения. Раньше в проекте было четыре его копии
|
||||
(.ui-switch, .totp-switch, .detail-totp-switch, .channel-switch) — остался
|
||||
один скин, размер при необходимости задаётся модификатором. */
|
||||
|
||||
@@ -44,14 +44,42 @@ export function FormField({
|
||||
);
|
||||
}
|
||||
|
||||
export function SelectField({ disabled = false, label, value, onChange, options }: { disabled?: boolean; label: string; value: string; onChange: (value: string) => void; options: Array<[string, string]> }) {
|
||||
return (
|
||||
<label className="readonly-field select-like">
|
||||
<span>{label}</span>
|
||||
type SelectFieldProps = {
|
||||
/** Метка перед значением: цветная точка группы на карточке агента. */
|
||||
adornment?: ReactNode;
|
||||
disabled?: boolean;
|
||||
invalid?: boolean;
|
||||
label: string;
|
||||
onChange: (value: string) => void;
|
||||
options: Array<[string, string]>;
|
||||
/** Нет прав на правку: вместо селекта значение показывается текстом. */
|
||||
readOnly?: boolean;
|
||||
/** Текст для режима без прав, когда значения нет в списке: список вариантов
|
||||
* грузят только тем, кто может править. */
|
||||
readOnlyText?: string;
|
||||
value: string;
|
||||
wide?: boolean;
|
||||
};
|
||||
|
||||
/** Селект приложения — один на всё: подпись, бокс поля и шеврон. Точку перед
|
||||
* значением и режим без прав правки держит `.select-box`: рамка тогда на нём,
|
||||
* а сам селект внутри без рамки и фона. */
|
||||
export function SelectField({ adornment, disabled = false, invalid = false, label, onChange, options, readOnly = false, readOnlyText, value, wide = false }: SelectFieldProps) {
|
||||
const className = ["readonly-field", "select-like", invalid ? "is-invalid" : "", readOnly ? "is-readonly" : "", wide ? "wide" : ""].filter(Boolean).join(" ");
|
||||
const control = readOnly
|
||||
? <span className="select-value">{readOnlyText ?? options.find(([optionValue]) => optionValue === value)?.[1] ?? ""}</span>
|
||||
: (
|
||||
<select disabled={disabled} value={value} onChange={(event) => onChange(event.target.value)}>
|
||||
{options.map(([optionValue, labelText]) => <option value={optionValue} key={optionValue}>{labelText}</option>)}
|
||||
</select>
|
||||
<Icon name="chevron" size={14} />
|
||||
);
|
||||
const chevron = <Icon name="chevron" size={14} strokeWidth={2.2} />;
|
||||
const boxed = Boolean(adornment) || readOnly;
|
||||
|
||||
return (
|
||||
<label className={className}>
|
||||
<span>{label}</span>
|
||||
{boxed ? <span className="select-box">{adornment}{control}{chevron}</span> : <>{control}{chevron}</>}
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
.readonly-field > span {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
color: var(--text-tertiary);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--text-secondary);
|
||||
font-size: 12.5px;
|
||||
font-weight: 600;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.readonly-field input,
|
||||
@@ -23,13 +24,22 @@
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.readonly-field input,
|
||||
.select-like select,
|
||||
.search-control {
|
||||
height: 38px;
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
/* Поле формы — дизайн-базлайн v2 (кадры G3, N1/N6, P1): 36px на подложке
|
||||
--n-10, текст 13.5px. Один скин на всё приложение. */
|
||||
.readonly-field input,
|
||||
.select-like select {
|
||||
height: 36px;
|
||||
padding: 0 12px;
|
||||
color: var(--n-1);
|
||||
background: var(--n-10);
|
||||
font-size: 13.5px;
|
||||
}
|
||||
|
||||
.readonly-field textarea {
|
||||
min-height: 92px;
|
||||
padding: 10px 12px;
|
||||
@@ -40,6 +50,7 @@
|
||||
.form-field.is-editable:focus-within input,
|
||||
.form-field.is-editable:focus-within textarea,
|
||||
.select-like:focus-within select,
|
||||
.select-like .select-box:focus-within,
|
||||
.search-control:focus-within {
|
||||
border-color: var(--primary);
|
||||
}
|
||||
@@ -71,14 +82,67 @@
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
.select-like .select-chevron {
|
||||
.select-like > .select-chevron {
|
||||
position: absolute;
|
||||
right: 11px;
|
||||
bottom: 12px;
|
||||
right: 13px;
|
||||
bottom: 11px;
|
||||
color: var(--text-tertiary);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Бокс селекта — для метки перед значением (точка группы на карточке агента) и
|
||||
для режима без прав правки. Рамку держит бокс, селект внутри без своей. */
|
||||
.select-like .select-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
height: 36px;
|
||||
padding: 0 12px;
|
||||
color: var(--n-1);
|
||||
background: var(--n-10);
|
||||
border: 1px solid var(--border-input);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: 13.5px;
|
||||
}
|
||||
|
||||
.select-like .select-box select {
|
||||
flex: 1;
|
||||
height: 34px;
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.select-like .select-box > .select-chevron {
|
||||
flex: none;
|
||||
color: var(--text-tertiary);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Пример метки перед значением: цветная точка группы. */
|
||||
.select-like .select-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
flex: none;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.select-like .select-value {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.select-like.is-invalid select,
|
||||
.select-like.is-invalid .select-box {
|
||||
color: var(--error-text);
|
||||
border-color: var(--error-border);
|
||||
}
|
||||
|
||||
.search-control {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -95,6 +95,9 @@
|
||||
<div class="ds-specimen-header"><strong>Select & textarea</strong><code>SelectField · TextAreaField</code></div>
|
||||
<div class="ds-specimen-body is-column">
|
||||
<label class="readonly-field select-like"><span>Статус</span><select aria-label="Статус"><option>Активен</option><option>Неактивен</option></select><svg class="icon select-chevron"><use href="#i-chevron"></use></svg></label>
|
||||
<label class="readonly-field select-like"><span>Группа</span><span class="select-box"><i class="select-dot" style="background:#4096ff;"></i><select aria-label="Группа"><option>Поддержка</option><option>Продажи</option></select><svg class="icon select-chevron"><use href="#i-chevron"></use></svg></span></label>
|
||||
<label class="readonly-field select-like is-invalid"><span>Провайдер</span><select aria-label="Провайдер"><option>Не выбран</option></select><svg class="icon select-chevron"><use href="#i-chevron"></use></svg></label>
|
||||
<label class="readonly-field select-like is-readonly"><span>Группа без прав правки</span><span class="select-box"><span class="select-value">Без группы</span><svg class="icon select-chevron"><use href="#i-chevron"></use></svg></span></label>
|
||||
<label class="readonly-field form-field is-editable"><span>Описание</span><textarea aria-label="Описание">Канал обработки обращений клиентов.</textarea></label>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
Reference in new issue
Block a user