Add type picker for entity creation, icon grid filter, and serial port placeholder

- Replace inline type selectors with pre-modal type picker grid for devices,
  color strip sources, and value sources
- Add filterable search to icon grid when items > 9 (no auto-focus on touch)
- Show disabled (grayed-out) filtered items instead of hiding them
- Responsive grid columns (2-5 cols based on viewport width)
- Add "Select a port..." placeholder to serial port dropdown
- Update en/ru/zh locales with new keys

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-14 21:44:26 +03:00
parent 6395709bb8
commit a922c6e052
9 changed files with 233 additions and 16 deletions

View File

@@ -13,7 +13,7 @@ import { showToast, desktopFocus } from '../core/ui.js';
import { Modal } from '../core/modal.js';
import { _computeMaxFps, _renderFpsHint } from './devices.js';
import { getDeviceTypeIcon, ICON_RADIO, ICON_GLOBE, ICON_CPU, ICON_KEYBOARD, ICON_MOUSE, ICON_HEADPHONES, ICON_PLUG, ICON_TARGET_ICON, ICON_ACTIVITY } from '../core/icons.js';
import { IconSelect } from '../core/icon-select.js';
import { IconSelect, showTypePicker } from '../core/icon-select.js';
class AddDeviceModal extends Modal {
constructor() { super('add-device-modal'); }
@@ -262,6 +262,7 @@ export function onDeviceTypeChanged() {
opt.value = '';
opt.textContent = t('device.serial_port.hint') || 'Click to discover ports...';
opt.disabled = true;
opt.selected = true;
serialSelect.appendChild(opt);
}
updateBaudFpsHint();
@@ -328,6 +329,7 @@ export function onDeviceTypeChanged() {
opt.value = '';
opt.textContent = t('device.serial_port.hint') || 'Click to discover ports...';
opt.disabled = true;
opt.selected = true;
serialSelect.appendChild(opt);
}
} else if (isHueDevice(deviceType)) {
@@ -525,6 +527,14 @@ function _populateSerialPortDropdown(devices) {
return;
}
// Default placeholder
const placeholder = document.createElement('option');
placeholder.value = '';
placeholder.textContent = t('device.serial_port.select') || 'Select a port...';
placeholder.disabled = true;
placeholder.selected = true;
select.appendChild(placeholder);
devices.forEach(device => {
const opt = document.createElement('option');
opt.value = device.url;
@@ -544,7 +554,17 @@ export function onSerialPortFocus() {
}
}
export function showAddDevice() {
export function showAddDevice(presetType = null) {
// When no type specified: show type picker first
if (!presetType) {
showTypePicker({
title: t('device.select_type'),
items: _buildDeviceTypeItems(),
onPick: (type) => showAddDevice(type),
});
return;
}
const form = document.getElementById('add-device-form');
const error = document.getElementById('add-device-error');
form.reset();
@@ -563,6 +583,14 @@ export function showAddDevice() {
const scanBtn = document.getElementById('scan-network-btn');
if (scanBtn) scanBtn.disabled = false;
_ensureDeviceTypeIconSelect();
// Pre-select type and hide the type selector (already chosen)
document.getElementById('device-type').value = presetType;
document.getElementById('device-type-group').style.display = 'none';
const typeIcon = getDeviceTypeIcon(presetType);
const typeName = t(`device.type.${presetType}`);
document.getElementById('add-device-modal-title').innerHTML = `${typeIcon} ${t('devices.add')}: ${typeName}`;
addDeviceModal.open();
onDeviceTypeChanged();
setTimeout(() => {