Files
chatballs/design/design-system/assets/component-forms.css
T
AndreyandClaude Opus 5 eca0e52667 ♻️ 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>
2026-09-13 03:59:10 +03:00

226 lines
4.3 KiB
CSS

.readonly-field {
display: block;
}
.readonly-field > span {
display: block;
margin-bottom: 6px;
color: var(--text-secondary);
font-size: 12.5px;
font-weight: 600;
line-height: normal;
}
.readonly-field input,
.readonly-field textarea,
.select-like select,
.search-control {
width: 100%;
color: var(--text-body);
background: #fff;
border: 1px solid var(--border-input);
border-radius: var(--radius-md);
outline: none;
font-size: 13px;
}
.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;
resize: vertical;
line-height: 1.5;
}
.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);
}
.form-field.is-readonly input,
.readonly-field input:disabled {
color: var(--text-tertiary);
background: var(--n-10);
border-color: var(--n-8);
}
.form-field.is-invalid input {
border-color: #ff7875;
}
.form-field-error {
display: block;
margin-top: 5px;
color: var(--error-text);
font-size: 11.5px;
}
.select-like {
position: relative;
}
.select-like select {
padding-right: 32px;
appearance: none;
}
.select-like > .select-chevron {
position: absolute;
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;
gap: 8px;
color: var(--text-disabled);
}
.search-control input {
width: 100%;
min-width: 0;
padding: 0;
background: transparent;
border: 0;
outline: 0;
}
.ui-switch {
width: 42px;
height: 24px;
display: flex;
align-items: center;
justify-content: flex-start;
flex: none;
padding: 2px;
background: var(--n-6);
border: 0;
border-radius: 14px;
transition: background 0.15s;
}
.ui-switch.on {
justify-content: flex-end;
background: var(--primary);
}
.ui-switch i {
width: 20px;
height: 20px;
display: block;
background: #fff;
border-radius: 50%;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
.ui-switch.is-large {
width: 46px;
height: 26px;
}
.ui-switch.is-large i {
width: 22px;
height: 22px;
}
.segmented {
display: inline-flex;
align-items: center;
gap: 3px;
padding: 3px;
background: #f0f2f5;
border-radius: var(--radius-md);
}
.segmented button {
height: 30px;
padding: 0 13px;
color: var(--text-secondary);
background: transparent;
border: 0;
border-radius: 6px;
font-size: 12.5px;
font-weight: 500;
}
.segmented button.active {
color: var(--text-heading);
background: #fff;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
font-weight: 600;
}