Выбор образа при создании машины карточками: название системы, размер, дата загрузки и поиск, когда образов много
Этот коммит содержится в:
@@ -333,6 +333,25 @@ hr { border: 0; border-top: 1px solid var(--border); margin: 20px 0; }
|
||||
.option input:checked + .option-body .option-check { border: 5px solid var(--brand); }
|
||||
.option-title { font-weight: 650; font-size: 14px; }
|
||||
.option-text { color: var(--text-2); font-size: 12.5px; }
|
||||
/* Выбор образа в форме создания машины */
|
||||
.image-picker { display: grid; gap: 10px; }
|
||||
.image-list { display: grid; gap: 8px; max-height: 360px; overflow-y: auto; padding: 2px; }
|
||||
.image-option { position: relative; display: block; cursor: pointer; }
|
||||
.image-option[hidden] { display: none; }
|
||||
.image-option input { position: absolute; opacity: 0; pointer-events: none; }
|
||||
.image-option-body { display: flex; align-items: center; gap: 12px; padding: 10px 14px; border: 1.5px solid var(--border); border-radius: var(--radius); background: var(--surface); transition: border-color .15s, background .15s; }
|
||||
.image-option:hover .image-option-body { border-color: var(--border-strong); }
|
||||
.image-option input:checked + .image-option-body { border-color: var(--brand); background: color-mix(in srgb, var(--brand-soft) 55%, var(--surface)); box-shadow: 0 0 0 4px var(--focus); }
|
||||
.image-option input:focus-visible + .image-option-body { box-shadow: 0 0 0 4px var(--focus); }
|
||||
.image-option input:checked + .image-option-body .option-check { border: 5px solid var(--brand); }
|
||||
.image-option .grow { flex: 1; display: grid; gap: 2px; min-width: 0; }
|
||||
.image-title { font-weight: 650; font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.image-meta { color: var(--text-3); font-size: 12.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
@media (max-width: 560px) {
|
||||
.image-option .list-icon { display: none; }
|
||||
.image-title, .image-meta { white-space: normal; overflow-wrap: anywhere; }
|
||||
}
|
||||
|
||||
/* Расписание копий: день недели и список машин видны, только когда нужны (без :has — видны всегда). */
|
||||
.schedule-form:has(input[name=frequency][value=daily]:checked) [data-when=weekly],
|
||||
.schedule-form:has(input[name=machines][value=all]:checked) [data-when=chosen] { display: none; }
|
||||
|
||||
@@ -435,7 +435,8 @@
|
||||
const field = (name) => form.elements[name];
|
||||
const value = (name) => { const el = field(name); if (!el) return ''; if (el instanceof RadioNodeList) return el.value; return el.value; };
|
||||
const setSummary = (key, text) => $$(`[data-summary="${key}"]`).forEach((el) => { el.textContent = text || '—'; });
|
||||
const optionText = (select) => select && select.selectedIndex >= 0 ? select.options[select.selectedIndex].dataset.label || select.options[select.selectedIndex].text : '';
|
||||
// Образы выбираются карточками (радиокнопки): берём отмеченную.
|
||||
const picked = (name) => $(`input[name="${name}"]:checked`, form);
|
||||
|
||||
function sync() {
|
||||
const source = value('source_type');
|
||||
@@ -454,9 +455,7 @@
|
||||
// Default login of the chosen cloud image (ubuntu, debian, …) until the user types a name.
|
||||
const login = field('ci_user');
|
||||
if (login && login.dataset.autoLogin === '1') {
|
||||
const image = field('disk_image_path');
|
||||
const option = image && image.selectedIndex >= 0 ? image.options[image.selectedIndex] : null;
|
||||
login.value = option?.dataset.login || 'admin';
|
||||
login.value = picked('disk_image_path')?.dataset.login || 'admin';
|
||||
}
|
||||
const preset = value('preset');
|
||||
const custom = preset === 'custom';
|
||||
@@ -469,7 +468,7 @@
|
||||
$$('[data-custom-resources] input', form).forEach((input) => { input.readOnly = !custom; });
|
||||
const memory = Number(value('memory')) || 0;
|
||||
setSummary('name', value('name'));
|
||||
setSummary('source', source === 'disk_image' ? optionText(field('disk_image_path')) : optionText(field('iso_path')));
|
||||
setSummary('source', picked(source === 'disk_image' ? 'disk_image_path' : 'iso_path')?.dataset.label || '');
|
||||
setSummary('cpu', `${value('vcpus')} ${Number(value('vcpus')) === 1 ? 'ядро' : 'ядра'}`);
|
||||
setSummary('memory', memory >= 1024 ? `${+(memory / 1024).toFixed(1)} ГБ` : `${memory} МБ`);
|
||||
setSummary('disk', `${value('disk_size')} ГБ`);
|
||||
@@ -480,9 +479,7 @@
|
||||
}
|
||||
// Установщик Windows панель узнаёт по ISO (data-os) и сама выбирает тип, пока пользователь не выбрал его руками.
|
||||
const guessOs = () => {
|
||||
const select = field('iso_path');
|
||||
const option = select && select.selectedIndex >= 0 ? select.options[select.selectedIndex] : null;
|
||||
const guess = option?.dataset.os || '';
|
||||
const guess = picked('iso_path')?.dataset.os || '';
|
||||
const hint = $('[data-os-hint]', form);
|
||||
if (form.dataset.osManual) return;
|
||||
const radio = $(`input[name=os_type][value="${guess || 'linux'}"]`, form);
|
||||
@@ -495,6 +492,17 @@
|
||||
if (event.target.name === 'iso_path') guessOs();
|
||||
sync();
|
||||
});
|
||||
// Поиск по образам, когда их много.
|
||||
$$('[data-image-picker]', form).forEach((picker) => {
|
||||
const search = $('[data-image-search]', picker);
|
||||
if (!search) return;
|
||||
search.addEventListener('input', () => {
|
||||
const query = search.value.trim().toLowerCase();
|
||||
let shown = 0;
|
||||
$$('.image-option', picker).forEach((item) => { item.hidden = Boolean(query) && !item.dataset.search.includes(query); shown += item.hidden ? 0 : 1; });
|
||||
$('[data-image-empty]', picker).hidden = shown > 0;
|
||||
});
|
||||
});
|
||||
guessOs();
|
||||
sync();
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user